@genesislcap/ai-assistant 15.3.3-alpha-4899ddd.0 → 15.3.4
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/ai-assistant.api.json +5 -391
- package/dist/ai-assistant.d.ts +6 -603
- package/dist/chat-driver.cjs +26 -285
- package/dist/chat-driver.cjs.map +3 -3
- package/dist/chat-driver.mjs +26 -285
- package/dist/chat-driver.mjs.map +3 -3
- package/dist/custom-elements.json +10 -236
- package/dist/dts/channel/ai-activity-channel.d.ts +1 -51
- package/dist/dts/channel/ai-activity-channel.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +1 -99
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.test.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +0 -14
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +6 -384
- package/dist/dts/main/main.d.ts.map +1 -1
- package/dist/dts/main/main.styles.d.ts.map +1 -1
- package/dist/dts/main/main.template.d.ts +0 -53
- package/dist/dts/main/main.template.d.ts.map +1 -1
- package/dist/dts/state/ai-assistant-slice.d.ts +6 -143
- package/dist/dts/state/ai-assistant-slice.d.ts.map +1 -1
- package/dist/dts/state/debug-event-log.d.ts +1 -6
- package/dist/dts/state/debug-event-log.d.ts.map +1 -1
- package/dist/dts/state/session-store.d.ts +0 -10
- package/dist/dts/state/session-store.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +21 -263
- package/dist/esm/components/chat-driver/chat-driver.test.js +1 -464
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +4 -89
- package/dist/esm/main/main.js +16 -680
- package/dist/esm/main/main.styles.js +0 -47
- package/dist/esm/main/main.template.js +4 -121
- package/dist/esm/state/ai-assistant-slice.js +7 -128
- package/dist/esm/state/ai-assistant-slice.test.js +1 -138
- package/dist/esm/state/debug-event-log.js +2 -7
- package/dist/esm/state/debug-event-log.test.js +1 -49
- package/dist/esm/state/persistence/session-snapshot.test.js +0 -18
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/docs/sub_agent.md +3 -20
- package/package.json +17 -17
- package/src/channel/ai-activity-channel.ts +2 -56
- package/src/components/chat-driver/chat-driver.test.ts +0 -549
- package/src/components/chat-driver/chat-driver.ts +14 -324
- package/src/components/orchestrating-driver/orchestrating-driver.ts +6 -101
- package/src/main/main.styles.ts +0 -47
- package/src/main/main.template.ts +4 -131
- package/src/main/main.ts +10 -680
- package/src/state/ai-assistant-slice.test.ts +0 -215
- package/src/state/ai-assistant-slice.ts +8 -181
- package/src/state/debug-event-log.test.ts +0 -63
- package/src/state/debug-event-log.ts +2 -7
- package/src/state/persistence/session-snapshot.test.ts +0 -22
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts +0 -2
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.budget.test.d.ts.map +0 -1
- package/dist/dts/main/blocked-state.test.d.ts +0 -2
- package/dist/dts/main/blocked-state.test.d.ts.map +0 -1
- package/dist/dts/main/main.styles.test.d.ts +0 -2
- package/dist/dts/main/main.styles.test.d.ts.map +0 -1
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.budget.test.js +0 -312
- package/dist/esm/main/blocked-state.test.js +0 -913
- package/dist/esm/main/main.styles.test.js +0 -86
- package/docs/migration-GENC-1464.md +0 -562
- package/src/components/orchestrating-driver/orchestrating-driver.budget.test.ts +0 -438
- package/src/main/blocked-state.test.ts +0 -1243
- package/src/main/main.styles.test.ts +0 -103
package/dist/chat-driver.cjs
CHANGED
|
@@ -1016,67 +1016,6 @@ var GeminiProvider = class {
|
|
|
1016
1016
|
}
|
|
1017
1017
|
};
|
|
1018
1018
|
|
|
1019
|
-
// ../../foundation-ai/dist/esm/transports/budget-exhausted-error.js
|
|
1020
|
-
var BUDGET_EXCEEDED_CODE = "BUDGET_EXCEEDED";
|
|
1021
|
-
var VENDOR_LABELS = Object.freeze({
|
|
1022
|
-
anthropic: "Anthropic",
|
|
1023
|
-
gemini: "Gemini",
|
|
1024
|
-
openai: "OpenAI",
|
|
1025
|
-
chrome: "Chrome"
|
|
1026
|
-
});
|
|
1027
|
-
var BUDGETED_VENDORS = Object.freeze([
|
|
1028
|
-
"anthropic",
|
|
1029
|
-
"gemini"
|
|
1030
|
-
]);
|
|
1031
|
-
var VENDOR_TYPE_BY_LABEL = new Map(Object.entries(VENDOR_LABELS).map(([type, label]) => [
|
|
1032
|
-
label.toLowerCase(),
|
|
1033
|
-
type
|
|
1034
|
-
]));
|
|
1035
|
-
function vendorTypeOfLabel(label) {
|
|
1036
|
-
if (typeof label !== "string")
|
|
1037
|
-
return void 0;
|
|
1038
|
-
return VENDOR_TYPE_BY_LABEL.get(label.trim().toLowerCase());
|
|
1039
|
-
}
|
|
1040
|
-
var BUDGET_EXCEEDED_STATUS = 402;
|
|
1041
|
-
var DEFAULT_BUDGET_EXHAUSTED_MESSAGE = "You've reached your AI usage limit. Contact your administrator to raise it.";
|
|
1042
|
-
var BudgetExhaustedError = class extends Error {
|
|
1043
|
-
constructor(vendorLabel, budgetUsd, spentUsd, detail, extra) {
|
|
1044
|
-
super(`${vendorLabel} request refused: the AI usage budget is exhausted` + (budgetUsd != null ? ` (spent ${spentUsd != null ? `$${spentUsd}` : "an unknown amount"} of a $${budgetUsd} budget)` : "") + (detail ? `: ${detail}` : "") + ". Retrying will not clear this \u2014 the budget must be raised.");
|
|
1045
|
-
this.vendorLabel = vendorLabel;
|
|
1046
|
-
this.budgetUsd = budgetUsd;
|
|
1047
|
-
this.spentUsd = spentUsd;
|
|
1048
|
-
this.detail = detail;
|
|
1049
|
-
this.name = "BudgetExhaustedError";
|
|
1050
|
-
this.otherVendorAvailable = extra === null || extra === void 0 ? void 0 : extra.otherVendorAvailable;
|
|
1051
|
-
this.serverVendor = extra === null || extra === void 0 ? void 0 : extra.serverVendor;
|
|
1052
|
-
}
|
|
1053
|
-
};
|
|
1054
|
-
var isBudgetRejection = (status, code) => status === BUDGET_EXCEEDED_STATUS || code === BUDGET_EXCEEDED_CODE;
|
|
1055
|
-
var parseJsonOrUndefined = (text) => {
|
|
1056
|
-
try {
|
|
1057
|
-
return JSON.parse(text);
|
|
1058
|
-
} catch (_a) {
|
|
1059
|
-
return void 0;
|
|
1060
|
-
}
|
|
1061
|
-
};
|
|
1062
|
-
var codeOf = (payload) => {
|
|
1063
|
-
const code = payload === null || payload === void 0 ? void 0 : payload.code;
|
|
1064
|
-
return typeof code === "string" ? code : void 0;
|
|
1065
|
-
};
|
|
1066
|
-
var numberOrUndefined = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1067
|
-
var booleanOrUndefined = (value) => typeof value === "boolean" ? value : void 0;
|
|
1068
|
-
var stringOrUndefined = (value) => typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
1069
|
-
function budgetExhaustedFrom(vendorLabel, payload, fallbackDetail) {
|
|
1070
|
-
var _a, _b, _c, _d;
|
|
1071
|
-
const body = typeof payload === "object" && payload !== null ? payload : {};
|
|
1072
|
-
const details = typeof body.details === "object" && body.details !== null ? body.details : {};
|
|
1073
|
-
const detail = typeof body.error === "string" ? body.error : fallbackDetail;
|
|
1074
|
-
return new BudgetExhaustedError(vendorLabel, (_a = numberOrUndefined(body.budgetUsd)) !== null && _a !== void 0 ? _a : numberOrUndefined(details.budgetUsd), (_b = numberOrUndefined(body.spentUsd)) !== null && _b !== void 0 ? _b : numberOrUndefined(details.spentUsd), detail, {
|
|
1075
|
-
otherVendorAvailable: (_c = booleanOrUndefined(body.otherVendorAvailable)) !== null && _c !== void 0 ? _c : booleanOrUndefined(details.otherVendorAvailable),
|
|
1076
|
-
serverVendor: (_d = stringOrUndefined(body.vendor)) !== null && _d !== void 0 ? _d : stringOrUndefined(details.vendor)
|
|
1077
|
-
});
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
1019
|
// ../../foundation-ai/dist/esm/types/config.types.js
|
|
1081
1020
|
var SUPPORTED_GEMINI_MODEL_IDS = [
|
|
1082
1021
|
"gemini-2.5-pro",
|
|
@@ -1231,19 +1170,15 @@ function postWithRetry(options) {
|
|
|
1231
1170
|
signal: combinedSignal,
|
|
1232
1171
|
credentials
|
|
1233
1172
|
});
|
|
1173
|
+
if (retryableStatuses.includes(response.status) && attempt < MAX_RETRIES) {
|
|
1174
|
+
logger.warn(`${vendorLabel}Transport: retryable status ${response.status}, retrying (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
1175
|
+
clearTimeout(timeoutId);
|
|
1176
|
+
yield backoff(attempt);
|
|
1177
|
+
continue;
|
|
1178
|
+
}
|
|
1234
1179
|
if (!response.ok) {
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
if (isBudgetRejection(response.status, codeOf(parsed))) {
|
|
1238
|
-
throw budgetExhaustedFrom(vendorLabel, parsed, errText || void 0);
|
|
1239
|
-
}
|
|
1240
|
-
if (retryableStatuses.includes(response.status) && attempt < MAX_RETRIES) {
|
|
1241
|
-
logger.warn(`${vendorLabel}Transport: retryable status ${response.status}, retrying (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
1242
|
-
clearTimeout(timeoutId);
|
|
1243
|
-
yield backoff(attempt);
|
|
1244
|
-
continue;
|
|
1245
|
-
}
|
|
1246
|
-
throw new Error(`${vendorLabel} request error ${response.status}: ${errText}`);
|
|
1180
|
+
const err = yield response.text();
|
|
1181
|
+
throw new Error(`${vendorLabel} request error ${response.status}: ${err}`);
|
|
1247
1182
|
}
|
|
1248
1183
|
if (((_b = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get("content-type")) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes("application/x-ndjson")) && response.body) {
|
|
1249
1184
|
rearmStallTimer();
|
|
@@ -1253,9 +1188,6 @@ function postWithRetry(options) {
|
|
|
1253
1188
|
if (frame.code === "UPSTREAM_STALLED") {
|
|
1254
1189
|
throw new DOMException(`${vendorLabel} request stalled: ${(_c = frame.error) !== null && _c !== void 0 ? _c : "no upstream data"}`, "TimeoutError");
|
|
1255
1190
|
}
|
|
1256
|
-
if (isBudgetRejection(frame.status, frame.code)) {
|
|
1257
|
-
throw budgetExhaustedFrom(vendorLabel, frame);
|
|
1258
|
-
}
|
|
1259
1191
|
if (retryableStatuses.includes(frame.status) && attempt < MAX_RETRIES) {
|
|
1260
1192
|
logger.warn(`${vendorLabel}Transport: retryable status ${frame.status} (err frame), retrying (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
1261
1193
|
clearTimeout(timeoutId);
|
|
@@ -1718,7 +1650,7 @@ ${att.content}` });
|
|
|
1718
1650
|
headers: requestHeaders,
|
|
1719
1651
|
body,
|
|
1720
1652
|
credentials,
|
|
1721
|
-
vendorLabel:
|
|
1653
|
+
vendorLabel: "Anthropic",
|
|
1722
1654
|
retryableStatuses: _AnthropicTransport.RETRYABLE_STATUSES,
|
|
1723
1655
|
timeout: this.timeout,
|
|
1724
1656
|
stallTimeout: this.stallTimeout,
|
|
@@ -2367,7 +2299,7 @@ ${att.content}`
|
|
|
2367
2299
|
headers,
|
|
2368
2300
|
body: payload,
|
|
2369
2301
|
credentials,
|
|
2370
|
-
vendorLabel:
|
|
2302
|
+
vendorLabel: "Gemini",
|
|
2371
2303
|
retryableStatuses: _GeminiTransport.RETRYABLE_STATUSES,
|
|
2372
2304
|
timeout: this.timeout,
|
|
2373
2305
|
stallTimeout: this.stallTimeout,
|
|
@@ -3069,19 +3001,6 @@ The above summarizes earlier parts of this conversation that were compacted. Con
|
|
|
3069
3001
|
var TOOL_FOLD_SYMBOL = Symbol("toolFold");
|
|
3070
3002
|
|
|
3071
3003
|
// src/components/chat-driver/chat-driver.ts
|
|
3072
|
-
var budgetDetailOf = (e) => {
|
|
3073
|
-
const vendor = vendorTypeOfLabel(e.vendorLabel) ?? vendorTypeOfLabel(e.serverVendor);
|
|
3074
|
-
if (e.budgetUsd == null && e.spentUsd == null && !vendor) return void 0;
|
|
3075
|
-
return {
|
|
3076
|
-
budgetUsd: e.budgetUsd,
|
|
3077
|
-
spentUsd: e.spentUsd,
|
|
3078
|
-
vendorLabel: e.vendorLabel,
|
|
3079
|
-
...vendor ? { vendor } : {},
|
|
3080
|
-
// Server-known truth the client cannot re-derive, so it rides all the way to
|
|
3081
|
-
// the banner rather than being re-guessed there from registry membership.
|
|
3082
|
-
...e.otherVendorAvailable != null ? { otherVendorAvailable: e.otherVendorAvailable } : {}
|
|
3083
|
-
};
|
|
3084
|
-
};
|
|
3085
3004
|
var DEFAULT_MAX_TOOL_ITERATIONS = 50;
|
|
3086
3005
|
var DEFAULT_MAX_FOLD_OPERATIONS = 5;
|
|
3087
3006
|
var DEFAULT_MAX_TURN_SNAPSHOTS = 400;
|
|
@@ -3264,22 +3183,6 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3264
3183
|
* picked up on the next turn.
|
|
3265
3184
|
*/
|
|
3266
3185
|
this.resolvedStatusCache = /* @__PURE__ */ new Map();
|
|
3267
|
-
/**
|
|
3268
|
-
* Set the moment a budget wall is observed anywhere in this turn — this
|
|
3269
|
-
* driver's own 402, or a sub-agent's (which surfaces here only as a
|
|
3270
|
-
* `'budget_exhausted'` tool result). Read at the top of the tool loop to end
|
|
3271
|
-
* the turn before issuing another model call that would hit the same wall.
|
|
3272
|
-
* Reset per turn alongside the other per-turn counters.
|
|
3273
|
-
*/
|
|
3274
|
-
this.budgetExhaustedThisTurn = false;
|
|
3275
|
-
/**
|
|
3276
|
-
* Whether this turn's budget wall came from a SUB-AGENT rather than this
|
|
3277
|
-
* driver's own request. Decides whether `lastResolvedProvider` is a valid
|
|
3278
|
-
* attribution fallback: for an own wall it is the refusing vendor, for a
|
|
3279
|
-
* child's wall it is the parent's vendor — the one known NOT to have refused.
|
|
3280
|
-
* Reset per turn alongside `budgetWallDetail`.
|
|
3281
|
-
*/
|
|
3282
|
-
this.budgetWallViaSubAgent = false;
|
|
3283
3186
|
const {
|
|
3284
3187
|
toolHandlers = {},
|
|
3285
3188
|
toolDefinitions = [],
|
|
@@ -3289,13 +3192,11 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3289
3192
|
maxFoldOperations = DEFAULT_MAX_FOLD_OPERATIONS,
|
|
3290
3193
|
maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS,
|
|
3291
3194
|
sessionKey = "",
|
|
3292
|
-
activityBus = NOOP_ACTIVITY_BUS
|
|
3293
|
-
budgetExhaustedMessage = DEFAULT_BUDGET_EXHAUSTED_MESSAGE
|
|
3195
|
+
activityBus = NOOP_ACTIVITY_BUS
|
|
3294
3196
|
} = config;
|
|
3295
3197
|
this.maxToolIterations = maxToolIterations;
|
|
3296
3198
|
this.sessionKey = sessionKey;
|
|
3297
3199
|
this.activityBus = activityBus;
|
|
3298
|
-
this.budgetExhaustedMessage = budgetExhaustedMessage;
|
|
3299
3200
|
if (typeof toolHandlers === "function") {
|
|
3300
3201
|
this.toolHandlersFactory = toolHandlers;
|
|
3301
3202
|
this.toolHandlers = {};
|
|
@@ -3402,56 +3303,8 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3402
3303
|
* (rather than set to `undefined`) so a happy-path result stays byte-identical to
|
|
3403
3304
|
* the historical `{ reason: 'done' }`.
|
|
3404
3305
|
*/
|
|
3405
|
-
turnDone(failureReason
|
|
3406
|
-
|
|
3407
|
-
return budget ? { reason: "done", failureReason, budget } : { reason: "done", failureReason };
|
|
3408
|
-
}
|
|
3409
|
-
/**
|
|
3410
|
-
* Terminal budget outcome for a wall hit **outside** the tool loop — today,
|
|
3411
|
-
* `OrchestratingDriver`'s classification phase, which calls the provider
|
|
3412
|
-
* directly and so never enters `runToolLoop`.
|
|
3413
|
-
*
|
|
3414
|
-
* Does **not** publish `tool-loop-end`: no `tool-loop-start` was published for
|
|
3415
|
-
* the classify phase, and an unbalanced end would break start/end pairing for
|
|
3416
|
-
* subscribers that rely on it. The driver **return value** is what reports this
|
|
3417
|
-
* case — see `FoundationAiAssistant`'s latch, which reads both seams for
|
|
3418
|
-
* exactly this reason.
|
|
3419
|
-
*
|
|
3420
|
-
* The non-sub-agent tail of the in-loop `BudgetExhaustedError` branch lives
|
|
3421
|
-
* here so there is one copy of the log line, the debug-log entry, the
|
|
3422
|
-
* transcript bubble and the result shape rather than two that can drift.
|
|
3423
|
-
*
|
|
3424
|
-
* @param pendingUserMessage - a user message that has NOT yet been appended,
|
|
3425
|
-
* appended first so the answer does not end up replying to nothing. Only the
|
|
3426
|
-
* classification seam passes it: `OrchestratingDriver` dispatches the user's
|
|
3427
|
-
* text as an optimistic `history-updated` detail and leaves the real append
|
|
3428
|
-
* to `chatDriver.sendMessage`, which never runs when `classify()` throws — so
|
|
3429
|
-
* the bubble below would re-dispatch a history the user's own message was
|
|
3430
|
-
* never in, and it would vanish from the transcript on the next render. The
|
|
3431
|
-
* in-loop caller has already appended it and passes nothing.
|
|
3432
|
-
*
|
|
3433
|
-
* @internal
|
|
3434
|
-
*/
|
|
3435
|
-
reportBudgetExhausted(e, pendingUserMessage) {
|
|
3436
|
-
if (pendingUserMessage) this.appendToHistory(pendingUserMessage);
|
|
3437
|
-
this.budgetExhaustedThisTurn = true;
|
|
3438
|
-
logger2.error("ChatDriver: AI budget exhausted", e);
|
|
3439
|
-
recordTurnError(this.sessionKey, "budget-exhausted", {
|
|
3440
|
-
agent: this.activeAgentName,
|
|
3441
|
-
provider: this.lastResolvedProviderName,
|
|
3442
|
-
// The registry ALIAS (e.g. 'high') is what `provider` records; the vendor
|
|
3443
|
-
// is the thing a per-vendor budget is actually scoped to, and it was known
|
|
3444
|
-
// at both ends and discarded in the middle until now. Taken from the
|
|
3445
|
-
// refusing transport's label first — this method also serves the
|
|
3446
|
-
// classification seam, where `lastResolvedProvider` is the PREVIOUS turn's
|
|
3447
|
-
// vendor (or nothing), because classify runs against the registry default.
|
|
3448
|
-
vendor: vendorTypeOfLabel(e.vendorLabel) ?? vendorTypeOfLabel(e.serverVendor) ?? this.lastResolvedProvider,
|
|
3449
|
-
budgetUsd: e.budgetUsd,
|
|
3450
|
-
spentUsd: e.spentUsd,
|
|
3451
|
-
isSubAgent: this.isSubAgent
|
|
3452
|
-
});
|
|
3453
|
-
this.appendToHistory({ role: "assistant", content: this.budgetExhaustedMessage });
|
|
3454
|
-
return this.turnDone("budget-exhausted", budgetDetailOf(e));
|
|
3306
|
+
turnDone(failureReason) {
|
|
3307
|
+
return failureReason ? { reason: "done", failureReason } : { reason: "done" };
|
|
3455
3308
|
}
|
|
3456
3309
|
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
3457
3310
|
static failureReasonOf(result) {
|
|
@@ -3461,28 +3314,10 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3461
3314
|
* Build the `tool-loop-end` event detail for a turn's result. A failure carries a
|
|
3462
3315
|
* `{ failureReason }` detail; a clean turn emits `undefined` — the historical shape,
|
|
3463
3316
|
* kept byte-identical so subscribers see exactly what they always have.
|
|
3464
|
-
*
|
|
3465
|
-
* A budget failure additionally carries `vendor` — the concrete vendor
|
|
3466
|
-
* (`'anthropic'`/`'gemini'`) the walled turn resolved to, which the driver knows
|
|
3467
|
-
* and used to discard. Optional and additive: a subscriber reading only
|
|
3468
|
-
* `failureReason` is unaffected, a non-budget failure still emits the historical
|
|
3469
|
-
* `{ failureReason }` with no `vendor` key, and the value is a plain string so
|
|
3470
|
-
* the detail stays structured-cloneable for the cross-tab hop. It is the field a
|
|
3471
|
-
* per-vendor budget model needs and the one that would be awkward to retrofit.
|
|
3472
3317
|
*/
|
|
3473
|
-
loopEndDetail(result) {
|
|
3318
|
+
static loopEndDetail(result) {
|
|
3474
3319
|
const failureReason = _ChatDriver.failureReasonOf(result);
|
|
3475
|
-
|
|
3476
|
-
if (failureReason !== "budget-exhausted") {
|
|
3477
|
-
return { failureReason };
|
|
3478
|
-
}
|
|
3479
|
-
const budget = result.reason === "done" ? result.budget : void 0;
|
|
3480
|
-
const vendor = budget?.vendor ?? (this.budgetWallViaSubAgent ? void 0 : this.lastResolvedProvider);
|
|
3481
|
-
return {
|
|
3482
|
-
failureReason,
|
|
3483
|
-
...vendor ? { vendor } : {},
|
|
3484
|
-
...budget ? { budget } : {}
|
|
3485
|
-
};
|
|
3320
|
+
return failureReason ? { failureReason } : void 0;
|
|
3486
3321
|
}
|
|
3487
3322
|
/**
|
|
3488
3323
|
* Swap in a new agent's configuration. Called by OrchestratingDriver before
|
|
@@ -3668,9 +3503,9 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3668
3503
|
* under a separate session key, so recording here would orphan the event off
|
|
3669
3504
|
* the user-visible debug-log timeline.)
|
|
3670
3505
|
*/
|
|
3671
|
-
failSubAgent(reason
|
|
3506
|
+
failSubAgent(reason) {
|
|
3672
3507
|
if (!this.isSubAgent || this.subAgentFailure) return;
|
|
3673
|
-
this.subAgentFailure =
|
|
3508
|
+
this.subAgentFailure = { reason };
|
|
3674
3509
|
}
|
|
3675
3510
|
/**
|
|
3676
3511
|
* Returns true if `releaseAgent` was called during the most recent turn.
|
|
@@ -4115,9 +3950,6 @@ Output format (strict):
|
|
|
4115
3950
|
this.subAgentCompletion = void 0;
|
|
4116
3951
|
this.subAgentFailure = void 0;
|
|
4117
3952
|
this.agentReleaseRequested = false;
|
|
4118
|
-
this.budgetExhaustedThisTurn = false;
|
|
4119
|
-
this.budgetWallDetail = void 0;
|
|
4120
|
-
this.budgetWallViaSubAgent = false;
|
|
4121
3953
|
this.appendToHistory({ role: "user", content: userInput, attachments });
|
|
4122
3954
|
this.turnStartedAt = Date.now();
|
|
4123
3955
|
recordMetaEvent(this.sessionKey, "turn.start", {
|
|
@@ -4152,7 +3984,7 @@ Output format (strict):
|
|
|
4152
3984
|
});
|
|
4153
3985
|
this.busy = false;
|
|
4154
3986
|
this.endTurn();
|
|
4155
|
-
this.activityBus.publish("tool-loop-end",
|
|
3987
|
+
this.activityBus.publish("tool-loop-end", _ChatDriver.loopEndDetail(result));
|
|
4156
3988
|
}
|
|
4157
3989
|
}
|
|
4158
3990
|
/**
|
|
@@ -4343,14 +4175,8 @@ Output format (strict):
|
|
|
4343
4175
|
recordMetaEvent(this.sessionKey, "subagent.completed", { agent: name });
|
|
4344
4176
|
return { outcome: { ok: true, result: completion.result }, trace };
|
|
4345
4177
|
}
|
|
4346
|
-
const
|
|
4347
|
-
const reason = failure?.reason ?? "max_iterations";
|
|
4178
|
+
const reason = child.getSubAgentFailure()?.reason ?? "max_iterations";
|
|
4348
4179
|
recordMetaEvent(this.sessionKey, "subagent.failed", { agent: name, reason });
|
|
4349
|
-
if (reason === "budget_exhausted") {
|
|
4350
|
-
this.budgetExhaustedThisTurn = true;
|
|
4351
|
-
this.budgetWallViaSubAgent = true;
|
|
4352
|
-
this.budgetWallDetail ??= failure?.budget;
|
|
4353
|
-
}
|
|
4354
4180
|
return { outcome: { ok: false, reason }, trace };
|
|
4355
4181
|
}
|
|
4356
4182
|
/**
|
|
@@ -4363,9 +4189,6 @@ Output format (strict):
|
|
|
4363
4189
|
this.beginTurn();
|
|
4364
4190
|
this.subAgentCompletion = void 0;
|
|
4365
4191
|
this.subAgentFailure = void 0;
|
|
4366
|
-
this.budgetExhaustedThisTurn = false;
|
|
4367
|
-
this.budgetWallDetail = void 0;
|
|
4368
|
-
this.budgetWallViaSubAgent = false;
|
|
4369
4192
|
this.turnStartedAt = Date.now();
|
|
4370
4193
|
recordMetaEvent(this.sessionKey, "turn.start", {
|
|
4371
4194
|
phase: "continueFromHistory",
|
|
@@ -4399,7 +4222,7 @@ Output format (strict):
|
|
|
4399
4222
|
});
|
|
4400
4223
|
this.busy = false;
|
|
4401
4224
|
this.endTurn();
|
|
4402
|
-
this.activityBus.publish("tool-loop-end",
|
|
4225
|
+
this.activityBus.publish("tool-loop-end", _ChatDriver.loopEndDetail(result));
|
|
4403
4226
|
}
|
|
4404
4227
|
}
|
|
4405
4228
|
// ---------------------------------------------------------------------------
|
|
@@ -4553,31 +4376,6 @@ Output format (strict):
|
|
|
4553
4376
|
if (this.turnController.signal.aborted) {
|
|
4554
4377
|
return this.completeAbortedTurn();
|
|
4555
4378
|
}
|
|
4556
|
-
if (this.budgetExhaustedThisTurn) {
|
|
4557
|
-
logger2.error("ChatDriver: ending the turn \u2014 a sub-agent hit the AI budget wall");
|
|
4558
|
-
recordTurnError(this.sessionKey, "budget-exhausted", {
|
|
4559
|
-
agent: this.activeAgentName,
|
|
4560
|
-
provider: this.lastResolvedProviderName,
|
|
4561
|
-
// The CHILD's vendor when it knew one, and NOTHING otherwise. On this
|
|
4562
|
-
// path the wall is definitionally the child's, and on a mixed registry
|
|
4563
|
-
// `lastResolvedProvider` is this driver's own vendor — the one known
|
|
4564
|
-
// NOT to have refused. An unattributable child wall must degrade to
|
|
4565
|
-
// the vendor-agnostic block (which `latchBlockedFrom` handles
|
|
4566
|
-
// fail-safe), never to a vendor that is known to be wrong: naming the
|
|
4567
|
-
// parent's vendor here walled BOTH — the child's via its own
|
|
4568
|
-
// tool-loop-end, the parent's via this event — and derived `blocked`
|
|
4569
|
-
// over headroom that still existed.
|
|
4570
|
-
vendor: this.budgetWallDetail?.vendor,
|
|
4571
|
-
via: "sub-agent",
|
|
4572
|
-
isSubAgent: this.isSubAgent
|
|
4573
|
-
});
|
|
4574
|
-
if (this.isSubAgent) {
|
|
4575
|
-
this.failSubAgent("budget_exhausted", this.budgetWallDetail);
|
|
4576
|
-
} else {
|
|
4577
|
-
this.appendToHistory({ role: "assistant", content: this.budgetExhaustedMessage });
|
|
4578
|
-
}
|
|
4579
|
-
return this.turnDone("budget-exhausted", this.budgetWallDetail);
|
|
4580
|
-
}
|
|
4581
4379
|
const promptCtx = {
|
|
4582
4380
|
agentName: this.activeAgentName ?? "",
|
|
4583
4381
|
history: this.history,
|
|
@@ -4748,25 +4546,6 @@ ${tailBody}
|
|
|
4748
4546
|
}
|
|
4749
4547
|
return this.turnDone("response-truncated");
|
|
4750
4548
|
}
|
|
4751
|
-
if (e instanceof BudgetExhaustedError) {
|
|
4752
|
-
this.budgetExhaustedThisTurn = true;
|
|
4753
|
-
this.budgetWallDetail = budgetDetailOf(e);
|
|
4754
|
-
this.budgetWallViaSubAgent = false;
|
|
4755
|
-
if (this.isSubAgent) {
|
|
4756
|
-
logger2.error("ChatDriver: AI budget exhausted", e);
|
|
4757
|
-
recordTurnError(this.sessionKey, "budget-exhausted", {
|
|
4758
|
-
agent: this.activeAgentName,
|
|
4759
|
-
provider: this.lastResolvedProviderName,
|
|
4760
|
-
vendor: vendorTypeOfLabel(e.vendorLabel) ?? vendorTypeOfLabel(e.serverVendor) ?? this.lastResolvedProvider,
|
|
4761
|
-
budgetUsd: e.budgetUsd,
|
|
4762
|
-
spentUsd: e.spentUsd,
|
|
4763
|
-
isSubAgent: true
|
|
4764
|
-
});
|
|
4765
|
-
this.failSubAgent("budget_exhausted", this.budgetWallDetail);
|
|
4766
|
-
return this.turnDone("budget-exhausted", this.budgetWallDetail);
|
|
4767
|
-
}
|
|
4768
|
-
return this.reportBudgetExhausted(e);
|
|
4769
|
-
}
|
|
4770
4549
|
if (e instanceof DOMException && e.name === "TimeoutError") {
|
|
4771
4550
|
logger2.error("ChatDriver: request timed out", e);
|
|
4772
4551
|
recordTurnError(this.sessionKey, "exception", {
|
|
@@ -5242,17 +5021,6 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
5242
5021
|
* user stops. Reset at the start of each `sendMessage`.
|
|
5243
5022
|
*/
|
|
5244
5023
|
this.cancelled = false;
|
|
5245
|
-
/**
|
|
5246
|
-
* Whether the CURRENT turn's user message has reached the inner driver's
|
|
5247
|
-
* history. False through the pre-first-turn classify (where the only echo of
|
|
5248
|
-
* the message is an optimistic `history-updated` dispatch), true from the
|
|
5249
|
-
* moment `chatDriver.sendMessage` is entered — including through every later
|
|
5250
|
-
* handoff classify. Read by the budget-wall catch in `sendMessage` to decide
|
|
5251
|
-
* whether `reportBudgetExhausted` must append the message itself: appending
|
|
5252
|
-
* it when already appended duplicated it; not appending it when unappended
|
|
5253
|
-
* made it vanish. Reset at the top of each `runOrchestratedTurn`.
|
|
5254
|
-
*/
|
|
5255
|
-
this.userMessageAppended = false;
|
|
5256
5024
|
/**
|
|
5257
5025
|
* Sticky user pick from the picker (or the host's `setAgent` API). Only
|
|
5258
5026
|
* changes on explicit user action. Survives flow completion: when a stateful
|
|
@@ -5294,8 +5062,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
5294
5062
|
maxFoldOperations: options.maxFoldOperations,
|
|
5295
5063
|
maxTurnSnapshots: options.maxTurnSnapshots,
|
|
5296
5064
|
sessionKey: this.sessionKey,
|
|
5297
|
-
activityBus: options.activityBus
|
|
5298
|
-
budgetExhaustedMessage: options.budgetExhaustedMessage
|
|
5065
|
+
activityBus: options.activityBus
|
|
5299
5066
|
});
|
|
5300
5067
|
this.chatDriver.addEventListener("history-updated", (e) => {
|
|
5301
5068
|
this.dispatchEvent(new CustomEvent("history-updated", { detail: e.detail }));
|
|
@@ -5394,25 +5161,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
5394
5161
|
return this.chatDriver.getSuggestions(history, prompt2, count, agentInfo);
|
|
5395
5162
|
}
|
|
5396
5163
|
async sendMessage(input, attachments) {
|
|
5397
|
-
try {
|
|
5398
|
-
return await this.runOrchestratedTurn(input, attachments);
|
|
5399
|
-
} catch (e) {
|
|
5400
|
-
if (e instanceof BudgetExhaustedError) {
|
|
5401
|
-
return this.chatDriver.reportBudgetExhausted(
|
|
5402
|
-
e,
|
|
5403
|
-
this.userMessageAppended ? void 0 : {
|
|
5404
|
-
role: "user",
|
|
5405
|
-
content: input,
|
|
5406
|
-
...attachments ? { attachments } : {}
|
|
5407
|
-
}
|
|
5408
|
-
);
|
|
5409
|
-
}
|
|
5410
|
-
throw e;
|
|
5411
|
-
}
|
|
5412
|
-
}
|
|
5413
|
-
async runOrchestratedTurn(input, attachments) {
|
|
5414
5164
|
this.cancelled = false;
|
|
5415
|
-
this.userMessageAppended = false;
|
|
5416
5165
|
const history = this.chatDriver.getHistory();
|
|
5417
5166
|
this.dispatchEvent(
|
|
5418
5167
|
new CustomEvent("history-updated", {
|
|
@@ -5426,7 +5175,6 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
5426
5175
|
let handoffs = 0;
|
|
5427
5176
|
let handoffSummary = "";
|
|
5428
5177
|
let remainingTask = "";
|
|
5429
|
-
let lastResult;
|
|
5430
5178
|
while (true) {
|
|
5431
5179
|
if (this.cancelled) break;
|
|
5432
5180
|
await this.applyAgent(currentAgent);
|
|
@@ -5435,10 +5183,8 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
5435
5183
|
const contextPrimer = handoffSummary ? [{ role: "user", content: `[Context from previous agent]: ${handoffSummary}` }] : [];
|
|
5436
5184
|
result = await this.chatDriver.continueFromHistory(contextPrimer);
|
|
5437
5185
|
} else {
|
|
5438
|
-
this.userMessageAppended = true;
|
|
5439
5186
|
result = await this.chatDriver.sendMessage(input, attachments);
|
|
5440
5187
|
}
|
|
5441
|
-
lastResult = result;
|
|
5442
5188
|
if (this.chatDriver.getAgentReleaseRequested()) {
|
|
5443
5189
|
await this.releaseActiveAgent();
|
|
5444
5190
|
break;
|
|
@@ -5460,7 +5206,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
5460
5206
|
this.dispatchEvent(new CustomEvent("orchestrating-start"));
|
|
5461
5207
|
currentAgent = await this.classify(remainingTask, updatedHistory);
|
|
5462
5208
|
}
|
|
5463
|
-
return
|
|
5209
|
+
return { reason: "done" };
|
|
5464
5210
|
}
|
|
5465
5211
|
async continueFromHistory(transientPrimer) {
|
|
5466
5212
|
return this.chatDriver.continueFromHistory(transientPrimer);
|
|
@@ -5643,7 +5389,6 @@ ${recentMessages}` : ""}`;
|
|
|
5643
5389
|
}
|
|
5644
5390
|
}
|
|
5645
5391
|
};
|
|
5646
|
-
let routedNoMatch = false;
|
|
5647
5392
|
for (let attempt = 0; attempt <= this.classifierRetries; attempt += 1) {
|
|
5648
5393
|
try {
|
|
5649
5394
|
const options = {
|
|
@@ -5663,10 +5408,8 @@ ${recentMessages}` : ""}`;
|
|
|
5663
5408
|
if (index >= 0 && index < this.specialists.length) {
|
|
5664
5409
|
return this.specialists[index];
|
|
5665
5410
|
}
|
|
5666
|
-
routedNoMatch = true;
|
|
5667
5411
|
break;
|
|
5668
5412
|
} catch (e) {
|
|
5669
|
-
if (e instanceof BudgetExhaustedError) throw e;
|
|
5670
5413
|
logger2.warn(`OrchestratingDriver: classifier attempt ${attempt + 1} failed:`, e);
|
|
5671
5414
|
if (attempt === this.classifierRetries) {
|
|
5672
5415
|
logger2.error("OrchestratingDriver: classifier failed after all retries, using fallback");
|
|
@@ -5674,12 +5417,10 @@ ${recentMessages}` : ""}`;
|
|
|
5674
5417
|
}
|
|
5675
5418
|
}
|
|
5676
5419
|
if (this.fallback) return this.fallback;
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
);
|
|
5682
|
-
}
|
|
5420
|
+
const specialistNames = this.specialists.map((s) => s.name).join(", ");
|
|
5421
|
+
this.appendInlineMessage(
|
|
5422
|
+
`I'm not sure how to help with that. I can assist with: ${specialistNames}.`
|
|
5423
|
+
);
|
|
5683
5424
|
return this.specialists[0];
|
|
5684
5425
|
}
|
|
5685
5426
|
/**
|