@adonis-agora/agent 0.52.0 → 0.53.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/CHANGELOG.md +102 -0
- package/dist/providers/agent_provider.d.ts +2 -2
- package/dist/providers/agent_provider.d.ts.map +1 -1
- package/dist/providers/agent_provider.js +6 -4
- package/dist/providers/agent_provider.js.map +1 -1
- package/dist/providers/mcp_provider.d.ts.map +1 -1
- package/dist/providers/mcp_provider.js +2 -1
- package/dist/providers/mcp_provider.js.map +1 -1
- package/dist/src/agent-deps-factory.d.ts.map +1 -1
- package/dist/src/agent-deps-factory.js +4 -0
- package/dist/src/agent-deps-factory.js.map +1 -1
- package/dist/src/agent-deps.d.ts +5 -0
- package/dist/src/agent-deps.d.ts.map +1 -1
- package/dist/src/agent-deps.js.map +1 -1
- package/dist/src/authorizer.d.ts +11 -3
- package/dist/src/authorizer.d.ts.map +1 -1
- package/dist/src/authorizer.js +13 -3
- package/dist/src/authorizer.js.map +1 -1
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.d.ts.map +1 -1
- package/dist/src/client/index.js +1 -1
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/client/sse.d.ts +5 -1
- package/dist/src/client/sse.d.ts.map +1 -1
- package/dist/src/client/sse.js +20 -2
- package/dist/src/client/sse.js.map +1 -1
- package/dist/src/confirm-token.d.ts +52 -0
- package/dist/src/confirm-token.d.ts.map +1 -0
- package/dist/src/confirm-token.js +103 -0
- package/dist/src/confirm-token.js.map +1 -0
- package/dist/src/confirmed-tool.d.ts +139 -0
- package/dist/src/confirmed-tool.d.ts.map +1 -0
- package/dist/src/confirmed-tool.js +203 -0
- package/dist/src/confirmed-tool.js.map +1 -0
- package/dist/src/define_config.d.ts +11 -2
- package/dist/src/define_config.d.ts.map +1 -1
- package/dist/src/define_config.js.map +1 -1
- package/dist/src/durable/agent-run-workflow.d.ts.map +1 -1
- package/dist/src/durable/agent-run-workflow.js +38 -7
- package/dist/src/durable/agent-run-workflow.js.map +1 -1
- package/dist/src/index.d.ts +13 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -4
- package/dist/src/index.js.map +1 -1
- package/dist/src/lucid-token-stream-sink.d.ts +124 -0
- package/dist/src/lucid-token-stream-sink.d.ts.map +1 -0
- package/dist/src/lucid-token-stream-sink.js +324 -0
- package/dist/src/lucid-token-stream-sink.js.map +1 -0
- package/dist/src/mcp/define_config.d.ts +7 -0
- package/dist/src/mcp/define_config.d.ts.map +1 -1
- package/dist/src/mcp/define_config.js.map +1 -1
- package/dist/src/spi/confirm-token-store.d.ts +30 -0
- package/dist/src/spi/confirm-token-store.d.ts.map +1 -0
- package/dist/src/spi/confirm-token-store.js +2 -0
- package/dist/src/spi/confirm-token-store.js.map +1 -0
- package/dist/src/spi/token-stream-sink.d.ts +9 -1
- package/dist/src/spi/token-stream-sink.d.ts.map +1 -1
- package/dist/src/spi/token-stream-sink.js +5 -4
- package/dist/src/spi/token-stream-sink.js.map +1 -1
- package/dist/src/stores/factory.d.ts +60 -1
- package/dist/src/stores/factory.d.ts.map +1 -1
- package/dist/src/stores/factory.js +21 -1
- package/dist/src/stores/factory.js.map +1 -1
- package/dist/src/stores/lucid-confirm-tokens.d.ts +38 -0
- package/dist/src/stores/lucid-confirm-tokens.d.ts.map +1 -0
- package/dist/src/stores/lucid-confirm-tokens.js +81 -0
- package/dist/src/stores/lucid-confirm-tokens.js.map +1 -0
- package/dist/src/stores/lucid-schema.d.ts +26 -6
- package/dist/src/stores/lucid-schema.d.ts.map +1 -1
- package/dist/src/stores/lucid-schema.js +50 -7
- package/dist/src/stores/lucid-schema.js.map +1 -1
- package/dist/src/testing/index.d.ts +1 -0
- package/dist/src/testing/index.d.ts.map +1 -1
- package/dist/src/testing/index.js +1 -0
- package/dist/src/testing/index.js.map +1 -1
- package/dist/src/tool-registry.d.ts +20 -1
- package/dist/src/tool-registry.d.ts.map +1 -1
- package/dist/src/tool-registry.js +15 -2
- package/dist/src/tool-registry.js.map +1 -1
- package/dist/src/types.d.ts +16 -0
- package/dist/src/types.d.ts.map +1 -1
- package/dist/stubs/config/agent.stub +1 -0
- package/dist/stubs/config/mcp.stub +5 -0
- package/dist/stubs/database/migrations/create_agent_tables.stub +3 -2
- package/package.json +1 -1
- package/skills/agent-governance/SKILL.md +1 -0
- package/skills/agent-setup/SKILL.md +22 -1
- package/skills/agent-tools/SKILL.md +37 -1
package/dist/src/authorizer.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { DefaultRolesPolicy } from './tool-registry.js';
|
|
1
|
+
import { DefaultRolesPolicy, type RolesPolicyOptions } from './tool-registry.js';
|
|
2
2
|
import type { Actor, ToolSpec } from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* The default tool authorizer the provider binds when `config/agent.ts` sets no `authorizer` /
|
|
5
5
|
* `rolesPolicy`. A tool that declares `roles` is offered (and invocable) only to an actor holding one
|
|
6
6
|
* of them; a tool that declares none takes `defaultRoles` — `[]` unless configured, which means no
|
|
7
7
|
* restriction (anyone the actor resolver resolved, an anonymous visitor included). Set
|
|
8
|
-
* `defaultRoles: ['ADMIN']` for the old fail-closed posture
|
|
8
|
+
* `defaultRoles: ['ADMIN']` for the old fail-closed posture, or `emptyRoles: 'deny'` to make an empty
|
|
9
|
+
* list reach nobody ({@link ClosedToolAuthorizer}).
|
|
9
10
|
*
|
|
10
11
|
* This is a thin, explicitly-named binding over core's {@link DefaultRolesPolicy} (the `RolesPolicy`
|
|
11
12
|
* seam) so apps that plug an ability-aware gate (`@adonis-agora/authz` Bouncer adapter) swap ONLY the
|
|
@@ -13,7 +14,14 @@ import type { Actor, ToolSpec } from './types.js';
|
|
|
13
14
|
* `ToolRegistry.invoke` — both run through this same `can(actor, tool)`.
|
|
14
15
|
*/
|
|
15
16
|
export declare class DefaultToolAuthorizer extends DefaultRolesPolicy {
|
|
16
|
-
constructor(defaultRoles?: string[]);
|
|
17
|
+
constructor(defaultRoles?: string[], options?: RolesPolicyOptions);
|
|
17
18
|
can(actor: Actor, tool: ToolSpec): boolean;
|
|
18
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* {@link DefaultToolAuthorizer} with `emptyRoles: 'deny'` — what `emptyRoles: 'deny'` in
|
|
22
|
+
* `config/agent.ts` / `config/mcp.ts` binds. An empty roles list reaches nobody.
|
|
23
|
+
*/
|
|
24
|
+
export declare class ClosedToolAuthorizer extends DefaultToolAuthorizer {
|
|
25
|
+
constructor(defaultRoles?: string[]);
|
|
26
|
+
}
|
|
19
27
|
//# sourceMappingURL=authorizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorizer.d.ts","sourceRoot":"","sources":["../../src/authorizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"authorizer.d.ts","sourceRoot":"","sources":["../../src/authorizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjF,OAAO,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;;;;;;GAYG;AACH,qBAAa,qBAAsB,SAAQ,kBAAkB;IAC3D,YAAY,YAAY,GAAE,MAAM,EAAO,EAAE,OAAO,GAAE,kBAAuB,EAExE;IAEQ,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAElD;CACF;AAED;;;GAGG;AACH,qBAAa,oBAAqB,SAAQ,qBAAqB;IAC7D,YAAY,YAAY,GAAE,MAAM,EAAO,EAEtC;CACF"}
|
package/dist/src/authorizer.js
CHANGED
|
@@ -4,7 +4,8 @@ import { DefaultRolesPolicy } from './tool-registry.js';
|
|
|
4
4
|
* `rolesPolicy`. A tool that declares `roles` is offered (and invocable) only to an actor holding one
|
|
5
5
|
* of them; a tool that declares none takes `defaultRoles` — `[]` unless configured, which means no
|
|
6
6
|
* restriction (anyone the actor resolver resolved, an anonymous visitor included). Set
|
|
7
|
-
* `defaultRoles: ['ADMIN']` for the old fail-closed posture
|
|
7
|
+
* `defaultRoles: ['ADMIN']` for the old fail-closed posture, or `emptyRoles: 'deny'` to make an empty
|
|
8
|
+
* list reach nobody ({@link ClosedToolAuthorizer}).
|
|
8
9
|
*
|
|
9
10
|
* This is a thin, explicitly-named binding over core's {@link DefaultRolesPolicy} (the `RolesPolicy`
|
|
10
11
|
* seam) so apps that plug an ability-aware gate (`@adonis-agora/authz` Bouncer adapter) swap ONLY the
|
|
@@ -12,11 +13,20 @@ import { DefaultRolesPolicy } from './tool-registry.js';
|
|
|
12
13
|
* `ToolRegistry.invoke` — both run through this same `can(actor, tool)`.
|
|
13
14
|
*/
|
|
14
15
|
export class DefaultToolAuthorizer extends DefaultRolesPolicy {
|
|
15
|
-
constructor(defaultRoles = []) {
|
|
16
|
-
super(defaultRoles);
|
|
16
|
+
constructor(defaultRoles = [], options = {}) {
|
|
17
|
+
super(defaultRoles, options);
|
|
17
18
|
}
|
|
18
19
|
can(actor, tool) {
|
|
19
20
|
return super.can(actor, tool);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* {@link DefaultToolAuthorizer} with `emptyRoles: 'deny'` — what `emptyRoles: 'deny'` in
|
|
25
|
+
* `config/agent.ts` / `config/mcp.ts` binds. An empty roles list reaches nobody.
|
|
26
|
+
*/
|
|
27
|
+
export class ClosedToolAuthorizer extends DefaultToolAuthorizer {
|
|
28
|
+
constructor(defaultRoles = []) {
|
|
29
|
+
super(defaultRoles, { emptyRoles: 'deny' });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
22
32
|
//# sourceMappingURL=authorizer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authorizer.js","sourceRoot":"","sources":["../../src/authorizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"authorizer.js","sourceRoot":"","sources":["../../src/authorizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAA2B,MAAM,oBAAoB,CAAC;AAGjF;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,qBAAsB,SAAQ,kBAAkB;IAC3D,YAAY,YAAY,GAAa,EAAE,EAAE,OAAO,GAAuB,EAAE;QACvE,KAAK,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC;IAEQ,GAAG,CAAC,KAAY,EAAE,IAAc;QACvC,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,OAAO,oBAAqB,SAAQ,qBAAqB;IAC7D,YAAY,YAAY,GAAa,EAAE;QACrC,KAAK,CAAC,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9C,CAAC;CACF"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { type AgentChatClient, type AgentChatClientOptions, AgentChatDisconnectedError, type AgentChatHandlers, AgentChatHttpError, type AgentChatQueued, type AgentChatRequestBody, type AgentChatResult, type AgentChatResumeOptions, type AgentChatSendOptions, AgentChatStreamError, createAgentChatClient, } from './chat-client.js';
|
|
2
|
-
export { type ChatFrame, type ChatPart, decodeFrame, foldPart, parseSseEvent, readSseStream, type SseEvent, } from './sse.js';
|
|
2
|
+
export { type ChatFrame, type ChatPart, decodeFrame, foldPart, parseSseEvent, readSseStream, type SseEvent, STEP_SEPARATOR, } from './sse.js';
|
|
3
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,0BAA0B,EAC1B,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,EACb,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,0BAA0B,EAC1B,KAAK,iBAAiB,EACtB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,SAAS,EACd,KAAK,QAAQ,EACb,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,EACb,KAAK,QAAQ,EACb,cAAc,GACf,MAAM,UAAU,CAAC"}
|
package/dist/src/client/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { AgentChatDisconnectedError, AgentChatHttpError, AgentChatStreamError, createAgentChatClient, } from './chat-client.js';
|
|
2
|
-
export { decodeFrame, foldPart, parseSseEvent, readSseStream, } from './sse.js';
|
|
2
|
+
export { decodeFrame, foldPart, parseSseEvent, readSseStream, STEP_SEPARATOR, } from './sse.js';
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,0BAA0B,EAE1B,kBAAkB,EAMlB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,0BAA0B,EAE1B,kBAAkB,EAMlB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAGL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,aAAa,EAEb,cAAc,GACf,MAAM,UAAU,CAAC"}
|
package/dist/src/client/sse.d.ts
CHANGED
|
@@ -99,11 +99,15 @@ export declare function parseSseEvent(frame: string): SseEvent | null;
|
|
|
99
99
|
export declare function decodeFrame(event: SseEvent): ChatFrame | null;
|
|
100
100
|
/**
|
|
101
101
|
* Folds a renderable {@link ChatFrame} (text or component) into the message's parts, concatenating
|
|
102
|
-
* consecutive text deltas into the trailing text part and appending components in order.
|
|
102
|
+
* consecutive text deltas into the trailing text part and appending components in order. A
|
|
103
|
+
* `step-start` closes the text before it with a paragraph break ({@link STEP_SEPARATOR}), so two
|
|
104
|
+
* model steps never run into each other.
|
|
103
105
|
* `meta`/`elicitation`/`approval`/`done` are control frames and are ignored here — a form to put to
|
|
104
106
|
* the user is not a part of the assistant's message. Returns a new array (never mutates the input).
|
|
105
107
|
*/
|
|
106
108
|
export declare function foldPart(parts: ChatPart[], frame: ChatFrame): ChatPart[];
|
|
109
|
+
/** What separates the text of two consecutive model steps in one message: a paragraph break. */
|
|
110
|
+
export declare const STEP_SEPARATOR = "\n\n";
|
|
107
111
|
/**
|
|
108
112
|
* Reads a byte {@link ReadableStream} (a `fetch` `response.body`) as a sequence of raw SSE events,
|
|
109
113
|
* splitting on the `\n\n` frame separator and buffering partial frames across chunks. Yields only
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/client/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,mGAAmG;AACnG,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,gEAAgE;AAChE,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;AACrD,qFAAqF;GACnF;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAClD,8EAA8E;GAC5E;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE;AACtE;;;;;;;GAOG;GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE;AACzF;;;;GAIG;GACD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACzF;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAiB5D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CAsC7D;AAgDD
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../../src/client/sse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D,mGAAmG;AACnG,MAAM,MAAM,QAAQ,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,gEAAgE;AAChE,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;AACrD,qFAAqF;GACnF;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AAClD,8EAA8E;GAC5E;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE;AACtE;;;;;;;GAOG;GACD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,kBAAkB,CAAA;CAAE;AACzF;;;;GAIG;GACD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GACzF;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB,+FAA+F;AAC/F,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAiB5D;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CAsC7D;AAgDD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE,SAAS,GAAG,QAAQ,EAAE,CAuCxE;AAED,gGAAgG;AAChG,eAAO,MAAM,cAAc,SAAS,CAAC;AAErC;;;;GAIG;AACH,wBAAuB,aAAa,CAAC,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,cAAc,CAAC,QAAQ,CAAC,CAyB/F"}
|
package/dist/src/client/sse.js
CHANGED
|
@@ -85,7 +85,7 @@ export function decodeFrame(event) {
|
|
|
85
85
|
* One frame of the agent stream protocol as a {@link ChatFrame}. The kinds this client folds into a
|
|
86
86
|
* message or acts on (text, ui, elicitation, approval-requested) get their own frame; every other
|
|
87
87
|
* kind (reasoning, tool calls, steps, title, …) arrives as `{ type: 'event' }`, which
|
|
88
|
-
* {@link foldPart} ignores and `onFrame` still sees.
|
|
88
|
+
* {@link foldPart} ignores — but for a `step-start`, see there — and `onFrame` still sees.
|
|
89
89
|
*/
|
|
90
90
|
function decodeAgentEvent(event) {
|
|
91
91
|
if (event.kind === 'text') {
|
|
@@ -127,7 +127,9 @@ function decodeAgentEvent(event) {
|
|
|
127
127
|
}
|
|
128
128
|
/**
|
|
129
129
|
* Folds a renderable {@link ChatFrame} (text or component) into the message's parts, concatenating
|
|
130
|
-
* consecutive text deltas into the trailing text part and appending components in order.
|
|
130
|
+
* consecutive text deltas into the trailing text part and appending components in order. A
|
|
131
|
+
* `step-start` closes the text before it with a paragraph break ({@link STEP_SEPARATOR}), so two
|
|
132
|
+
* model steps never run into each other.
|
|
131
133
|
* `meta`/`elicitation`/`approval`/`done` are control frames and are ignored here — a form to put to
|
|
132
134
|
* the user is not a part of the assistant's message. Returns a new array (never mutates the input).
|
|
133
135
|
*/
|
|
@@ -152,8 +154,24 @@ export function foldPart(parts, frame) {
|
|
|
152
154
|
}
|
|
153
155
|
return [...parts, { type: 'text', text: frame.delta }];
|
|
154
156
|
}
|
|
157
|
+
if (frame.type === 'event' && frame.event.kind === 'step-start') {
|
|
158
|
+
// A new model step. What it says is a new paragraph, not the continuation of a sentence: the
|
|
159
|
+
// step before it ended to call tools, so its text stops wherever the model stopped ("…before
|
|
160
|
+
// answering.") and the next one would otherwise be glued to it ("…before answering.Let me…").
|
|
161
|
+
// Only text the message already ends with is closed — a step that opens after a component, or
|
|
162
|
+
// first, has nothing to be separated from.
|
|
163
|
+
const last = parts[parts.length - 1];
|
|
164
|
+
if (last &&
|
|
165
|
+
last.type === 'text' &&
|
|
166
|
+
last.text.trim().length > 0 &&
|
|
167
|
+
!last.text.endsWith('\n\n')) {
|
|
168
|
+
return [...parts.slice(0, -1), { type: 'text', text: `${last.text}${STEP_SEPARATOR}` }];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
155
171
|
return parts;
|
|
156
172
|
}
|
|
173
|
+
/** What separates the text of two consecutive model steps in one message: a paragraph break. */
|
|
174
|
+
export const STEP_SEPARATOR = '\n\n';
|
|
157
175
|
/**
|
|
158
176
|
* Reads a byte {@link ReadableStream} (a `fetch` `response.body`) as a sequence of raw SSE events,
|
|
159
177
|
* splitting on the `\n\n` frame separator and buffering partial frames across chunks. Yields only
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../../src/client/sse.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../../../src/client/sse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ConfirmTokenClaim, ConfirmTokenStore } from './spi/confirm-token-store.js';
|
|
2
|
+
/** How long a preview stays confirmable unless the tool says otherwise. */
|
|
3
|
+
export declare const DEFAULT_CONFIRM_TTL_MS: number;
|
|
4
|
+
/** What a confirm token is bound to. Change any of it and the token stops matching. */
|
|
5
|
+
export interface ConfirmTokenSubject {
|
|
6
|
+
/** The tool's name. */
|
|
7
|
+
tool: string;
|
|
8
|
+
/** `ctx.actor.id`. */
|
|
9
|
+
actorId: string;
|
|
10
|
+
/** `ctx.actor.tenantRef`; absent and `''` sign the same. */
|
|
11
|
+
tenantRef?: string;
|
|
12
|
+
/** The call's arguments, WITHOUT `confirm` / `confirmToken`. */
|
|
13
|
+
args: unknown;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* JSON with object keys sorted at every depth, so two argument objects that differ only in key
|
|
17
|
+
* order produce the same text. `undefined` properties are dropped (as `JSON.stringify` does); a
|
|
18
|
+
* bare `undefined` and an `undefined` array element are `null`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function canonicalJson(value: unknown): string;
|
|
21
|
+
/**
|
|
22
|
+
* Issue a confirm token: `<expiresAt>.<signature>`, an HMAC-SHA256 (keyed by `secret`) over the tool,
|
|
23
|
+
* the actor, the tenant, the expiry and the canonical arguments. Stateless — nothing is stored — and
|
|
24
|
+
* useless for another actor, tenant, tool or argument, or after `expiresAt` (epoch-ms).
|
|
25
|
+
*/
|
|
26
|
+
export declare function signConfirmToken(subject: ConfirmTokenSubject, options: {
|
|
27
|
+
secret: string;
|
|
28
|
+
expiresAt: number;
|
|
29
|
+
}): string;
|
|
30
|
+
/** The expiry (epoch-ms) a token carries, or `undefined` for a malformed one. Not a verification. */
|
|
31
|
+
export declare function confirmTokenExpiry(token: string): number | undefined;
|
|
32
|
+
/** Was this token issued for exactly this subject, with this secret, and is it still valid at `now`? */
|
|
33
|
+
export declare function verifyConfirmToken(token: string | null | undefined, subject: ConfirmTokenSubject, options: {
|
|
34
|
+
secret: string;
|
|
35
|
+
now?: number;
|
|
36
|
+
}): boolean;
|
|
37
|
+
/** SHA-256 (hex) of a token — what a {@link ConfirmTokenStore} keys on, so the token is never stored. */
|
|
38
|
+
export declare function hashConfirmToken(token: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* A single-process {@link ConfirmTokenStore}: tests, scripts, and a deployment that runs ONE replica.
|
|
41
|
+
* With more than one, a token spent on replica A is still fresh on replica B — use the Lucid store.
|
|
42
|
+
* Expired marks are dropped lazily, on the next claim.
|
|
43
|
+
*/
|
|
44
|
+
export declare class InMemoryConfirmTokenStore implements ConfirmTokenStore {
|
|
45
|
+
private readonly claims;
|
|
46
|
+
claim(input: ConfirmTokenClaim): Promise<boolean>;
|
|
47
|
+
release(hash: string): Promise<void>;
|
|
48
|
+
purgeExpired(now?: number): Promise<number>;
|
|
49
|
+
/** How many tokens are currently marked as spent. */
|
|
50
|
+
get size(): number;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=confirm-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-token.d.ts","sourceRoot":"","sources":["../../src/confirm-token.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEzF,2EAA2E;AAC3E,eAAO,MAAM,sBAAsB,QAAiB,CAAC;AAErD,uFAAuF;AACvF,MAAM,WAAW,mBAAmB;IAClC,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gEAAgE;IAChE,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAWpD;AAqBD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAC7C,MAAM,CAER;AAED,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAKpE;AAED,wGAAwG;AACxG,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAChC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAOT;AAED,yGAAyG;AACzG,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;GAIG;AACH,qBAAa,yBAA0B,YAAW,iBAAiB;IACjE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;IAEzD,KAAK,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC,CAKtD;IAEK,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEzC;IAEK,YAAY,CAAC,GAAG,GAAE,MAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5D;IAED,qDAAqD;IACrD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { createHash, createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/** How long a preview stays confirmable unless the tool says otherwise. */
|
|
3
|
+
export const DEFAULT_CONFIRM_TTL_MS = 15 * 60 * 1000;
|
|
4
|
+
/**
|
|
5
|
+
* JSON with object keys sorted at every depth, so two argument objects that differ only in key
|
|
6
|
+
* order produce the same text. `undefined` properties are dropped (as `JSON.stringify` does); a
|
|
7
|
+
* bare `undefined` and an `undefined` array element are `null`.
|
|
8
|
+
*/
|
|
9
|
+
export function canonicalJson(value) {
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return 'null';
|
|
12
|
+
if (value === null || typeof value !== 'object')
|
|
13
|
+
return JSON.stringify(value) ?? 'null';
|
|
14
|
+
if (Array.isArray(value))
|
|
15
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
16
|
+
if (typeof value.toJSON === 'function') {
|
|
17
|
+
return canonicalJson(value.toJSON());
|
|
18
|
+
}
|
|
19
|
+
const entries = Object.entries(value)
|
|
20
|
+
.filter(([, entry]) => entry !== undefined)
|
|
21
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
22
|
+
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(',')}}`;
|
|
23
|
+
}
|
|
24
|
+
/** The signed text is itself canonical JSON, so no field can bleed into its neighbour. */
|
|
25
|
+
function signature(subject, expiresAt, secret) {
|
|
26
|
+
if (secret === '') {
|
|
27
|
+
throw new Error('confirm token: the secret is empty');
|
|
28
|
+
}
|
|
29
|
+
return createHmac('sha256', secret)
|
|
30
|
+
.update(canonicalJson([
|
|
31
|
+
'agent-confirm',
|
|
32
|
+
subject.tool,
|
|
33
|
+
subject.actorId,
|
|
34
|
+
subject.tenantRef ?? '',
|
|
35
|
+
expiresAt,
|
|
36
|
+
subject.args,
|
|
37
|
+
]))
|
|
38
|
+
.digest('base64url');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Issue a confirm token: `<expiresAt>.<signature>`, an HMAC-SHA256 (keyed by `secret`) over the tool,
|
|
42
|
+
* the actor, the tenant, the expiry and the canonical arguments. Stateless — nothing is stored — and
|
|
43
|
+
* useless for another actor, tenant, tool or argument, or after `expiresAt` (epoch-ms).
|
|
44
|
+
*/
|
|
45
|
+
export function signConfirmToken(subject, options) {
|
|
46
|
+
return `${options.expiresAt}.${signature(subject, options.expiresAt, options.secret)}`;
|
|
47
|
+
}
|
|
48
|
+
/** The expiry (epoch-ms) a token carries, or `undefined` for a malformed one. Not a verification. */
|
|
49
|
+
export function confirmTokenExpiry(token) {
|
|
50
|
+
const separator = token.indexOf('.');
|
|
51
|
+
if (separator <= 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
const expiresAt = Number(token.slice(0, separator));
|
|
54
|
+
return Number.isSafeInteger(expiresAt) && expiresAt > 0 ? expiresAt : undefined;
|
|
55
|
+
}
|
|
56
|
+
/** Was this token issued for exactly this subject, with this secret, and is it still valid at `now`? */
|
|
57
|
+
export function verifyConfirmToken(token, subject, options) {
|
|
58
|
+
if (typeof token !== 'string')
|
|
59
|
+
return false;
|
|
60
|
+
const expiresAt = confirmTokenExpiry(token);
|
|
61
|
+
if (expiresAt === undefined || expiresAt < (options.now ?? Date.now()))
|
|
62
|
+
return false;
|
|
63
|
+
const expected = Buffer.from(signature(subject, expiresAt, options.secret));
|
|
64
|
+
const given = Buffer.from(token.slice(token.indexOf('.') + 1));
|
|
65
|
+
return expected.length === given.length && timingSafeEqual(expected, given);
|
|
66
|
+
}
|
|
67
|
+
/** SHA-256 (hex) of a token — what a {@link ConfirmTokenStore} keys on, so the token is never stored. */
|
|
68
|
+
export function hashConfirmToken(token) {
|
|
69
|
+
return createHash('sha256').update(token).digest('hex');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A single-process {@link ConfirmTokenStore}: tests, scripts, and a deployment that runs ONE replica.
|
|
73
|
+
* With more than one, a token spent on replica A is still fresh on replica B — use the Lucid store.
|
|
74
|
+
* Expired marks are dropped lazily, on the next claim.
|
|
75
|
+
*/
|
|
76
|
+
export class InMemoryConfirmTokenStore {
|
|
77
|
+
claims = new Map();
|
|
78
|
+
async claim(input) {
|
|
79
|
+
await this.purgeExpired();
|
|
80
|
+
if (this.claims.has(input.hash))
|
|
81
|
+
return false;
|
|
82
|
+
this.claims.set(input.hash, { ...input });
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
async release(hash) {
|
|
86
|
+
this.claims.delete(hash);
|
|
87
|
+
}
|
|
88
|
+
async purgeExpired(now = Date.now()) {
|
|
89
|
+
let purged = 0;
|
|
90
|
+
for (const [hash, claim] of this.claims) {
|
|
91
|
+
if (claim.expiresAt < now) {
|
|
92
|
+
this.claims.delete(hash);
|
|
93
|
+
purged += 1;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return purged;
|
|
97
|
+
}
|
|
98
|
+
/** How many tokens are currently marked as spent. */
|
|
99
|
+
get size() {
|
|
100
|
+
return this.claims.size;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=confirm-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirm-token.js","sourceRoot":"","sources":["../../src/confirm-token.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
import { type AiToolOptions, type BrandedFunctionalTool } from './ai-tool-ref.js';
|
|
3
|
+
import type { ConfirmTokenStore } from './spi/confirm-token-store.js';
|
|
4
|
+
import type { AiToolCtx } from './spi/tool.js';
|
|
5
|
+
/** The two fields {@link defineConfirmedTool} adds to a tool's input. */
|
|
6
|
+
export interface ConfirmFields {
|
|
7
|
+
/** `true` to commit; absent (or `false`) to preview. */
|
|
8
|
+
confirm?: boolean;
|
|
9
|
+
/** The `confirmToken` the preview returned. */
|
|
10
|
+
confirmToken?: string;
|
|
11
|
+
}
|
|
12
|
+
/** The words a confirmed tool says to the model. Each has an English default. */
|
|
13
|
+
export interface ConfirmedToolMessages {
|
|
14
|
+
/** Sent with every preview: how to confirm. `{minutes}` is replaced with the token's lifetime. */
|
|
15
|
+
confirm?: string;
|
|
16
|
+
/** Thrown for a missing, tampered, mismatched or expired token. */
|
|
17
|
+
invalid?: string;
|
|
18
|
+
/** Thrown for a token that already committed. */
|
|
19
|
+
used?: string;
|
|
20
|
+
}
|
|
21
|
+
/** Thrown by a confirmed tool that refused a confirmation. Nothing was written. */
|
|
22
|
+
export declare class ConfirmTokenError extends Error {
|
|
23
|
+
/** `invalid`: missing, tampered, mismatched or expired. `used`: it already committed once. */
|
|
24
|
+
readonly reason: 'invalid' | 'used';
|
|
25
|
+
constructor(
|
|
26
|
+
/** `invalid`: missing, tampered, mismatched or expired. `used`: it already committed once. */
|
|
27
|
+
reason: 'invalid' | 'used', message: string);
|
|
28
|
+
}
|
|
29
|
+
/** What {@link defineConfirmedTool} takes besides the usual tool options. */
|
|
30
|
+
export interface ConfirmedToolOptions extends Omit<AiToolOptions, 'kind'> {
|
|
31
|
+
name: string;
|
|
32
|
+
/**
|
|
33
|
+
* The HMAC key the tokens are signed with — the app key, or a secret of its own. A function is
|
|
34
|
+
* read on every call. Required: there is no default, and an empty one throws.
|
|
35
|
+
*/
|
|
36
|
+
secret: string | (() => string);
|
|
37
|
+
/** How long a preview stays confirmable. Default 15 minutes. */
|
|
38
|
+
ttlMs?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Makes a token single use. WITHOUT a store a token commits as many times as it is sent until it
|
|
41
|
+
* expires — a retry or a repeated call writes twice.
|
|
42
|
+
*/
|
|
43
|
+
store?: ConfirmTokenStore;
|
|
44
|
+
messages?: ConfirmedToolMessages;
|
|
45
|
+
}
|
|
46
|
+
/** What a step reports: a sentence for the model to relay, and the structured detail behind it. */
|
|
47
|
+
export interface ConfirmedToolOutcome {
|
|
48
|
+
summary: string;
|
|
49
|
+
data?: unknown;
|
|
50
|
+
}
|
|
51
|
+
/** The three steps of a confirmed write. */
|
|
52
|
+
export interface ConfirmedToolSteps<Args, Prepared> {
|
|
53
|
+
/**
|
|
54
|
+
* Resolve and validate everything the write needs, applying the SAME rules the commit relies on.
|
|
55
|
+
* Throw to refuse. Writes nothing. Runs for the preview AND again for the confirmation — state may
|
|
56
|
+
* have changed in between — and a refusal here does not spend the token.
|
|
57
|
+
*/
|
|
58
|
+
prepare(args: Args, ctx: AiToolCtx): Promise<Prepared> | Prepared;
|
|
59
|
+
/** What is about to happen. Writes nothing. */
|
|
60
|
+
preview(prepared: Prepared, ctx: AiToolCtx): Promise<ConfirmedToolOutcome> | ConfirmedToolOutcome;
|
|
61
|
+
/** Write, and say what was written. A throw gives the token back. */
|
|
62
|
+
commit(prepared: Prepared, ctx: AiToolCtx): Promise<ConfirmedToolOutcome> | ConfirmedToolOutcome;
|
|
63
|
+
}
|
|
64
|
+
/** A confirmed tool's answer to a call without `confirm`. */
|
|
65
|
+
export interface ConfirmedToolPreview {
|
|
66
|
+
status: 'preview';
|
|
67
|
+
summary: string;
|
|
68
|
+
data?: unknown;
|
|
69
|
+
confirmToken: string;
|
|
70
|
+
/** ISO-8601 instant the token stops working. */
|
|
71
|
+
expiresAt: string;
|
|
72
|
+
/** Instructions for the model: show the preview, then call again with the token. */
|
|
73
|
+
confirm: string;
|
|
74
|
+
}
|
|
75
|
+
/** A confirmed tool's answer to a confirmation that committed. */
|
|
76
|
+
export interface ConfirmedToolDone {
|
|
77
|
+
status: 'done';
|
|
78
|
+
summary: string;
|
|
79
|
+
data?: unknown;
|
|
80
|
+
}
|
|
81
|
+
export type ConfirmedToolResult = ConfirmedToolPreview | ConfirmedToolDone;
|
|
82
|
+
/** The JSON Schema of {@link ConfirmFields}, merged into the schema the model is shown. */
|
|
83
|
+
export declare const CONFIRM_JSON_SCHEMA_PROPERTIES: {
|
|
84
|
+
readonly confirm: {
|
|
85
|
+
readonly type: 'boolean';
|
|
86
|
+
readonly description: 'Omit to get a preview (nothing is written). Send true, with confirmToken, to commit.';
|
|
87
|
+
};
|
|
88
|
+
readonly confirmToken: {
|
|
89
|
+
readonly type: 'string';
|
|
90
|
+
readonly description: 'The confirmToken the preview returned. Required when confirm is true.';
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Wrap a tool's input schema so it also accepts {@link ConfirmFields}, without the app declaring
|
|
95
|
+
* them. `validate` takes the two fields off, checks them itself, hands the REST to the app's schema
|
|
96
|
+
* (so a strict schema never sees a key it did not declare) and puts them back on the validated value.
|
|
97
|
+
*
|
|
98
|
+
* The wrapper always carries the Standard JSON Schema extension, because that is the only form the
|
|
99
|
+
* model gets to see: the app schema's own JSON Schema plus the two properties. A schema without the
|
|
100
|
+
* extension (Zod 3, a bare Standard Schema) degrades to a permissive object that still declares the
|
|
101
|
+
* two fields — `validate` stays the authority either way.
|
|
102
|
+
*/
|
|
103
|
+
export declare function withConfirmFields<Schema extends StandardSchemaV1>(schema: Schema): StandardSchemaV1<StandardSchemaV1.InferInput<Schema> & ConfirmFields, StandardSchemaV1.InferOutput<Schema> & ConfirmFields>;
|
|
104
|
+
/**
|
|
105
|
+
* A write with a human gate that lives INSIDE the tool: preview first, commit on confirmation.
|
|
106
|
+
*
|
|
107
|
+
* - Called without `confirm`: runs `prepare`, writes nothing, and returns the `preview` with a signed
|
|
108
|
+
* `confirmToken`.
|
|
109
|
+
* - Called again with the SAME arguments, `confirm: true` and that token: runs `prepare` again, then
|
|
110
|
+
* `commit`.
|
|
111
|
+
*
|
|
112
|
+
* The token is an HMAC over the tool, `ctx.actor.id`, `ctx.actor.tenantRef`, the expiry and the
|
|
113
|
+
* canonical arguments, so it cannot be replayed by another actor, in another tenant, on another
|
|
114
|
+
* tool or with a changed argument. With a `store` it is also single use: it is claimed right before
|
|
115
|
+
* `commit` (a refusal in `prepare` does not spend it) and released if `commit` throws.
|
|
116
|
+
*
|
|
117
|
+
* Registered as `kind: 'read'` on purpose. An `action` parks on the loop's HITL approval, which an
|
|
118
|
+
* MCP caller cannot answer — `createMcpServer` keeps `action` tools off the surface, and MCP clients
|
|
119
|
+
* without elicitation have no other channel. The gate being in the handler is what lets the same
|
|
120
|
+
* tool serve the chat loop and MCP; the user sees the preview because the model has to relay it to
|
|
121
|
+
* get a token it can confirm with.
|
|
122
|
+
*
|
|
123
|
+
* ```ts
|
|
124
|
+
* export const refundOrder = defineConfirmedTool(
|
|
125
|
+
* { name: 'refund_order', description: '...', input: z.object({ orderId: z.string() }),
|
|
126
|
+
* secret: () => env.get('APP_KEY').release(), store: new LucidConfirmTokenStore(db) },
|
|
127
|
+
* {
|
|
128
|
+
* prepare: async ({ orderId }, ctx) => loadRefundableOrder(orderId, ctx.actor),
|
|
129
|
+
* preview: (order) => ({ summary: `Refund ${order.total} to ${order.customer}?`, data: order }),
|
|
130
|
+
* commit: async (order) => ({ summary: 'Refunded.', data: await refund(order) }),
|
|
131
|
+
* },
|
|
132
|
+
* )
|
|
133
|
+
* ```
|
|
134
|
+
*
|
|
135
|
+
* `input` is the app's own schema: `confirm` and `confirmToken` are added by
|
|
136
|
+
* {@link withConfirmFields}, and `prepare` receives the arguments without them.
|
|
137
|
+
*/
|
|
138
|
+
export declare function defineConfirmedTool<Args = unknown, Prepared = unknown>(options: ConfirmedToolOptions, steps: ConfirmedToolSteps<Args, Prepared>): BrandedFunctionalTool;
|
|
139
|
+
//# sourceMappingURL=confirmed-tool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmed-tool.d.ts","sourceRoot":"","sources":["../../src/confirmed-tool.ts"],"names":[],"mappings":"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"}
|