@genesislcap/ai-assistant 15.20.2 → 15.21.1
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 +135 -1
- package/dist/ai-assistant.d.ts +152 -0
- package/dist/chat-driver.cjs +281 -81
- package/dist/chat-driver.cjs.map +2 -2
- package/dist/chat-driver.mjs +277 -81
- package/dist/chat-driver.mjs.map +2 -2
- package/dist/custom-elements.json +175 -8
- package/dist/dts/components/chat-driver/chat-driver.d.ts +39 -0
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/index.d.ts +2 -1
- package/dist/dts/components/orchestrating-driver/index.d.ts.map +1 -1
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +101 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/config/fallback-agents.d.ts +7 -0
- package/dist/dts/config/fallback-agents.d.ts.map +1 -1
- package/dist/dts/state/debug-event-log.d.ts +1 -1
- package/dist/dts/state/debug-event-log.d.ts.map +1 -1
- package/dist/dts/utils/context-tokens.d.ts +25 -3
- package/dist/dts/utils/context-tokens.d.ts.map +1 -1
- package/dist/dts/utils/history-transform.d.ts +23 -3
- package/dist/dts/utils/history-transform.d.ts.map +1 -1
- package/dist/esm/components/chat-driver/chat-driver.js +72 -56
- package/dist/esm/components/orchestrating-driver/index.js +1 -1
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +266 -36
- package/dist/esm/config/fallback-agents.js +12 -1
- package/dist/esm/state/debug-event-log.js +7 -2
- package/dist/esm/utils/context-tokens.js +27 -3
- package/dist/esm/utils/history-transform.js +54 -5
- package/package.json +16 -16
package/dist/chat-driver.cjs
CHANGED
|
@@ -27,10 +27,12 @@ __export(chat_driver_node_exports, {
|
|
|
27
27
|
AnthropicTransport: () => AnthropicTransport,
|
|
28
28
|
ChatDriver: () => ChatDriver,
|
|
29
29
|
DEFAULT_PROVIDER_REFUSED_MESSAGE: () => DEFAULT_PROVIDER_REFUSED_MESSAGE,
|
|
30
|
+
EMPTY_RESPONSE_NUDGE: () => EMPTY_RESPONSE_NUDGE,
|
|
30
31
|
GEMINI_CACHED_INPUT_MULTIPLIER: () => GEMINI_CACHED_INPUT_MULTIPLIER,
|
|
31
32
|
GEMINI_LONG_CONTEXT_THRESHOLD: () => GEMINI_LONG_CONTEXT_THRESHOLD,
|
|
32
33
|
GeminiProvider: () => GeminiProvider,
|
|
33
34
|
GeminiTransport: () => GeminiTransport,
|
|
35
|
+
MALFORMED_CALL_NUDGE: () => MALFORMED_CALL_NUDGE,
|
|
34
36
|
MutableAIProviderRegistry: () => MutableAIProviderRegistry,
|
|
35
37
|
NOOP_ACTIVITY_BUS: () => NOOP_ACTIVITY_BUS,
|
|
36
38
|
OrchestratingDriver: () => OrchestratingDriver,
|
|
@@ -39,6 +41,8 @@ __export(chat_driver_node_exports, {
|
|
|
39
41
|
REQUEST_CONTINUATION_TOOL: () => REQUEST_CONTINUATION_TOOL,
|
|
40
42
|
SUPPORTED_ANTHROPIC_MODEL_IDS: () => SUPPORTED_ANTHROPIC_MODEL_IDS,
|
|
41
43
|
SUPPORTED_GEMINI_MODEL_IDS: () => SUPPORTED_GEMINI_MODEL_IDS,
|
|
44
|
+
UNROUTABLE_INSTRUCTION: () => UNROUTABLE_INSTRUCTION,
|
|
45
|
+
UNROUTABLE_PLACEHOLDER: () => UNROUTABLE_PLACEHOLDER,
|
|
42
46
|
addUsage: () => addUsage,
|
|
43
47
|
agenticActivityBus: () => agenticActivityBus,
|
|
44
48
|
anthropicRatesFor: () => anthropicRatesFor,
|
|
@@ -2240,7 +2244,8 @@ ${att.content}` });
|
|
|
2240
2244
|
return { images, texts };
|
|
2241
2245
|
};
|
|
2242
2246
|
for (const msg of history) {
|
|
2243
|
-
if (msg.role === "system" || msg.role === "system-event" || msg.role === "synthetic-user" || msg.role === "compacted-summary" || msg.category === "reasoning" || msg.category === "narration" ||
|
|
2247
|
+
if (msg.role === "system" || msg.role === "system-event" || msg.role === "synthetic-user" || msg.role === "compacted-summary" || msg.category === "reasoning" || msg.category === "narration" || // Driver-authored prose no model produced — see ChatMessage.excludeFromProvider.
|
|
2248
|
+
msg.excludeFromProvider || msg.thinking)
|
|
2244
2249
|
continue;
|
|
2245
2250
|
if (msg.toolResult) {
|
|
2246
2251
|
const resultImages = (_a = msg.toolResult.attachments) !== null && _a !== void 0 ? _a : [];
|
|
@@ -2503,12 +2508,16 @@ ${att.content}` });
|
|
|
2503
2508
|
if (cost != null)
|
|
2504
2509
|
base.cost = cost;
|
|
2505
2510
|
base.model = servingModel;
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2511
|
+
const responseMeta = {
|
|
2512
|
+
parts: {
|
|
2513
|
+
functionCall: blocks.filter((b) => b.type === "tool_use").length,
|
|
2514
|
+
thought: blocks.filter((b) => b.type === "thinking" || b.type === "redacted_thinking").length,
|
|
2515
|
+
text: blocks.filter((b) => b.type === "text").length
|
|
2516
|
+
}
|
|
2517
|
+
};
|
|
2518
|
+
if (response.stop_reason)
|
|
2519
|
+
responseMeta.finishReason = response.stop_reason;
|
|
2520
|
+
base.responseMeta = responseMeta;
|
|
2512
2521
|
return base;
|
|
2513
2522
|
}
|
|
2514
2523
|
buildEndpoint() {
|
|
@@ -3057,7 +3066,8 @@ var GeminiTransport = class _GeminiTransport {
|
|
|
3057
3066
|
const contents = [];
|
|
3058
3067
|
const toolCallNameById = /* @__PURE__ */ new Map();
|
|
3059
3068
|
for (const msg of history) {
|
|
3060
|
-
if (msg.role === "system" || msg.role === "system-event" || msg.role === "synthetic-user" || msg.role === "compacted-summary" || msg.category === "reasoning" || msg.category === "narration" ||
|
|
3069
|
+
if (msg.role === "system" || msg.role === "system-event" || msg.role === "synthetic-user" || msg.role === "compacted-summary" || msg.category === "reasoning" || msg.category === "narration" || // Driver-authored prose no model produced — see ChatMessage.excludeFromProvider.
|
|
3070
|
+
msg.excludeFromProvider || msg.thinking)
|
|
3061
3071
|
continue;
|
|
3062
3072
|
const role = msg.role === "user" ? "user" : "model";
|
|
3063
3073
|
if (msg.toolResult) {
|
|
@@ -3642,6 +3652,9 @@ var META_EVENT_IMPORTANCE = {
|
|
|
3642
3652
|
"tool.unresolved": "normal",
|
|
3643
3653
|
"subagent.started": "normal",
|
|
3644
3654
|
"subagent.completed": "normal",
|
|
3655
|
+
// One per routing decision, with what the classifier answered and what the call cost. Same
|
|
3656
|
+
// level as the handoff it produces: you read them together to follow how a turn was routed.
|
|
3657
|
+
"routing.decided": "normal",
|
|
3645
3658
|
"agent.handoff": "normal",
|
|
3646
3659
|
"agent.pinned": "normal",
|
|
3647
3660
|
"agent.unpinned": "normal",
|
|
@@ -3803,6 +3816,7 @@ var COMPACTION_SUMMARY_TOKEN_ESTIMATE = 1200;
|
|
|
3803
3816
|
function reachesProvider(m) {
|
|
3804
3817
|
if (m.role === "system-event" || m.role === "synthetic-user") return false;
|
|
3805
3818
|
if (m.category === "reasoning" || m.category === "narration") return false;
|
|
3819
|
+
if (m.excludeFromProvider) return false;
|
|
3806
3820
|
return !m.thinking;
|
|
3807
3821
|
}
|
|
3808
3822
|
function imageCount(m) {
|
|
@@ -4132,10 +4146,27 @@ function maskToolPayload(msg) {
|
|
|
4132
4146
|
}
|
|
4133
4147
|
return msg;
|
|
4134
4148
|
}
|
|
4149
|
+
function foreignToolCallText(msg) {
|
|
4150
|
+
const names = (msg.toolCalls ?? []).map((tc) => tc.name).join(", ");
|
|
4151
|
+
const trace = names ? `[Earlier tool call: ${names}]` : "";
|
|
4152
|
+
return msg.content && trace ? `${msg.content}
|
|
4153
|
+
${trace}` : msg.content || trace;
|
|
4154
|
+
}
|
|
4135
4155
|
function transformHistoryForAgent(history, agentName) {
|
|
4156
|
+
const isForeign = (msg) => !!msg.agentName && msg.agentName !== agentName;
|
|
4157
|
+
const foreignCallIds = /* @__PURE__ */ new Set();
|
|
4158
|
+
for (const msg of history) {
|
|
4159
|
+
if (!isForeign(msg)) continue;
|
|
4160
|
+
for (const tc of msg.toolCalls ?? []) foreignCallIds.add(tc.id);
|
|
4161
|
+
if (msg.toolResult) foreignCallIds.add(msg.toolResult.toolCallId);
|
|
4162
|
+
}
|
|
4136
4163
|
return history.map((msg) => {
|
|
4137
|
-
|
|
4138
|
-
return
|
|
4164
|
+
const pairIsForeign = (msg.toolCalls ?? []).some((tc) => foreignCallIds.has(tc.id)) || (msg.toolResult ? foreignCallIds.has(msg.toolResult.toolCallId) : false);
|
|
4165
|
+
if (!pairIsForeign) return msg;
|
|
4166
|
+
if (msg.toolResult) {
|
|
4167
|
+
return { ...maskToolPayload(msg), excludeFromProvider: true };
|
|
4168
|
+
}
|
|
4169
|
+
return { ...msg, content: foreignToolCallText(msg), toolCalls: void 0 };
|
|
4139
4170
|
});
|
|
4140
4171
|
}
|
|
4141
4172
|
function applyHistoryCap(history, cap) {
|
|
@@ -4339,6 +4370,8 @@ var DEFAULT_MAX_UNKNOWN_TOOL_CALLS = 5;
|
|
|
4339
4370
|
var MAX_STALE_TOOL_CALLS = DEFAULT_MAX_UNKNOWN_TOOL_CALLS * 2;
|
|
4340
4371
|
var MAX_MALFORMED_RETRIES = 5;
|
|
4341
4372
|
var MAX_EMPTY_RESPONSE_RETRIES = 3;
|
|
4373
|
+
var MALFORMED_CALL_NUDGE = "IMPORTANT: Use only the structured function-call API to invoke tools. Do not write Python code or use Python-style syntax to call tools.";
|
|
4374
|
+
var EMPTY_RESPONSE_NUDGE = "IMPORTANT: Your previous attempt returned nothing at all, which is never a valid response. Answer the user's last message now, in prose \u2014 even if all you can honestly say is that you cannot help with it and why. If a tool is the right way to answer, call it. Do not return an empty response.";
|
|
4342
4375
|
var MAX_SETUP_TRANSPORT_RETRIES = 3;
|
|
4343
4376
|
var SUGGESTIONS_HISTORY_WINDOW = 8;
|
|
4344
4377
|
var HARVESTED_SUBAGENT_EVENTS = /* @__PURE__ */ new Set([
|
|
@@ -4725,7 +4758,7 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
4725
4758
|
spentUsd: e.spentUsd,
|
|
4726
4759
|
isSubAgent: this.isSubAgent
|
|
4727
4760
|
});
|
|
4728
|
-
this.
|
|
4761
|
+
this.appendDriverNotice(this.budgetExhaustedBubble(e));
|
|
4729
4762
|
return this.turnDone("budget-exhausted", budgetDetailOf(e));
|
|
4730
4763
|
}
|
|
4731
4764
|
/**
|
|
@@ -4761,7 +4794,7 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
4761
4794
|
upstreamType: e.upstreamType,
|
|
4762
4795
|
isSubAgent: this.isSubAgent
|
|
4763
4796
|
});
|
|
4764
|
-
this.
|
|
4797
|
+
this.appendDriverNotice(this.providerRefusedBubble());
|
|
4765
4798
|
return this.turnDone("provider-refused", void 0, detail);
|
|
4766
4799
|
}
|
|
4767
4800
|
/** The typed failure reason on a loop result, or `undefined` for a clean turn / handoff. */
|
|
@@ -5285,12 +5318,12 @@ Output format (strict):
|
|
|
5285
5318
|
summaryText = (await defaultProvider.prompt(userMessage, { systemPrompt }))?.trim() ?? "";
|
|
5286
5319
|
} catch (e) {
|
|
5287
5320
|
logger2.warn(`ChatDriver: compaction summary failed: ${e instanceof Error ? e.message : e}`);
|
|
5288
|
-
this.
|
|
5321
|
+
this.appendDriverNotice(failMessage);
|
|
5289
5322
|
return null;
|
|
5290
5323
|
}
|
|
5291
5324
|
if (!summaryText) {
|
|
5292
5325
|
logger2.warn("ChatDriver: compaction summary was empty; leaving history untouched");
|
|
5293
|
-
this.
|
|
5326
|
+
this.appendDriverNotice(failMessage);
|
|
5294
5327
|
return null;
|
|
5295
5328
|
}
|
|
5296
5329
|
let tokensBefore;
|
|
@@ -5607,10 +5640,9 @@ Output format (strict):
|
|
|
5607
5640
|
name: e instanceof Error ? e.name : void 0,
|
|
5608
5641
|
message: e instanceof Error ? e.message : String(e)
|
|
5609
5642
|
});
|
|
5610
|
-
this.
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
});
|
|
5643
|
+
this.appendDriverNotice(
|
|
5644
|
+
"Sorry, something went wrong on my end. Please try again in a moment."
|
|
5645
|
+
);
|
|
5614
5646
|
result = this.turnDone("exception");
|
|
5615
5647
|
return result;
|
|
5616
5648
|
} finally {
|
|
@@ -5872,10 +5904,9 @@ Output format (strict):
|
|
|
5872
5904
|
name: e instanceof Error ? e.name : void 0,
|
|
5873
5905
|
message: e instanceof Error ? e.message : String(e)
|
|
5874
5906
|
});
|
|
5875
|
-
this.
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
});
|
|
5907
|
+
this.appendDriverNotice(
|
|
5908
|
+
"Sorry, something went wrong on my end. Please try again in a moment."
|
|
5909
|
+
);
|
|
5879
5910
|
result = this.turnDone("exception");
|
|
5880
5911
|
return result;
|
|
5881
5912
|
} finally {
|
|
@@ -6054,7 +6085,7 @@ Output format (strict):
|
|
|
6054
6085
|
if (this.isSubAgent) {
|
|
6055
6086
|
this.failSubAgent("provider_refused", void 0, this.providerRefusedDetail);
|
|
6056
6087
|
} else {
|
|
6057
|
-
this.
|
|
6088
|
+
this.appendDriverNotice(this.providerRefusedBubble());
|
|
6058
6089
|
}
|
|
6059
6090
|
return this.turnDone("provider-refused", void 0, this.providerRefusedDetail);
|
|
6060
6091
|
}
|
|
@@ -6079,10 +6110,7 @@ Output format (strict):
|
|
|
6079
6110
|
if (this.isSubAgent) {
|
|
6080
6111
|
this.failSubAgent("budget_exhausted", this.budgetWallDetail);
|
|
6081
6112
|
} else {
|
|
6082
|
-
this.
|
|
6083
|
-
role: "assistant",
|
|
6084
|
-
content: this.budgetExhaustedBubble(this.budgetWallDetail)
|
|
6085
|
-
});
|
|
6113
|
+
this.appendDriverNotice(this.budgetExhaustedBubble(this.budgetWallDetail));
|
|
6086
6114
|
}
|
|
6087
6115
|
return this.turnDone("budget-exhausted", this.budgetWallDetail);
|
|
6088
6116
|
}
|
|
@@ -6119,7 +6147,11 @@ Output format (strict):
|
|
|
6119
6147
|
this.activeAgentLabel = await this.displayName(promptCtx);
|
|
6120
6148
|
}
|
|
6121
6149
|
const foldSuffix = this.buildFoldSystemPromptSuffix();
|
|
6122
|
-
const retryNudge = malformedAttempts > 0 ?
|
|
6150
|
+
const retryNudge = malformedAttempts > 0 ? `
|
|
6151
|
+
|
|
6152
|
+
${MALFORMED_CALL_NUDGE}` : emptyResponseAttempts > 0 ? `
|
|
6153
|
+
|
|
6154
|
+
${EMPTY_RESPONSE_NUDGE}` : "";
|
|
6123
6155
|
const frameworkSystemSuffix = `${foldSuffix}${retryNudge}`;
|
|
6124
6156
|
const primer = normalizeForProvider([
|
|
6125
6157
|
...this.primerHistory ?? [],
|
|
@@ -6243,10 +6275,9 @@ ${tailBody}
|
|
|
6243
6275
|
if (this.isSubAgent) {
|
|
6244
6276
|
this.failSubAgent("context_exhausted");
|
|
6245
6277
|
} else {
|
|
6246
|
-
this.
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
});
|
|
6278
|
+
this.appendDriverNotice(
|
|
6279
|
+
"I had to stop here \u2014 this conversation has filled the available context. Compact it to free up room, then ask me to continue."
|
|
6280
|
+
);
|
|
6250
6281
|
}
|
|
6251
6282
|
return this.turnDone("context-exhausted");
|
|
6252
6283
|
}
|
|
@@ -6281,10 +6312,9 @@ ${tailBody}
|
|
|
6281
6312
|
if (this.isSubAgent) {
|
|
6282
6313
|
this.failSubAgent("malformed_tool_call");
|
|
6283
6314
|
} else {
|
|
6284
|
-
this.
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
});
|
|
6315
|
+
this.appendDriverNotice(
|
|
6316
|
+
"While working on your request, I repeatedly called my tools incorrectly. This often works on a second try \u2014 would you like me to try again? If it happens again, try breaking your request into smaller steps."
|
|
6317
|
+
);
|
|
6288
6318
|
}
|
|
6289
6319
|
return this.turnDone("malformed-function-call");
|
|
6290
6320
|
}
|
|
@@ -6302,10 +6332,9 @@ ${tailBody}
|
|
|
6302
6332
|
if (this.isSubAgent) {
|
|
6303
6333
|
this.failSubAgent("response_truncated");
|
|
6304
6334
|
} else {
|
|
6305
|
-
this.
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
});
|
|
6335
|
+
this.appendDriverNotice(
|
|
6336
|
+
"My response was cut off because a single step reached the model output limit. This usually means one step tried to produce too much at once \u2014 try breaking your request into smaller steps."
|
|
6337
|
+
);
|
|
6309
6338
|
}
|
|
6310
6339
|
return this.turnDone("response-truncated");
|
|
6311
6340
|
}
|
|
@@ -6323,10 +6352,7 @@ ${tailBody}
|
|
|
6323
6352
|
if (this.isSubAgent) {
|
|
6324
6353
|
this.failSubAgent("context_exhausted");
|
|
6325
6354
|
} else {
|
|
6326
|
-
this.
|
|
6327
|
-
role: "assistant",
|
|
6328
|
-
content: DEFAULT_CONTEXT_OVERFLOW_MESSAGE
|
|
6329
|
-
});
|
|
6355
|
+
this.appendDriverNotice(DEFAULT_CONTEXT_OVERFLOW_MESSAGE);
|
|
6330
6356
|
}
|
|
6331
6357
|
return this.turnDone("context-exhausted");
|
|
6332
6358
|
}
|
|
@@ -6379,10 +6405,9 @@ ${tailBody}
|
|
|
6379
6405
|
if (this.isSubAgent) {
|
|
6380
6406
|
this.failSubAgent("timeout");
|
|
6381
6407
|
} else {
|
|
6382
|
-
this.
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
});
|
|
6408
|
+
this.appendDriverNotice(
|
|
6409
|
+
"The request timed out. You can ask me to try again, or break this into a smaller step."
|
|
6410
|
+
);
|
|
6386
6411
|
}
|
|
6387
6412
|
return this.turnDone("exception");
|
|
6388
6413
|
}
|
|
@@ -6422,6 +6447,11 @@ ${tailBody}
|
|
|
6422
6447
|
provider: this.lastResolvedProviderName,
|
|
6423
6448
|
attempt: emptyResponseAttempts,
|
|
6424
6449
|
maxAttempts: MAX_EMPTY_RESPONSE_RETRIES,
|
|
6450
|
+
// How many tools the agent was holding. Every empty response in the session that
|
|
6451
|
+
// motivated this (GENC-1573) came from an agent with ZERO — a tool-less agent asked
|
|
6452
|
+
// something its prompt gives it no instruction for has nothing it is allowed to do,
|
|
6453
|
+
// and reads as a model fault unless this number is on the record.
|
|
6454
|
+
toolCount: this.toolDefinitions.length,
|
|
6425
6455
|
// Provider diagnostic (Gemini: finishReason, thoughtsTokens, parts,
|
|
6426
6456
|
// blockReason) so the timeline shows *why* the turn came back blank —
|
|
6427
6457
|
// e.g. a high thoughtsTokens with a 'STOP' finish is "thought, then
|
|
@@ -6439,16 +6469,18 @@ ${tailBody}
|
|
|
6439
6469
|
agent: this.activeAgentName,
|
|
6440
6470
|
provider: this.lastResolvedProviderName,
|
|
6441
6471
|
attempts: emptyResponseAttempts,
|
|
6472
|
+
// See the retry above: a zero here says the agent had no tools, which is most of the
|
|
6473
|
+
// explanation for a blank turn and is otherwise invisible.
|
|
6474
|
+
toolCount: this.toolDefinitions.length,
|
|
6442
6475
|
isSubAgent: this.isSubAgent,
|
|
6443
6476
|
...response.responseMeta
|
|
6444
6477
|
});
|
|
6445
6478
|
if (this.isSubAgent) {
|
|
6446
6479
|
this.failSubAgent(isRefusal ? "refusal" : "empty_response");
|
|
6447
6480
|
} else {
|
|
6448
|
-
this.
|
|
6449
|
-
|
|
6450
|
-
|
|
6451
|
-
});
|
|
6481
|
+
this.appendDriverNotice(
|
|
6482
|
+
isRefusal ? "This request was declined. Try rephrasing it, or breaking it into smaller steps." : "While working on your request, I repeatedly generated a blank response. This often works on a second try \u2014 would you like me to try again? If it happens again, try breaking your request into smaller steps."
|
|
6483
|
+
);
|
|
6452
6484
|
}
|
|
6453
6485
|
return this.turnDone(failureReason2);
|
|
6454
6486
|
} else {
|
|
@@ -6753,10 +6785,9 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
6753
6785
|
if (this.isSubAgent) {
|
|
6754
6786
|
this.failSubAgent("unknown_tool_limit");
|
|
6755
6787
|
} else {
|
|
6756
|
-
this.
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
});
|
|
6788
|
+
this.appendDriverNotice(
|
|
6789
|
+
"I'm sorry, I repeatedly tried to use tools that aren't available to me, so I couldn't complete that. If a 'Download agent log' option appears in the Settings (cog) menu, you can download the log and share it with whoever set up this assistant to help fix the issue."
|
|
6790
|
+
);
|
|
6760
6791
|
}
|
|
6761
6792
|
return this.turnDone("unknown-tool-limit");
|
|
6762
6793
|
}
|
|
@@ -6782,15 +6813,33 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
6782
6813
|
if (this.isSubAgent) {
|
|
6783
6814
|
this.failSubAgent("max_iterations");
|
|
6784
6815
|
} else {
|
|
6785
|
-
this.
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
});
|
|
6816
|
+
this.appendDriverNotice(
|
|
6817
|
+
"I've reached my limit for this response. You can ask me to continue and I'll pick up where I left off."
|
|
6818
|
+
);
|
|
6789
6819
|
}
|
|
6790
6820
|
failureReason = "max-iterations";
|
|
6791
6821
|
}
|
|
6792
6822
|
return this.turnDone(failureReason);
|
|
6793
6823
|
}
|
|
6824
|
+
/**
|
|
6825
|
+
* Append prose the DRIVER wrote rather than the model — a failure notice, a limit explanation,
|
|
6826
|
+
* the budget wall. Renders as an ordinary assistant bubble and is flagged
|
|
6827
|
+
* `excludeFromProvider`, so it never re-enters the next request.
|
|
6828
|
+
*
|
|
6829
|
+
* Every such notice must go through here. Written as one funnel rather than a flag repeated at
|
|
6830
|
+
* eleven call sites because the failure mode is silent: a notice that keeps the flag reads
|
|
6831
|
+
* identically in the transcript to one that loses it, and the damage only shows up a turn later
|
|
6832
|
+
* as the model echoing its own error message. Measured (GENC-1573): with the empty-response
|
|
6833
|
+
* notice in history, the next turn reproduced it verbatim in 45 output tokens — succeeding,
|
|
6834
|
+
* with no retry and no `turn.error`, while still showing the user a failure. From there the
|
|
6835
|
+
* loop sustains itself whether or not the model answers, which is why "try again" never worked.
|
|
6836
|
+
*
|
|
6837
|
+
* Not for anything the model produced, and not for a widget/interaction message: those are real
|
|
6838
|
+
* turn content and the model must keep seeing them.
|
|
6839
|
+
*/
|
|
6840
|
+
appendDriverNotice(content) {
|
|
6841
|
+
this.appendToHistory({ role: "assistant", content, excludeFromProvider: true });
|
|
6842
|
+
}
|
|
6794
6843
|
appendToHistory(message) {
|
|
6795
6844
|
const tagged = {
|
|
6796
6845
|
...message,
|
|
@@ -6815,6 +6864,8 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
6815
6864
|
var DEFAULT_MAX_HANDOFFS = 3;
|
|
6816
6865
|
var DEFAULT_CLASSIFIER_HISTORY_LENGTH = 4;
|
|
6817
6866
|
var DEFAULT_CLASSIFIER_RETRIES = 2;
|
|
6867
|
+
var CLASSIFIER_MESSAGE_CHARS = 600;
|
|
6868
|
+
var CLASSIFIER_TRANSCRIPT_CHARS = 3e3;
|
|
6818
6869
|
var REQUEST_CONTINUATION_DEFINITION = {
|
|
6819
6870
|
name: REQUEST_CONTINUATION_TOOL,
|
|
6820
6871
|
description: "Call this when you have completed your part of the task but fulfilling the user's full request requires capabilities outside your domain. Do not call this if you can complete the request yourself. Pass a plain-language description of what still needs to be done \u2014 another specialist will be selected automatically.",
|
|
@@ -6839,10 +6890,19 @@ function isSpecialist(agent) {
|
|
|
6839
6890
|
function isFallback(agent) {
|
|
6840
6891
|
return agent.fallback === true;
|
|
6841
6892
|
}
|
|
6893
|
+
var UNROUTABLE_PLACEHOLDER = "{{unroutable}}";
|
|
6894
|
+
var UNROUTABLE_INSTRUCTION = `When the request is substantive but nothing above covers it, say so directly. Do not offer to do it yourself, and do not imply a specialist will pick it up.
|
|
6895
|
+
|
|
6896
|
+
If the user's message is a short follow-up to something already discussed \u2014 "fix it", "try again", "why?" \u2014 read it against the conversation above and answer that, rather than treating it as a new request with no context.
|
|
6897
|
+
|
|
6898
|
+
Never end your turn without saying something. An empty reply is never a valid response: if you genuinely cannot help, say briefly that you cannot, and why.`;
|
|
6842
6899
|
function buildFallbackSystemPrompt(fallback, specialists) {
|
|
6843
6900
|
const agentList = specialists.map((s) => `- ${s.name}: ${s.description}`).join("\n");
|
|
6844
6901
|
if (typeof fallback.systemPrompt === "string") {
|
|
6845
|
-
|
|
6902
|
+
const withAgents = fallback.systemPrompt.replace("{{agents}}", agentList);
|
|
6903
|
+
return withAgents.includes(UNROUTABLE_PLACEHOLDER) ? withAgents.replace(UNROUTABLE_PLACEHOLDER, UNROUTABLE_INSTRUCTION) : `${withAgents}
|
|
6904
|
+
|
|
6905
|
+
${UNROUTABLE_INSTRUCTION}`;
|
|
6846
6906
|
}
|
|
6847
6907
|
if (typeof fallback.systemPrompt === "function") {
|
|
6848
6908
|
return fallback.systemPrompt;
|
|
@@ -6851,7 +6911,9 @@ function buildFallbackSystemPrompt(fallback, specialists) {
|
|
|
6851
6911
|
|
|
6852
6912
|
${agentList}
|
|
6853
6913
|
|
|
6854
|
-
Politely let the user know what you can help with and invite them to rephrase their request
|
|
6914
|
+
Politely let the user know what you can help with and invite them to rephrase their request.
|
|
6915
|
+
|
|
6916
|
+
${UNROUTABLE_INSTRUCTION}`;
|
|
6855
6917
|
}
|
|
6856
6918
|
var OrchestratingDriver = class extends EventTarget {
|
|
6857
6919
|
constructor(providerRegistry, agents, options = {}) {
|
|
@@ -7082,11 +7144,19 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
7082
7144
|
);
|
|
7083
7145
|
const pinned = this.resolvePinnedAgent();
|
|
7084
7146
|
if (!pinned) this.dispatchEvent(new CustomEvent("orchestrating-start"));
|
|
7085
|
-
let
|
|
7147
|
+
let routed;
|
|
7148
|
+
if (pinned) {
|
|
7149
|
+
this.recordRouting("pinned", pinned, { durationMs: 0 });
|
|
7150
|
+
routed = { agent: pinned, decision: "pinned" };
|
|
7151
|
+
} else {
|
|
7152
|
+
routed = await this.classify(input, history, this.activeAgent);
|
|
7153
|
+
}
|
|
7154
|
+
let currentAgent = routed.agent;
|
|
7086
7155
|
let isHandoff = false;
|
|
7087
7156
|
let handoffs = 0;
|
|
7088
7157
|
let handoffSummary = "";
|
|
7089
7158
|
let remainingTask = "";
|
|
7159
|
+
const attempted = [];
|
|
7090
7160
|
let lastResult;
|
|
7091
7161
|
while (true) {
|
|
7092
7162
|
if (this.cancelled) break;
|
|
@@ -7094,6 +7164,14 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
7094
7164
|
let result;
|
|
7095
7165
|
if (isHandoff) {
|
|
7096
7166
|
const contextPrimer = handoffSummary ? [{ role: "user", content: `[Context from previous agent]: ${handoffSummary}` }] : [];
|
|
7167
|
+
if (isFallback(currentAgent)) {
|
|
7168
|
+
const work = remainingTask?.trim() || handoffSummary?.trim();
|
|
7169
|
+
contextPrimer.push({
|
|
7170
|
+
role: "user",
|
|
7171
|
+
content: (work ? `[Routing note]: no specialist is available for the remaining work: ${work}
|
|
7172
|
+
` : "[Routing note]: no specialist is available for the work that remains.\n") + "Tell the user plainly what was established, that this part falls outside what any specialist here can do, and what they could do next. Do not claim you will do it yourself, and do not return an empty response."
|
|
7173
|
+
});
|
|
7174
|
+
}
|
|
7097
7175
|
result = await this.chatDriver.continueFromHistory(contextPrimer);
|
|
7098
7176
|
} else {
|
|
7099
7177
|
this.userMessageAppended = true;
|
|
@@ -7117,9 +7195,13 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
7117
7195
|
handoffSummary = result.summary;
|
|
7118
7196
|
remainingTask = result.remainingTask;
|
|
7119
7197
|
isHandoff = true;
|
|
7198
|
+
if (isSpecialist(currentAgent) && !attempted.includes(currentAgent.name)) {
|
|
7199
|
+
attempted.push(currentAgent.name);
|
|
7200
|
+
}
|
|
7120
7201
|
const updatedHistory = this.chatDriver.getHistory();
|
|
7121
7202
|
this.dispatchEvent(new CustomEvent("orchestrating-start"));
|
|
7122
|
-
|
|
7203
|
+
routed = await this.classify(remainingTask, updatedHistory, void 0, attempted);
|
|
7204
|
+
currentAgent = routed.agent;
|
|
7123
7205
|
}
|
|
7124
7206
|
return lastResult?.reason === "done" ? lastResult : { reason: "done" };
|
|
7125
7207
|
}
|
|
@@ -7265,28 +7347,99 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
7265
7347
|
this.chatDriver.dispose();
|
|
7266
7348
|
this.activeAgent = void 0;
|
|
7267
7349
|
}
|
|
7268
|
-
|
|
7350
|
+
/**
|
|
7351
|
+
* Record one routing decision on the debug timeline — what was chosen, why, what it cost, and
|
|
7352
|
+
* (on a failure) what went wrong.
|
|
7353
|
+
*
|
|
7354
|
+
* This is the event whose absence made GENC-1573 undiagnosable. `classify` reaches the fallback
|
|
7355
|
+
* by four different routes and every one of them only warned to the console; because it runs
|
|
7356
|
+
* outside any ChatDriver turn it produces no `kind:'turn'` entry either, so both the decision
|
|
7357
|
+
* and its spend were invisible in an exported log.
|
|
7358
|
+
*
|
|
7359
|
+
* `usage` is why the spend line exists at all. A classifier call is a real billed request that
|
|
7360
|
+
* never enters the transcript, and `sessionUsage` is derived from the transcript — so classifier
|
|
7361
|
+
* spend is absent from the session total by construction. Recording it per decision keeps it
|
|
7362
|
+
* auditable and tells a reconciler exactly where the gap comes from: sum these to close it.
|
|
7363
|
+
*/
|
|
7364
|
+
recordRouting(decision, agent, detail = {}) {
|
|
7365
|
+
const { response, ballot, ...rest } = detail;
|
|
7366
|
+
const usage = response ? messageUsage(response) : void 0;
|
|
7367
|
+
recordMetaEvent(this.sessionKey, "routing.decided", {
|
|
7368
|
+
decision,
|
|
7369
|
+
to: agent.name,
|
|
7370
|
+
fallback: isFallback(agent),
|
|
7371
|
+
// The BALLOT, not `this.specialists`. `index` is a position in the ballot (see its
|
|
7372
|
+
// construction in `classify`), so publishing the unfiltered specialist list beside it made
|
|
7373
|
+
// `candidates[index]` resolve to the wrong agent whenever `excluded` was non-empty — and the
|
|
7374
|
+
// README sentence invited exactly that join. `this.specialists` stays recoverable as
|
|
7375
|
+
// `candidates ∪ excluded`. Absent where nothing was put to a vote, which is more truthful
|
|
7376
|
+
// than a full list. Reviewer catch on #2496.
|
|
7377
|
+
...ballot ? { candidates: ballot } : {},
|
|
7378
|
+
...rest,
|
|
7379
|
+
...usage ? { usage } : {},
|
|
7380
|
+
...response?.model ? { model: response.model } : {}
|
|
7381
|
+
});
|
|
7382
|
+
}
|
|
7383
|
+
/**
|
|
7384
|
+
* The recent-conversation excerpt for the classifier prompt, bounded in size as well as in
|
|
7385
|
+
* message count — see the `CLASSIFIER_MESSAGE_CHARS` docblock for why.
|
|
7386
|
+
*/
|
|
7387
|
+
classifierTranscript(history) {
|
|
7388
|
+
const lines = history.filter((m) => (m.role === "user" || m.role === "assistant") && reachesProvider(m)).filter((m) => !!m.content?.trim()).slice(-this.classifierHistoryLength).map((m) => {
|
|
7389
|
+
const speaker = m.role === "user" ? "User" : "Assistant";
|
|
7390
|
+
const content = m.content ?? "";
|
|
7391
|
+
const body = content.length > CLASSIFIER_MESSAGE_CHARS ? `${content.slice(0, CLASSIFIER_MESSAGE_CHARS)}\u2026` : content;
|
|
7392
|
+
return `${speaker}: ${body}`;
|
|
7393
|
+
});
|
|
7394
|
+
while (lines.length > 1 && lines.join("\n").length > CLASSIFIER_TRANSCRIPT_CHARS) {
|
|
7395
|
+
lines.shift();
|
|
7396
|
+
}
|
|
7397
|
+
return lines.join("\n");
|
|
7398
|
+
}
|
|
7399
|
+
async classify(input, history, contextAgent, excludeAgents = []) {
|
|
7400
|
+
const classifyStartedAt = Date.now();
|
|
7269
7401
|
if (this.specialists.length === 1 && !this.fallback) {
|
|
7270
|
-
|
|
7402
|
+
const only = this.specialists[0];
|
|
7403
|
+
this.recordRouting("single-candidate", only, {
|
|
7404
|
+
durationMs: Date.now() - classifyStartedAt
|
|
7405
|
+
});
|
|
7406
|
+
return { agent: only, decision: "single-candidate" };
|
|
7271
7407
|
}
|
|
7272
7408
|
if (this.specialists.length === 0) {
|
|
7273
7409
|
if (!this.fallback) {
|
|
7274
7410
|
const routable = this.agents.filter((a) => !isFallback(a));
|
|
7275
7411
|
if (routable.length === 1) {
|
|
7276
|
-
|
|
7412
|
+
this.recordRouting("no-specialists", routable[0], {
|
|
7413
|
+
durationMs: Date.now() - classifyStartedAt
|
|
7414
|
+
});
|
|
7415
|
+
return { agent: routable[0], decision: "no-specialists" };
|
|
7277
7416
|
}
|
|
7278
7417
|
}
|
|
7279
|
-
|
|
7418
|
+
const leaf = this.fallback ?? { name: "Assistant", fallback: true };
|
|
7419
|
+
this.recordRouting("no-specialists", leaf, { durationMs: Date.now() - classifyStartedAt });
|
|
7420
|
+
return { agent: leaf, decision: "no-specialists" };
|
|
7280
7421
|
}
|
|
7281
|
-
const
|
|
7282
|
-
|
|
7283
|
-
|
|
7422
|
+
const ballot = excludeAgents.length ? this.specialists.filter((a) => !excludeAgents.includes(a.name)) : this.specialists;
|
|
7423
|
+
if (ballot.length === 0) {
|
|
7424
|
+
const leaf = this.fallback ?? this.specialists[0];
|
|
7425
|
+
this.recordRouting("ballot-exhausted", leaf, {
|
|
7426
|
+
excluded: excludeAgents,
|
|
7427
|
+
durationMs: Date.now() - classifyStartedAt
|
|
7428
|
+
});
|
|
7429
|
+
return { agent: leaf, decision: "ballot-exhausted" };
|
|
7430
|
+
}
|
|
7431
|
+
const agentList = ballot.map((a, i) => `${i}: ${a.name} \u2014 ${a.description}`).join("\n");
|
|
7432
|
+
const recentMessages = this.classifierTranscript(history);
|
|
7433
|
+
const continuationHint = contextAgent && isSpecialist(contextAgent) && !excludeAgents.includes(contextAgent.name) ? `
|
|
7284
7434
|
|
|
7285
7435
|
The previous turn was handled by "${contextAgent.name}". If the new message is a refinement, correction, or continuation of that work, prefer "${contextAgent.name}" \u2014 unless the topic has clearly shifted to another specialist's domain.` : "";
|
|
7436
|
+
const attemptedHint = excludeAgents.length ? `
|
|
7437
|
+
|
|
7438
|
+
Already attempted this turn and handed the work on, so not listed above: ${excludeAgents.join(", ")}. Do not expect another agent to repeat their part.` : "";
|
|
7286
7439
|
const classifierPrompt = `You are a routing classifier. Call select_agent with the index of the best matching agent for the user message. Use -1 only if the request is entirely unrelated to every agent listed \u2014 prefer the closest match when in doubt. If the request spans multiple agents, pick the one that should act first; I will re-classify and route again once that agent is done.
|
|
7287
7440
|
|
|
7288
7441
|
Agents:
|
|
7289
|
-
${agentList}${continuationHint}${recentMessages ? `
|
|
7442
|
+
${agentList}${continuationHint}${attemptedHint}${recentMessages ? `
|
|
7290
7443
|
|
|
7291
7444
|
Recent conversation:
|
|
7292
7445
|
${recentMessages}` : ""}`;
|
|
@@ -7304,8 +7457,12 @@ ${recentMessages}` : ""}`;
|
|
|
7304
7457
|
}
|
|
7305
7458
|
}
|
|
7306
7459
|
};
|
|
7307
|
-
let
|
|
7460
|
+
let decision = "classifier-failed";
|
|
7461
|
+
let lastError;
|
|
7462
|
+
let attempts = 0;
|
|
7463
|
+
let lastResponse;
|
|
7308
7464
|
for (let attempt = 0; attempt <= this.classifierRetries; attempt += 1) {
|
|
7465
|
+
attempts = attempt + 1;
|
|
7309
7466
|
try {
|
|
7310
7467
|
const options = {
|
|
7311
7468
|
systemPrompt: classifierPrompt,
|
|
@@ -7316,32 +7473,56 @@ ${recentMessages}` : ""}`;
|
|
|
7316
7473
|
logger2.warn(
|
|
7317
7474
|
"OrchestratingDriver: default AI provider does not implement chat() \u2014 cannot classify."
|
|
7318
7475
|
);
|
|
7476
|
+
decision = "provider-unavailable";
|
|
7319
7477
|
break;
|
|
7320
7478
|
}
|
|
7321
7479
|
const response = await classifierProvider.chat([], input, options);
|
|
7480
|
+
lastResponse = response;
|
|
7322
7481
|
const tc = response.toolCalls?.[0];
|
|
7323
7482
|
const index = tc?.name === "select_agent" ? tc.args.agent_index : -1;
|
|
7324
|
-
if (index >= 0 && index <
|
|
7325
|
-
|
|
7483
|
+
if (index >= 0 && index < ballot.length) {
|
|
7484
|
+
const chosen = ballot[index];
|
|
7485
|
+
this.recordRouting("classified", chosen, {
|
|
7486
|
+
index,
|
|
7487
|
+
attempts,
|
|
7488
|
+
durationMs: Date.now() - classifyStartedAt,
|
|
7489
|
+
response,
|
|
7490
|
+
ballot: ballot.map((a) => a.name),
|
|
7491
|
+
...excludeAgents.length ? { excluded: excludeAgents } : {}
|
|
7492
|
+
});
|
|
7493
|
+
return { agent: chosen, decision: "classified" };
|
|
7326
7494
|
}
|
|
7327
|
-
|
|
7495
|
+
decision = "no-match";
|
|
7328
7496
|
break;
|
|
7329
7497
|
} catch (e) {
|
|
7330
7498
|
if (e instanceof BudgetExhaustedError) throw e;
|
|
7499
|
+
lastError = e instanceof Error ? `${e.name}: ${e.message}` : String(e);
|
|
7331
7500
|
logger2.warn(`OrchestratingDriver: classifier attempt ${attempt + 1} failed:`, e);
|
|
7332
7501
|
if (attempt === this.classifierRetries) {
|
|
7333
7502
|
logger2.error("OrchestratingDriver: classifier failed after all retries, using fallback");
|
|
7334
7503
|
}
|
|
7335
7504
|
}
|
|
7336
7505
|
}
|
|
7337
|
-
|
|
7338
|
-
|
|
7506
|
+
const routingDetail = {
|
|
7507
|
+
attempts,
|
|
7508
|
+
durationMs: Date.now() - classifyStartedAt,
|
|
7509
|
+
ballot: ballot.map((a) => a.name),
|
|
7510
|
+
...lastError ? { error: lastError } : {},
|
|
7511
|
+
...lastResponse ? { response: lastResponse } : {},
|
|
7512
|
+
...excludeAgents.length ? { excluded: excludeAgents } : {}
|
|
7513
|
+
};
|
|
7514
|
+
if (this.fallback) {
|
|
7515
|
+
this.recordRouting(decision, this.fallback, routingDetail);
|
|
7516
|
+
return { agent: this.fallback, decision };
|
|
7517
|
+
}
|
|
7518
|
+
if (decision === "no-match") {
|
|
7339
7519
|
const specialistNames = this.specialists.map((s) => s.name).join(", ");
|
|
7340
7520
|
this.appendInlineMessage(
|
|
7341
7521
|
`I'm not sure how to help with that. I can assist with: ${specialistNames}.`
|
|
7342
7522
|
);
|
|
7343
7523
|
}
|
|
7344
|
-
|
|
7524
|
+
this.recordRouting(decision, this.specialists[0], routingDetail);
|
|
7525
|
+
return { agent: this.specialists[0], decision };
|
|
7345
7526
|
}
|
|
7346
7527
|
/**
|
|
7347
7528
|
* Returns the agent that the current turn is locked to, if any. Flow-owner
|
|
@@ -7363,11 +7544,22 @@ ${recentMessages}` : ""}`;
|
|
|
7363
7544
|
}
|
|
7364
7545
|
return match;
|
|
7365
7546
|
}
|
|
7547
|
+
/**
|
|
7548
|
+
* Append prose the ORCHESTRATOR wrote — the handoff cap, the no-specialist apology. Flagged
|
|
7549
|
+
* `excludeFromProvider` for the same reason the driver's own notices are: it is not something a
|
|
7550
|
+
* model said, and feeding it back invites the model to echo it. See
|
|
7551
|
+
* `ChatMessage.excludeFromProvider`.
|
|
7552
|
+
*/
|
|
7366
7553
|
appendInlineMessage(content) {
|
|
7367
7554
|
const history = this.chatDriver.getHistory();
|
|
7368
7555
|
this.chatDriver.loadHistory([
|
|
7369
7556
|
...history,
|
|
7370
|
-
{
|
|
7557
|
+
{
|
|
7558
|
+
role: "assistant",
|
|
7559
|
+
content,
|
|
7560
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7561
|
+
excludeFromProvider: true
|
|
7562
|
+
}
|
|
7371
7563
|
]);
|
|
7372
7564
|
this.dispatchEvent(
|
|
7373
7565
|
new CustomEvent("history-updated", { detail: this.chatDriver.getHistory() })
|
|
@@ -7583,7 +7775,11 @@ Politely let the user know what you can help with and invite them to rephrase th
|
|
|
7583
7775
|
var strictFallbackAgent = {
|
|
7584
7776
|
name: "Assistant",
|
|
7585
7777
|
fallback: true,
|
|
7586
|
-
systemPrompt: `You are a concise assistant. The user's request is outside your scope.
|
|
7778
|
+
systemPrompt: `You are a concise assistant. The user's request is outside your scope. The available specialists are: {{agents}}.
|
|
7779
|
+
|
|
7780
|
+
{{unroutable}}
|
|
7781
|
+
|
|
7782
|
+
Answer in a single short sentence. Do not elaborate.`
|
|
7587
7783
|
};
|
|
7588
7784
|
|
|
7589
7785
|
// src/utils/usage-rows.ts
|
|
@@ -7749,10 +7945,12 @@ function withFreshMetaSnapshot(stored, fresh) {
|
|
|
7749
7945
|
AnthropicTransport,
|
|
7750
7946
|
ChatDriver,
|
|
7751
7947
|
DEFAULT_PROVIDER_REFUSED_MESSAGE,
|
|
7948
|
+
EMPTY_RESPONSE_NUDGE,
|
|
7752
7949
|
GEMINI_CACHED_INPUT_MULTIPLIER,
|
|
7753
7950
|
GEMINI_LONG_CONTEXT_THRESHOLD,
|
|
7754
7951
|
GeminiProvider,
|
|
7755
7952
|
GeminiTransport,
|
|
7953
|
+
MALFORMED_CALL_NUDGE,
|
|
7756
7954
|
MutableAIProviderRegistry,
|
|
7757
7955
|
NOOP_ACTIVITY_BUS,
|
|
7758
7956
|
OrchestratingDriver,
|
|
@@ -7761,6 +7959,8 @@ function withFreshMetaSnapshot(stored, fresh) {
|
|
|
7761
7959
|
REQUEST_CONTINUATION_TOOL,
|
|
7762
7960
|
SUPPORTED_ANTHROPIC_MODEL_IDS,
|
|
7763
7961
|
SUPPORTED_GEMINI_MODEL_IDS,
|
|
7962
|
+
UNROUTABLE_INSTRUCTION,
|
|
7963
|
+
UNROUTABLE_PLACEHOLDER,
|
|
7764
7964
|
addUsage,
|
|
7765
7965
|
agenticActivityBus,
|
|
7766
7966
|
anthropicRatesFor,
|