@dudousxd/nestjs-agent-core 0.36.0 → 0.38.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/dist/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(src_exports, {
25
25
  AGENT_ACTOR_RESOLVER: () => AGENT_ACTOR_RESOLVER,
26
26
  AGENT_APPROVAL_PORT: () => AGENT_APPROVAL_PORT,
27
27
  AGENT_ATTACHMENT_STAGING: () => AGENT_ATTACHMENT_STAGING,
28
+ AGENT_CONFIRM_TOKEN_STORE: () => AGENT_CONFIRM_TOKEN_STORE,
28
29
  AGENT_DEPS_FACTORY: () => AGENT_DEPS_FACTORY,
29
30
  AGENT_DIAGNOSTIC_EVENTS: () => AGENT_DIAGNOSTIC_EVENTS,
30
31
  AGENT_DURABLE_RUNNER: () => AGENT_DURABLE_RUNNER,
@@ -54,7 +55,10 @@ __export(src_exports, {
54
55
  ASK_TOOL_NAME: () => ASK_TOOL_NAME,
55
56
  AgentRegistry: () => AgentRegistry,
56
57
  AgentStreamError: () => AgentStreamError,
58
+ CONFIRM_JSON_SCHEMA_PROPERTIES: () => CONFIRM_JSON_SCHEMA_PROPERTIES,
57
59
  ClosedRolesPolicy: () => ClosedRolesPolicy,
60
+ ConfirmTokenError: () => ConfirmTokenError,
61
+ DEFAULT_CONFIRM_TTL_MS: () => DEFAULT_CONFIRM_TTL_MS,
58
62
  DEFAULT_HISTORY_SUMMARY_INSTRUCTION: () => DEFAULT_HISTORY_SUMMARY_INSTRUCTION,
59
63
  DEFAULT_INCREMENTAL_LOOKBACK_CHARS: () => DEFAULT_INCREMENTAL_LOOKBACK_CHARS,
60
64
  DEFAULT_INTAKE_PREAMBLE: () => DEFAULT_INTAKE_PREAMBLE,
@@ -70,6 +74,7 @@ __export(src_exports, {
70
74
  ELICITATION_INPUT_TYPES: () => ELICITATION_INPUT_TYPES,
71
75
  GLOBAL_SCOPE: () => GLOBAL_SCOPE,
72
76
  InMemoryAgentStore: () => InMemoryAgentStore,
77
+ InMemoryConfirmTokenStore: () => InMemoryConfirmTokenStore,
73
78
  MAX_ASK_QUESTIONS: () => MAX_ASK_QUESTIONS,
74
79
  OutputRejectedError: () => OutputRejectedError,
75
80
  ProcessorFailedError: () => ProcessorFailedError,
@@ -83,8 +88,10 @@ __export(src_exports, {
83
88
  RUN_NOT_ACTIVE_MESSAGE: () => RUN_NOT_ACTIVE_MESSAGE,
84
89
  RUN_NO_LONGER_RUNNING: () => RUN_NO_LONGER_RUNNING,
85
90
  RunCancelledError: () => RunCancelledError,
91
+ SCHEMA_EXTENSION: () => SCHEMA_EXTENSION,
86
92
  SKILL_TOOL_DESCRIPTION: () => SKILL_TOOL_DESCRIPTION,
87
93
  SKILL_TOOL_NAME: () => SKILL_TOOL_NAME,
94
+ SqlTokenStreamSink: () => SqlTokenStreamSink,
88
95
  StructuredOutputError: () => StructuredOutputError,
89
96
  THREAD_DETAIL_CONTENT_CHARS: () => THREAD_DETAIL_CONTENT_CHARS,
90
97
  ToolDisabledError: () => ToolDisabledError,
@@ -105,8 +112,10 @@ __export(src_exports, {
105
112
  buildMemoryBlock: () => buildMemoryBlock,
106
113
  buildSkillsBlock: () => buildSkillsBlock,
107
114
  canActorUseTool: () => canActorUseTool,
115
+ canonicalJson: () => canonicalJson,
108
116
  closeEmptyRoles: () => closeEmptyRoles,
109
117
  compositeSkillProvider: () => compositeSkillProvider,
118
+ confirmTokenExpiry: () => confirmTokenExpiry,
110
119
  createFrameBuffer: () => createFrameBuffer,
111
120
  createIncrementalGate: () => createIncrementalGate,
112
121
  createNoopEmitUi: () => createNoopEmitUi,
@@ -116,6 +125,7 @@ __export(src_exports, {
116
125
  decodeStreamEvent: () => decodeStreamEvent,
117
126
  defaultCanDecide: () => defaultCanDecide,
118
127
  defaultScopeResolver: () => defaultScopeResolver,
128
+ defineConfirmedTool: () => defineConfirmedTool,
119
129
  detachedDelivered: () => detachedDelivered,
120
130
  detachedStarted: () => detachedStarted,
121
131
  detachedUnsettled: () => detachedUnsettled,
@@ -132,6 +142,7 @@ __export(src_exports, {
132
142
  findCatalogModel: () => findCatalogModel,
133
143
  gateFollowUps: () => gateFollowUps,
134
144
  gateTail: () => gateTail,
145
+ hashConfirmToken: () => hashConfirmToken,
135
146
  invokeWithTransientRetry: () => invokeWithTransientRetry,
136
147
  isChatQueueStore: () => isChatQueueStore,
137
148
  isControlFlowSignal: () => isControlFlowSignal,
@@ -184,12 +195,14 @@ __export(src_exports, {
184
195
  runAgentLoop: () => runAgentLoop,
185
196
  runInputProcessors: () => runInputProcessors,
186
197
  runOutputProcessors: () => runOutputProcessors,
198
+ schemaExtensionOf: () => schemaExtensionOf,
187
199
  seedModelPrices: () => seedModelPrices,
188
200
  settleAll: () => settleAll,
189
201
  settleDanglingToolCalls: () => settleDanglingToolCalls,
190
202
  settleDeadRun: () => settleDeadRun,
191
203
  settleElicitation: () => settleElicitation,
192
204
  settleUnsettledDelegation: () => settleUnsettledDelegation,
205
+ signConfirmToken: () => signConfirmToken,
193
206
  skillInputSchema: () => skillInputSchema,
194
207
  skillToolDefinition: () => skillToolDefinition,
195
208
  skillWriteVerdict: () => skillWriteVerdict,
@@ -208,8 +221,10 @@ __export(src_exports, {
208
221
  validateElicitationAnswer: () => validateElicitationAnswer,
209
222
  validateElicitationValue: () => validateElicitationValue,
210
223
  validateStructured: () => validateStructured,
224
+ verifyConfirmToken: () => verifyConfirmToken,
211
225
  windowHistory: () => windowHistory,
212
226
  withAskTool: () => withAskTool,
227
+ withConfirmFields: () => withConfirmFields,
213
228
  withMemoryTool: () => withMemoryTool,
214
229
  withSelectedModel: () => withSelectedModel,
215
230
  withSkillTool: () => withSkillTool,
@@ -246,6 +261,7 @@ var AGENT_APPROVAL_PORT = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:app
246
261
  var AGENT_SKILLS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:skills");
247
262
  var AGENT_MEMORY = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:memory");
248
263
  var AGENT_SKILL_SOURCES = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:skill-sources");
264
+ var AGENT_CONFIRM_TOKEN_STORE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:confirm-token-store");
249
265
 
250
266
  // src/spi/model-catalog.ts
251
267
  function staticModelCatalog(view) {
@@ -306,7 +322,7 @@ __name(decodeStreamEvent, "decodeStreamEvent");
306
322
 
307
323
  // src/turn-frames.ts
308
324
  function observeTurnFrames(writer, now = Date.now) {
309
- const decoder2 = new TextDecoder();
325
+ const decoder3 = new TextDecoder();
310
326
  let pending = "";
311
327
  let reasoning = "";
312
328
  let reasoningMs = 0;
@@ -346,7 +362,7 @@ function observeTurnFrames(writer, now = Date.now) {
346
362
  }
347
363
  __name(observeLine, "observeLine");
348
364
  function observe(chunk) {
349
- pending += decoder2.decode(chunk, {
365
+ pending += decoder3.decode(chunk, {
350
366
  stream: true
351
367
  });
352
368
  let newline = pending.indexOf("\n");
@@ -6006,12 +6022,561 @@ var InMemoryAgentStore = class {
6006
6022
  };
6007
6023
  }
6008
6024
  };
6025
+
6026
+ // src/confirm-token.ts
6027
+ var import_node_crypto2 = require("crypto");
6028
+ var DEFAULT_CONFIRM_TTL_MS = 15 * 60 * 1e3;
6029
+ function canonicalJson(value) {
6030
+ if (value === void 0) return "null";
6031
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
6032
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
6033
+ if (typeof value.toJSON === "function") {
6034
+ return canonicalJson(value.toJSON());
6035
+ }
6036
+ const entries = Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
6037
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
6038
+ }
6039
+ __name(canonicalJson, "canonicalJson");
6040
+ function signature(subject, expiresAt, secret) {
6041
+ if (secret === "") {
6042
+ throw new Error("confirm token: the secret is empty");
6043
+ }
6044
+ return (0, import_node_crypto2.createHmac)("sha256", secret).update(canonicalJson([
6045
+ "agent-confirm",
6046
+ subject.tool,
6047
+ subject.actorId,
6048
+ subject.tenantRef ?? "",
6049
+ expiresAt,
6050
+ subject.args
6051
+ ])).digest("base64url");
6052
+ }
6053
+ __name(signature, "signature");
6054
+ function signConfirmToken(subject, options) {
6055
+ return `${options.expiresAt}.${signature(subject, options.expiresAt, options.secret)}`;
6056
+ }
6057
+ __name(signConfirmToken, "signConfirmToken");
6058
+ function confirmTokenExpiry(token) {
6059
+ const separator = token.indexOf(".");
6060
+ if (separator <= 0) return void 0;
6061
+ const expiresAt = Number(token.slice(0, separator));
6062
+ return Number.isSafeInteger(expiresAt) && expiresAt > 0 ? expiresAt : void 0;
6063
+ }
6064
+ __name(confirmTokenExpiry, "confirmTokenExpiry");
6065
+ function verifyConfirmToken(token, subject, options) {
6066
+ if (typeof token !== "string") return false;
6067
+ const expiresAt = confirmTokenExpiry(token);
6068
+ if (expiresAt === void 0 || expiresAt < (options.now ?? Date.now())) return false;
6069
+ const expected = Buffer.from(signature(subject, expiresAt, options.secret));
6070
+ const given = Buffer.from(token.slice(token.indexOf(".") + 1));
6071
+ return expected.length === given.length && (0, import_node_crypto2.timingSafeEqual)(expected, given);
6072
+ }
6073
+ __name(verifyConfirmToken, "verifyConfirmToken");
6074
+ function hashConfirmToken(token) {
6075
+ return (0, import_node_crypto2.createHash)("sha256").update(token).digest("hex");
6076
+ }
6077
+ __name(hashConfirmToken, "hashConfirmToken");
6078
+ var InMemoryConfirmTokenStore = class {
6079
+ static {
6080
+ __name(this, "InMemoryConfirmTokenStore");
6081
+ }
6082
+ claims = /* @__PURE__ */ new Map();
6083
+ async claim(input) {
6084
+ await this.purgeExpired();
6085
+ if (this.claims.has(input.hash)) return false;
6086
+ this.claims.set(input.hash, {
6087
+ ...input
6088
+ });
6089
+ return true;
6090
+ }
6091
+ async release(hash) {
6092
+ this.claims.delete(hash);
6093
+ }
6094
+ async purgeExpired(now = Date.now()) {
6095
+ let purged = 0;
6096
+ for (const [hash, claim] of this.claims) {
6097
+ if (claim.expiresAt < now) {
6098
+ this.claims.delete(hash);
6099
+ purged += 1;
6100
+ }
6101
+ }
6102
+ return purged;
6103
+ }
6104
+ /** How many tokens are currently marked as spent. */
6105
+ get size() {
6106
+ return this.claims.size;
6107
+ }
6108
+ };
6109
+
6110
+ // src/confirmed-tool.ts
6111
+ var DEFAULT_MESSAGES = {
6112
+ 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).',
6113
+ 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.',
6114
+ used: "This preview was already confirmed: nothing was written again. To repeat the operation, ask for a new preview."
6115
+ };
6116
+ var ConfirmTokenError = class extends Error {
6117
+ static {
6118
+ __name(this, "ConfirmTokenError");
6119
+ }
6120
+ reason;
6121
+ constructor(reason, message) {
6122
+ super(message), this.reason = reason;
6123
+ this.name = "ConfirmTokenError";
6124
+ }
6125
+ };
6126
+ var CONFIRM_JSON_SCHEMA_PROPERTIES = {
6127
+ confirm: {
6128
+ type: "boolean",
6129
+ description: "Omit to get a preview (nothing is written). Send true, with confirmToken, to commit."
6130
+ },
6131
+ confirmToken: {
6132
+ type: "string",
6133
+ description: "The confirmToken the preview returned. Required when confirm is true."
6134
+ }
6135
+ };
6136
+ var SCHEMA_EXTENSION = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:schema-extension");
6137
+ function schemaExtensionOf(schema) {
6138
+ const extension = schema[SCHEMA_EXTENSION];
6139
+ return typeof extension === "object" && extension !== null ? extension : void 0;
6140
+ }
6141
+ __name(schemaExtensionOf, "schemaExtensionOf");
6142
+ function isRecord(value) {
6143
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6144
+ }
6145
+ __name(isRecord, "isRecord");
6146
+ function splitConfirm(input) {
6147
+ if (!isRecord(input)) return {
6148
+ args: input,
6149
+ fields: {}
6150
+ };
6151
+ const { confirm, confirmToken, ...args } = input;
6152
+ return {
6153
+ args,
6154
+ fields: {
6155
+ ...typeof confirm === "boolean" ? {
6156
+ confirm
6157
+ } : {},
6158
+ ...typeof confirmToken === "string" ? {
6159
+ confirmToken
6160
+ } : {}
6161
+ }
6162
+ };
6163
+ }
6164
+ __name(splitConfirm, "splitConfirm");
6165
+ function jsonSchemaInputOf(schema) {
6166
+ const standard = schema["~standard"];
6167
+ const input = standard.jsonSchema?.input;
6168
+ return typeof input === "function" ? (options) => input.call(standard.jsonSchema, options) : void 0;
6169
+ }
6170
+ __name(jsonSchemaInputOf, "jsonSchemaInputOf");
6171
+ function withConfirmFields(schema) {
6172
+ const inner = schema["~standard"];
6173
+ const innerJsonSchema = jsonSchemaInputOf(schema);
6174
+ const describe = /* @__PURE__ */ __name((options) => {
6175
+ const base = innerJsonSchema?.(options) ?? {
6176
+ additionalProperties: true
6177
+ };
6178
+ return {
6179
+ ...base,
6180
+ type: "object",
6181
+ properties: {
6182
+ ...isRecord(base.properties) ? base.properties : {},
6183
+ ...CONFIRM_JSON_SCHEMA_PROPERTIES
6184
+ }
6185
+ };
6186
+ }, "describe");
6187
+ const wrapped = {
6188
+ [SCHEMA_EXTENSION]: {
6189
+ base: schema,
6190
+ properties: CONFIRM_JSON_SCHEMA_PROPERTIES
6191
+ },
6192
+ "~standard": {
6193
+ version: 1,
6194
+ vendor: "@dudousxd/nestjs-agent",
6195
+ validate: /* @__PURE__ */ __name(async (value) => {
6196
+ if (!isRecord(value)) {
6197
+ return {
6198
+ issues: [
6199
+ {
6200
+ message: "expected an object"
6201
+ }
6202
+ ]
6203
+ };
6204
+ }
6205
+ const issues = [];
6206
+ if (value.confirm != null && typeof value.confirm !== "boolean") {
6207
+ issues.push({
6208
+ message: "expected a boolean",
6209
+ path: [
6210
+ "confirm"
6211
+ ]
6212
+ });
6213
+ }
6214
+ if (value.confirmToken != null && typeof value.confirmToken !== "string") {
6215
+ issues.push({
6216
+ message: "expected a string",
6217
+ path: [
6218
+ "confirmToken"
6219
+ ]
6220
+ });
6221
+ }
6222
+ const { args, fields } = splitConfirm(value);
6223
+ const result = await inner.validate(args);
6224
+ if (result.issues !== void 0) {
6225
+ return {
6226
+ issues: [
6227
+ ...issues,
6228
+ ...result.issues
6229
+ ]
6230
+ };
6231
+ }
6232
+ if (issues.length > 0) {
6233
+ return {
6234
+ issues
6235
+ };
6236
+ }
6237
+ return {
6238
+ value: isRecord(result.value) ? {
6239
+ ...result.value,
6240
+ ...fields
6241
+ } : result.value
6242
+ };
6243
+ }, "validate"),
6244
+ jsonSchema: {
6245
+ input: describe,
6246
+ output: describe
6247
+ }
6248
+ }
6249
+ };
6250
+ return wrapped;
6251
+ }
6252
+ __name(withConfirmFields, "withConfirmFields");
6253
+ function defineConfirmedTool(options, steps) {
6254
+ const { secret, ttlMs = DEFAULT_CONFIRM_TTL_MS, store, messages, input, ...specFields } = options;
6255
+ const text = {
6256
+ ...DEFAULT_MESSAGES,
6257
+ ...messages
6258
+ };
6259
+ const howToConfirm = text.confirm.replaceAll("{minutes}", String(Math.round(ttlMs / 6e4)));
6260
+ const secretNow = /* @__PURE__ */ __name(() => typeof secret === "function" ? secret() : secret, "secretNow");
6261
+ const handler = {
6262
+ async execute(value, ctx) {
6263
+ const { args, fields } = splitConfirm(value);
6264
+ const subject = {
6265
+ tool: options.name,
6266
+ actorId: ctx.actor.id,
6267
+ ...ctx.actor.tenantRef !== void 0 ? {
6268
+ tenantRef: ctx.actor.tenantRef
6269
+ } : {},
6270
+ args
6271
+ };
6272
+ const confirming = fields.confirm === true;
6273
+ const token = fields.confirmToken;
6274
+ if (confirming && !verifyConfirmToken(token, subject, {
6275
+ secret: secretNow()
6276
+ })) {
6277
+ throw new ConfirmTokenError("invalid", text.invalid);
6278
+ }
6279
+ const prepared = await steps.prepare(args, ctx);
6280
+ if (!confirming || token === void 0) {
6281
+ const preview = await steps.preview(prepared, ctx);
6282
+ const expiresAt = Date.now() + ttlMs;
6283
+ return {
6284
+ status: "preview",
6285
+ summary: preview.summary,
6286
+ ...preview.data !== void 0 ? {
6287
+ data: preview.data
6288
+ } : {},
6289
+ confirmToken: signConfirmToken(subject, {
6290
+ secret: secretNow(),
6291
+ expiresAt
6292
+ }),
6293
+ expiresAt: new Date(expiresAt).toISOString(),
6294
+ confirm: howToConfirm
6295
+ };
6296
+ }
6297
+ const hash = hashConfirmToken(token);
6298
+ if (store !== void 0) {
6299
+ const claimed = await store.claim({
6300
+ hash,
6301
+ actorRef: ctx.actor.id,
6302
+ tool: options.name,
6303
+ expiresAt: confirmTokenExpiry(token) ?? Date.now() + ttlMs
6304
+ });
6305
+ if (!claimed) {
6306
+ throw new ConfirmTokenError("used", text.used);
6307
+ }
6308
+ }
6309
+ try {
6310
+ const done = await steps.commit(prepared, ctx);
6311
+ return {
6312
+ status: "done",
6313
+ summary: done.summary,
6314
+ ...done.data !== void 0 ? {
6315
+ data: done.data
6316
+ } : {}
6317
+ };
6318
+ } catch (error) {
6319
+ await store?.release(hash).catch(() => void 0);
6320
+ throw error;
6321
+ }
6322
+ }
6323
+ };
6324
+ return {
6325
+ spec: {
6326
+ ...specFields,
6327
+ kind: "read",
6328
+ inputSchema: withConfirmFields(input)
6329
+ },
6330
+ handler
6331
+ };
6332
+ }
6333
+ __name(defineConfirmedTool, "defineConfirmedTool");
6334
+
6335
+ // src/sql-token-stream-sink.ts
6336
+ var DEFAULT_TTL_SECONDS = 3600;
6337
+ var DEFAULT_POLL_INTERVAL_MS = 250;
6338
+ var DEFAULT_IDLE_POLL_INTERVAL_MS = 1e3;
6339
+ var IDLE_AFTER_MS = 5e3;
6340
+ var DEFAULT_FLUSH_MS = 50;
6341
+ var READ_PAGE = 500;
6342
+ var PURGE_CHUNK = 200;
6343
+ var AUTO_PURGE_EVERY_MS = 6e4;
6344
+ var APPEND_ATTEMPTS = 8;
6345
+ var decoder2 = new TextDecoder();
6346
+ var encoder2 = new TextEncoder();
6347
+ var sleep = /* @__PURE__ */ __name((ms) => new Promise((resolve) => setTimeout(resolve, ms)), "sleep");
6348
+ function textOf(line) {
6349
+ if (!line.startsWith('{"kind":"text"')) return null;
6350
+ try {
6351
+ const parsed = JSON.parse(line);
6352
+ if (typeof parsed === "object" && parsed !== null && Object.keys(parsed).length === 2 && parsed.kind === "text" && typeof parsed.text === "string") {
6353
+ return parsed.text;
6354
+ }
6355
+ } catch {
6356
+ }
6357
+ return null;
6358
+ }
6359
+ __name(textOf, "textOf");
6360
+ var SqlTokenStreamSink = class {
6361
+ static {
6362
+ __name(this, "SqlTokenStreamSink");
6363
+ }
6364
+ table;
6365
+ pollIntervalMs;
6366
+ idlePollIntervalMs;
6367
+ flushMs;
6368
+ ttlSeconds;
6369
+ autoPurge;
6370
+ writes = /* @__PURE__ */ new Map();
6371
+ lastAutoPurge = 0;
6372
+ constructor(table, options = {}) {
6373
+ this.table = table;
6374
+ this.pollIntervalMs = Math.max(1, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
6375
+ this.idlePollIntervalMs = Math.max(this.pollIntervalMs, options.idlePollIntervalMs ?? DEFAULT_IDLE_POLL_INTERVAL_MS);
6376
+ this.flushMs = Math.max(0, options.flushMs ?? DEFAULT_FLUSH_MS);
6377
+ this.ttlSeconds = options.ttlSeconds ?? DEFAULT_TTL_SECONDS;
6378
+ this.autoPurge = options.autoPurge ?? true;
6379
+ }
6380
+ runWrites(runId) {
6381
+ let state = this.writes.get(runId);
6382
+ if (state === void 0) {
6383
+ state = {
6384
+ text: null,
6385
+ timer: void 0,
6386
+ tail: Promise.resolve(),
6387
+ pending: 0,
6388
+ failure: void 0
6389
+ };
6390
+ this.writes.set(runId, state);
6391
+ }
6392
+ return state;
6393
+ }
6394
+ /** Forget a run's write state once nothing is gathered, queued or left to report. */
6395
+ release(runId, state) {
6396
+ if (state.pending === 0 && state.text === null && state.failure === void 0 && this.writes.get(runId) === state) {
6397
+ this.writes.delete(runId);
6398
+ }
6399
+ }
6400
+ /** Queue `work` behind every write already queued for the run, so rows land in call order. */
6401
+ enqueue(runId, state, work) {
6402
+ state.pending += 1;
6403
+ const next = state.tail.then(work);
6404
+ state.tail = next.then(() => void 0, () => void 0).then(() => {
6405
+ state.pending -= 1;
6406
+ this.release(runId, state);
6407
+ });
6408
+ return next;
6409
+ }
6410
+ /** Queue the gathered text (if any) as one `text` row. */
6411
+ flushText(runId, state) {
6412
+ if (state.timer !== void 0) {
6413
+ clearTimeout(state.timer);
6414
+ state.timer = void 0;
6415
+ }
6416
+ if (state.text === null) return Promise.resolve();
6417
+ const line = decoder2.decode(encodeStreamEvent({
6418
+ kind: "text",
6419
+ text: state.text
6420
+ }));
6421
+ state.text = null;
6422
+ return this.enqueue(runId, state, () => this.append(runId, line, null));
6423
+ }
6424
+ /** Raise, once, a gathered write that failed with nobody awaiting it. */
6425
+ raiseFailure(state) {
6426
+ const failure = state.failure;
6427
+ if (failure !== void 0) {
6428
+ state.failure = void 0;
6429
+ throw failure.error;
6430
+ }
6431
+ }
6432
+ /** Append one row — a frame, or the terminal row (`frame: null`, with `error` on a failure). */
6433
+ async append(runId, frame, error) {
6434
+ for (let attempt = 1; ; attempt += 1) {
6435
+ try {
6436
+ await this.table.append(runId, {
6437
+ frame,
6438
+ error,
6439
+ createdAt: Date.now()
6440
+ });
6441
+ return;
6442
+ } catch (caught) {
6443
+ if (attempt >= APPEND_ATTEMPTS || !this.table.isUniqueViolation(caught)) throw caught;
6444
+ }
6445
+ }
6446
+ }
6447
+ /** Write out what is gathered, then a terminal row; raise a failure nobody saw. */
6448
+ async terminate(runId, error) {
6449
+ const state = this.runWrites(runId);
6450
+ const flushed = this.flushText(runId, state);
6451
+ const ended = this.enqueue(runId, state, () => this.append(runId, null, error));
6452
+ await Promise.all([
6453
+ flushed,
6454
+ ended
6455
+ ]);
6456
+ this.raiseFailure(state);
6457
+ this.release(runId, state);
6458
+ this.purgeInBackground();
6459
+ }
6460
+ /**
6461
+ * The run's writer. Writers opened for the same run in one process share what they are gathering,
6462
+ * so a delegated run's text (forwarded through a child writer) and its parent's next frame keep
6463
+ * the order they were written in.
6464
+ */
6465
+ open(runId) {
6466
+ return {
6467
+ write: /* @__PURE__ */ __name(async (chunk) => {
6468
+ const state = this.runWrites(runId);
6469
+ this.raiseFailure(state);
6470
+ const line = decoder2.decode(chunk);
6471
+ const text = this.flushMs > 0 ? textOf(line.trimEnd()) : null;
6472
+ if (text !== null) {
6473
+ state.text = (state.text ?? "") + text;
6474
+ if (state.timer === void 0) {
6475
+ state.timer = setTimeout(() => {
6476
+ state.timer = void 0;
6477
+ this.flushText(runId, state).catch((error) => {
6478
+ state.failure = {
6479
+ error
6480
+ };
6481
+ });
6482
+ }, this.flushMs);
6483
+ }
6484
+ return;
6485
+ }
6486
+ const flushed = this.flushText(runId, state);
6487
+ const appended = this.enqueue(runId, state, () => this.append(runId, line, null));
6488
+ await Promise.all([
6489
+ flushed,
6490
+ appended
6491
+ ]);
6492
+ }, "write"),
6493
+ flush: /* @__PURE__ */ __name(async () => {
6494
+ const state = this.runWrites(runId);
6495
+ const flushed = this.flushText(runId, state);
6496
+ await Promise.all([
6497
+ flushed,
6498
+ state.tail
6499
+ ]);
6500
+ this.raiseFailure(state);
6501
+ this.release(runId, state);
6502
+ }, "flush"),
6503
+ end: /* @__PURE__ */ __name(() => this.terminate(runId, null), "end"),
6504
+ fail: /* @__PURE__ */ __name((error) => this.terminate(runId, JSON.stringify({
6505
+ code: error.code,
6506
+ message: error.message
6507
+ })), "fail")
6508
+ };
6509
+ }
6510
+ async *subscribe(runId) {
6511
+ let cursor = 0;
6512
+ let quietSince = Date.now();
6513
+ while (true) {
6514
+ const rows = await this.table.read(runId, cursor, READ_PAGE);
6515
+ for (const row of rows) {
6516
+ cursor = row.seq;
6517
+ if (row.frame === null) {
6518
+ if (row.error !== null) {
6519
+ throw new AgentStreamError(JSON.parse(row.error));
6520
+ }
6521
+ return;
6522
+ }
6523
+ yield encoder2.encode(row.frame);
6524
+ }
6525
+ if (rows.length > 0) {
6526
+ quietSince = Date.now();
6527
+ if (rows.length >= READ_PAGE) continue;
6528
+ }
6529
+ await sleep(Date.now() - quietSince >= IDLE_AFTER_MS ? this.idlePollIntervalMs : this.pollIntervalMs);
6530
+ }
6531
+ }
6532
+ /** Does the table hold anything for the run — a frame, or its end? */
6533
+ has(runId) {
6534
+ return this.table.has(runId);
6535
+ }
6536
+ async close(runId) {
6537
+ const state = this.writes.get(runId);
6538
+ if (state !== void 0) {
6539
+ if (state.timer !== void 0) clearTimeout(state.timer);
6540
+ state.timer = void 0;
6541
+ state.text = null;
6542
+ state.failure = void 0;
6543
+ await state.tail;
6544
+ this.writes.delete(runId);
6545
+ }
6546
+ await this.table.remove([
6547
+ runId
6548
+ ]);
6549
+ }
6550
+ /**
6551
+ * Delete every run whose last write is older than `ttlSeconds` at `now` (epoch-ms) — ended runs
6552
+ * past their replay window and runs that crashed without ending alike. Returns how many runs went.
6553
+ * Safe from any replica, and from several at once. A no-op under `ttlSeconds: 0`.
6554
+ */
6555
+ async purgeExpired(now = Date.now()) {
6556
+ if (this.ttlSeconds <= 0) return 0;
6557
+ const lapsed = await this.table.lapsedRuns(now - this.ttlSeconds * 1e3);
6558
+ for (let index = 0; index < lapsed.length; index += PURGE_CHUNK) {
6559
+ await this.table.remove(lapsed.slice(index, index + PURGE_CHUNK));
6560
+ }
6561
+ return lapsed.length;
6562
+ }
6563
+ /** {@link purgeExpired}, unawaited and at most once a minute. Its failure is not the run's. */
6564
+ purgeInBackground() {
6565
+ if (!this.autoPurge || this.ttlSeconds <= 0) return;
6566
+ const now = Date.now();
6567
+ if (now - this.lastAutoPurge < AUTO_PURGE_EVERY_MS) return;
6568
+ this.lastAutoPurge = now;
6569
+ this.purgeExpired(now).catch(() => {
6570
+ });
6571
+ }
6572
+ };
6009
6573
  // Annotate the CommonJS export names for ESM import in node:
6010
6574
  0 && (module.exports = {
6011
6575
  AGENT_ACTOR_DIRECTORY,
6012
6576
  AGENT_ACTOR_RESOLVER,
6013
6577
  AGENT_APPROVAL_PORT,
6014
6578
  AGENT_ATTACHMENT_STAGING,
6579
+ AGENT_CONFIRM_TOKEN_STORE,
6015
6580
  AGENT_DEPS_FACTORY,
6016
6581
  AGENT_DIAGNOSTIC_EVENTS,
6017
6582
  AGENT_DURABLE_RUNNER,
@@ -6041,7 +6606,10 @@ var InMemoryAgentStore = class {
6041
6606
  ASK_TOOL_NAME,
6042
6607
  AgentRegistry,
6043
6608
  AgentStreamError,
6609
+ CONFIRM_JSON_SCHEMA_PROPERTIES,
6044
6610
  ClosedRolesPolicy,
6611
+ ConfirmTokenError,
6612
+ DEFAULT_CONFIRM_TTL_MS,
6045
6613
  DEFAULT_HISTORY_SUMMARY_INSTRUCTION,
6046
6614
  DEFAULT_INCREMENTAL_LOOKBACK_CHARS,
6047
6615
  DEFAULT_INTAKE_PREAMBLE,
@@ -6057,6 +6625,7 @@ var InMemoryAgentStore = class {
6057
6625
  ELICITATION_INPUT_TYPES,
6058
6626
  GLOBAL_SCOPE,
6059
6627
  InMemoryAgentStore,
6628
+ InMemoryConfirmTokenStore,
6060
6629
  MAX_ASK_QUESTIONS,
6061
6630
  OutputRejectedError,
6062
6631
  ProcessorFailedError,
@@ -6070,8 +6639,10 @@ var InMemoryAgentStore = class {
6070
6639
  RUN_NOT_ACTIVE_MESSAGE,
6071
6640
  RUN_NO_LONGER_RUNNING,
6072
6641
  RunCancelledError,
6642
+ SCHEMA_EXTENSION,
6073
6643
  SKILL_TOOL_DESCRIPTION,
6074
6644
  SKILL_TOOL_NAME,
6645
+ SqlTokenStreamSink,
6075
6646
  StructuredOutputError,
6076
6647
  THREAD_DETAIL_CONTENT_CHARS,
6077
6648
  ToolDisabledError,
@@ -6092,8 +6663,10 @@ var InMemoryAgentStore = class {
6092
6663
  buildMemoryBlock,
6093
6664
  buildSkillsBlock,
6094
6665
  canActorUseTool,
6666
+ canonicalJson,
6095
6667
  closeEmptyRoles,
6096
6668
  compositeSkillProvider,
6669
+ confirmTokenExpiry,
6097
6670
  createFrameBuffer,
6098
6671
  createIncrementalGate,
6099
6672
  createNoopEmitUi,
@@ -6103,6 +6676,7 @@ var InMemoryAgentStore = class {
6103
6676
  decodeStreamEvent,
6104
6677
  defaultCanDecide,
6105
6678
  defaultScopeResolver,
6679
+ defineConfirmedTool,
6106
6680
  detachedDelivered,
6107
6681
  detachedStarted,
6108
6682
  detachedUnsettled,
@@ -6119,6 +6693,7 @@ var InMemoryAgentStore = class {
6119
6693
  findCatalogModel,
6120
6694
  gateFollowUps,
6121
6695
  gateTail,
6696
+ hashConfirmToken,
6122
6697
  invokeWithTransientRetry,
6123
6698
  isChatQueueStore,
6124
6699
  isControlFlowSignal,
@@ -6171,12 +6746,14 @@ var InMemoryAgentStore = class {
6171
6746
  runAgentLoop,
6172
6747
  runInputProcessors,
6173
6748
  runOutputProcessors,
6749
+ schemaExtensionOf,
6174
6750
  seedModelPrices,
6175
6751
  settleAll,
6176
6752
  settleDanglingToolCalls,
6177
6753
  settleDeadRun,
6178
6754
  settleElicitation,
6179
6755
  settleUnsettledDelegation,
6756
+ signConfirmToken,
6180
6757
  skillInputSchema,
6181
6758
  skillToolDefinition,
6182
6759
  skillWriteVerdict,
@@ -6195,8 +6772,10 @@ var InMemoryAgentStore = class {
6195
6772
  validateElicitationAnswer,
6196
6773
  validateElicitationValue,
6197
6774
  validateStructured,
6775
+ verifyConfirmToken,
6198
6776
  windowHistory,
6199
6777
  withAskTool,
6778
+ withConfirmFields,
6200
6779
  withMemoryTool,
6201
6780
  withSelectedModel,
6202
6781
  withSkillTool,