@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/ag-ui/index.cjs.map +1 -1
- package/dist/ag-ui/index.js.map +1 -1
- package/dist/index.cjs +581 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +395 -1
- package/dist/index.d.ts +395 -1
- package/dist/index.js +566 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
|
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 +=
|
|
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,
|