SDK ยท GiL v1

@human-exe/gil-sdk

The TypeScript client for the Human.Exe Governance API. One key, one client: every request is governed and audited, and a conversation can carry memory from one call to the next. The SDK signs requests for you, so your code stays a single method call. It runs anywhere that has fetch and Web Crypto โ€” Node 20+, Deno, Bun, Cloudflare Workers, Vercel Edge.

The SDK is the recommended way to integrate. It is a normal npm package with a normal client class. It never writes instruction files into your workspace, and nothing is added to your prompts.

INSTALL
npm install @human-exe/gil-sdk
# or: pnpm add @human-exe/gil-sdk
# or: yarn add @human-exe/gil-sdk

Ships compiled JavaScript with TypeScript types. No runtime dependencies.

Quickstart

Create a key in your dashboard, put it in an environment variable, install the package, and make a call. Every account can do this on the free Basic plan.

Paste into Claude Code, Cursor, GitHub Copilot Chat, or any coding agent โ€” it wires up the SDK in your project without ever asking you to paste a live key into chat.

# 1. Create a key at https://human-exe.ca/dashboard/keys  (the secret is shown once)
# 2. Keep it in your environment or secrets manager
export GIL_API_KEY=gil_<80 hex characters>

# 3. Install
npm install @human-exe/gil-sdk
import { GiLClient } from '@human-exe/gil-sdk';

const gil = new GiLClient({ apiKey: process.env.GIL_API_KEY! });

const res = await gil.analyze({ prompt: 'Summarize the open decisions for this week.' });

console.log(res.response);          // the governed response
console.log(res.audit.state);       // 'green' | 'amber' | 'red' | 'black'
console.log(res.sessionId);         // pass this back to continue the conversation

Never paste your GIL_API_KEY into a chat prompt, into a file an AI agent reads, or into any model context. It is a service credential, not part of the conversation.

Sessions & memory

Pass the sessionId from one response into the next call and GiL keeps the exchange with that session. Memory is recorded for calls that carry a sessionId, so the very first call of a conversation is not stored; everything after it is.

const first = await gil.analyze({ prompt: 'We are reviewing the Q3 plan.' });

const next = await gil.analyze({
  prompt: 'What did we just decide?',
  sessionId: first.sessionId,         // continues the same conversation
});

console.log(next.audit.sessionMemoryActive);   // true

A session keeps the most recent 32 messages. Memory stores the text of the exchange so later calls can use it as context; audit records store hashes only.

Your own model (optional)

You do not need a model-provider account to use GiL. If you do want your calls answered by your own Anthropic, OpenAI or Google model, add provider, providerKey and model to the same call. Your provider key is used for that call only and is not stored.

const res = await gil.analyze({
  prompt: 'Draft the summary.',
  provider: 'anthropic',
  providerKey: process.env.ANTHROPIC_API_KEY!,
  model: 'claude-opus-4-5',
});

console.log(res.stub);     // false when your provider answered

Without a provider key, stub is true: GiL returns its built-in governed response and still records the audit trail, usage and (with a session) memory.

Client options

new GiLClient({
  apiKey:   string,           // required โ€” your key (gil_ followed by 80 hex characters)
  baseUrl?: string,           // default: https://human-exe.ca
  fetch?:   typeof fetch,     // default: globalThis.fetch โ€” inject your own for tests
});

The SDK reads no environment variables itself; you pass everything to the constructor. The key itself is never sent in a URL or logged. client.publicKeyId gives you a safe-to-log identifier for the key.

gil.analyze()

Send one governed request. Only prompt is required.

FieldTypeMeaning
promptstringRequired. The text to govern.
sessionIdstringContinue a conversation. Use the sessionId from an earlier response.
provider'anthropic' | 'openai' | 'google' | 'local'Optional. Answer with your own model.
providerKeystringOptional. Your key for that provider.
modelstringOptional. For example claude-opus-4-5.
temperature, maxTokensnumberOptional model settings.

What you get back:

FieldMeaning
responseThe governed response text.
sessionIdPass back on the next call to keep context.
stubtrue when no provider key was supplied and GiL answered itself.
usagegovernanceUnits, providerTokensIn, providerTokensOut.
audit.auditId, audit.timestampThe audit record for this call.
audit.stateGovernance state: green, amber, red or black.
audit.decisionChainThe ordered steps the request went through.
audit.provenanceSignatureA signature over the call's record, or null.
audit.coherencyScore, audit.driftScoreGovernance signals for the call.
audit.sessionMemoryActiveWhether this call used session memory.
advisoriesNotes from the pipeline, as { kind, message } entries.

gil.usage()

Per-day totals for your account across all of your keys. Dates are inclusive and UTC.

const u = await gil.usage({ from: '2026-10-01', to: '2026-10-31' });

// u.days:  [{ date, governanceUnits, providerTokensIn, providerTokensOut }]
// u.total: { governanceUnits, providerTokensIn, providerTokensOut }

gil.reportIssue()

Report a problem or observation about GiL itself: an SDK bug, unexpected latency, a governance result that looks wrong, or a documentation gap. If you asked your human user whether a response looked right, forward their answer in userFeedback.

await gil.reportIssue({
  severity: 'medium',            // 'low' | 'medium' | 'high' | 'critical'
  category: 'incorrect-governance',
  message:  'Two near-identical prompts returned different governance states.',
  context:  { requestId: 'req_...', model: 'claude-opus-4-5' },
  userFeedback: 'The user said the second response looked ungoverned.',   // optional
});

Reports are stored against your account for triage and are never shown publicly. It throws GiLError on a non-2xx response, like every other method.

withMeta variants

analyzeWithMeta, usageWithMeta and reportIssueWithMeta also return the response headers when you want to log or react to them.

const { data, meta } = await gil.analyzeWithMeta({ prompt: 'Hello.' });

meta.governanceState;     // 'green' | 'amber' | 'red' | 'black' | null
meta.requestId;           // include this when you contact support
meta.governanceAdvisory;  // short note when the state is not green

Error handling

A non-2xx response throws GiLError. A failed network call throws a GiLError with code gil.client.network. Mistakes the SDK can see before sending (a missing prompt, a malformed key) throw straight away.

import { GiLClient, GiLError } from '@human-exe/gil-sdk';

try {
  await gil.analyze({ prompt: 'Hello.' });
} catch (err) {
  if (err instanceof GiLError) {
    console.error(err.code, err.httpStatus, err.requestId, err.message);
  } else {
    throw err;
  }
}
CodeHTTPMeaning
gil.unauthorized401Missing Authorization, or the key is not valid.
gil.invalid_signature401The request signature does not match.
gil.invalid_timestamp401Timestamp missing, malformed, or more than 5 minutes from server time. Check your clock.
gil.replay_detected401That exact signed request was already received. The SDK never repeats a timestamp, so this only appears when signing by hand.
gil.key_revoked401The key was revoked or rotated. Create a new key from your dashboard.
gil.key_expired401The key has passed its expiry date. Create a new key.
MISSING_PROMPT400The request had no prompt.
INVALID_BODY400The body was not valid JSON.
gil.provider.failed502Your model provider returned an error.
gil.governance.unavailable502 / 503The service was unreachable. Safe to retry shortly.
gil.client.invalid_requestโ€”Raised by the SDK before sending (for example, no prompt).
gil.client.invalid_keyโ€”The key is not in the expected format.
gil.client.networkโ€”The network call failed.

The service can add new codes over time. Treat an unfamiliar code as a generic failure and use httpStatus to decide whether to retry.

Environment variables

These names are conventions for your own application. The SDK itself reads none of them.

VariablePurpose
GIL_API_KEYYour key.
ANTHROPIC_API_KEY, OPENAI_API_KEY, GOOGLE_API_KEYOnly if you answer calls with your own model (see above).
NOT USING TYPESCRIPT?

The API is plain HTTPS with a signed request, so any language can call it. The REST reference shows the signing steps and the raw request and response formats.