@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.js CHANGED
@@ -27,6 +27,7 @@ var AGENT_APPROVAL_PORT = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:app
27
27
  var AGENT_SKILLS = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:skills");
28
28
  var AGENT_MEMORY = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:memory");
29
29
  var AGENT_SKILL_SOURCES = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:skill-sources");
30
+ var AGENT_CONFIRM_TOKEN_STORE = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:confirm-token-store");
30
31
 
31
32
  // src/spi/model-catalog.ts
32
33
  function staticModelCatalog(view) {
@@ -87,7 +88,7 @@ __name(decodeStreamEvent, "decodeStreamEvent");
87
88
 
88
89
  // src/turn-frames.ts
89
90
  function observeTurnFrames(writer, now = Date.now) {
90
- const decoder2 = new TextDecoder();
91
+ const decoder3 = new TextDecoder();
91
92
  let pending = "";
92
93
  let reasoning = "";
93
94
  let reasoningMs = 0;
@@ -127,7 +128,7 @@ function observeTurnFrames(writer, now = Date.now) {
127
128
  }
128
129
  __name(observeLine, "observeLine");
129
130
  function observe(chunk) {
130
- pending += decoder2.decode(chunk, {
131
+ pending += decoder3.decode(chunk, {
131
132
  stream: true
132
133
  });
133
134
  let newline = pending.indexOf("\n");
@@ -5787,11 +5788,560 @@ var InMemoryAgentStore = class {
5787
5788
  };
5788
5789
  }
5789
5790
  };
5791
+
5792
+ // src/confirm-token.ts
5793
+ import { createHash as createHash2, createHmac, timingSafeEqual } from "crypto";
5794
+ var DEFAULT_CONFIRM_TTL_MS = 15 * 60 * 1e3;
5795
+ function canonicalJson(value) {
5796
+ if (value === void 0) return "null";
5797
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
5798
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
5799
+ if (typeof value.toJSON === "function") {
5800
+ return canonicalJson(value.toJSON());
5801
+ }
5802
+ const entries = Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
5803
+ return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
5804
+ }
5805
+ __name(canonicalJson, "canonicalJson");
5806
+ function signature(subject, expiresAt, secret) {
5807
+ if (secret === "") {
5808
+ throw new Error("confirm token: the secret is empty");
5809
+ }
5810
+ return createHmac("sha256", secret).update(canonicalJson([
5811
+ "agent-confirm",
5812
+ subject.tool,
5813
+ subject.actorId,
5814
+ subject.tenantRef ?? "",
5815
+ expiresAt,
5816
+ subject.args
5817
+ ])).digest("base64url");
5818
+ }
5819
+ __name(signature, "signature");
5820
+ function signConfirmToken(subject, options) {
5821
+ return `${options.expiresAt}.${signature(subject, options.expiresAt, options.secret)}`;
5822
+ }
5823
+ __name(signConfirmToken, "signConfirmToken");
5824
+ function confirmTokenExpiry(token) {
5825
+ const separator = token.indexOf(".");
5826
+ if (separator <= 0) return void 0;
5827
+ const expiresAt = Number(token.slice(0, separator));
5828
+ return Number.isSafeInteger(expiresAt) && expiresAt > 0 ? expiresAt : void 0;
5829
+ }
5830
+ __name(confirmTokenExpiry, "confirmTokenExpiry");
5831
+ function verifyConfirmToken(token, subject, options) {
5832
+ if (typeof token !== "string") return false;
5833
+ const expiresAt = confirmTokenExpiry(token);
5834
+ if (expiresAt === void 0 || expiresAt < (options.now ?? Date.now())) return false;
5835
+ const expected = Buffer.from(signature(subject, expiresAt, options.secret));
5836
+ const given = Buffer.from(token.slice(token.indexOf(".") + 1));
5837
+ return expected.length === given.length && timingSafeEqual(expected, given);
5838
+ }
5839
+ __name(verifyConfirmToken, "verifyConfirmToken");
5840
+ function hashConfirmToken(token) {
5841
+ return createHash2("sha256").update(token).digest("hex");
5842
+ }
5843
+ __name(hashConfirmToken, "hashConfirmToken");
5844
+ var InMemoryConfirmTokenStore = class {
5845
+ static {
5846
+ __name(this, "InMemoryConfirmTokenStore");
5847
+ }
5848
+ claims = /* @__PURE__ */ new Map();
5849
+ async claim(input) {
5850
+ await this.purgeExpired();
5851
+ if (this.claims.has(input.hash)) return false;
5852
+ this.claims.set(input.hash, {
5853
+ ...input
5854
+ });
5855
+ return true;
5856
+ }
5857
+ async release(hash) {
5858
+ this.claims.delete(hash);
5859
+ }
5860
+ async purgeExpired(now = Date.now()) {
5861
+ let purged = 0;
5862
+ for (const [hash, claim] of this.claims) {
5863
+ if (claim.expiresAt < now) {
5864
+ this.claims.delete(hash);
5865
+ purged += 1;
5866
+ }
5867
+ }
5868
+ return purged;
5869
+ }
5870
+ /** How many tokens are currently marked as spent. */
5871
+ get size() {
5872
+ return this.claims.size;
5873
+ }
5874
+ };
5875
+
5876
+ // src/confirmed-tool.ts
5877
+ var DEFAULT_MESSAGES = {
5878
+ 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).',
5879
+ 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.',
5880
+ used: "This preview was already confirmed: nothing was written again. To repeat the operation, ask for a new preview."
5881
+ };
5882
+ var ConfirmTokenError = class extends Error {
5883
+ static {
5884
+ __name(this, "ConfirmTokenError");
5885
+ }
5886
+ reason;
5887
+ constructor(reason, message) {
5888
+ super(message), this.reason = reason;
5889
+ this.name = "ConfirmTokenError";
5890
+ }
5891
+ };
5892
+ var CONFIRM_JSON_SCHEMA_PROPERTIES = {
5893
+ confirm: {
5894
+ type: "boolean",
5895
+ description: "Omit to get a preview (nothing is written). Send true, with confirmToken, to commit."
5896
+ },
5897
+ confirmToken: {
5898
+ type: "string",
5899
+ description: "The confirmToken the preview returned. Required when confirm is true."
5900
+ }
5901
+ };
5902
+ var SCHEMA_EXTENSION = /* @__PURE__ */ Symbol.for("@dudousxd/nestjs-agent:schema-extension");
5903
+ function schemaExtensionOf(schema) {
5904
+ const extension = schema[SCHEMA_EXTENSION];
5905
+ return typeof extension === "object" && extension !== null ? extension : void 0;
5906
+ }
5907
+ __name(schemaExtensionOf, "schemaExtensionOf");
5908
+ function isRecord(value) {
5909
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5910
+ }
5911
+ __name(isRecord, "isRecord");
5912
+ function splitConfirm(input) {
5913
+ if (!isRecord(input)) return {
5914
+ args: input,
5915
+ fields: {}
5916
+ };
5917
+ const { confirm, confirmToken, ...args } = input;
5918
+ return {
5919
+ args,
5920
+ fields: {
5921
+ ...typeof confirm === "boolean" ? {
5922
+ confirm
5923
+ } : {},
5924
+ ...typeof confirmToken === "string" ? {
5925
+ confirmToken
5926
+ } : {}
5927
+ }
5928
+ };
5929
+ }
5930
+ __name(splitConfirm, "splitConfirm");
5931
+ function jsonSchemaInputOf(schema) {
5932
+ const standard = schema["~standard"];
5933
+ const input = standard.jsonSchema?.input;
5934
+ return typeof input === "function" ? (options) => input.call(standard.jsonSchema, options) : void 0;
5935
+ }
5936
+ __name(jsonSchemaInputOf, "jsonSchemaInputOf");
5937
+ function withConfirmFields(schema) {
5938
+ const inner = schema["~standard"];
5939
+ const innerJsonSchema = jsonSchemaInputOf(schema);
5940
+ const describe = /* @__PURE__ */ __name((options) => {
5941
+ const base = innerJsonSchema?.(options) ?? {
5942
+ additionalProperties: true
5943
+ };
5944
+ return {
5945
+ ...base,
5946
+ type: "object",
5947
+ properties: {
5948
+ ...isRecord(base.properties) ? base.properties : {},
5949
+ ...CONFIRM_JSON_SCHEMA_PROPERTIES
5950
+ }
5951
+ };
5952
+ }, "describe");
5953
+ const wrapped = {
5954
+ [SCHEMA_EXTENSION]: {
5955
+ base: schema,
5956
+ properties: CONFIRM_JSON_SCHEMA_PROPERTIES
5957
+ },
5958
+ "~standard": {
5959
+ version: 1,
5960
+ vendor: "@dudousxd/nestjs-agent",
5961
+ validate: /* @__PURE__ */ __name(async (value) => {
5962
+ if (!isRecord(value)) {
5963
+ return {
5964
+ issues: [
5965
+ {
5966
+ message: "expected an object"
5967
+ }
5968
+ ]
5969
+ };
5970
+ }
5971
+ const issues = [];
5972
+ if (value.confirm != null && typeof value.confirm !== "boolean") {
5973
+ issues.push({
5974
+ message: "expected a boolean",
5975
+ path: [
5976
+ "confirm"
5977
+ ]
5978
+ });
5979
+ }
5980
+ if (value.confirmToken != null && typeof value.confirmToken !== "string") {
5981
+ issues.push({
5982
+ message: "expected a string",
5983
+ path: [
5984
+ "confirmToken"
5985
+ ]
5986
+ });
5987
+ }
5988
+ const { args, fields } = splitConfirm(value);
5989
+ const result = await inner.validate(args);
5990
+ if (result.issues !== void 0) {
5991
+ return {
5992
+ issues: [
5993
+ ...issues,
5994
+ ...result.issues
5995
+ ]
5996
+ };
5997
+ }
5998
+ if (issues.length > 0) {
5999
+ return {
6000
+ issues
6001
+ };
6002
+ }
6003
+ return {
6004
+ value: isRecord(result.value) ? {
6005
+ ...result.value,
6006
+ ...fields
6007
+ } : result.value
6008
+ };
6009
+ }, "validate"),
6010
+ jsonSchema: {
6011
+ input: describe,
6012
+ output: describe
6013
+ }
6014
+ }
6015
+ };
6016
+ return wrapped;
6017
+ }
6018
+ __name(withConfirmFields, "withConfirmFields");
6019
+ function defineConfirmedTool(options, steps) {
6020
+ const { secret, ttlMs = DEFAULT_CONFIRM_TTL_MS, store, messages, input, ...specFields } = options;
6021
+ const text = {
6022
+ ...DEFAULT_MESSAGES,
6023
+ ...messages
6024
+ };
6025
+ const howToConfirm = text.confirm.replaceAll("{minutes}", String(Math.round(ttlMs / 6e4)));
6026
+ const secretNow = /* @__PURE__ */ __name(() => typeof secret === "function" ? secret() : secret, "secretNow");
6027
+ const handler = {
6028
+ async execute(value, ctx) {
6029
+ const { args, fields } = splitConfirm(value);
6030
+ const subject = {
6031
+ tool: options.name,
6032
+ actorId: ctx.actor.id,
6033
+ ...ctx.actor.tenantRef !== void 0 ? {
6034
+ tenantRef: ctx.actor.tenantRef
6035
+ } : {},
6036
+ args
6037
+ };
6038
+ const confirming = fields.confirm === true;
6039
+ const token = fields.confirmToken;
6040
+ if (confirming && !verifyConfirmToken(token, subject, {
6041
+ secret: secretNow()
6042
+ })) {
6043
+ throw new ConfirmTokenError("invalid", text.invalid);
6044
+ }
6045
+ const prepared = await steps.prepare(args, ctx);
6046
+ if (!confirming || token === void 0) {
6047
+ const preview = await steps.preview(prepared, ctx);
6048
+ const expiresAt = Date.now() + ttlMs;
6049
+ return {
6050
+ status: "preview",
6051
+ summary: preview.summary,
6052
+ ...preview.data !== void 0 ? {
6053
+ data: preview.data
6054
+ } : {},
6055
+ confirmToken: signConfirmToken(subject, {
6056
+ secret: secretNow(),
6057
+ expiresAt
6058
+ }),
6059
+ expiresAt: new Date(expiresAt).toISOString(),
6060
+ confirm: howToConfirm
6061
+ };
6062
+ }
6063
+ const hash = hashConfirmToken(token);
6064
+ if (store !== void 0) {
6065
+ const claimed = await store.claim({
6066
+ hash,
6067
+ actorRef: ctx.actor.id,
6068
+ tool: options.name,
6069
+ expiresAt: confirmTokenExpiry(token) ?? Date.now() + ttlMs
6070
+ });
6071
+ if (!claimed) {
6072
+ throw new ConfirmTokenError("used", text.used);
6073
+ }
6074
+ }
6075
+ try {
6076
+ const done = await steps.commit(prepared, ctx);
6077
+ return {
6078
+ status: "done",
6079
+ summary: done.summary,
6080
+ ...done.data !== void 0 ? {
6081
+ data: done.data
6082
+ } : {}
6083
+ };
6084
+ } catch (error) {
6085
+ await store?.release(hash).catch(() => void 0);
6086
+ throw error;
6087
+ }
6088
+ }
6089
+ };
6090
+ return {
6091
+ spec: {
6092
+ ...specFields,
6093
+ kind: "read",
6094
+ inputSchema: withConfirmFields(input)
6095
+ },
6096
+ handler
6097
+ };
6098
+ }
6099
+ __name(defineConfirmedTool, "defineConfirmedTool");
6100
+
6101
+ // src/sql-token-stream-sink.ts
6102
+ var DEFAULT_TTL_SECONDS = 3600;
6103
+ var DEFAULT_POLL_INTERVAL_MS = 250;
6104
+ var DEFAULT_IDLE_POLL_INTERVAL_MS = 1e3;
6105
+ var IDLE_AFTER_MS = 5e3;
6106
+ var DEFAULT_FLUSH_MS = 50;
6107
+ var READ_PAGE = 500;
6108
+ var PURGE_CHUNK = 200;
6109
+ var AUTO_PURGE_EVERY_MS = 6e4;
6110
+ var APPEND_ATTEMPTS = 8;
6111
+ var decoder2 = new TextDecoder();
6112
+ var encoder2 = new TextEncoder();
6113
+ var sleep = /* @__PURE__ */ __name((ms) => new Promise((resolve) => setTimeout(resolve, ms)), "sleep");
6114
+ function textOf(line) {
6115
+ if (!line.startsWith('{"kind":"text"')) return null;
6116
+ try {
6117
+ const parsed = JSON.parse(line);
6118
+ if (typeof parsed === "object" && parsed !== null && Object.keys(parsed).length === 2 && parsed.kind === "text" && typeof parsed.text === "string") {
6119
+ return parsed.text;
6120
+ }
6121
+ } catch {
6122
+ }
6123
+ return null;
6124
+ }
6125
+ __name(textOf, "textOf");
6126
+ var SqlTokenStreamSink = class {
6127
+ static {
6128
+ __name(this, "SqlTokenStreamSink");
6129
+ }
6130
+ table;
6131
+ pollIntervalMs;
6132
+ idlePollIntervalMs;
6133
+ flushMs;
6134
+ ttlSeconds;
6135
+ autoPurge;
6136
+ writes = /* @__PURE__ */ new Map();
6137
+ lastAutoPurge = 0;
6138
+ constructor(table, options = {}) {
6139
+ this.table = table;
6140
+ this.pollIntervalMs = Math.max(1, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
6141
+ this.idlePollIntervalMs = Math.max(this.pollIntervalMs, options.idlePollIntervalMs ?? DEFAULT_IDLE_POLL_INTERVAL_MS);
6142
+ this.flushMs = Math.max(0, options.flushMs ?? DEFAULT_FLUSH_MS);
6143
+ this.ttlSeconds = options.ttlSeconds ?? DEFAULT_TTL_SECONDS;
6144
+ this.autoPurge = options.autoPurge ?? true;
6145
+ }
6146
+ runWrites(runId) {
6147
+ let state = this.writes.get(runId);
6148
+ if (state === void 0) {
6149
+ state = {
6150
+ text: null,
6151
+ timer: void 0,
6152
+ tail: Promise.resolve(),
6153
+ pending: 0,
6154
+ failure: void 0
6155
+ };
6156
+ this.writes.set(runId, state);
6157
+ }
6158
+ return state;
6159
+ }
6160
+ /** Forget a run's write state once nothing is gathered, queued or left to report. */
6161
+ release(runId, state) {
6162
+ if (state.pending === 0 && state.text === null && state.failure === void 0 && this.writes.get(runId) === state) {
6163
+ this.writes.delete(runId);
6164
+ }
6165
+ }
6166
+ /** Queue `work` behind every write already queued for the run, so rows land in call order. */
6167
+ enqueue(runId, state, work) {
6168
+ state.pending += 1;
6169
+ const next = state.tail.then(work);
6170
+ state.tail = next.then(() => void 0, () => void 0).then(() => {
6171
+ state.pending -= 1;
6172
+ this.release(runId, state);
6173
+ });
6174
+ return next;
6175
+ }
6176
+ /** Queue the gathered text (if any) as one `text` row. */
6177
+ flushText(runId, state) {
6178
+ if (state.timer !== void 0) {
6179
+ clearTimeout(state.timer);
6180
+ state.timer = void 0;
6181
+ }
6182
+ if (state.text === null) return Promise.resolve();
6183
+ const line = decoder2.decode(encodeStreamEvent({
6184
+ kind: "text",
6185
+ text: state.text
6186
+ }));
6187
+ state.text = null;
6188
+ return this.enqueue(runId, state, () => this.append(runId, line, null));
6189
+ }
6190
+ /** Raise, once, a gathered write that failed with nobody awaiting it. */
6191
+ raiseFailure(state) {
6192
+ const failure = state.failure;
6193
+ if (failure !== void 0) {
6194
+ state.failure = void 0;
6195
+ throw failure.error;
6196
+ }
6197
+ }
6198
+ /** Append one row — a frame, or the terminal row (`frame: null`, with `error` on a failure). */
6199
+ async append(runId, frame, error) {
6200
+ for (let attempt = 1; ; attempt += 1) {
6201
+ try {
6202
+ await this.table.append(runId, {
6203
+ frame,
6204
+ error,
6205
+ createdAt: Date.now()
6206
+ });
6207
+ return;
6208
+ } catch (caught) {
6209
+ if (attempt >= APPEND_ATTEMPTS || !this.table.isUniqueViolation(caught)) throw caught;
6210
+ }
6211
+ }
6212
+ }
6213
+ /** Write out what is gathered, then a terminal row; raise a failure nobody saw. */
6214
+ async terminate(runId, error) {
6215
+ const state = this.runWrites(runId);
6216
+ const flushed = this.flushText(runId, state);
6217
+ const ended = this.enqueue(runId, state, () => this.append(runId, null, error));
6218
+ await Promise.all([
6219
+ flushed,
6220
+ ended
6221
+ ]);
6222
+ this.raiseFailure(state);
6223
+ this.release(runId, state);
6224
+ this.purgeInBackground();
6225
+ }
6226
+ /**
6227
+ * The run's writer. Writers opened for the same run in one process share what they are gathering,
6228
+ * so a delegated run's text (forwarded through a child writer) and its parent's next frame keep
6229
+ * the order they were written in.
6230
+ */
6231
+ open(runId) {
6232
+ return {
6233
+ write: /* @__PURE__ */ __name(async (chunk) => {
6234
+ const state = this.runWrites(runId);
6235
+ this.raiseFailure(state);
6236
+ const line = decoder2.decode(chunk);
6237
+ const text = this.flushMs > 0 ? textOf(line.trimEnd()) : null;
6238
+ if (text !== null) {
6239
+ state.text = (state.text ?? "") + text;
6240
+ if (state.timer === void 0) {
6241
+ state.timer = setTimeout(() => {
6242
+ state.timer = void 0;
6243
+ this.flushText(runId, state).catch((error) => {
6244
+ state.failure = {
6245
+ error
6246
+ };
6247
+ });
6248
+ }, this.flushMs);
6249
+ }
6250
+ return;
6251
+ }
6252
+ const flushed = this.flushText(runId, state);
6253
+ const appended = this.enqueue(runId, state, () => this.append(runId, line, null));
6254
+ await Promise.all([
6255
+ flushed,
6256
+ appended
6257
+ ]);
6258
+ }, "write"),
6259
+ flush: /* @__PURE__ */ __name(async () => {
6260
+ const state = this.runWrites(runId);
6261
+ const flushed = this.flushText(runId, state);
6262
+ await Promise.all([
6263
+ flushed,
6264
+ state.tail
6265
+ ]);
6266
+ this.raiseFailure(state);
6267
+ this.release(runId, state);
6268
+ }, "flush"),
6269
+ end: /* @__PURE__ */ __name(() => this.terminate(runId, null), "end"),
6270
+ fail: /* @__PURE__ */ __name((error) => this.terminate(runId, JSON.stringify({
6271
+ code: error.code,
6272
+ message: error.message
6273
+ })), "fail")
6274
+ };
6275
+ }
6276
+ async *subscribe(runId) {
6277
+ let cursor = 0;
6278
+ let quietSince = Date.now();
6279
+ while (true) {
6280
+ const rows = await this.table.read(runId, cursor, READ_PAGE);
6281
+ for (const row of rows) {
6282
+ cursor = row.seq;
6283
+ if (row.frame === null) {
6284
+ if (row.error !== null) {
6285
+ throw new AgentStreamError(JSON.parse(row.error));
6286
+ }
6287
+ return;
6288
+ }
6289
+ yield encoder2.encode(row.frame);
6290
+ }
6291
+ if (rows.length > 0) {
6292
+ quietSince = Date.now();
6293
+ if (rows.length >= READ_PAGE) continue;
6294
+ }
6295
+ await sleep(Date.now() - quietSince >= IDLE_AFTER_MS ? this.idlePollIntervalMs : this.pollIntervalMs);
6296
+ }
6297
+ }
6298
+ /** Does the table hold anything for the run — a frame, or its end? */
6299
+ has(runId) {
6300
+ return this.table.has(runId);
6301
+ }
6302
+ async close(runId) {
6303
+ const state = this.writes.get(runId);
6304
+ if (state !== void 0) {
6305
+ if (state.timer !== void 0) clearTimeout(state.timer);
6306
+ state.timer = void 0;
6307
+ state.text = null;
6308
+ state.failure = void 0;
6309
+ await state.tail;
6310
+ this.writes.delete(runId);
6311
+ }
6312
+ await this.table.remove([
6313
+ runId
6314
+ ]);
6315
+ }
6316
+ /**
6317
+ * Delete every run whose last write is older than `ttlSeconds` at `now` (epoch-ms) — ended runs
6318
+ * past their replay window and runs that crashed without ending alike. Returns how many runs went.
6319
+ * Safe from any replica, and from several at once. A no-op under `ttlSeconds: 0`.
6320
+ */
6321
+ async purgeExpired(now = Date.now()) {
6322
+ if (this.ttlSeconds <= 0) return 0;
6323
+ const lapsed = await this.table.lapsedRuns(now - this.ttlSeconds * 1e3);
6324
+ for (let index = 0; index < lapsed.length; index += PURGE_CHUNK) {
6325
+ await this.table.remove(lapsed.slice(index, index + PURGE_CHUNK));
6326
+ }
6327
+ return lapsed.length;
6328
+ }
6329
+ /** {@link purgeExpired}, unawaited and at most once a minute. Its failure is not the run's. */
6330
+ purgeInBackground() {
6331
+ if (!this.autoPurge || this.ttlSeconds <= 0) return;
6332
+ const now = Date.now();
6333
+ if (now - this.lastAutoPurge < AUTO_PURGE_EVERY_MS) return;
6334
+ this.lastAutoPurge = now;
6335
+ this.purgeExpired(now).catch(() => {
6336
+ });
6337
+ }
6338
+ };
5790
6339
  export {
5791
6340
  AGENT_ACTOR_DIRECTORY,
5792
6341
  AGENT_ACTOR_RESOLVER,
5793
6342
  AGENT_APPROVAL_PORT,
5794
6343
  AGENT_ATTACHMENT_STAGING,
6344
+ AGENT_CONFIRM_TOKEN_STORE,
5795
6345
  AGENT_DEPS_FACTORY,
5796
6346
  AGENT_DIAGNOSTIC_EVENTS,
5797
6347
  AGENT_DURABLE_RUNNER,
@@ -5821,7 +6371,10 @@ export {
5821
6371
  ASK_TOOL_NAME,
5822
6372
  AgentRegistry,
5823
6373
  AgentStreamError,
6374
+ CONFIRM_JSON_SCHEMA_PROPERTIES,
5824
6375
  ClosedRolesPolicy,
6376
+ ConfirmTokenError,
6377
+ DEFAULT_CONFIRM_TTL_MS,
5825
6378
  DEFAULT_HISTORY_SUMMARY_INSTRUCTION,
5826
6379
  DEFAULT_INCREMENTAL_LOOKBACK_CHARS,
5827
6380
  DEFAULT_INTAKE_PREAMBLE,
@@ -5837,6 +6390,7 @@ export {
5837
6390
  ELICITATION_INPUT_TYPES,
5838
6391
  GLOBAL_SCOPE,
5839
6392
  InMemoryAgentStore,
6393
+ InMemoryConfirmTokenStore,
5840
6394
  MAX_ASK_QUESTIONS,
5841
6395
  OutputRejectedError,
5842
6396
  ProcessorFailedError,
@@ -5850,8 +6404,10 @@ export {
5850
6404
  RUN_NOT_ACTIVE_MESSAGE,
5851
6405
  RUN_NO_LONGER_RUNNING,
5852
6406
  RunCancelledError,
6407
+ SCHEMA_EXTENSION,
5853
6408
  SKILL_TOOL_DESCRIPTION,
5854
6409
  SKILL_TOOL_NAME,
6410
+ SqlTokenStreamSink,
5855
6411
  StructuredOutputError,
5856
6412
  THREAD_DETAIL_CONTENT_CHARS,
5857
6413
  ToolDisabledError,
@@ -5872,8 +6428,10 @@ export {
5872
6428
  buildMemoryBlock,
5873
6429
  buildSkillsBlock,
5874
6430
  canActorUseTool,
6431
+ canonicalJson,
5875
6432
  closeEmptyRoles,
5876
6433
  compositeSkillProvider,
6434
+ confirmTokenExpiry,
5877
6435
  createFrameBuffer,
5878
6436
  createIncrementalGate,
5879
6437
  createNoopEmitUi,
@@ -5883,6 +6441,7 @@ export {
5883
6441
  decodeStreamEvent,
5884
6442
  defaultCanDecide,
5885
6443
  defaultScopeResolver,
6444
+ defineConfirmedTool,
5886
6445
  detachedDelivered,
5887
6446
  detachedStarted,
5888
6447
  detachedUnsettled,
@@ -5899,6 +6458,7 @@ export {
5899
6458
  findCatalogModel,
5900
6459
  gateFollowUps,
5901
6460
  gateTail,
6461
+ hashConfirmToken,
5902
6462
  invokeWithTransientRetry,
5903
6463
  isChatQueueStore,
5904
6464
  isControlFlowSignal,
@@ -5951,12 +6511,14 @@ export {
5951
6511
  runAgentLoop,
5952
6512
  runInputProcessors,
5953
6513
  runOutputProcessors,
6514
+ schemaExtensionOf,
5954
6515
  seedModelPrices,
5955
6516
  settleAll,
5956
6517
  settleDanglingToolCalls,
5957
6518
  settleDeadRun,
5958
6519
  settleElicitation,
5959
6520
  settleUnsettledDelegation,
6521
+ signConfirmToken,
5960
6522
  skillInputSchema,
5961
6523
  skillToolDefinition,
5962
6524
  skillWriteVerdict,
@@ -5975,8 +6537,10 @@ export {
5975
6537
  validateElicitationAnswer,
5976
6538
  validateElicitationValue,
5977
6539
  validateStructured,
6540
+ verifyConfirmToken,
5978
6541
  windowHistory,
5979
6542
  withAskTool,
6543
+ withConfirmFields,
5980
6544
  withMemoryTool,
5981
6545
  withSelectedModel,
5982
6546
  withSkillTool,