@genesislcap/ai-assistant 15.14.0 → 15.14.2
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 +37 -1
- package/dist/ai-assistant.d.ts +55 -0
- package/dist/chat-driver.cjs +285 -9
- package/dist/chat-driver.cjs.map +3 -3
- package/dist/chat-driver.mjs +282 -9
- package/dist/chat-driver.mjs.map +3 -3
- package/dist/custom-elements.json +101 -3
- package/dist/dts/chat-driver-node.d.ts +1 -1
- package/dist/dts/chat-driver-node.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +54 -0
- 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/esm/chat-driver-node.js +9 -1
- package/dist/esm/components/chat-driver/chat-driver.js +154 -22
- package/dist/esm/components/chat-driver/chat-driver.test.js +210 -1
- package/dist/esm/state/debug-event-log.js +1 -1
- package/dist/esm/state/debug-event-log.test.js +2 -0
- package/docs/migration-GENC-1506.md +209 -0
- package/package.json +17 -17
- package/src/chat-driver-node.ts +10 -0
- package/src/components/chat-driver/chat-driver.test.ts +294 -0
- package/src/components/chat-driver/chat-driver.ts +215 -9
- package/src/state/debug-event-log.test.ts +2 -0
- package/src/state/debug-event-log.ts +1 -1
package/dist/chat-driver.mjs
CHANGED
|
@@ -1041,6 +1041,94 @@ function budgetExhaustedFrom(vendorLabel, payload, fallbackDetail) {
|
|
|
1041
1041
|
});
|
|
1042
1042
|
}
|
|
1043
1043
|
|
|
1044
|
+
// ../../foundation-ai/dist/esm/transports/provider-refused.js
|
|
1045
|
+
var PROVIDER_REFUSED_CODE = "PROVIDER_REFUSED";
|
|
1046
|
+
var DEFAULT_PROVIDER_REFUSED_MESSAGE = "AI requests are currently being refused by the provider. This isn't related to your account or your usage and needs no action from you \u2014 please contact your support team so it can be restored.";
|
|
1047
|
+
var ProviderRefusedError = class extends Error {
|
|
1048
|
+
constructor(vendorLabel, kind, upstreamStatus, upstreamType, detail) {
|
|
1049
|
+
super(`${vendorLabel} refused the request (${kind})` + (upstreamStatus != null ? ` \u2014 HTTP ${upstreamStatus}` : "") + (upstreamType ? ` ${upstreamType}` : "") + (detail ? `: ${detail}` : "") + ". Retrying will not clear this \u2014 the provider account must be fixed.");
|
|
1050
|
+
this.vendorLabel = vendorLabel;
|
|
1051
|
+
this.kind = kind;
|
|
1052
|
+
this.upstreamStatus = upstreamStatus;
|
|
1053
|
+
this.upstreamType = upstreamType;
|
|
1054
|
+
this.detail = detail;
|
|
1055
|
+
this.name = "ProviderRefusedError";
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
var ANTHROPIC_SPEND_PHRASES = Object.freeze([
|
|
1059
|
+
/credit balance/i,
|
|
1060
|
+
/usage limit/i,
|
|
1061
|
+
/spend limit/i,
|
|
1062
|
+
/insufficient (credit|funds|balance)/i
|
|
1063
|
+
]);
|
|
1064
|
+
var ANTHROPIC_AUTH_TYPES = Object.freeze([
|
|
1065
|
+
"authentication_error",
|
|
1066
|
+
"permission_error"
|
|
1067
|
+
]);
|
|
1068
|
+
function anthropicErrorBody(payload) {
|
|
1069
|
+
var _a;
|
|
1070
|
+
const outer = typeof payload === "object" && payload !== null ? payload : {};
|
|
1071
|
+
for (const candidate of [
|
|
1072
|
+
outer.error,
|
|
1073
|
+
(_a = outer.details) === null || _a === void 0 ? void 0 : _a.error
|
|
1074
|
+
]) {
|
|
1075
|
+
if (typeof candidate === "object" && candidate !== null) {
|
|
1076
|
+
const { type, message } = candidate;
|
|
1077
|
+
return {
|
|
1078
|
+
type: typeof type === "string" ? type : void 0,
|
|
1079
|
+
message: typeof message === "string" ? message : void 0
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return void 0;
|
|
1084
|
+
}
|
|
1085
|
+
function statedKind(payload) {
|
|
1086
|
+
const outer = typeof payload === "object" && payload !== null ? payload : {};
|
|
1087
|
+
const nested = typeof outer.details === "object" && outer.details !== null ? outer.details : {};
|
|
1088
|
+
for (const candidate of [outer.kind, nested.kind]) {
|
|
1089
|
+
if (candidate === "auth" || candidate === "spend")
|
|
1090
|
+
return candidate;
|
|
1091
|
+
}
|
|
1092
|
+
return void 0;
|
|
1093
|
+
}
|
|
1094
|
+
function kindFromEnvelopeType(payload) {
|
|
1095
|
+
const body = anthropicErrorBody(payload);
|
|
1096
|
+
if (!(body === null || body === void 0 ? void 0 : body.type))
|
|
1097
|
+
return void 0;
|
|
1098
|
+
if (ANTHROPIC_AUTH_TYPES.includes(body.type))
|
|
1099
|
+
return "auth";
|
|
1100
|
+
if (body.type === "billing_error")
|
|
1101
|
+
return "spend";
|
|
1102
|
+
return void 0;
|
|
1103
|
+
}
|
|
1104
|
+
function providerRefusalOf(vendor, code, payload) {
|
|
1105
|
+
var _a, _b;
|
|
1106
|
+
if (code === PROVIDER_REFUSED_CODE) {
|
|
1107
|
+
return (_b = (_a = statedKind(payload)) !== null && _a !== void 0 ? _a : kindFromEnvelopeType(payload)) !== null && _b !== void 0 ? _b : "spend";
|
|
1108
|
+
}
|
|
1109
|
+
if (vendor !== "anthropic")
|
|
1110
|
+
return void 0;
|
|
1111
|
+
const structural = kindFromEnvelopeType(payload);
|
|
1112
|
+
if (structural)
|
|
1113
|
+
return structural;
|
|
1114
|
+
const body = anthropicErrorBody(payload);
|
|
1115
|
+
if ((body === null || body === void 0 ? void 0 : body.type) === "invalid_request_error" && body.message) {
|
|
1116
|
+
const message = body.message;
|
|
1117
|
+
if (ANTHROPIC_SPEND_PHRASES.some((phrase) => phrase.test(message)))
|
|
1118
|
+
return "spend";
|
|
1119
|
+
}
|
|
1120
|
+
return void 0;
|
|
1121
|
+
}
|
|
1122
|
+
function providerRefusedFrom(vendorLabel, kind, upstreamStatus, payload, fallbackDetail) {
|
|
1123
|
+
var _a;
|
|
1124
|
+
const body = anthropicErrorBody(payload);
|
|
1125
|
+
return new ProviderRefusedError(vendorLabel, kind, upstreamStatus, body === null || body === void 0 ? void 0 : body.type, (_a = body === null || body === void 0 ? void 0 : body.message) !== null && _a !== void 0 ? _a : fallbackDetail);
|
|
1126
|
+
}
|
|
1127
|
+
function vendorOfTransportLabel(vendorLabel) {
|
|
1128
|
+
const entry = Object.entries(VENDOR_LABELS).find(([, label]) => label === vendorLabel);
|
|
1129
|
+
return entry === null || entry === void 0 ? void 0 : entry[0];
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1044
1132
|
// ../../foundation-ai/dist/esm/types/config.types.js
|
|
1045
1133
|
var SUPPORTED_GEMINI_MODEL_IDS = [
|
|
1046
1134
|
"gemini-2.5-pro",
|
|
@@ -1459,13 +1547,81 @@ function readFramedBody(stream, onChunk, vendorLabel, stallTimeout) {
|
|
|
1459
1547
|
});
|
|
1460
1548
|
}
|
|
1461
1549
|
|
|
1550
|
+
// ../../foundation-ai/dist/esm/transports/retry-hints.js
|
|
1551
|
+
var MAX_SERVER_REQUESTED_BACKOFF_MS = 6e4;
|
|
1552
|
+
var MAX_TOTAL_SERVER_REQUESTED_BACKOFF_MS = 12e4;
|
|
1553
|
+
var SHOULD_RETRY_HEADER = "x-should-retry";
|
|
1554
|
+
var MS_PER_SECOND = 1e3;
|
|
1555
|
+
function shouldNotRetry(headers) {
|
|
1556
|
+
var _a;
|
|
1557
|
+
const value = (_a = headers === null || headers === void 0 ? void 0 : headers.get) === null || _a === void 0 ? void 0 : _a.call(headers, SHOULD_RETRY_HEADER);
|
|
1558
|
+
return typeof value === "string" && value.trim().toLowerCase() === "false";
|
|
1559
|
+
}
|
|
1560
|
+
function retryAfterMs(headers) {
|
|
1561
|
+
var _a;
|
|
1562
|
+
const raw = (_a = headers === null || headers === void 0 ? void 0 : headers.get) === null || _a === void 0 ? void 0 : _a.call(headers, "retry-after");
|
|
1563
|
+
if (typeof raw !== "string" || raw.trim() === "")
|
|
1564
|
+
return void 0;
|
|
1565
|
+
const seconds = Number(raw.trim());
|
|
1566
|
+
if (Number.isFinite(seconds))
|
|
1567
|
+
return Math.max(0, seconds * MS_PER_SECOND);
|
|
1568
|
+
const at = Date.parse(raw);
|
|
1569
|
+
if (Number.isNaN(at))
|
|
1570
|
+
return void 0;
|
|
1571
|
+
return Math.max(0, at - Date.now());
|
|
1572
|
+
}
|
|
1573
|
+
function retryInfoMs(payload) {
|
|
1574
|
+
const outer = typeof payload === "object" && payload !== null ? payload : {};
|
|
1575
|
+
const framed = typeof outer.details === "object" && outer.details !== null ? outer.details : void 0;
|
|
1576
|
+
for (const holder of [outer.error, framed === null || framed === void 0 ? void 0 : framed.error]) {
|
|
1577
|
+
const details = holder === null || holder === void 0 ? void 0 : holder.details;
|
|
1578
|
+
if (!Array.isArray(details))
|
|
1579
|
+
continue;
|
|
1580
|
+
for (const entry of details) {
|
|
1581
|
+
const type = entry === null || entry === void 0 ? void 0 : entry["@type"];
|
|
1582
|
+
if (typeof type !== "string" || !type.endsWith("google.rpc.RetryInfo"))
|
|
1583
|
+
continue;
|
|
1584
|
+
const delay = entry === null || entry === void 0 ? void 0 : entry.retryDelay;
|
|
1585
|
+
if (typeof delay !== "string")
|
|
1586
|
+
continue;
|
|
1587
|
+
const seconds = Number(delay.replace(/s$/, ""));
|
|
1588
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
1589
|
+
return seconds * MS_PER_SECOND;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
return void 0;
|
|
1593
|
+
}
|
|
1594
|
+
function resolveBackoffMs(attempt, baseMs, serverMs, serverAllowanceMs = Number.POSITIVE_INFINITY) {
|
|
1595
|
+
const ladder = baseMs * Math.pow(2, attempt);
|
|
1596
|
+
if (serverMs == null || !Number.isFinite(serverMs))
|
|
1597
|
+
return ladder;
|
|
1598
|
+
const honoured = Math.min(serverMs, MAX_SERVER_REQUESTED_BACKOFF_MS, Math.max(0, serverAllowanceMs));
|
|
1599
|
+
return Math.max(ladder, honoured);
|
|
1600
|
+
}
|
|
1601
|
+
function serverBackoffSpentMs(resolvedMs, ladderMs) {
|
|
1602
|
+
return Math.max(0, resolvedMs - ladderMs);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1462
1605
|
// ../../foundation-ai/dist/esm/transports/post-with-retry.js
|
|
1463
1606
|
var MAX_RETRIES = 5;
|
|
1464
1607
|
function postWithRetry(options) {
|
|
1465
1608
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1466
1609
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
1467
1610
|
const { url, headers, body, credentials, vendorLabel, retryableStatuses, timeout, stallTimeout, backoffBaseMs, signal } = options;
|
|
1468
|
-
const
|
|
1611
|
+
const vendor = vendorOfTransportLabel(vendorLabel);
|
|
1612
|
+
let serverBackoffSpent = 0;
|
|
1613
|
+
const backoff = (attempt, serverMs) => {
|
|
1614
|
+
const ladderMs = backoffBaseMs * Math.pow(2, attempt);
|
|
1615
|
+
const allowanceMs = MAX_TOTAL_SERVER_REQUESTED_BACKOFF_MS - serverBackoffSpent;
|
|
1616
|
+
const waitMs = resolveBackoffMs(attempt, backoffBaseMs, serverMs, allowanceMs);
|
|
1617
|
+
serverBackoffSpent += serverBackoffSpentMs(waitMs, ladderMs);
|
|
1618
|
+
if (serverMs != null && waitMs > ladderMs) {
|
|
1619
|
+
logger.warn(`${vendorLabel}Transport: honouring the server's requested retry delay (${Math.round(waitMs)}ms, asked ${Math.round(serverMs)}ms)`);
|
|
1620
|
+
} else if (serverMs != null && serverMs > ladderMs) {
|
|
1621
|
+
logger.warn(`${vendorLabel}Transport: declining the server's requested retry delay (asked ${Math.round(serverMs)}ms; the ${MAX_TOTAL_SERVER_REQUESTED_BACKOFF_MS}ms sequence allowance is spent) \u2014 using the ladder's ${Math.round(waitMs)}ms`);
|
|
1622
|
+
}
|
|
1623
|
+
return abortableDelay(waitMs, signal);
|
|
1624
|
+
};
|
|
1469
1625
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {
|
|
1470
1626
|
const timeoutController = new AbortController();
|
|
1471
1627
|
const startedAt = performance.now();
|
|
@@ -1499,13 +1655,18 @@ function postWithRetry(options) {
|
|
|
1499
1655
|
if (!response.ok) {
|
|
1500
1656
|
const errText = yield response.text();
|
|
1501
1657
|
const parsed = parseJsonOrUndefined(errText);
|
|
1658
|
+
const refusal = providerRefusalOf(vendor, codeOf(parsed), parsed);
|
|
1659
|
+
if (refusal) {
|
|
1660
|
+
throw providerRefusedFrom(vendorLabel, refusal, response.status, parsed, errText || void 0);
|
|
1661
|
+
}
|
|
1502
1662
|
if (isBudgetRejection(response.status, codeOf(parsed))) {
|
|
1503
1663
|
throw budgetExhaustedFrom(vendorLabel, parsed, errText || void 0);
|
|
1504
1664
|
}
|
|
1505
|
-
|
|
1665
|
+
const refusedRetry = shouldNotRetry(response.headers);
|
|
1666
|
+
if (retryableStatuses.includes(response.status) && !refusedRetry && attempt < MAX_RETRIES) {
|
|
1506
1667
|
logger.warn(`${vendorLabel}Transport: retryable status ${response.status}, retrying (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
1507
1668
|
clearTimeout(timeoutId);
|
|
1508
|
-
yield backoff(attempt);
|
|
1669
|
+
yield backoff(attempt, retryAfterMs(response.headers) || retryInfoMs(parsed));
|
|
1509
1670
|
continue;
|
|
1510
1671
|
}
|
|
1511
1672
|
throw new Error(`${vendorLabel} request error ${response.status}: ${errText}`);
|
|
@@ -1518,13 +1679,17 @@ function postWithRetry(options) {
|
|
|
1518
1679
|
if (frame.code === "UPSTREAM_STALLED") {
|
|
1519
1680
|
throw new DOMException(`${vendorLabel} request stalled: ${(_f = frame.error) !== null && _f !== void 0 ? _f : "no upstream data"}`, "TimeoutError");
|
|
1520
1681
|
}
|
|
1682
|
+
const framedRefusal = providerRefusalOf(vendor, frame.code, frame);
|
|
1683
|
+
if (framedRefusal) {
|
|
1684
|
+
throw providerRefusedFrom(vendorLabel, framedRefusal, frame.status, frame, frame.error);
|
|
1685
|
+
}
|
|
1521
1686
|
if (isBudgetRejection(frame.status, frame.code)) {
|
|
1522
1687
|
throw budgetExhaustedFrom(vendorLabel, frame);
|
|
1523
1688
|
}
|
|
1524
1689
|
if (retryableStatuses.includes(frame.status) && attempt < MAX_RETRIES) {
|
|
1525
1690
|
logger.warn(`${vendorLabel}Transport: retryable status ${frame.status} (err frame), retrying (attempt ${attempt + 1}/${MAX_RETRIES})`);
|
|
1526
1691
|
clearTimeout(timeoutId);
|
|
1527
|
-
yield backoff(attempt);
|
|
1692
|
+
yield backoff(attempt, retryInfoMs(frame));
|
|
1528
1693
|
continue;
|
|
1529
1694
|
}
|
|
1530
1695
|
throw new Error(`${vendorLabel} request error ${frame.status}: ${(_g = frame.error) !== null && _g !== void 0 ? _g : JSON.stringify(frame.details)}`);
|
|
@@ -3607,6 +3772,12 @@ function accumulate(messages, into) {
|
|
|
3607
3772
|
var TOOL_FOLD_SYMBOL = Symbol("toolFold");
|
|
3608
3773
|
|
|
3609
3774
|
// src/components/chat-driver/chat-driver.ts
|
|
3775
|
+
var providerRefusedDetailOf = (e) => ({
|
|
3776
|
+
vendorLabel: e.vendorLabel,
|
|
3777
|
+
kind: e.kind,
|
|
3778
|
+
...e.upstreamStatus != null ? { upstreamStatus: e.upstreamStatus } : {},
|
|
3779
|
+
...e.upstreamType ? { upstreamType: e.upstreamType } : {}
|
|
3780
|
+
});
|
|
3610
3781
|
var budgetDetailOf = (e) => {
|
|
3611
3782
|
const vendor = vendorTypeOfLabel(e.vendorLabel) ?? vendorTypeOfLabel(e.serverVendor);
|
|
3612
3783
|
if (e.budgetUsd == null && e.spentUsd == null && !vendor) return void 0;
|
|
@@ -3802,6 +3973,16 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3802
3973
|
* picked up on the next turn.
|
|
3803
3974
|
*/
|
|
3804
3975
|
this.resolvedStatusCache = /* @__PURE__ */ new Map();
|
|
3976
|
+
/**
|
|
3977
|
+
* A provider refusal was observed this turn, so the tool loop must not call the model again
|
|
3978
|
+
* (GENC-1506).
|
|
3979
|
+
*
|
|
3980
|
+
* Exists for the SUB-AGENT path only, exactly like `budgetExhaustedThisTurn`: this driver's own
|
|
3981
|
+
* refusal returns straight out of the catch, whereas a child's refusal reaches the parent as a tool
|
|
3982
|
+
* result, and without this flag the loop would issue another call into the same wall — N batched
|
|
3983
|
+
* children costing N doomed calls plus a doomed parent one.
|
|
3984
|
+
*/
|
|
3985
|
+
this.providerRefusedThisTurn = false;
|
|
3805
3986
|
/**
|
|
3806
3987
|
* Set the moment a budget wall is observed anywhere in this turn — this
|
|
3807
3988
|
* driver's own 402, or a sub-agent's (which surfaces here only as a
|
|
@@ -3829,13 +4010,15 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3829
4010
|
maxTurnSnapshots = DEFAULT_MAX_TURN_SNAPSHOTS,
|
|
3830
4011
|
sessionKey = "",
|
|
3831
4012
|
activityBus = NOOP_ACTIVITY_BUS,
|
|
3832
|
-
budgetExhaustedMessage
|
|
4013
|
+
budgetExhaustedMessage,
|
|
4014
|
+
providerRefusedMessage
|
|
3833
4015
|
} = config;
|
|
3834
4016
|
this.maxToolIterations = maxToolIterations;
|
|
3835
4017
|
this.condenseBatchCalls = condenseBatchCalls;
|
|
3836
4018
|
this.sessionKey = sessionKey;
|
|
3837
4019
|
this.activityBus = activityBus;
|
|
3838
4020
|
this.budgetExhaustedMessageOverride = budgetExhaustedMessage;
|
|
4021
|
+
this.providerRefusedMessageOverride = providerRefusedMessage;
|
|
3839
4022
|
if (typeof toolHandlers === "function") {
|
|
3840
4023
|
this.toolHandlersFactory = toolHandlers;
|
|
3841
4024
|
this.toolHandlers = {};
|
|
@@ -3942,9 +4125,14 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3942
4125
|
* (rather than set to `undefined`) so a happy-path result stays byte-identical to
|
|
3943
4126
|
* the historical `{ reason: 'done' }`.
|
|
3944
4127
|
*/
|
|
3945
|
-
turnDone(failureReason, budget) {
|
|
4128
|
+
turnDone(failureReason, budget, providerRefused) {
|
|
3946
4129
|
if (!failureReason) return { reason: "done" };
|
|
3947
|
-
return
|
|
4130
|
+
return {
|
|
4131
|
+
reason: "done",
|
|
4132
|
+
failureReason,
|
|
4133
|
+
...budget ? { budget } : {},
|
|
4134
|
+
...providerRefused ? { providerRefused } : {}
|
|
4135
|
+
};
|
|
3948
4136
|
}
|
|
3949
4137
|
/**
|
|
3950
4138
|
* Terminal budget outcome for a wall hit **outside** the tool loop — today,
|
|
@@ -3999,6 +4187,42 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
3999
4187
|
this.appendToHistory({ role: "assistant", content: this.budgetExhaustedBubble(e) });
|
|
4000
4188
|
return this.turnDone("budget-exhausted", budgetDetailOf(e));
|
|
4001
4189
|
}
|
|
4190
|
+
/**
|
|
4191
|
+
* The sentence shown when the upstream provider refuses (GENC-1506).
|
|
4192
|
+
*
|
|
4193
|
+
* Deliberately far simpler than `budgetExhaustedBubble`: no vendor name, no figures, no
|
|
4194
|
+
* switch-provider advice, and no branching on `kind`. The copy is cause-free by design — a user who
|
|
4195
|
+
* can see their own remaining spend must not be told about a limit, and we must not imply the bill
|
|
4196
|
+
* has gone unpaid — so there is nothing here to compose. A host override wins verbatim.
|
|
4197
|
+
*/
|
|
4198
|
+
providerRefusedBubble() {
|
|
4199
|
+
return this.providerRefusedMessageOverride ?? DEFAULT_PROVIDER_REFUSED_MESSAGE;
|
|
4200
|
+
}
|
|
4201
|
+
/**
|
|
4202
|
+
* Terminal provider-refusal outcome. Mirrors `reportBudgetExhausted` so the two walls behave
|
|
4203
|
+
* identically from the caller's side, while keeping their diagnostics distinct.
|
|
4204
|
+
*
|
|
4205
|
+
* `kind` reaches the debug log and the result but never the transcript: it is what lets an operator
|
|
4206
|
+
* tell "top up the account" from "rotate the key", and with cause-free user copy this is the only
|
|
4207
|
+
* place that distinction survives.
|
|
4208
|
+
*/
|
|
4209
|
+
reportProviderRefused(e) {
|
|
4210
|
+
const detail = providerRefusedDetailOf(e);
|
|
4211
|
+
this.providerRefusedThisTurn = true;
|
|
4212
|
+
this.providerRefusedDetail = detail;
|
|
4213
|
+
logger2.error("ChatDriver: provider refused the request", e);
|
|
4214
|
+
recordTurnError(this.sessionKey, "provider-refused", {
|
|
4215
|
+
agent: this.activeAgentName,
|
|
4216
|
+
provider: this.lastResolvedProviderName,
|
|
4217
|
+
vendor: vendorTypeOfLabel(e.vendorLabel) ?? this.lastResolvedProvider,
|
|
4218
|
+
kind: e.kind,
|
|
4219
|
+
upstreamStatus: e.upstreamStatus,
|
|
4220
|
+
upstreamType: e.upstreamType,
|
|
4221
|
+
isSubAgent: this.isSubAgent
|
|
4222
|
+
});
|
|
4223
|
+
this.appendToHistory({ role: "assistant", content: this.providerRefusedBubble() });
|
|
4224
|
+
return this.turnDone("provider-refused", void 0, detail);
|
|
4225
|
+
}
|
|
4002
4226
|
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
4003
4227
|
static failureReasonOf(result) {
|
|
4004
4228
|
return result.reason === "done" ? result.failureReason : void 0;
|
|
@@ -4222,9 +4446,13 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
4222
4446
|
* under a separate session key, so recording here would orphan the event off
|
|
4223
4447
|
* the user-visible debug-log timeline.)
|
|
4224
4448
|
*/
|
|
4225
|
-
failSubAgent(reason, budget) {
|
|
4449
|
+
failSubAgent(reason, budget, providerRefused) {
|
|
4226
4450
|
if (!this.isSubAgent || this.subAgentFailure) return;
|
|
4227
|
-
this.subAgentFailure =
|
|
4451
|
+
this.subAgentFailure = {
|
|
4452
|
+
reason,
|
|
4453
|
+
...budget ? { budget } : {},
|
|
4454
|
+
...providerRefused ? { providerRefused } : {}
|
|
4455
|
+
};
|
|
4228
4456
|
}
|
|
4229
4457
|
/**
|
|
4230
4458
|
* Returns true if `releaseAgent` was called during the most recent turn.
|
|
@@ -4683,6 +4911,8 @@ Output format (strict):
|
|
|
4683
4911
|
this.budgetExhaustedThisTurn = false;
|
|
4684
4912
|
this.budgetWallDetail = void 0;
|
|
4685
4913
|
this.budgetWallViaSubAgent = false;
|
|
4914
|
+
this.providerRefusedThisTurn = false;
|
|
4915
|
+
this.providerRefusedDetail = void 0;
|
|
4686
4916
|
this.appendToHistory({ role: "user", content: userInput, attachments });
|
|
4687
4917
|
this.turnStartedAt = Date.now();
|
|
4688
4918
|
recordMetaEvent(this.sessionKey, "turn.start", {
|
|
@@ -4927,6 +5157,10 @@ Output format (strict):
|
|
|
4927
5157
|
this.budgetWallViaSubAgent = true;
|
|
4928
5158
|
this.budgetWallDetail ??= failure?.budget;
|
|
4929
5159
|
}
|
|
5160
|
+
if (reason === "provider_refused") {
|
|
5161
|
+
this.providerRefusedThisTurn = true;
|
|
5162
|
+
this.providerRefusedDetail ??= failure?.providerRefused;
|
|
5163
|
+
}
|
|
4930
5164
|
return { outcome: { ok: false, reason }, trace };
|
|
4931
5165
|
}
|
|
4932
5166
|
/**
|
|
@@ -4942,6 +5176,8 @@ Output format (strict):
|
|
|
4942
5176
|
this.budgetExhaustedThisTurn = false;
|
|
4943
5177
|
this.budgetWallDetail = void 0;
|
|
4944
5178
|
this.budgetWallViaSubAgent = false;
|
|
5179
|
+
this.providerRefusedThisTurn = false;
|
|
5180
|
+
this.providerRefusedDetail = void 0;
|
|
4945
5181
|
this.turnStartedAt = Date.now();
|
|
4946
5182
|
recordMetaEvent(this.sessionKey, "turn.start", {
|
|
4947
5183
|
phase: "continueFromHistory",
|
|
@@ -5131,6 +5367,22 @@ Output format (strict):
|
|
|
5131
5367
|
if (this.turnController.signal.aborted) {
|
|
5132
5368
|
return this.completeAbortedTurn();
|
|
5133
5369
|
}
|
|
5370
|
+
if (this.providerRefusedThisTurn) {
|
|
5371
|
+
logger2.error("ChatDriver: ending the turn \u2014 a sub-agent hit the provider wall");
|
|
5372
|
+
recordTurnError(this.sessionKey, "provider-refused", {
|
|
5373
|
+
agent: this.activeAgentName,
|
|
5374
|
+
provider: this.lastResolvedProviderName,
|
|
5375
|
+
kind: this.providerRefusedDetail?.kind,
|
|
5376
|
+
via: "sub-agent",
|
|
5377
|
+
isSubAgent: this.isSubAgent
|
|
5378
|
+
});
|
|
5379
|
+
if (this.isSubAgent) {
|
|
5380
|
+
this.failSubAgent("provider_refused", void 0, this.providerRefusedDetail);
|
|
5381
|
+
} else {
|
|
5382
|
+
this.appendToHistory({ role: "assistant", content: this.providerRefusedBubble() });
|
|
5383
|
+
}
|
|
5384
|
+
return this.turnDone("provider-refused", void 0, this.providerRefusedDetail);
|
|
5385
|
+
}
|
|
5134
5386
|
if (this.budgetExhaustedThisTurn) {
|
|
5135
5387
|
logger2.error("ChatDriver: ending the turn \u2014 a sub-agent hit the AI budget wall");
|
|
5136
5388
|
recordTurnError(this.sessionKey, "budget-exhausted", {
|
|
@@ -5358,6 +5610,24 @@ ${tailBody}
|
|
|
5358
5610
|
}
|
|
5359
5611
|
return this.turnDone("response-truncated");
|
|
5360
5612
|
}
|
|
5613
|
+
if (e instanceof ProviderRefusedError) {
|
|
5614
|
+
if (this.isSubAgent) {
|
|
5615
|
+
logger2.error("ChatDriver: provider refused the request", e);
|
|
5616
|
+
recordTurnError(this.sessionKey, "provider-refused", {
|
|
5617
|
+
agent: this.activeAgentName,
|
|
5618
|
+
provider: this.lastResolvedProviderName,
|
|
5619
|
+
vendor: vendorTypeOfLabel(e.vendorLabel) ?? this.lastResolvedProvider,
|
|
5620
|
+
kind: e.kind,
|
|
5621
|
+
upstreamStatus: e.upstreamStatus,
|
|
5622
|
+
upstreamType: e.upstreamType,
|
|
5623
|
+
isSubAgent: true
|
|
5624
|
+
});
|
|
5625
|
+
const detail = providerRefusedDetailOf(e);
|
|
5626
|
+
this.failSubAgent("provider_refused", void 0, detail);
|
|
5627
|
+
return this.turnDone("provider-refused", void 0, detail);
|
|
5628
|
+
}
|
|
5629
|
+
return this.reportProviderRefused(e);
|
|
5630
|
+
}
|
|
5361
5631
|
if (e instanceof BudgetExhaustedError) {
|
|
5362
5632
|
this.budgetExhaustedThisTurn = true;
|
|
5363
5633
|
this.budgetWallDetail = budgetDetailOf(e);
|
|
@@ -6714,6 +6984,7 @@ export {
|
|
|
6714
6984
|
AnthropicProvider,
|
|
6715
6985
|
AnthropicTransport,
|
|
6716
6986
|
ChatDriver,
|
|
6987
|
+
DEFAULT_PROVIDER_REFUSED_MESSAGE,
|
|
6717
6988
|
GEMINI_CACHED_INPUT_MULTIPLIER,
|
|
6718
6989
|
GEMINI_LONG_CONTEXT_THRESHOLD,
|
|
6719
6990
|
GeminiProvider,
|
|
@@ -6721,6 +6992,8 @@ export {
|
|
|
6721
6992
|
MutableAIProviderRegistry,
|
|
6722
6993
|
NOOP_ACTIVITY_BUS,
|
|
6723
6994
|
OrchestratingDriver,
|
|
6995
|
+
PROVIDER_REFUSED_CODE,
|
|
6996
|
+
ProviderRefusedError,
|
|
6724
6997
|
REQUEST_CONTINUATION_TOOL,
|
|
6725
6998
|
SUPPORTED_ANTHROPIC_MODEL_IDS,
|
|
6726
6999
|
SUPPORTED_GEMINI_MODEL_IDS,
|