@orangecheck/agent-vercel 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +7 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +15 -15
package/README.md
CHANGED
|
@@ -91,4 +91,4 @@ MIT.
|
|
|
91
91
|
- [oc-agent-protocol](https://github.com/orangecheck/oc-agent-protocol) — normative wire format.
|
|
92
92
|
- [@orangecheck/agent-core](https://www.npmjs.com/package/@orangecheck/agent-core) — verifier library.
|
|
93
93
|
- [@orangecheck/agent-mcp](https://www.npmjs.com/package/@orangecheck/agent-mcp) / [-anthropic](https://www.npmjs.com/package/@orangecheck/agent-anthropic) / [-openai](https://www.npmjs.com/package/@orangecheck/agent-openai) — sibling adapters.
|
|
94
|
-
- [
|
|
94
|
+
- [fleet.ochk.io/integrations/vercel-ai](https://fleet.ochk.io/integrations/vercel-ai) — managed-tier story.
|
package/dist/index.d.mts
CHANGED
|
@@ -31,7 +31,7 @@ interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {
|
|
|
31
31
|
description?: string;
|
|
32
32
|
execute: (args: TArgs, ctx: AgentContext & {
|
|
33
33
|
callId: string;
|
|
34
|
-
|
|
34
|
+
fleet?: FleetClient;
|
|
35
35
|
}) => Promise<{
|
|
36
36
|
result: TResult;
|
|
37
37
|
action: ActionEnvelope;
|
|
@@ -39,7 +39,7 @@ interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {
|
|
|
39
39
|
}>;
|
|
40
40
|
}
|
|
41
41
|
declare function ocTool<TArgs extends Record<string, unknown>, TResult>(input: OcToolInput<TArgs, TResult>): OcToolWrapped<TArgs, TResult>;
|
|
42
|
-
interface
|
|
42
|
+
interface FleetClient {
|
|
43
43
|
baseUrl?: string;
|
|
44
44
|
apiToken: string;
|
|
45
45
|
projectId: string;
|
|
@@ -50,6 +50,6 @@ interface PostActionResult {
|
|
|
50
50
|
project_id: string;
|
|
51
51
|
delegation_id: string;
|
|
52
52
|
}
|
|
53
|
-
declare function
|
|
53
|
+
declare function postActionToFleet(action: ActionEnvelope, client: FleetClient): Promise<PostActionResult>;
|
|
54
54
|
|
|
55
|
-
export { type AgentContext, type
|
|
55
|
+
export { type AgentContext, type FleetClient, type OcToolInput, type OcToolWrapped, type PostActionResult, type StampToolCallInput, type VercelToolCall, canonicalizeToolCall, ocTool, postActionToFleet, stampToolCall, toolCallActionId, toolCallHash };
|
package/dist/index.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {
|
|
|
31
31
|
description?: string;
|
|
32
32
|
execute: (args: TArgs, ctx: AgentContext & {
|
|
33
33
|
callId: string;
|
|
34
|
-
|
|
34
|
+
fleet?: FleetClient;
|
|
35
35
|
}) => Promise<{
|
|
36
36
|
result: TResult;
|
|
37
37
|
action: ActionEnvelope;
|
|
@@ -39,7 +39,7 @@ interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {
|
|
|
39
39
|
}>;
|
|
40
40
|
}
|
|
41
41
|
declare function ocTool<TArgs extends Record<string, unknown>, TResult>(input: OcToolInput<TArgs, TResult>): OcToolWrapped<TArgs, TResult>;
|
|
42
|
-
interface
|
|
42
|
+
interface FleetClient {
|
|
43
43
|
baseUrl?: string;
|
|
44
44
|
apiToken: string;
|
|
45
45
|
projectId: string;
|
|
@@ -50,6 +50,6 @@ interface PostActionResult {
|
|
|
50
50
|
project_id: string;
|
|
51
51
|
delegation_id: string;
|
|
52
52
|
}
|
|
53
|
-
declare function
|
|
53
|
+
declare function postActionToFleet(action: ActionEnvelope, client: FleetClient): Promise<PostActionResult>;
|
|
54
54
|
|
|
55
|
-
export { type AgentContext, type
|
|
55
|
+
export { type AgentContext, type FleetClient, type OcToolInput, type OcToolWrapped, type PostActionResult, type StampToolCallInput, type VercelToolCall, canonicalizeToolCall, ocTool, postActionToFleet, stampToolCall, toolCallActionId, toolCallHash };
|
package/dist/index.js
CHANGED
|
@@ -65,19 +65,19 @@ function ocTool(input) {
|
|
|
65
65
|
});
|
|
66
66
|
const result = await input.execute(args);
|
|
67
67
|
let posted = null;
|
|
68
|
-
if (ctx.
|
|
68
|
+
if (ctx.fleet) {
|
|
69
69
|
try {
|
|
70
|
-
posted = await
|
|
70
|
+
posted = await postActionToFleet(action, ctx.fleet);
|
|
71
71
|
} catch (err) {
|
|
72
|
-
console.error("[oc-agent-vercel]
|
|
72
|
+
console.error("[oc-agent-vercel] postActionToFleet failed:", err);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
return { result, action, posted };
|
|
76
76
|
}
|
|
77
77
|
};
|
|
78
78
|
}
|
|
79
|
-
async function
|
|
80
|
-
const baseUrl = client.baseUrl ?? "https://
|
|
79
|
+
async function postActionToFleet(action, client) {
|
|
80
|
+
const baseUrl = client.baseUrl ?? "https://fleet.ochk.io";
|
|
81
81
|
const f = client.fetch ?? fetch;
|
|
82
82
|
const body = {
|
|
83
83
|
project_id: client.projectId,
|
|
@@ -106,7 +106,7 @@ async function postActionToConsole(action, client) {
|
|
|
106
106
|
if (j2.reason) reason = j2.reason;
|
|
107
107
|
} catch {
|
|
108
108
|
}
|
|
109
|
-
throw new Error(`
|
|
109
|
+
throw new Error(`postActionToFleet failed: ${reason}`);
|
|
110
110
|
}
|
|
111
111
|
const j = await r.json();
|
|
112
112
|
return j.action;
|
|
@@ -114,7 +114,7 @@ async function postActionToConsole(action, client) {
|
|
|
114
114
|
|
|
115
115
|
exports.canonicalizeToolCall = canonicalizeToolCall;
|
|
116
116
|
exports.ocTool = ocTool;
|
|
117
|
-
exports.
|
|
117
|
+
exports.postActionToFleet = postActionToFleet;
|
|
118
118
|
exports.stampToolCall = stampToolCall;
|
|
119
119
|
exports.toolCallActionId = toolCallActionId;
|
|
120
120
|
exports.toolCallHash = toolCallHash;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["canonicalize","hexEncode","sha256","computeActionId","parseScope","isSubScope","signAsAgent","j"],"mappings":"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","file":"index.js","sourcesContent":["// @orangecheck/agent-vercel — wrap Vercel AI SDK `tool()` invocations in\n// OC Agent action envelopes.\n//\n// Provider-agnostic: the AI SDK is the abstraction layer. Whatever model\n// you use underneath (Anthropic, OpenAI, Cohere, …) the tool primitive is\n// the same {description, parameters, execute} shape. This adapter wraps\n// `execute` so every successful call produces a signed agent-action\n// envelope citing the active delegation.\n//\n// Two integration shapes:\n//\n// 1. ocTool({verb, parameters, execute})\n// A drop-in replacement for `tool()`. The wrapped execute runs the\n// scope check, signs the canonical (verb, args, callId) tuple,\n// then runs your real handler.\n//\n// 2. stampToolCall(input)\n// Lower-level stamping helper for cases where you have your own\n// tool-execution loop and want to emit envelopes manually.\n//\n// The agent + delegation are passed via a per-request context object\n// (or threaded through closures) — we don't depend on AsyncLocalStorage\n// to keep edge-runtime compatibility maximal.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalize,\n computeActionId,\n hexEncode,\n isSubScope,\n parseScope,\n type DelegationEnvelope,\n} from '@orangecheck/agent-core';\nimport {\n signAsAgent,\n type ActionEnvelope,\n type SignerRef,\n} from '@orangecheck/agent-signer';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalization of a tool call\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface VercelToolCall {\n /** Unique id assigned by the AI SDK for this tool invocation. */\n callId: string;\n /** Tool verb / name — passed in via ocTool() or set per-call by your wrapper. */\n verb: string;\n /** JSON-serializable arguments produced by the model. */\n args: Record<string, unknown>;\n}\n\n/**\n * Produce the canonical bytes for a Vercel AI SDK tool call.\n *\n * Shape (RFC 8785 canonical JSON, then trailing LF):\n * {\n * \"args\": <canonical JSON of args>,\n * \"call_id\": <callId>,\n * \"verb\": <verb>\n * }\n */\nexport function canonicalizeToolCall(c: VercelToolCall): Uint8Array {\n const canon = {\n args: c.args,\n call_id: c.callId,\n verb: c.verb,\n };\n const str = canonicalize(canon as unknown as Parameters<typeof canonicalize>[0]);\n return new TextEncoder().encode(str + '\\n');\n}\n\nexport function toolCallHash(c: VercelToolCall): string {\n return 'sha256:' + hexEncode(sha256(canonicalizeToolCall(c)));\n}\n\nexport function toolCallActionId(\n agentAddress: string,\n signedAt: string,\n delegationId: string,\n scopeExercised: string,\n c: VercelToolCall\n): string {\n return computeActionId({\n address: agentAddress,\n content_hash: toolCallHash(c),\n content_length: canonicalizeToolCall(c).byteLength,\n content_mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n signed_at: signedAt,\n delegation_id: delegationId,\n scope_exercised: scopeExercised,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stamping primitive\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AgentContext {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n}\n\nexport interface StampToolCallInput extends AgentContext {\n call: VercelToolCall;\n /** Defaults to `vercel:tool(verb=<verb>)` — the tightest admissible sub-scope. */\n scopeExercised?: string;\n signedAt?: Date;\n}\n\nexport async function stampToolCall(input: StampToolCallInput): Promise<ActionEnvelope> {\n const scopeExercised =\n input.scopeExercised ?? `vercel:tool(verb=${input.call.verb})`;\n\n const granted = (input.delegation.scopes ?? []).map(parseScope);\n const exercisedParsed = parseScope(scopeExercised);\n if (!granted.some((g) => isSubScope(exercisedParsed, g))) {\n throw new Error(\n `stampToolCall: scope_exercised (${scopeExercised}) is not a sub-scope of any granted scope`\n );\n }\n\n const bytes = canonicalizeToolCall(input.call);\n return signAsAgent({\n agent: input.agent,\n delegation: input.delegation as never,\n content: { hash: toolCallHash(input.call), length: bytes.byteLength },\n mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n scopeExercised,\n signedAt: input.signedAt,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ocTool — drop-in tool() replacement\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface OcToolInput<TArgs extends Record<string, unknown>, TResult> {\n /** OC Agent verb under which this tool is exercised. */\n verb: string;\n /** Pass-through to AI SDK's `tool()` — the JSON Schema for inputs. */\n parameters?: unknown;\n /** Pass-through description shown to the model. */\n description?: string;\n /** Your real handler. Returns whatever the tool returns. */\n execute: (args: TArgs) => Promise<TResult>;\n}\n\nexport interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {\n verb: string;\n parameters?: unknown;\n description?: string;\n /**\n * The execute fn the AI SDK calls. Receives args + a callId (the AI\n * SDK passes its own callId through — adapter glue passes it via\n * context). Optional `console` field on the context tells the\n * wrapper to fire-and-forget POST the stamped action to console.\n * ochk.io/api/actions after the underlying execute() returns.\n */\n execute: (\n args: TArgs,\n ctx: AgentContext & { callId: string; console?: ConsoleClient }\n ) => Promise<{\n result: TResult;\n action: ActionEnvelope;\n posted: PostActionResult | null;\n }>;\n}\n\n/**\n * Wrap a tool() definition so its execute path is scope-checked and\n * envelope-emitting. The exact glue depends on which AI-SDK release you're\n * on — the typical pattern is:\n *\n * const tools = {\n * 'invoice.create': tool({\n * description: '…',\n * parameters: schema,\n * execute: async (args, { toolCallId }) => {\n * const { result, action } = await invoiceCreate.execute(args, {\n * agent, delegation, callId: toolCallId,\n * });\n * await yourAuditPipeline.append(action);\n * return result;\n * },\n * }),\n * };\n *\n * const invoiceCreate = ocTool({\n * verb: 'invoice.create',\n * parameters: schema,\n * execute: async (args) => myInvoiceCreateImpl(args),\n * });\n *\n * The `ocTool` value is provider/SDK-agnostic — bring your own glue around\n * it. v1 of the adapter intentionally does not import `ai` so it works on\n * any release.\n */\nexport function ocTool<TArgs extends Record<string, unknown>, TResult>(\n input: OcToolInput<TArgs, TResult>\n): OcToolWrapped<TArgs, TResult> {\n return {\n verb: input.verb,\n parameters: input.parameters,\n description: input.description,\n execute: async (args, ctx) => {\n const call: VercelToolCall = {\n callId: ctx.callId,\n verb: input.verb,\n args,\n };\n const action = await stampToolCall({\n agent: ctx.agent,\n delegation: ctx.delegation,\n call,\n });\n const result = await input.execute(args);\n let posted: PostActionResult | null = null;\n if (ctx.console) {\n try {\n posted = await postActionToConsole(action, ctx.console);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('[oc-agent-vercel] postActionToConsole failed:', err);\n }\n }\n return { result, action, posted };\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Console integration: POST stamped actions to console.ochk.io/api/actions\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ConsoleClient {\n /** Defaults to https://console.ochk.io. */\n baseUrl?: string;\n /** Bearer token from /settings § 03 (`ock_<hex>`). */\n apiToken: string;\n /** Project the action belongs to (proj_*). */\n projectId: string;\n /** Optional fetch override for runtimes that need it. */\n fetch?: typeof fetch;\n}\n\nexport interface PostActionResult {\n id: string;\n project_id: string;\n delegation_id: string;\n}\n\n/**\n * POST a stamped action envelope to console.ochk.io/api/actions. The\n * console re-derives the action id, validates agent-must-match-\n * delegation, persists, fans out to Nostr (kind 30084), submits to\n * OC Stamp, and triggers any subscribed webhooks. Throws on non-2xx\n * with the server's reason string.\n */\nexport async function postActionToConsole(\n action: ActionEnvelope,\n client: ConsoleClient\n): Promise<PostActionResult> {\n const baseUrl = client.baseUrl ?? 'https://console.ochk.io';\n const f = client.fetch ?? fetch;\n const body = {\n project_id: client.projectId,\n delegation_id: action.delegation_id,\n agent_address: action.signer.address,\n scope_exercised: action.scope_exercised,\n content_hash: action.content.hash,\n content_length: action.content.length,\n content_mime: action.content.mime,\n signed_at: action.signed_at,\n signature: action.sig.value,\n id: action.id,\n };\n const r = await f(`${baseUrl}/api/actions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${client.apiToken}`,\n },\n body: JSON.stringify(body),\n });\n if (!r.ok) {\n let reason = `http_${r.status}`;\n try {\n const j = (await r.json()) as { reason?: string };\n if (j.reason) reason = j.reason;\n } catch {\n // body wasn't json\n }\n throw new Error(`postActionToConsole failed: ${reason}`);\n }\n const j = (await r.json()) as { ok: true; action: PostActionResult };\n return j.action;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["canonicalize","hexEncode","sha256","computeActionId","parseScope","isSubScope","signAsAgent","j"],"mappings":";;;;;;;AA8DO,SAAS,qBAAqB,CAAA,EAA+B;AAChE,EAAA,MAAM,KAAA,GAAQ;AAAA,IACV,MAAM,CAAA,CAAE,IAAA;AAAA,IACR,SAAS,CAAA,CAAE,MAAA;AAAA,IACX,MAAM,CAAA,CAAE;AAAA,GACZ;AACA,EAAA,MAAM,GAAA,GAAMA,uBAAa,KAAsD,CAAA;AAC/E,EAAA,OAAO,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,MAAM,IAAI,CAAA;AAC9C;AAEO,SAAS,aAAa,CAAA,EAA2B;AACpD,EAAA,OAAO,YAAYC,mBAAA,CAAUC,aAAA,CAAO,oBAAA,CAAqB,CAAC,CAAC,CAAC,CAAA;AAChE;AAEO,SAAS,gBAAA,CACZ,YAAA,EACA,QAAA,EACA,YAAA,EACA,gBACA,CAAA,EACM;AACN,EAAA,OAAOC,yBAAA,CAAgB;AAAA,IACnB,OAAA,EAAS,YAAA;AAAA,IACT,YAAA,EAAc,aAAa,CAAC,CAAA;AAAA,IAC5B,cAAA,EAAgB,oBAAA,CAAqB,CAAC,CAAA,CAAE,UAAA;AAAA,IACxC,YAAA,EAAc,gDAAA;AAAA,IACd,SAAA,EAAW,QAAA;AAAA,IACX,aAAA,EAAe,YAAA;AAAA,IACf,eAAA,EAAiB;AAAA,GACpB,CAAA;AACL;AAkBA,eAAsB,cAAc,KAAA,EAAoD;AACpF,EAAA,MAAM,iBACF,KAAA,CAAM,cAAA,IAAkB,CAAA,iBAAA,EAAoB,KAAA,CAAM,KAAK,IAAI,CAAA,CAAA,CAAA;AAE/D,EAAA,MAAM,WAAW,KAAA,CAAM,UAAA,CAAW,UAAU,EAAC,EAAG,IAAIC,oBAAU,CAAA;AAC9D,EAAA,MAAM,eAAA,GAAkBA,qBAAW,cAAc,CAAA;AACjD,EAAA,IAAI,CAAC,QAAQ,IAAA,CAAK,CAAC,MAAMC,oBAAA,CAAW,eAAA,EAAiB,CAAC,CAAC,CAAA,EAAG;AACtD,IAAA,MAAM,IAAI,KAAA;AAAA,MACN,mCAAmC,cAAc,CAAA,yCAAA;AAAA,KACrD;AAAA,EACJ;AAEA,EAAA,MAAM,KAAA,GAAQ,oBAAA,CAAqB,KAAA,CAAM,IAAI,CAAA;AAC7C,EAAA,OAAOC,uBAAA,CAAY;AAAA,IACf,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,YAAY,KAAA,CAAM,UAAA;AAAA,IAClB,OAAA,EAAS,EAAE,IAAA,EAAM,YAAA,CAAa,MAAM,IAAI,CAAA,EAAG,MAAA,EAAQ,KAAA,CAAM,UAAA,EAAW;AAAA,IACpE,IAAA,EAAM,gDAAA;AAAA,IACN,cAAA;AAAA,IACA,UAAU,KAAA,CAAM;AAAA,GACnB,CAAA;AACL;AAmEO,SAAS,OACZ,KAAA,EAC6B;AAC7B,EAAA,OAAO;AAAA,IACH,MAAM,KAAA,CAAM,IAAA;AAAA,IACZ,YAAY,KAAA,CAAM,UAAA;AAAA,IAClB,aAAa,KAAA,CAAM,WAAA;AAAA,IACnB,OAAA,EAAS,OAAO,IAAA,EAAM,GAAA,KAAQ;AAC1B,MAAA,MAAM,IAAA,GAAuB;AAAA,QACzB,QAAQ,GAAA,CAAI,MAAA;AAAA,QACZ,MAAM,KAAA,CAAM,IAAA;AAAA,QACZ;AAAA,OACJ;AACA,MAAA,MAAM,MAAA,GAAS,MAAM,aAAA,CAAc;AAAA,QAC/B,OAAO,GAAA,CAAI,KAAA;AAAA,QACX,YAAY,GAAA,CAAI,UAAA;AAAA,QAChB;AAAA,OACH,CAAA;AACD,MAAA,MAAM,MAAA,GAAS,MAAM,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA;AACvC,MAAA,IAAI,MAAA,GAAkC,IAAA;AACtC,MAAA,IAAI,IAAI,KAAA,EAAO;AACX,QAAA,IAAI;AACA,UAAA,MAAA,GAAS,MAAM,iBAAA,CAAkB,MAAA,EAAQ,GAAA,CAAI,KAAK,CAAA;AAAA,QACtD,SAAS,GAAA,EAAK;AAEV,UAAA,OAAA,CAAQ,KAAA,CAAM,+CAA+C,GAAG,CAAA;AAAA,QACpE;AAAA,MACJ;AACA,MAAA,OAAO,EAAE,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO;AAAA,IACpC;AAAA,GACJ;AACJ;AA8BA,eAAsB,iBAAA,CAClB,QACA,MAAA,EACyB;AACzB,EAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,uBAAA;AAClC,EAAA,MAAM,CAAA,GAAI,OAAO,KAAA,IAAS,KAAA;AAC1B,EAAA,MAAM,IAAA,GAAO;AAAA,IACT,YAAY,MAAA,CAAO,SAAA;AAAA,IACnB,eAAe,MAAA,CAAO,aAAA;AAAA,IACtB,aAAA,EAAe,OAAO,MAAA,CAAO,OAAA;AAAA,IAC7B,iBAAiB,MAAA,CAAO,eAAA;AAAA,IACxB,YAAA,EAAc,OAAO,OAAA,CAAQ,IAAA;AAAA,IAC7B,cAAA,EAAgB,OAAO,OAAA,CAAQ,MAAA;AAAA,IAC/B,YAAA,EAAc,OAAO,OAAA,CAAQ,IAAA;AAAA,IAC7B,WAAW,MAAA,CAAO,SAAA;AAAA,IAClB,SAAA,EAAW,OAAO,GAAA,CAAI,KAAA;AAAA,IACtB,IAAI,MAAA,CAAO;AAAA,GACf;AACA,EAAA,MAAM,CAAA,GAAI,MAAM,CAAA,CAAE,CAAA,EAAG,OAAO,CAAA,YAAA,CAAA,EAAgB;AAAA,IACxC,MAAA,EAAQ,MAAA;AAAA,IACR,OAAA,EAAS;AAAA,MACL,cAAA,EAAgB,kBAAA;AAAA,MAChB,aAAA,EAAe,CAAA,OAAA,EAAU,MAAA,CAAO,QAAQ,CAAA;AAAA,KAC5C;AAAA,IACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI;AAAA,GAC5B,CAAA;AACD,EAAA,IAAI,CAAC,EAAE,EAAA,EAAI;AACP,IAAA,IAAI,MAAA,GAAS,CAAA,KAAA,EAAQ,CAAA,CAAE,MAAM,CAAA,CAAA;AAC7B,IAAA,IAAI;AACA,MAAA,MAAMC,EAAAA,GAAK,MAAM,CAAA,CAAE,IAAA,EAAK;AACxB,MAAA,IAAIA,EAAAA,CAAE,MAAA,EAAQ,MAAA,GAASA,EAAAA,CAAE,MAAA;AAAA,IAC7B,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,0BAAA,EAA6B,MAAM,CAAA,CAAE,CAAA;AAAA,EACzD;AACA,EAAA,MAAM,CAAA,GAAK,MAAM,CAAA,CAAE,IAAA,EAAK;AACxB,EAAA,OAAO,CAAA,CAAE,MAAA;AACb","file":"index.js","sourcesContent":["// @orangecheck/agent-vercel — wrap Vercel AI SDK `tool()` invocations in\n// OC Agent action envelopes.\n//\n// Provider-agnostic: the AI SDK is the abstraction layer. Whatever model\n// you use underneath (Anthropic, OpenAI, Cohere, …) the tool primitive is\n// the same {description, parameters, execute} shape. This adapter wraps\n// `execute` so every successful call produces a signed agent-action\n// envelope citing the active delegation.\n//\n// Two integration shapes:\n//\n// 1. ocTool({verb, parameters, execute})\n// A drop-in replacement for `tool()`. The wrapped execute runs the\n// scope check, signs the canonical (verb, args, callId) tuple,\n// then runs your real handler.\n//\n// 2. stampToolCall(input)\n// Lower-level stamping helper for cases where you have your own\n// tool-execution loop and want to emit envelopes manually.\n//\n// The agent + delegation are passed via a per-request context object\n// (or threaded through closures) — we don't depend on AsyncLocalStorage\n// to keep edge-runtime compatibility maximal.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalize,\n computeActionId,\n hexEncode,\n isSubScope,\n parseScope,\n type DelegationEnvelope,\n} from '@orangecheck/agent-core';\nimport {\n signAsAgent,\n type ActionEnvelope,\n type SignerRef,\n} from '@orangecheck/agent-signer';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalization of a tool call\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface VercelToolCall {\n /** Unique id assigned by the AI SDK for this tool invocation. */\n callId: string;\n /** Tool verb / name — passed in via ocTool() or set per-call by your wrapper. */\n verb: string;\n /** JSON-serializable arguments produced by the model. */\n args: Record<string, unknown>;\n}\n\n/**\n * Produce the canonical bytes for a Vercel AI SDK tool call.\n *\n * Shape (RFC 8785 canonical JSON, then trailing LF):\n * {\n * \"args\": <canonical JSON of args>,\n * \"call_id\": <callId>,\n * \"verb\": <verb>\n * }\n */\nexport function canonicalizeToolCall(c: VercelToolCall): Uint8Array {\n const canon = {\n args: c.args,\n call_id: c.callId,\n verb: c.verb,\n };\n const str = canonicalize(canon as unknown as Parameters<typeof canonicalize>[0]);\n return new TextEncoder().encode(str + '\\n');\n}\n\nexport function toolCallHash(c: VercelToolCall): string {\n return 'sha256:' + hexEncode(sha256(canonicalizeToolCall(c)));\n}\n\nexport function toolCallActionId(\n agentAddress: string,\n signedAt: string,\n delegationId: string,\n scopeExercised: string,\n c: VercelToolCall\n): string {\n return computeActionId({\n address: agentAddress,\n content_hash: toolCallHash(c),\n content_length: canonicalizeToolCall(c).byteLength,\n content_mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n signed_at: signedAt,\n delegation_id: delegationId,\n scope_exercised: scopeExercised,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stamping primitive\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AgentContext {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n}\n\nexport interface StampToolCallInput extends AgentContext {\n call: VercelToolCall;\n /** Defaults to `vercel:tool(verb=<verb>)` — the tightest admissible sub-scope. */\n scopeExercised?: string;\n signedAt?: Date;\n}\n\nexport async function stampToolCall(input: StampToolCallInput): Promise<ActionEnvelope> {\n const scopeExercised =\n input.scopeExercised ?? `vercel:tool(verb=${input.call.verb})`;\n\n const granted = (input.delegation.scopes ?? []).map(parseScope);\n const exercisedParsed = parseScope(scopeExercised);\n if (!granted.some((g) => isSubScope(exercisedParsed, g))) {\n throw new Error(\n `stampToolCall: scope_exercised (${scopeExercised}) is not a sub-scope of any granted scope`\n );\n }\n\n const bytes = canonicalizeToolCall(input.call);\n return signAsAgent({\n agent: input.agent,\n delegation: input.delegation as never,\n content: { hash: toolCallHash(input.call), length: bytes.byteLength },\n mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n scopeExercised,\n signedAt: input.signedAt,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ocTool — drop-in tool() replacement\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface OcToolInput<TArgs extends Record<string, unknown>, TResult> {\n /** OC Agent verb under which this tool is exercised. */\n verb: string;\n /** Pass-through to AI SDK's `tool()` — the JSON Schema for inputs. */\n parameters?: unknown;\n /** Pass-through description shown to the model. */\n description?: string;\n /** Your real handler. Returns whatever the tool returns. */\n execute: (args: TArgs) => Promise<TResult>;\n}\n\nexport interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {\n verb: string;\n parameters?: unknown;\n description?: string;\n /**\n * The execute fn the AI SDK calls. Receives args + a callId (the AI\n * SDK passes its own callId through — adapter glue passes it via\n * context). Optional `fleet` field on the context tells the\n * wrapper to fire-and-forget POST the stamped action to fleet.\n * ochk.io/api/actions after the underlying execute() returns.\n */\n execute: (\n args: TArgs,\n ctx: AgentContext & { callId: string; fleet?: FleetClient }\n ) => Promise<{\n result: TResult;\n action: ActionEnvelope;\n posted: PostActionResult | null;\n }>;\n}\n\n/**\n * Wrap a tool() definition so its execute path is scope-checked and\n * envelope-emitting. The exact glue depends on which AI-SDK release you're\n * on — the typical pattern is:\n *\n * const tools = {\n * 'invoice.create': tool({\n * description: '…',\n * parameters: schema,\n * execute: async (args, { toolCallId }) => {\n * const { result, action } = await invoiceCreate.execute(args, {\n * agent, delegation, callId: toolCallId,\n * });\n * await yourAuditPipeline.append(action);\n * return result;\n * },\n * }),\n * };\n *\n * const invoiceCreate = ocTool({\n * verb: 'invoice.create',\n * parameters: schema,\n * execute: async (args) => myInvoiceCreateImpl(args),\n * });\n *\n * The `ocTool` value is provider/SDK-agnostic — bring your own glue around\n * it. v1 of the adapter intentionally does not import `ai` so it works on\n * any release.\n */\nexport function ocTool<TArgs extends Record<string, unknown>, TResult>(\n input: OcToolInput<TArgs, TResult>\n): OcToolWrapped<TArgs, TResult> {\n return {\n verb: input.verb,\n parameters: input.parameters,\n description: input.description,\n execute: async (args, ctx) => {\n const call: VercelToolCall = {\n callId: ctx.callId,\n verb: input.verb,\n args,\n };\n const action = await stampToolCall({\n agent: ctx.agent,\n delegation: ctx.delegation,\n call,\n });\n const result = await input.execute(args);\n let posted: PostActionResult | null = null;\n if (ctx.fleet) {\n try {\n posted = await postActionToFleet(action, ctx.fleet);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('[oc-agent-vercel] postActionToFleet failed:', err);\n }\n }\n return { result, action, posted };\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Fleet integration: POST stamped actions to fleet.ochk.io/api/actions\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface FleetClient {\n /** Defaults to https://fleet.ochk.io. */\n baseUrl?: string;\n /** Bearer token from /settings § 03 (`ock_<hex>`). */\n apiToken: string;\n /** Project the action belongs to (proj_*). */\n projectId: string;\n /** Optional fetch override for runtimes that need it. */\n fetch?: typeof fetch;\n}\n\nexport interface PostActionResult {\n id: string;\n project_id: string;\n delegation_id: string;\n}\n\n/**\n * POST a stamped action envelope to fleet.ochk.io/api/actions. The\n * fleet re-derives the action id, validates agent-must-match-\n * delegation, persists, fans out to Nostr (kind 30084), submits to\n * OC Stamp, and triggers any subscribed webhooks. Throws on non-2xx\n * with the server's reason string.\n */\nexport async function postActionToFleet(\n action: ActionEnvelope,\n client: FleetClient\n): Promise<PostActionResult> {\n const baseUrl = client.baseUrl ?? 'https://fleet.ochk.io';\n const f = client.fetch ?? fetch;\n const body = {\n project_id: client.projectId,\n delegation_id: action.delegation_id,\n agent_address: action.signer.address,\n scope_exercised: action.scope_exercised,\n content_hash: action.content.hash,\n content_length: action.content.length,\n content_mime: action.content.mime,\n signed_at: action.signed_at,\n signature: action.sig.value,\n id: action.id,\n };\n const r = await f(`${baseUrl}/api/actions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${client.apiToken}`,\n },\n body: JSON.stringify(body),\n });\n if (!r.ok) {\n let reason = `http_${r.status}`;\n try {\n const j = (await r.json()) as { reason?: string };\n if (j.reason) reason = j.reason;\n } catch {\n // body wasn't json\n }\n throw new Error(`postActionToFleet failed: ${reason}`);\n }\n const j = (await r.json()) as { ok: true; action: PostActionResult };\n return j.action;\n}\n"]}
|
package/dist/index.mjs
CHANGED
|
@@ -63,19 +63,19 @@ function ocTool(input) {
|
|
|
63
63
|
});
|
|
64
64
|
const result = await input.execute(args);
|
|
65
65
|
let posted = null;
|
|
66
|
-
if (ctx.
|
|
66
|
+
if (ctx.fleet) {
|
|
67
67
|
try {
|
|
68
|
-
posted = await
|
|
68
|
+
posted = await postActionToFleet(action, ctx.fleet);
|
|
69
69
|
} catch (err) {
|
|
70
|
-
console.error("[oc-agent-vercel]
|
|
70
|
+
console.error("[oc-agent-vercel] postActionToFleet failed:", err);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
return { result, action, posted };
|
|
74
74
|
}
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
|
-
async function
|
|
78
|
-
const baseUrl = client.baseUrl ?? "https://
|
|
77
|
+
async function postActionToFleet(action, client) {
|
|
78
|
+
const baseUrl = client.baseUrl ?? "https://fleet.ochk.io";
|
|
79
79
|
const f = client.fetch ?? fetch;
|
|
80
80
|
const body = {
|
|
81
81
|
project_id: client.projectId,
|
|
@@ -104,12 +104,12 @@ async function postActionToConsole(action, client) {
|
|
|
104
104
|
if (j2.reason) reason = j2.reason;
|
|
105
105
|
} catch {
|
|
106
106
|
}
|
|
107
|
-
throw new Error(`
|
|
107
|
+
throw new Error(`postActionToFleet failed: ${reason}`);
|
|
108
108
|
}
|
|
109
109
|
const j = await r.json();
|
|
110
110
|
return j.action;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
export { canonicalizeToolCall, ocTool,
|
|
113
|
+
export { canonicalizeToolCall, ocTool, postActionToFleet, stampToolCall, toolCallActionId, toolCallHash };
|
|
114
114
|
//# sourceMappingURL=index.mjs.map
|
|
115
115
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":["j"],"mappings":"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","file":"index.mjs","sourcesContent":["// @orangecheck/agent-vercel — wrap Vercel AI SDK `tool()` invocations in\n// OC Agent action envelopes.\n//\n// Provider-agnostic: the AI SDK is the abstraction layer. Whatever model\n// you use underneath (Anthropic, OpenAI, Cohere, …) the tool primitive is\n// the same {description, parameters, execute} shape. This adapter wraps\n// `execute` so every successful call produces a signed agent-action\n// envelope citing the active delegation.\n//\n// Two integration shapes:\n//\n// 1. ocTool({verb, parameters, execute})\n// A drop-in replacement for `tool()`. The wrapped execute runs the\n// scope check, signs the canonical (verb, args, callId) tuple,\n// then runs your real handler.\n//\n// 2. stampToolCall(input)\n// Lower-level stamping helper for cases where you have your own\n// tool-execution loop and want to emit envelopes manually.\n//\n// The agent + delegation are passed via a per-request context object\n// (or threaded through closures) — we don't depend on AsyncLocalStorage\n// to keep edge-runtime compatibility maximal.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalize,\n computeActionId,\n hexEncode,\n isSubScope,\n parseScope,\n type DelegationEnvelope,\n} from '@orangecheck/agent-core';\nimport {\n signAsAgent,\n type ActionEnvelope,\n type SignerRef,\n} from '@orangecheck/agent-signer';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalization of a tool call\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface VercelToolCall {\n /** Unique id assigned by the AI SDK for this tool invocation. */\n callId: string;\n /** Tool verb / name — passed in via ocTool() or set per-call by your wrapper. */\n verb: string;\n /** JSON-serializable arguments produced by the model. */\n args: Record<string, unknown>;\n}\n\n/**\n * Produce the canonical bytes for a Vercel AI SDK tool call.\n *\n * Shape (RFC 8785 canonical JSON, then trailing LF):\n * {\n * \"args\": <canonical JSON of args>,\n * \"call_id\": <callId>,\n * \"verb\": <verb>\n * }\n */\nexport function canonicalizeToolCall(c: VercelToolCall): Uint8Array {\n const canon = {\n args: c.args,\n call_id: c.callId,\n verb: c.verb,\n };\n const str = canonicalize(canon as unknown as Parameters<typeof canonicalize>[0]);\n return new TextEncoder().encode(str + '\\n');\n}\n\nexport function toolCallHash(c: VercelToolCall): string {\n return 'sha256:' + hexEncode(sha256(canonicalizeToolCall(c)));\n}\n\nexport function toolCallActionId(\n agentAddress: string,\n signedAt: string,\n delegationId: string,\n scopeExercised: string,\n c: VercelToolCall\n): string {\n return computeActionId({\n address: agentAddress,\n content_hash: toolCallHash(c),\n content_length: canonicalizeToolCall(c).byteLength,\n content_mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n signed_at: signedAt,\n delegation_id: delegationId,\n scope_exercised: scopeExercised,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stamping primitive\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AgentContext {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n}\n\nexport interface StampToolCallInput extends AgentContext {\n call: VercelToolCall;\n /** Defaults to `vercel:tool(verb=<verb>)` — the tightest admissible sub-scope. */\n scopeExercised?: string;\n signedAt?: Date;\n}\n\nexport async function stampToolCall(input: StampToolCallInput): Promise<ActionEnvelope> {\n const scopeExercised =\n input.scopeExercised ?? `vercel:tool(verb=${input.call.verb})`;\n\n const granted = (input.delegation.scopes ?? []).map(parseScope);\n const exercisedParsed = parseScope(scopeExercised);\n if (!granted.some((g) => isSubScope(exercisedParsed, g))) {\n throw new Error(\n `stampToolCall: scope_exercised (${scopeExercised}) is not a sub-scope of any granted scope`\n );\n }\n\n const bytes = canonicalizeToolCall(input.call);\n return signAsAgent({\n agent: input.agent,\n delegation: input.delegation as never,\n content: { hash: toolCallHash(input.call), length: bytes.byteLength },\n mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n scopeExercised,\n signedAt: input.signedAt,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ocTool — drop-in tool() replacement\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface OcToolInput<TArgs extends Record<string, unknown>, TResult> {\n /** OC Agent verb under which this tool is exercised. */\n verb: string;\n /** Pass-through to AI SDK's `tool()` — the JSON Schema for inputs. */\n parameters?: unknown;\n /** Pass-through description shown to the model. */\n description?: string;\n /** Your real handler. Returns whatever the tool returns. */\n execute: (args: TArgs) => Promise<TResult>;\n}\n\nexport interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {\n verb: string;\n parameters?: unknown;\n description?: string;\n /**\n * The execute fn the AI SDK calls. Receives args + a callId (the AI\n * SDK passes its own callId through — adapter glue passes it via\n * context). Optional `console` field on the context tells the\n * wrapper to fire-and-forget POST the stamped action to console.\n * ochk.io/api/actions after the underlying execute() returns.\n */\n execute: (\n args: TArgs,\n ctx: AgentContext & { callId: string; console?: ConsoleClient }\n ) => Promise<{\n result: TResult;\n action: ActionEnvelope;\n posted: PostActionResult | null;\n }>;\n}\n\n/**\n * Wrap a tool() definition so its execute path is scope-checked and\n * envelope-emitting. The exact glue depends on which AI-SDK release you're\n * on — the typical pattern is:\n *\n * const tools = {\n * 'invoice.create': tool({\n * description: '…',\n * parameters: schema,\n * execute: async (args, { toolCallId }) => {\n * const { result, action } = await invoiceCreate.execute(args, {\n * agent, delegation, callId: toolCallId,\n * });\n * await yourAuditPipeline.append(action);\n * return result;\n * },\n * }),\n * };\n *\n * const invoiceCreate = ocTool({\n * verb: 'invoice.create',\n * parameters: schema,\n * execute: async (args) => myInvoiceCreateImpl(args),\n * });\n *\n * The `ocTool` value is provider/SDK-agnostic — bring your own glue around\n * it. v1 of the adapter intentionally does not import `ai` so it works on\n * any release.\n */\nexport function ocTool<TArgs extends Record<string, unknown>, TResult>(\n input: OcToolInput<TArgs, TResult>\n): OcToolWrapped<TArgs, TResult> {\n return {\n verb: input.verb,\n parameters: input.parameters,\n description: input.description,\n execute: async (args, ctx) => {\n const call: VercelToolCall = {\n callId: ctx.callId,\n verb: input.verb,\n args,\n };\n const action = await stampToolCall({\n agent: ctx.agent,\n delegation: ctx.delegation,\n call,\n });\n const result = await input.execute(args);\n let posted: PostActionResult | null = null;\n if (ctx.console) {\n try {\n posted = await postActionToConsole(action, ctx.console);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('[oc-agent-vercel] postActionToConsole failed:', err);\n }\n }\n return { result, action, posted };\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Console integration: POST stamped actions to console.ochk.io/api/actions\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface ConsoleClient {\n /** Defaults to https://console.ochk.io. */\n baseUrl?: string;\n /** Bearer token from /settings § 03 (`ock_<hex>`). */\n apiToken: string;\n /** Project the action belongs to (proj_*). */\n projectId: string;\n /** Optional fetch override for runtimes that need it. */\n fetch?: typeof fetch;\n}\n\nexport interface PostActionResult {\n id: string;\n project_id: string;\n delegation_id: string;\n}\n\n/**\n * POST a stamped action envelope to console.ochk.io/api/actions. The\n * console re-derives the action id, validates agent-must-match-\n * delegation, persists, fans out to Nostr (kind 30084), submits to\n * OC Stamp, and triggers any subscribed webhooks. Throws on non-2xx\n * with the server's reason string.\n */\nexport async function postActionToConsole(\n action: ActionEnvelope,\n client: ConsoleClient\n): Promise<PostActionResult> {\n const baseUrl = client.baseUrl ?? 'https://console.ochk.io';\n const f = client.fetch ?? fetch;\n const body = {\n project_id: client.projectId,\n delegation_id: action.delegation_id,\n agent_address: action.signer.address,\n scope_exercised: action.scope_exercised,\n content_hash: action.content.hash,\n content_length: action.content.length,\n content_mime: action.content.mime,\n signed_at: action.signed_at,\n signature: action.sig.value,\n id: action.id,\n };\n const r = await f(`${baseUrl}/api/actions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${client.apiToken}`,\n },\n body: JSON.stringify(body),\n });\n if (!r.ok) {\n let reason = `http_${r.status}`;\n try {\n const j = (await r.json()) as { reason?: string };\n if (j.reason) reason = j.reason;\n } catch {\n // body wasn't json\n }\n throw new Error(`postActionToConsole failed: ${reason}`);\n }\n const j = (await r.json()) as { ok: true; action: PostActionResult };\n return j.action;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":["j"],"mappings":"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","file":"index.mjs","sourcesContent":["// @orangecheck/agent-vercel — wrap Vercel AI SDK `tool()` invocations in\n// OC Agent action envelopes.\n//\n// Provider-agnostic: the AI SDK is the abstraction layer. Whatever model\n// you use underneath (Anthropic, OpenAI, Cohere, …) the tool primitive is\n// the same {description, parameters, execute} shape. This adapter wraps\n// `execute` so every successful call produces a signed agent-action\n// envelope citing the active delegation.\n//\n// Two integration shapes:\n//\n// 1. ocTool({verb, parameters, execute})\n// A drop-in replacement for `tool()`. The wrapped execute runs the\n// scope check, signs the canonical (verb, args, callId) tuple,\n// then runs your real handler.\n//\n// 2. stampToolCall(input)\n// Lower-level stamping helper for cases where you have your own\n// tool-execution loop and want to emit envelopes manually.\n//\n// The agent + delegation are passed via a per-request context object\n// (or threaded through closures) — we don't depend on AsyncLocalStorage\n// to keep edge-runtime compatibility maximal.\n\nimport { sha256 } from '@noble/hashes/sha256';\nimport {\n canonicalize,\n computeActionId,\n hexEncode,\n isSubScope,\n parseScope,\n type DelegationEnvelope,\n} from '@orangecheck/agent-core';\nimport {\n signAsAgent,\n type ActionEnvelope,\n type SignerRef,\n} from '@orangecheck/agent-signer';\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Canonicalization of a tool call\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface VercelToolCall {\n /** Unique id assigned by the AI SDK for this tool invocation. */\n callId: string;\n /** Tool verb / name — passed in via ocTool() or set per-call by your wrapper. */\n verb: string;\n /** JSON-serializable arguments produced by the model. */\n args: Record<string, unknown>;\n}\n\n/**\n * Produce the canonical bytes for a Vercel AI SDK tool call.\n *\n * Shape (RFC 8785 canonical JSON, then trailing LF):\n * {\n * \"args\": <canonical JSON of args>,\n * \"call_id\": <callId>,\n * \"verb\": <verb>\n * }\n */\nexport function canonicalizeToolCall(c: VercelToolCall): Uint8Array {\n const canon = {\n args: c.args,\n call_id: c.callId,\n verb: c.verb,\n };\n const str = canonicalize(canon as unknown as Parameters<typeof canonicalize>[0]);\n return new TextEncoder().encode(str + '\\n');\n}\n\nexport function toolCallHash(c: VercelToolCall): string {\n return 'sha256:' + hexEncode(sha256(canonicalizeToolCall(c)));\n}\n\nexport function toolCallActionId(\n agentAddress: string,\n signedAt: string,\n delegationId: string,\n scopeExercised: string,\n c: VercelToolCall\n): string {\n return computeActionId({\n address: agentAddress,\n content_hash: toolCallHash(c),\n content_length: canonicalizeToolCall(c).byteLength,\n content_mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n signed_at: signedAt,\n delegation_id: delegationId,\n scope_exercised: scopeExercised,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stamping primitive\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface AgentContext {\n agent: SignerRef;\n delegation: DelegationEnvelope;\n}\n\nexport interface StampToolCallInput extends AgentContext {\n call: VercelToolCall;\n /** Defaults to `vercel:tool(verb=<verb>)` — the tightest admissible sub-scope. */\n scopeExercised?: string;\n signedAt?: Date;\n}\n\nexport async function stampToolCall(input: StampToolCallInput): Promise<ActionEnvelope> {\n const scopeExercised =\n input.scopeExercised ?? `vercel:tool(verb=${input.call.verb})`;\n\n const granted = (input.delegation.scopes ?? []).map(parseScope);\n const exercisedParsed = parseScope(scopeExercised);\n if (!granted.some((g) => isSubScope(exercisedParsed, g))) {\n throw new Error(\n `stampToolCall: scope_exercised (${scopeExercised}) is not a sub-scope of any granted scope`\n );\n }\n\n const bytes = canonicalizeToolCall(input.call);\n return signAsAgent({\n agent: input.agent,\n delegation: input.delegation as never,\n content: { hash: toolCallHash(input.call), length: bytes.byteLength },\n mime: 'application/vnd.oc-agent.vercel-tool-call+json',\n scopeExercised,\n signedAt: input.signedAt,\n });\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// ocTool — drop-in tool() replacement\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface OcToolInput<TArgs extends Record<string, unknown>, TResult> {\n /** OC Agent verb under which this tool is exercised. */\n verb: string;\n /** Pass-through to AI SDK's `tool()` — the JSON Schema for inputs. */\n parameters?: unknown;\n /** Pass-through description shown to the model. */\n description?: string;\n /** Your real handler. Returns whatever the tool returns. */\n execute: (args: TArgs) => Promise<TResult>;\n}\n\nexport interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {\n verb: string;\n parameters?: unknown;\n description?: string;\n /**\n * The execute fn the AI SDK calls. Receives args + a callId (the AI\n * SDK passes its own callId through — adapter glue passes it via\n * context). Optional `fleet` field on the context tells the\n * wrapper to fire-and-forget POST the stamped action to fleet.\n * ochk.io/api/actions after the underlying execute() returns.\n */\n execute: (\n args: TArgs,\n ctx: AgentContext & { callId: string; fleet?: FleetClient }\n ) => Promise<{\n result: TResult;\n action: ActionEnvelope;\n posted: PostActionResult | null;\n }>;\n}\n\n/**\n * Wrap a tool() definition so its execute path is scope-checked and\n * envelope-emitting. The exact glue depends on which AI-SDK release you're\n * on — the typical pattern is:\n *\n * const tools = {\n * 'invoice.create': tool({\n * description: '…',\n * parameters: schema,\n * execute: async (args, { toolCallId }) => {\n * const { result, action } = await invoiceCreate.execute(args, {\n * agent, delegation, callId: toolCallId,\n * });\n * await yourAuditPipeline.append(action);\n * return result;\n * },\n * }),\n * };\n *\n * const invoiceCreate = ocTool({\n * verb: 'invoice.create',\n * parameters: schema,\n * execute: async (args) => myInvoiceCreateImpl(args),\n * });\n *\n * The `ocTool` value is provider/SDK-agnostic — bring your own glue around\n * it. v1 of the adapter intentionally does not import `ai` so it works on\n * any release.\n */\nexport function ocTool<TArgs extends Record<string, unknown>, TResult>(\n input: OcToolInput<TArgs, TResult>\n): OcToolWrapped<TArgs, TResult> {\n return {\n verb: input.verb,\n parameters: input.parameters,\n description: input.description,\n execute: async (args, ctx) => {\n const call: VercelToolCall = {\n callId: ctx.callId,\n verb: input.verb,\n args,\n };\n const action = await stampToolCall({\n agent: ctx.agent,\n delegation: ctx.delegation,\n call,\n });\n const result = await input.execute(args);\n let posted: PostActionResult | null = null;\n if (ctx.fleet) {\n try {\n posted = await postActionToFleet(action, ctx.fleet);\n } catch (err) {\n // eslint-disable-next-line no-console\n console.error('[oc-agent-vercel] postActionToFleet failed:', err);\n }\n }\n return { result, action, posted };\n },\n };\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Fleet integration: POST stamped actions to fleet.ochk.io/api/actions\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport interface FleetClient {\n /** Defaults to https://fleet.ochk.io. */\n baseUrl?: string;\n /** Bearer token from /settings § 03 (`ock_<hex>`). */\n apiToken: string;\n /** Project the action belongs to (proj_*). */\n projectId: string;\n /** Optional fetch override for runtimes that need it. */\n fetch?: typeof fetch;\n}\n\nexport interface PostActionResult {\n id: string;\n project_id: string;\n delegation_id: string;\n}\n\n/**\n * POST a stamped action envelope to fleet.ochk.io/api/actions. The\n * fleet re-derives the action id, validates agent-must-match-\n * delegation, persists, fans out to Nostr (kind 30084), submits to\n * OC Stamp, and triggers any subscribed webhooks. Throws on non-2xx\n * with the server's reason string.\n */\nexport async function postActionToFleet(\n action: ActionEnvelope,\n client: FleetClient\n): Promise<PostActionResult> {\n const baseUrl = client.baseUrl ?? 'https://fleet.ochk.io';\n const f = client.fetch ?? fetch;\n const body = {\n project_id: client.projectId,\n delegation_id: action.delegation_id,\n agent_address: action.signer.address,\n scope_exercised: action.scope_exercised,\n content_hash: action.content.hash,\n content_length: action.content.length,\n content_mime: action.content.mime,\n signed_at: action.signed_at,\n signature: action.sig.value,\n id: action.id,\n };\n const r = await f(`${baseUrl}/api/actions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${client.apiToken}`,\n },\n body: JSON.stringify(body),\n });\n if (!r.ok) {\n let reason = `http_${r.status}`;\n try {\n const j = (await r.json()) as { reason?: string };\n if (j.reason) reason = j.reason;\n } catch {\n // body wasn't json\n }\n throw new Error(`postActionToFleet failed: ${reason}`);\n }\n const j = (await r.json()) as { ok: true; action: PostActionResult };\n return j.action;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orangecheck/agent-vercel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Wrap Vercel AI SDK tool() invocations with OC Agent scope enforcement and emit a signed agent-action envelope per tool execution. Provider-agnostic — works under Anthropic, OpenAI, Cohere, etc.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin",
|
package/src/index.ts
CHANGED
|
@@ -153,13 +153,13 @@ export interface OcToolWrapped<TArgs extends Record<string, unknown>, TResult> {
|
|
|
153
153
|
/**
|
|
154
154
|
* The execute fn the AI SDK calls. Receives args + a callId (the AI
|
|
155
155
|
* SDK passes its own callId through — adapter glue passes it via
|
|
156
|
-
* context). Optional `
|
|
157
|
-
* wrapper to fire-and-forget POST the stamped action to
|
|
156
|
+
* context). Optional `fleet` field on the context tells the
|
|
157
|
+
* wrapper to fire-and-forget POST the stamped action to fleet.
|
|
158
158
|
* ochk.io/api/actions after the underlying execute() returns.
|
|
159
159
|
*/
|
|
160
160
|
execute: (
|
|
161
161
|
args: TArgs,
|
|
162
|
-
ctx: AgentContext & { callId: string;
|
|
162
|
+
ctx: AgentContext & { callId: string; fleet?: FleetClient }
|
|
163
163
|
) => Promise<{
|
|
164
164
|
result: TResult;
|
|
165
165
|
action: ActionEnvelope;
|
|
@@ -216,12 +216,12 @@ export function ocTool<TArgs extends Record<string, unknown>, TResult>(
|
|
|
216
216
|
});
|
|
217
217
|
const result = await input.execute(args);
|
|
218
218
|
let posted: PostActionResult | null = null;
|
|
219
|
-
if (ctx.
|
|
219
|
+
if (ctx.fleet) {
|
|
220
220
|
try {
|
|
221
|
-
posted = await
|
|
221
|
+
posted = await postActionToFleet(action, ctx.fleet);
|
|
222
222
|
} catch (err) {
|
|
223
223
|
// eslint-disable-next-line no-console
|
|
224
|
-
console.error('[oc-agent-vercel]
|
|
224
|
+
console.error('[oc-agent-vercel] postActionToFleet failed:', err);
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
return { result, action, posted };
|
|
@@ -230,11 +230,11 @@ export function ocTool<TArgs extends Record<string, unknown>, TResult>(
|
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
233
|
-
//
|
|
233
|
+
// Fleet integration: POST stamped actions to fleet.ochk.io/api/actions
|
|
234
234
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
235
235
|
|
|
236
|
-
export interface
|
|
237
|
-
/** Defaults to https://
|
|
236
|
+
export interface FleetClient {
|
|
237
|
+
/** Defaults to https://fleet.ochk.io. */
|
|
238
238
|
baseUrl?: string;
|
|
239
239
|
/** Bearer token from /settings § 03 (`ock_<hex>`). */
|
|
240
240
|
apiToken: string;
|
|
@@ -251,17 +251,17 @@ export interface PostActionResult {
|
|
|
251
251
|
}
|
|
252
252
|
|
|
253
253
|
/**
|
|
254
|
-
* POST a stamped action envelope to
|
|
255
|
-
*
|
|
254
|
+
* POST a stamped action envelope to fleet.ochk.io/api/actions. The
|
|
255
|
+
* fleet re-derives the action id, validates agent-must-match-
|
|
256
256
|
* delegation, persists, fans out to Nostr (kind 30084), submits to
|
|
257
257
|
* OC Stamp, and triggers any subscribed webhooks. Throws on non-2xx
|
|
258
258
|
* with the server's reason string.
|
|
259
259
|
*/
|
|
260
|
-
export async function
|
|
260
|
+
export async function postActionToFleet(
|
|
261
261
|
action: ActionEnvelope,
|
|
262
|
-
client:
|
|
262
|
+
client: FleetClient
|
|
263
263
|
): Promise<PostActionResult> {
|
|
264
|
-
const baseUrl = client.baseUrl ?? 'https://
|
|
264
|
+
const baseUrl = client.baseUrl ?? 'https://fleet.ochk.io';
|
|
265
265
|
const f = client.fetch ?? fetch;
|
|
266
266
|
const body = {
|
|
267
267
|
project_id: client.projectId,
|
|
@@ -291,7 +291,7 @@ export async function postActionToConsole(
|
|
|
291
291
|
} catch {
|
|
292
292
|
// body wasn't json
|
|
293
293
|
}
|
|
294
|
-
throw new Error(`
|
|
294
|
+
throw new Error(`postActionToFleet failed: ${reason}`);
|
|
295
295
|
}
|
|
296
296
|
const j = (await r.json()) as { ok: true; action: PostActionResult };
|
|
297
297
|
return j.action;
|