Skip to content

TypeScript

Terminal window
npm install @vectadyne/sdk

Browser or Node. Same seven beats as Go and Python.

Read this before you start, so nothing is a surprise three files in:

GoPythonTypeScript
Typed verbs, governance interpretation
Retries, Retry-After, typed errors
Compile-time request validationpartialpartialbest here
Context meter❌ use callTool
Testing fake control plane
Auth module / credential exchange❌ wire it yourself
Environment facadeFromEnv()from_env()❌ read process.env

The gaps are real and are listed rather than discovered. ingest and report go through client.callTool with the same Zod-validated shapes the typed verbs use, so you lose a convenience method, not a capability.

The win is the first row. Request shapes are z.strictObject, so a misplaced key is caught locally, at compile time, with the path — instead of becoming a validation fault one round trip away:

await vd.recall("reconcile failures", { top_k: 5 });
// ~~~~~ error: 'top_k' does not exist in type
// SearchOptions. Did you mean 'ranking.top_k'?

That mistake is a runtime round trip in the other two languages.

There is no environment facade, so this is the wiring you write:

import { Vectadyne, VectadyneMCPClient } from "@vectadyne/sdk";
const mcpUrl = process.env.VECTADYNE_MCP_URL;
const token = process.env.VECTADYNE_TOKEN;
if (!mcpUrl || !token) throw new Error("VECTADYNE_MCP_URL and VECTADYNE_TOKEN are required");
const client = new VectadyneMCPClient({ mcpUrl, token, app: "release-bot" });
const vd = new Vectadyne(client);
await client.discover(); // no credential needed — proves reachability

discover() takes no token, so a failure there is a network problem and nothing else.

await vd.remember(
"The nightly reconcile job fails when billing/reconcile.py is edited without regenerating fixtures.",
{ kind: "lesson", scope_key: "repo:payments" },
);
const hits = await vd.recall("reconcile job failures");
for (const h of hits) console.log(h.content, h.memory_id);

Exactly one of content or messages. Enforced by the type, so the compiler catches it.

An empty recall is a legitimate answer — admission filtering happens at read time.

const assembled = await vd.assemble({
action: {
action_type: "code.apply_patch",
summary: "Rewrite the reconcile loop",
changed_files: ["billing/reconcile.py"],
scope_key: "repo:payments",
},
purpose: "refactor",
});

action_type and purpose are union types, so an invalid value is a compile error rather than a validation fault. Every closed vocabulary in the reference is a union here.

The polarity is inverted between our SDKs. TypeScript matches Go, not Python:

SDKFieldtrue means
GoGuidance.Holdstop
TypeScriptguidance.holdstop
PythonGovernanceDecision.proceedgo
import { interpretContext } from "@vectadyne/sdk";
const g = interpretContext(assembled);
if (g.hold) {
console.warn("holding:", g.reason);
return;
}
for (const a of g.advisories) console.info("advisory:", a.message);
  • A warn is never a hold.
  • An unknown decision sets unknown and holds. Fails closed, like the other two.
import { AuthError, ValidationError, RateLimitedError, TransportError } from "@vectadyne/sdk";
try {
await vd.assemble({ /* … */ });
} catch (e) {
if (e instanceof AuthError) { /* refresh the credential; do not loop */ }
else if (e instanceof ValidationError) { /* fix the call site */ }
else if (e instanceof RateLimitedError) { /* e.retryAfter */ }
else if (e instanceof TransportError) { /* the platform did NOT answer */ }
else throw e;
}

“The platform said no” is not “the platform did not answer.” The SDK never synthesizes a verdict. See Handle errors.

No convenience wrappers here, so use callTool — same validated shapes:

await client.callTool("evidence.ingest", {
agent: "release-bot",
items: [{
source_type: "pr",
summary: "Reconcile loop rewritten; fixtures regenerated",
scope_key: "repo:payments",
}],
});
await client.callTool("context.report", {
agent: "release-bot",
window_total: 128_000,
segments: { system_prompt: 1_200, retrieved_memory: 4_800 },
});

There is no meter to accumulate segments for you, so build the segments object yourself. Omit a label you did not measure — an absent label means “not reported”, and sending 0 claims you measured zero. That distinction is the difference between a gap in your instrumentation and a finding.

There is no bundled fake, so stub fetch or point at any HTTP mock and assert on requests:

const calls: string[] = [];
globalThis.fetch = async (_url, init) => {
const body = JSON.parse(String(init?.body));
calls.push(body.params?.name ?? body.method);
return new Response(JSON.stringify({ /* your canned verdict */ }), { status: 200 });
};
// … run your agent …
expect(calls).toContain("preflight.action"); // it asked
expect(calls.length).toBe(1); // and did not retry a refusal

Assert the count. A block must produce exactly one request.