@adonis-agora/agent 0.52.1 → 0.53.1
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 +106 -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 +24 -7
- 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/agent-loop.d.ts +27 -2
- package/dist/src/agent-loop.d.ts.map +1 -1
- package/dist/src/agent-loop.js +83 -13
- package/dist/src/agent-loop.js.map +1 -1
- package/dist/src/agent-service.d.ts +7 -0
- package/dist/src/agent-service.d.ts.map +1 -1
- package/dist/src/agent-service.js +31 -4
- package/dist/src/agent-service.js.map +1 -1
- package/dist/src/ai-sdk/ai-sdk-model.js +7 -0
- package/dist/src/ai-sdk/ai-sdk-model.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/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/dangling-tool-calls.d.ts +35 -0
- package/dist/src/dangling-tool-calls.d.ts.map +1 -0
- package/dist/src/dangling-tool-calls.js +84 -0
- package/dist/src/dangling-tool-calls.js.map +1 -0
- package/dist/src/dead-run.d.ts +28 -0
- package/dist/src/dead-run.d.ts.map +1 -0
- package/dist/src/dead-run.js +37 -0
- package/dist/src/dead-run.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 +18 -3
- package/dist/src/durable/agent-run-workflow.js.map +1 -1
- package/dist/src/index.d.ts +16 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -5
- 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/runners/inline-agent-runner.d.ts.map +1 -1
- package/dist/src/runners/inline-agent-runner.js +14 -3
- package/dist/src/runners/inline-agent-runner.js.map +1 -1
- package/dist/src/spi/agent-store.d.ts +16 -0
- package/dist/src/spi/agent-store.d.ts.map +1 -1
- package/dist/src/spi/agent-store.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/stores/lucid.d.ts +3 -0
- package/dist/src/stores/lucid.d.ts.map +1 -1
- package/dist/src/stores/lucid.js +28 -0
- package/dist/src/stores/lucid.js.map +1 -1
- package/dist/src/stream-events.d.ts +5 -1
- package/dist/src/stream-events.d.ts.map +1 -1
- package/dist/src/testing/in-memory-store.d.ts +3 -0
- package/dist/src/testing/in-memory-store.d.ts.map +1 -1
- package/dist/src/testing/in-memory-store.js +26 -0
- package/dist/src/testing/in-memory-store.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
|
@@ -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":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,qBAAqB,EAAc,MAAM,kBAAkB,CAAC;AAQ9F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,wDAAwD;IACxD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,iFAAiF;AACjF,MAAM,WAAW,qBAAqB;IACpC,kGAAkG;IAClG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAUD,mFAAmF;AACnF,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,8FAA8F;IAC9F,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM;IAFrC;IACE,8FAA8F;IACrF,MAAM,EAAE,SAAS,GAAG,MAAM,EACnC,OAAO,EAAE,MAAM,EAIhB;CACF;AAED,6EAA6E;AAC7E,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC;IACvE,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC;IAChC,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,CAAC,EAAE,qBAAqB,CAAC;CAClC;AAED,mGAAmG;AACnG,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB,CAAC,IAAI,EAAE,QAAQ;IAChD;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC;IAClE,+CAA+C;IAC/C,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG,oBAAoB,CAAC;IAClG,qEAAqE;IACrE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,oBAAoB,CAAC,GAAG,oBAAoB,CAAC;CAClG;AAED,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,gDAAgD;IAChD,SAAS,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,kEAAkE;AAClE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,GAAG,iBAAiB,CAAC;AAE3E,2FAA2F;AAC3F,eAAO,MAAM,8BAA8B;aACzC,OAAO;iBACL,IAAI,EAAE,SAAS;iBACf,WAAW,EACT,sFAAsF;;aAE1F,YAAY;iBACV,IAAI,EAAE,QAAQ;iBACd,WAAW,EAAE,uEAAuE;;CAE9E,CAAC;AA8BX;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,SAAS,gBAAgB,EAC/D,MAAM,EAAE,MAAM,GACb,gBAAgB,CACjB,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,aAAa,EACnD,gBAAgB,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,aAAa,CACrD,CA+CA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,GAAG,OAAO,EAAE,QAAQ,GAAG,OAAO,EACpE,OAAO,EAAE,oBAAoB,EAC7B,KAAK,EAAE,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,GACxC,qBAAqB,CAiEvB"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { defineTool } from './ai-tool-ref.js';
|
|
2
|
+
import { confirmTokenExpiry, DEFAULT_CONFIRM_TTL_MS, hashConfirmToken, signConfirmToken, verifyConfirmToken, } from './confirm-token.js';
|
|
3
|
+
const DEFAULT_MESSAGES = {
|
|
4
|
+
confirm: 'Nothing was written. Show this preview to the user and ask them to confirm. To commit, call this tool again with the SAME arguments plus "confirm": true and this "confirmToken" (valid for {minutes} minutes).',
|
|
5
|
+
invalid: 'Invalid or expired confirmation: nothing was written. Call the tool without "confirm" to get a fresh preview, then confirm with the "confirmToken" it returns, without changing the arguments.',
|
|
6
|
+
used: 'This preview was already confirmed: nothing was written again. To repeat the operation, ask for a new preview.',
|
|
7
|
+
};
|
|
8
|
+
/** Thrown by a confirmed tool that refused a confirmation. Nothing was written. */
|
|
9
|
+
export class ConfirmTokenError extends Error {
|
|
10
|
+
reason;
|
|
11
|
+
constructor(
|
|
12
|
+
/** `invalid`: missing, tampered, mismatched or expired. `used`: it already committed once. */
|
|
13
|
+
reason, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.reason = reason;
|
|
16
|
+
this.name = 'ConfirmTokenError';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** The JSON Schema of {@link ConfirmFields}, merged into the schema the model is shown. */
|
|
20
|
+
export const CONFIRM_JSON_SCHEMA_PROPERTIES = {
|
|
21
|
+
confirm: {
|
|
22
|
+
type: 'boolean',
|
|
23
|
+
description: 'Omit to get a preview (nothing is written). Send true, with confirmToken, to commit.',
|
|
24
|
+
},
|
|
25
|
+
confirmToken: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'The confirmToken the preview returned. Required when confirm is true.',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
/** Split a call's input into the app's arguments and the two confirmation fields. */
|
|
34
|
+
function splitConfirm(input) {
|
|
35
|
+
if (!isRecord(input))
|
|
36
|
+
return { args: input, fields: {} };
|
|
37
|
+
const { confirm, confirmToken, ...args } = input;
|
|
38
|
+
return {
|
|
39
|
+
args,
|
|
40
|
+
fields: {
|
|
41
|
+
...(typeof confirm === 'boolean' ? { confirm } : {}),
|
|
42
|
+
...(typeof confirmToken === 'string' ? { confirmToken } : {}),
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** The schema's Standard JSON Schema `input` converter, when it has one. */
|
|
47
|
+
function jsonSchemaInputOf(schema) {
|
|
48
|
+
const standard = schema['~standard'];
|
|
49
|
+
const input = standard.jsonSchema?.input;
|
|
50
|
+
return typeof input === 'function'
|
|
51
|
+
? (options) => input.call(standard.jsonSchema, options)
|
|
52
|
+
: undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Wrap a tool's input schema so it also accepts {@link ConfirmFields}, without the app declaring
|
|
56
|
+
* them. `validate` takes the two fields off, checks them itself, hands the REST to the app's schema
|
|
57
|
+
* (so a strict schema never sees a key it did not declare) and puts them back on the validated value.
|
|
58
|
+
*
|
|
59
|
+
* The wrapper always carries the Standard JSON Schema extension, because that is the only form the
|
|
60
|
+
* model gets to see: the app schema's own JSON Schema plus the two properties. A schema without the
|
|
61
|
+
* extension (Zod 3, a bare Standard Schema) degrades to a permissive object that still declares the
|
|
62
|
+
* two fields — `validate` stays the authority either way.
|
|
63
|
+
*/
|
|
64
|
+
export function withConfirmFields(schema) {
|
|
65
|
+
const inner = schema['~standard'];
|
|
66
|
+
const innerJsonSchema = jsonSchemaInputOf(schema);
|
|
67
|
+
const describe = (options) => {
|
|
68
|
+
const base = innerJsonSchema?.(options) ?? { additionalProperties: true };
|
|
69
|
+
return {
|
|
70
|
+
...base,
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
...(isRecord(base.properties) ? base.properties : {}),
|
|
74
|
+
...CONFIRM_JSON_SCHEMA_PROPERTIES,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
const wrapped = {
|
|
79
|
+
'~standard': {
|
|
80
|
+
version: 1,
|
|
81
|
+
vendor: '@adonis-agora/agent',
|
|
82
|
+
validate: async (value) => {
|
|
83
|
+
if (!isRecord(value)) {
|
|
84
|
+
return { issues: [{ message: 'expected an object' }] };
|
|
85
|
+
}
|
|
86
|
+
const issues = [];
|
|
87
|
+
// `null` is how some models say "absent"; it is read as absent rather than refused.
|
|
88
|
+
if (value.confirm != null && typeof value.confirm !== 'boolean') {
|
|
89
|
+
issues.push({ message: 'expected a boolean', path: ['confirm'] });
|
|
90
|
+
}
|
|
91
|
+
if (value.confirmToken != null && typeof value.confirmToken !== 'string') {
|
|
92
|
+
issues.push({ message: 'expected a string', path: ['confirmToken'] });
|
|
93
|
+
}
|
|
94
|
+
const { args, fields } = splitConfirm(value);
|
|
95
|
+
const result = await inner.validate(args);
|
|
96
|
+
if (result.issues !== undefined) {
|
|
97
|
+
return { issues: [...issues, ...result.issues] };
|
|
98
|
+
}
|
|
99
|
+
if (issues.length > 0) {
|
|
100
|
+
return { issues };
|
|
101
|
+
}
|
|
102
|
+
return { value: isRecord(result.value) ? { ...result.value, ...fields } : result.value };
|
|
103
|
+
},
|
|
104
|
+
jsonSchema: { input: describe, output: describe },
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
return wrapped;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* A write with a human gate that lives INSIDE the tool: preview first, commit on confirmation.
|
|
111
|
+
*
|
|
112
|
+
* - Called without `confirm`: runs `prepare`, writes nothing, and returns the `preview` with a signed
|
|
113
|
+
* `confirmToken`.
|
|
114
|
+
* - Called again with the SAME arguments, `confirm: true` and that token: runs `prepare` again, then
|
|
115
|
+
* `commit`.
|
|
116
|
+
*
|
|
117
|
+
* The token is an HMAC over the tool, `ctx.actor.id`, `ctx.actor.tenantRef`, the expiry and the
|
|
118
|
+
* canonical arguments, so it cannot be replayed by another actor, in another tenant, on another
|
|
119
|
+
* tool or with a changed argument. With a `store` it is also single use: it is claimed right before
|
|
120
|
+
* `commit` (a refusal in `prepare` does not spend it) and released if `commit` throws.
|
|
121
|
+
*
|
|
122
|
+
* Registered as `kind: 'read'` on purpose. An `action` parks on the loop's HITL approval, which an
|
|
123
|
+
* MCP caller cannot answer — `createMcpServer` keeps `action` tools off the surface, and MCP clients
|
|
124
|
+
* without elicitation have no other channel. The gate being in the handler is what lets the same
|
|
125
|
+
* tool serve the chat loop and MCP; the user sees the preview because the model has to relay it to
|
|
126
|
+
* get a token it can confirm with.
|
|
127
|
+
*
|
|
128
|
+
* ```ts
|
|
129
|
+
* export const refundOrder = defineConfirmedTool(
|
|
130
|
+
* { name: 'refund_order', description: '...', input: z.object({ orderId: z.string() }),
|
|
131
|
+
* secret: () => env.get('APP_KEY').release(), store: new LucidConfirmTokenStore(db) },
|
|
132
|
+
* {
|
|
133
|
+
* prepare: async ({ orderId }, ctx) => loadRefundableOrder(orderId, ctx.actor),
|
|
134
|
+
* preview: (order) => ({ summary: `Refund ${order.total} to ${order.customer}?`, data: order }),
|
|
135
|
+
* commit: async (order) => ({ summary: 'Refunded.', data: await refund(order) }),
|
|
136
|
+
* },
|
|
137
|
+
* )
|
|
138
|
+
* ```
|
|
139
|
+
*
|
|
140
|
+
* `input` is the app's own schema: `confirm` and `confirmToken` are added by
|
|
141
|
+
* {@link withConfirmFields}, and `prepare` receives the arguments without them.
|
|
142
|
+
*/
|
|
143
|
+
export function defineConfirmedTool(options, steps) {
|
|
144
|
+
const { secret, ttlMs = DEFAULT_CONFIRM_TTL_MS, store, messages, ...toolOptions } = options;
|
|
145
|
+
const text = { ...DEFAULT_MESSAGES, ...messages };
|
|
146
|
+
const howToConfirm = text.confirm.replaceAll('{minutes}', String(Math.round(ttlMs / 60_000)));
|
|
147
|
+
const secretNow = () => (typeof secret === 'function' ? secret() : secret);
|
|
148
|
+
return defineTool({ ...toolOptions, kind: 'read', input: withConfirmFields(options.input) }, async (input, ctx) => {
|
|
149
|
+
const { args, fields } = splitConfirm(input);
|
|
150
|
+
const subject = {
|
|
151
|
+
tool: options.name,
|
|
152
|
+
actorId: ctx.actor.id,
|
|
153
|
+
...(ctx.actor.tenantRef !== undefined ? { tenantRef: ctx.actor.tenantRef } : {}),
|
|
154
|
+
args,
|
|
155
|
+
};
|
|
156
|
+
const confirming = fields.confirm === true;
|
|
157
|
+
const token = fields.confirmToken;
|
|
158
|
+
if (confirming && !verifyConfirmToken(token, subject, { secret: secretNow() })) {
|
|
159
|
+
throw new ConfirmTokenError('invalid', text.invalid);
|
|
160
|
+
}
|
|
161
|
+
const prepared = await steps.prepare(args, ctx);
|
|
162
|
+
if (!confirming || token === undefined) {
|
|
163
|
+
const preview = await steps.preview(prepared, ctx);
|
|
164
|
+
const expiresAt = Date.now() + ttlMs;
|
|
165
|
+
return {
|
|
166
|
+
status: 'preview',
|
|
167
|
+
summary: preview.summary,
|
|
168
|
+
...(preview.data !== undefined ? { data: preview.data } : {}),
|
|
169
|
+
confirmToken: signConfirmToken(subject, { secret: secretNow(), expiresAt }),
|
|
170
|
+
expiresAt: new Date(expiresAt).toISOString(),
|
|
171
|
+
confirm: howToConfirm,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
// Claimed only now: everything `prepare` refuses leaves the token usable, and a commit that
|
|
175
|
+
// throws gives it back so the same preview can be confirmed again.
|
|
176
|
+
const hash = hashConfirmToken(token);
|
|
177
|
+
if (store !== undefined) {
|
|
178
|
+
const claimed = await store.claim({
|
|
179
|
+
hash,
|
|
180
|
+
actorRef: ctx.actor.id,
|
|
181
|
+
tool: options.name,
|
|
182
|
+
expiresAt: confirmTokenExpiry(token) ?? Date.now() + ttlMs,
|
|
183
|
+
});
|
|
184
|
+
if (!claimed) {
|
|
185
|
+
throw new ConfirmTokenError('used', text.used);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const done = await steps.commit(prepared, ctx);
|
|
190
|
+
return {
|
|
191
|
+
status: 'done',
|
|
192
|
+
summary: done.summary,
|
|
193
|
+
...(done.data !== undefined ? { data: done.data } : {}),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
// The commit's own error is the one worth reporting, whatever the release does.
|
|
198
|
+
await store?.release(hash).catch(() => undefined);
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
//# sourceMappingURL=confirmed-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confirmed-tool.js","sourceRoot":"","sources":["../../src/confirmed-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { ModelMessage, ToolCallStatus } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* What a store knows about a tool call, read back when its message carries no result for it — see
|
|
4
|
+
* {@link import('./spi/agent-store.js').AgentStore.toolCallOutcomes}.
|
|
5
|
+
*/
|
|
6
|
+
export interface ToolCallOutcome {
|
|
7
|
+
id: string;
|
|
8
|
+
status: ToolCallStatus;
|
|
9
|
+
output?: unknown;
|
|
10
|
+
error?: string;
|
|
11
|
+
}
|
|
12
|
+
/** What the model is told about a call whose turn died before the call was settled. */
|
|
13
|
+
export declare const UNFINISHED_TOOL_CALL: string;
|
|
14
|
+
/** What is written on the row of a call whose run ended without settling it. */
|
|
15
|
+
export declare const RUN_ENDED_BEFORE_TOOL_CALL = "the run ended before this tool call was settled";
|
|
16
|
+
/** The ids of every tool call in `messages` that the message asking for it holds no result for. */
|
|
17
|
+
export declare function danglingToolCallIds(messages: readonly ModelMessage[]): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Give every tool call in a thread's history a result.
|
|
20
|
+
*
|
|
21
|
+
* A turn writes its results onto the assistant message once the step's LAST tool has settled. A turn
|
|
22
|
+
* that dies before that — its run failed, its worker was killed, an approval was never answered and
|
|
23
|
+
* the run was abandoned — leaves an assistant message that asks for tools and is answered by
|
|
24
|
+
* nothing. A provider refuses a prompt shaped like that (a tool call must be followed by its
|
|
25
|
+
* result), so every LATER turn on the thread fails too, with an error about the stream rather than
|
|
26
|
+
* about the history: one dead turn makes the whole conversation unusable.
|
|
27
|
+
*
|
|
28
|
+
* Here each such call is settled from what is actually known: the call's own row where the store
|
|
29
|
+
* can read it (`outcomes` — a tool that DID run hands the model its real output, so it is not run a
|
|
30
|
+
* second time), and otherwise a result saying the call was never completed. Pure: the same messages
|
|
31
|
+
* and outcomes always produce the same prompt, so a replay composes what the first attempt did.
|
|
32
|
+
* Messages with nothing dangling are returned as they are.
|
|
33
|
+
*/
|
|
34
|
+
export declare function settleDanglingToolCalls(messages: ModelMessage[], outcomes?: readonly ToolCallOutcome[]): ModelMessage[];
|
|
35
|
+
//# sourceMappingURL=dangling-tool-calls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dangling-tool-calls.d.ts","sourceRoot":"","sources":["../../src/dangling-tool-calls.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAc,MAAM,YAAY,CAAC;AAE3E;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,QAIF,CAAC;AAEhC,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,oDAAoD,CAAC;AAO5F,mGAAmG;AACnG,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,EAAE,CAe/E;AA4BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,YAAY,EAAE,EACxB,QAAQ,GAAE,SAAS,eAAe,EAAO,GACxC,YAAY,EAAE,CAkBhB"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/** What the model is told about a call whose turn died before the call was settled. */
|
|
2
|
+
export const UNFINISHED_TOOL_CALL = 'This tool call was never completed: the turn it belonged to ended unexpectedly before it was ' +
|
|
3
|
+
'settled. Do not assume it ran, and do not assume it did not — check the current state with a ' +
|
|
4
|
+
'read tool if one exists, tell the person plainly what is and is not confirmed, and do it again ' +
|
|
5
|
+
'only if they still want it.';
|
|
6
|
+
/** What is written on the row of a call whose run ended without settling it. */
|
|
7
|
+
export const RUN_ENDED_BEFORE_TOOL_CALL = 'the run ended before this tool call was settled';
|
|
8
|
+
const DECLINED = 'The person was asked to approve this action and declined it. Nothing ran and nothing changed.';
|
|
9
|
+
const EXPIRED = 'This action needed approval, and the request expired before anyone decided. Nothing ran.';
|
|
10
|
+
/** The ids of every tool call in `messages` that the message asking for it holds no result for. */
|
|
11
|
+
export function danglingToolCallIds(messages) {
|
|
12
|
+
const ids = [];
|
|
13
|
+
for (const message of messages) {
|
|
14
|
+
const calls = message.toolCalls ?? [];
|
|
15
|
+
if (message.role !== 'assistant' || calls.length === 0) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const settled = new Set((message.toolResults ?? []).map((result) => result.id));
|
|
19
|
+
for (const call of calls) {
|
|
20
|
+
if (!settled.has(call.id)) {
|
|
21
|
+
ids.push(call.id);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return ids;
|
|
26
|
+
}
|
|
27
|
+
function resultFor(call, outcome) {
|
|
28
|
+
const base = { id: call.id, name: call.name };
|
|
29
|
+
switch (outcome?.status) {
|
|
30
|
+
case 'executed':
|
|
31
|
+
case 'auto_executed':
|
|
32
|
+
return { ...base, output: outcome.output ?? null };
|
|
33
|
+
case 'failed':
|
|
34
|
+
return { ...base, output: null, error: outcome.error ?? UNFINISHED_TOOL_CALL };
|
|
35
|
+
case 'rejected':
|
|
36
|
+
return { ...base, output: { rejected: true }, denied: true, error: DECLINED };
|
|
37
|
+
case 'expired':
|
|
38
|
+
return {
|
|
39
|
+
...base,
|
|
40
|
+
output: { rejected: true, expired: true },
|
|
41
|
+
denied: true,
|
|
42
|
+
expired: true,
|
|
43
|
+
error: EXPIRED,
|
|
44
|
+
};
|
|
45
|
+
default:
|
|
46
|
+
return { ...base, output: null, error: UNFINISHED_TOOL_CALL };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Give every tool call in a thread's history a result.
|
|
51
|
+
*
|
|
52
|
+
* A turn writes its results onto the assistant message once the step's LAST tool has settled. A turn
|
|
53
|
+
* that dies before that — its run failed, its worker was killed, an approval was never answered and
|
|
54
|
+
* the run was abandoned — leaves an assistant message that asks for tools and is answered by
|
|
55
|
+
* nothing. A provider refuses a prompt shaped like that (a tool call must be followed by its
|
|
56
|
+
* result), so every LATER turn on the thread fails too, with an error about the stream rather than
|
|
57
|
+
* about the history: one dead turn makes the whole conversation unusable.
|
|
58
|
+
*
|
|
59
|
+
* Here each such call is settled from what is actually known: the call's own row where the store
|
|
60
|
+
* can read it (`outcomes` — a tool that DID run hands the model its real output, so it is not run a
|
|
61
|
+
* second time), and otherwise a result saying the call was never completed. Pure: the same messages
|
|
62
|
+
* and outcomes always produce the same prompt, so a replay composes what the first attempt did.
|
|
63
|
+
* Messages with nothing dangling are returned as they are.
|
|
64
|
+
*/
|
|
65
|
+
export function settleDanglingToolCalls(messages, outcomes = []) {
|
|
66
|
+
const known = new Map(outcomes.map((outcome) => [outcome.id, outcome]));
|
|
67
|
+
return messages.map((message) => {
|
|
68
|
+
const calls = message.toolCalls ?? [];
|
|
69
|
+
if (message.role !== 'assistant' || calls.length === 0) {
|
|
70
|
+
return message;
|
|
71
|
+
}
|
|
72
|
+
const results = message.toolResults ?? [];
|
|
73
|
+
const settled = new Set(results.map((result) => result.id));
|
|
74
|
+
const missing = calls.filter((call) => !settled.has(call.id));
|
|
75
|
+
if (missing.length === 0) {
|
|
76
|
+
return message;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
...message,
|
|
80
|
+
toolResults: [...results, ...missing.map((call) => resultFor(call, known.get(call.id)))],
|
|
81
|
+
};
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=dangling-tool-calls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dangling-tool-calls.js","sourceRoot":"","sources":["../../src/dangling-tool-calls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AgentStore } from './spi/agent-store.js';
|
|
2
|
+
/**
|
|
3
|
+
* Leave nothing waiting on a run that is over and did not say so itself.
|
|
4
|
+
*
|
|
5
|
+
* A run normally settles its own row, its calls and its thread from inside its body. One that is
|
|
6
|
+
* refused a checkpoint position, or whose worker died, never gets there: its row stays `running`,
|
|
7
|
+
* a call it had put to a person stays `pending_approval` — an approval card that takes a "yes" and
|
|
8
|
+
* then does nothing, for ever — and its thread stays pointed at it. Everything here is written
|
|
9
|
+
* straight to the store, outside any checkpoint, because the callers are exactly the paths that
|
|
10
|
+
* have no journal left to write to; each write is idempotent (first-terminal run row, only
|
|
11
|
+
* still-pending calls, a release conditional on the holder), so doing it twice changes nothing.
|
|
12
|
+
*
|
|
13
|
+
* Best-effort on purpose: this runs while something else is already failing, and must not replace
|
|
14
|
+
* that failure with one of its own.
|
|
15
|
+
*/
|
|
16
|
+
export declare function settleDeadRun(store: AgentStore, args: {
|
|
17
|
+
runId: string;
|
|
18
|
+
threadId?: string;
|
|
19
|
+
error: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
/** `approve` / `reject` / `answer` addressed at a run that is no longer running. */
|
|
22
|
+
export declare class RunNotActiveError extends Error {
|
|
23
|
+
readonly runId: string;
|
|
24
|
+
readonly status = 409;
|
|
25
|
+
readonly code = "run_not_active";
|
|
26
|
+
constructor(runId: string);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=dead-run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-run.d.ts","sourceRoot":"","sources":["../../src/dead-run.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,oFAAoF;AACpF,qBAAa,iBAAkB,SAAQ,KAAK;IAG9B,QAAQ,CAAC,KAAK,EAAE,MAAM;IAFlC,QAAQ,CAAC,MAAM,OAAO;IACtB,QAAQ,CAAC,IAAI,oBAAoB;IACjC,YAAqB,KAAK,EAAE,MAAM,EAKjC;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { RUN_ENDED_BEFORE_TOOL_CALL } from './dangling-tool-calls.js';
|
|
2
|
+
import { releaseThreadRun } from './spi/chat-queue.js';
|
|
3
|
+
/**
|
|
4
|
+
* Leave nothing waiting on a run that is over and did not say so itself.
|
|
5
|
+
*
|
|
6
|
+
* A run normally settles its own row, its calls and its thread from inside its body. One that is
|
|
7
|
+
* refused a checkpoint position, or whose worker died, never gets there: its row stays `running`,
|
|
8
|
+
* a call it had put to a person stays `pending_approval` — an approval card that takes a "yes" and
|
|
9
|
+
* then does nothing, for ever — and its thread stays pointed at it. Everything here is written
|
|
10
|
+
* straight to the store, outside any checkpoint, because the callers are exactly the paths that
|
|
11
|
+
* have no journal left to write to; each write is idempotent (first-terminal run row, only
|
|
12
|
+
* still-pending calls, a release conditional on the holder), so doing it twice changes nothing.
|
|
13
|
+
*
|
|
14
|
+
* Best-effort on purpose: this runs while something else is already failing, and must not replace
|
|
15
|
+
* that failure with one of its own.
|
|
16
|
+
*/
|
|
17
|
+
export async function settleDeadRun(store, args) {
|
|
18
|
+
await store
|
|
19
|
+
.recordRunEnd({ runId: args.runId, status: 'failed', error: args.error })
|
|
20
|
+
.catch(() => undefined);
|
|
21
|
+
await store.failUnsettledToolCalls?.(args.runId, RUN_ENDED_BEFORE_TOOL_CALL).catch(() => 0);
|
|
22
|
+
if (args.threadId !== undefined) {
|
|
23
|
+
await releaseThreadRun(store, args.threadId, args.runId).catch(() => undefined);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** `approve` / `reject` / `answer` addressed at a run that is no longer running. */
|
|
27
|
+
export class RunNotActiveError extends Error {
|
|
28
|
+
runId;
|
|
29
|
+
status = 409;
|
|
30
|
+
code = 'run_not_active';
|
|
31
|
+
constructor(runId) {
|
|
32
|
+
super('This request is no longer waiting for an answer: the turn it belonged to has ended. Send the message again.');
|
|
33
|
+
this.runId = runId;
|
|
34
|
+
this.name = 'RunNotActiveError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=dead-run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dead-run.js","sourceRoot":"","sources":["../../src/dead-run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAEtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAiB,EACjB,IAAyD;IAEzD,MAAM,KAAK;SACR,YAAY,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;SACxE,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAC1B,MAAM,KAAK,CAAC,sBAAsB,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,0BAA0B,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC5F,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;IAClF,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAGrB,KAAK;IAFjB,MAAM,GAAG,GAAG,CAAC;IACb,IAAI,GAAG,gBAAgB,CAAC;IACjC,YAAqB,KAAa;QAChC,KAAK,CACH,6GAA6G,CAC9G,CAAC;qBAHiB,KAAK;QAIxB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF"}
|
|
@@ -19,9 +19,10 @@ import type { QuotaProvider } from './spi/quota-provider.js';
|
|
|
19
19
|
import type { Retriever } from './spi/retriever.js';
|
|
20
20
|
import type { RolesPolicy } from './spi/roles-policy.js';
|
|
21
21
|
import type { TokenStreamSink } from './spi/token-stream-sink.js';
|
|
22
|
-
import type { ActorDirectoryFactory, AttachmentStagingContext, AttachmentStagingFactory, EmbeddingFactory, GovernanceQueriesContext, GovernanceQueriesFactory, LucidGovernanceConfig, LucidPricingConfig, LucidStoreConfig, MemoryActorDirectoryConfig, MemoryRetrieverConfig, MemoryStoreConfig, PgVectorRetrieverConfig, PricingContext, PricingFactory, RedisTokenSinkConfig, RetrieverContext, RetrieverFactory, StoreContext, StoreFactory, TokenSinkFactory } from './stores/factory.js';
|
|
22
|
+
import type { ActorDirectoryFactory, AttachmentStagingContext, AttachmentStagingFactory, EmbeddingFactory, GovernanceQueriesContext, GovernanceQueriesFactory, LucidGovernanceConfig, LucidPricingConfig, LucidStoreConfig, LucidTokenSinkConfig, MemoryActorDirectoryConfig, MemoryRetrieverConfig, MemoryStoreConfig, PgVectorRetrieverConfig, PricingContext, PricingFactory, RedisTokenSinkConfig, RetrieverContext, RetrieverFactory, StoreContext, StoreFactory, TokenSinkFactory } from './stores/factory.js';
|
|
23
23
|
import { actorDirectories, attachmentStores, governanceQueries, pricingStores, retrievers, stores, streamTransports, tokenSinks } from './stores/factory.js';
|
|
24
24
|
import type { QuotaLimits } from './stores/ledger-quota-provider.js';
|
|
25
|
+
import type { EmptyRoles } from './tool-registry.js';
|
|
25
26
|
import type { ToolTransientRetrySetting } from './tool-retry.js';
|
|
26
27
|
import type { Actor, AgentDefinition } from './types.js';
|
|
27
28
|
/** A lazy factory thunk, so the peer (`ai`/a provider SDK) is imported only when the config loads. */
|
|
@@ -61,6 +62,8 @@ export interface AgentConfig {
|
|
|
61
62
|
* Live token transport ("data plane"), or a lazy factory. Defaults to the in-process sink (single
|
|
62
63
|
* replica). Use `tokenSinks.redis({...})` (a.k.a. `streamTransports.redis`) for the multi-replica
|
|
63
64
|
* Redis sink so any pod can serve any run's SSE stream — the SSE envelope is unchanged either way.
|
|
65
|
+
* With several replicas and no Redis, `tokenSinks.lucid()` keeps the frames in the app's SQL
|
|
66
|
+
* database instead (polled; see `LucidTokenStreamSink`).
|
|
64
67
|
*/
|
|
65
68
|
sink?: TokenStreamSink | SinkFactory;
|
|
66
69
|
/**
|
|
@@ -164,6 +167,12 @@ export interface AgentConfig {
|
|
|
164
167
|
* fail-closed default.
|
|
165
168
|
*/
|
|
166
169
|
defaultRoles?: string[];
|
|
170
|
+
/**
|
|
171
|
+
* What an empty roles list means to the default authorizer: `'allow'` (default) — no restriction;
|
|
172
|
+
* `'deny'` — nobody, so a tool needs `roles` (or a non-empty `defaultRoles`) to be reachable. Only
|
|
173
|
+
* consulted when no `authorizer` is set.
|
|
174
|
+
*/
|
|
175
|
+
emptyRoles?: EmptyRoles;
|
|
167
176
|
/**
|
|
168
177
|
* Resolves the acting actor per request (the identity seam). Defaults to a resolver that THROWS on
|
|
169
178
|
* every request — the agent never fabricates a caller. Wire `AuthActorResolver` / `HeaderActorResolver`.
|
|
@@ -300,6 +309,6 @@ export interface AgentConfig {
|
|
|
300
309
|
}
|
|
301
310
|
/** Identity helper giving `config/agent.ts` full type-checking. */
|
|
302
311
|
export declare function defineConfig(config: AgentConfig): AgentConfig;
|
|
303
|
-
export type { ActorDirectoryFactory, AttachmentStagingContext, AttachmentStagingFactory, EmbeddingFactory, GovernanceQueriesContext, GovernanceQueriesFactory, LucidGovernanceConfig, LucidPricingConfig, LucidStoreConfig, MemoryActorDirectoryConfig, MemoryRetrieverConfig, MemoryStoreConfig, PgVectorRetrieverConfig, PricingContext, PricingFactory, RedisTokenSinkConfig, RetrieverContext, RetrieverFactory, StoreContext, StoreFactory, TokenSinkFactory, };
|
|
312
|
+
export type { ActorDirectoryFactory, AttachmentStagingContext, AttachmentStagingFactory, EmbeddingFactory, GovernanceQueriesContext, GovernanceQueriesFactory, LucidGovernanceConfig, LucidPricingConfig, LucidStoreConfig, LucidTokenSinkConfig, MemoryActorDirectoryConfig, MemoryRetrieverConfig, MemoryStoreConfig, PgVectorRetrieverConfig, PricingContext, PricingFactory, RedisTokenSinkConfig, RetrieverContext, RetrieverFactory, StoreContext, StoreFactory, TokenSinkFactory, };
|
|
304
313
|
export { actorDirectories, attachmentStores, governanceQueries, pricingStores, retrievers, stores, streamTransports, tokenSinks, };
|
|
305
314
|
//# sourceMappingURL=define_config.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"define_config.d.ts","sourceRoot":"","sources":["../../src/define_config.ts"],"names":[],"mappings":"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"}
|