@genesislcap/ai-assistant 15.58.2 → 15.60.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ai-assistant.api.json +10096 -6986
- package/dist/ai-assistant.d.ts +562 -101
- package/dist/chat-driver.cjs +624 -207
- package/dist/chat-driver.cjs.map +4 -4
- package/dist/chat-driver.mjs +624 -207
- package/dist/chat-driver.mjs.map +4 -4
- package/dist/custom-elements.json +5426 -3570
- package/dist/dts/components/agent-session/agent-session.d.ts +19 -3
- package/dist/dts/components/agent-session/agent-session.d.ts.map +1 -1
- package/dist/dts/components/ai-driver/ai-driver.d.ts +120 -10
- package/dist/dts/components/ai-driver/ai-driver.d.ts.map +1 -1
- package/dist/dts/components/ai-driver/base-driver-decorator.d.ts +103 -0
- package/dist/dts/components/ai-driver/base-driver-decorator.d.ts.map +1 -0
- package/dist/dts/components/ai-driver/index.d.ts +3 -1
- package/dist/dts/components/ai-driver/index.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/chat-driver.d.ts +84 -8
- package/dist/dts/components/chat-driver/chat-driver.d.ts.map +1 -1
- package/dist/dts/components/chat-driver/interaction-controller.d.ts +37 -3
- package/dist/dts/components/chat-driver/interaction-controller.d.ts.map +1 -1
- package/dist/dts/components/message-feedback/index.d.ts +2 -0
- package/dist/dts/components/message-feedback/index.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.d.ts +61 -0
- package/dist/dts/components/message-feedback/message-feedback.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.styles.d.ts +2 -0
- package/dist/dts/components/message-feedback/message-feedback.styles.d.ts.map +1 -0
- package/dist/dts/components/message-feedback/message-feedback.template.d.ts +4 -0
- package/dist/dts/components/message-feedback/message-feedback.template.d.ts.map +1 -0
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts +30 -93
- package/dist/dts/components/orchestrating-driver/orchestrating-driver.d.ts.map +1 -1
- package/dist/dts/index.d.ts +4 -1
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/main/main.d.ts +21 -5
- 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.map +1 -1
- package/dist/dts/react.d.ts +10 -0
- 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/state/interaction-context.d.ts +7 -1
- package/dist/dts/state/interaction-context.d.ts.map +1 -1
- package/dist/dts/state/persistence/session-persister.d.ts.map +1 -1
- package/dist/dts/suggestions/generate-suggestions.d.ts.map +1 -1
- package/dist/dts/types/handler-capability.d.ts +28 -0
- package/dist/dts/types/handler-capability.d.ts.map +1 -0
- package/dist/dts/types/interaction-context.d.ts +16 -0
- package/dist/dts/types/interaction-context.d.ts.map +1 -1
- package/dist/dts/types/interaction-interceptor.d.ts +109 -0
- package/dist/dts/types/interaction-interceptor.d.ts.map +1 -0
- package/dist/dts/utils/delegated-message.d.ts +52 -0
- package/dist/dts/utils/delegated-message.d.ts.map +1 -0
- package/dist/dts/utils/history-transform.d.ts.map +1 -1
- package/dist/esm/components/agent-session/agent-session.js +17 -0
- package/dist/esm/components/ai-driver/ai-driver.js +21 -1
- package/dist/esm/components/ai-driver/base-driver-decorator.js +211 -0
- package/dist/esm/components/ai-driver/index.js +2 -1
- package/dist/esm/components/chat-driver/chat-driver.js +195 -14
- package/dist/esm/components/chat-driver/interaction-controller.js +168 -14
- package/dist/esm/components/message-feedback/index.js +1 -0
- package/dist/esm/components/message-feedback/message-feedback.js +241 -0
- package/dist/esm/components/message-feedback/message-feedback.styles.js +94 -0
- package/dist/esm/components/message-feedback/message-feedback.template.js +80 -0
- package/dist/esm/components/orchestrating-driver/orchestrating-driver.js +72 -196
- package/dist/esm/index.js +1 -0
- package/dist/esm/main/main.js +123 -78
- package/dist/esm/main/main.styles.js +27 -0
- package/dist/esm/main/main.template.js +13 -0
- package/dist/esm/state/debug-event-log.js +7 -2
- package/dist/esm/state/interaction-context.js +11 -0
- package/dist/esm/state/persistence/session-persister.js +5 -6
- package/dist/esm/suggestions/generate-suggestions.js +8 -1
- package/dist/esm/types/handler-capability.js +1 -0
- package/dist/esm/types/interaction-interceptor.js +1 -0
- package/dist/esm/utils/delegated-message.js +25 -0
- package/dist/esm/utils/history-transform.js +12 -1
- package/dist/react.cjs +7 -0
- package/dist/react.mjs +6 -0
- package/package.json +17 -17
- package/sandbox/README.md +1 -1
- package/sandbox/sandbox.ts +35 -0
package/dist/chat-driver.mjs
CHANGED
|
@@ -4027,6 +4027,10 @@ var META_EVENT_IMPORTANCE = {
|
|
|
4027
4027
|
"interaction.outcome": "normal",
|
|
4028
4028
|
// An outcome that could not be recorded: the widget goes on showing only the answer.
|
|
4029
4029
|
"interaction.outcome-dropped": "high",
|
|
4030
|
+
// A claimant handing a pending interaction to the person, or taking it back.
|
|
4031
|
+
"interaction.held": "normal",
|
|
4032
|
+
// A claim on a `userOnly` interaction, refused. Expected when an interceptor claims broadly.
|
|
4033
|
+
"interaction.claim-refused": "low",
|
|
4030
4034
|
"external-diagnostics.folded": "normal",
|
|
4031
4035
|
"session.cleared": "normal",
|
|
4032
4036
|
"session.switched": "low",
|
|
@@ -4238,6 +4242,14 @@ function projectCompaction(history, cut, summaryTokens = COMPACTION_SUMMARY_TOKE
|
|
|
4238
4242
|
};
|
|
4239
4243
|
}
|
|
4240
4244
|
|
|
4245
|
+
// src/utils/delegated-message.ts
|
|
4246
|
+
var DEFAULT_DELEGATE_FRAMING = "Sent on the user's behalf.";
|
|
4247
|
+
function frameDelegatedContent(content, framing) {
|
|
4248
|
+
return `(${framing ?? DEFAULT_DELEGATE_FRAMING})
|
|
4249
|
+
|
|
4250
|
+
${content}`;
|
|
4251
|
+
}
|
|
4252
|
+
|
|
4241
4253
|
// src/utils/logger.ts
|
|
4242
4254
|
var logger2 = createLogger("ai-assistant");
|
|
4243
4255
|
|
|
@@ -4289,7 +4301,9 @@ Additional guidance: "${prompt2}"` : "";
|
|
|
4289
4301
|
}
|
|
4290
4302
|
const conversationContext = history.filter(
|
|
4291
4303
|
(m) => reachesProvider(m) && (m.role === "user" || m.role === "assistant") && !m.toolCalls?.length && !m.thinking && !!m.content?.trim()
|
|
4292
|
-
).slice(-HISTORY_WINDOW).map(
|
|
4304
|
+
).slice(-HISTORY_WINDOW).map(
|
|
4305
|
+
(m) => m.role === "user" ? `User: ${m.delegate ? frameDelegatedContent(m.content ?? "", m.delegateFraming) : m.content}` : `Assistant: ${m.content}`
|
|
4306
|
+
).join("\n");
|
|
4293
4307
|
return `<conversation_history>
|
|
4294
4308
|
${conversationContext}
|
|
4295
4309
|
</conversation_history>
|
|
@@ -4679,7 +4693,8 @@ function renderMessageForSummary(m) {
|
|
|
4679
4693
|
case "compacted-summary":
|
|
4680
4694
|
return `[Earlier summary] ${m.content}`;
|
|
4681
4695
|
case "user":
|
|
4682
|
-
|
|
4696
|
+
if (!m.content) return "";
|
|
4697
|
+
return `User: ${m.delegate ? frameDelegatedContent(m.content, m.delegateFraming) : m.content}`;
|
|
4683
4698
|
case "assistant": {
|
|
4684
4699
|
if (m.category === "reasoning") {
|
|
4685
4700
|
return m.content ? `Thinking: ${m.content}` : "";
|
|
@@ -4711,6 +4726,9 @@ Summarize the conversation above so it can be dropped from context while preserv
|
|
|
4711
4726
|
}
|
|
4712
4727
|
function normalizeForProvider(history) {
|
|
4713
4728
|
return history.map((m) => {
|
|
4729
|
+
if (m.role === "user" && m.delegate) {
|
|
4730
|
+
return { ...m, content: frameDelegatedContent(m.content ?? "", m.delegateFraming) };
|
|
4731
|
+
}
|
|
4714
4732
|
if (m.role !== "compacted-summary") return m;
|
|
4715
4733
|
return {
|
|
4716
4734
|
...m,
|
|
@@ -4925,6 +4943,7 @@ var AgentSession = class {
|
|
|
4925
4943
|
transition.flowOwnerChangedTo = agent.name;
|
|
4926
4944
|
}
|
|
4927
4945
|
this.active = agent;
|
|
4946
|
+
if (switched) this.reportTransition(transition);
|
|
4928
4947
|
return transition;
|
|
4929
4948
|
}
|
|
4930
4949
|
/**
|
|
@@ -4946,8 +4965,21 @@ var AgentSession = class {
|
|
|
4946
4965
|
this.flowOwner = null;
|
|
4947
4966
|
transition.flowOwnerChangedTo = null;
|
|
4948
4967
|
}
|
|
4968
|
+
this.reportTransition(transition);
|
|
4949
4969
|
return transition;
|
|
4950
4970
|
}
|
|
4971
|
+
/**
|
|
4972
|
+
* Hand a transition to the owner. A throwing listener is logged rather than propagated, for the
|
|
4973
|
+
* same reason a throwing hook is: the transition has already happened, and failing the caller
|
|
4974
|
+
* would leave it believing it had not.
|
|
4975
|
+
*/
|
|
4976
|
+
reportTransition(transition) {
|
|
4977
|
+
try {
|
|
4978
|
+
this.deps.onTransition?.(transition);
|
|
4979
|
+
} catch (e) {
|
|
4980
|
+
logger2.warn(`${WARN_PREFIX}: onTransition threw:`, e);
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4951
4983
|
/**
|
|
4952
4984
|
* Fire `onDeactivate` on the active agent and abort pending lifecycle work, for session
|
|
4953
4985
|
* teardown, so machines release resources cleanly. The session is not reusable afterwards: the
|
|
@@ -5173,8 +5205,12 @@ function createInteractionContext(interactionId) {
|
|
|
5173
5205
|
const listeners = /* @__PURE__ */ new Set();
|
|
5174
5206
|
const disposers = /* @__PURE__ */ new Set();
|
|
5175
5207
|
let disposed = false;
|
|
5208
|
+
let heldBy = "user";
|
|
5176
5209
|
const context = {
|
|
5177
5210
|
interactionId,
|
|
5211
|
+
get heldBy() {
|
|
5212
|
+
return heldBy;
|
|
5213
|
+
},
|
|
5178
5214
|
state,
|
|
5179
5215
|
once(key, factory) {
|
|
5180
5216
|
if (onceCache.has(key)) return onceCache.get(key);
|
|
@@ -5226,11 +5262,18 @@ function createInteractionContext(interactionId) {
|
|
|
5226
5262
|
disposers.clear();
|
|
5227
5263
|
listeners.clear();
|
|
5228
5264
|
onceCache.clear();
|
|
5265
|
+
},
|
|
5266
|
+
setHeldBy(holder) {
|
|
5267
|
+
if (disposed || holder === heldBy) return false;
|
|
5268
|
+
heldBy = holder;
|
|
5269
|
+
context.notify();
|
|
5270
|
+
return true;
|
|
5229
5271
|
}
|
|
5230
5272
|
};
|
|
5231
5273
|
}
|
|
5232
5274
|
|
|
5233
5275
|
// src/components/chat-driver/interaction-controller.ts
|
|
5276
|
+
var DEFAULT_DECISION_FRAMING = "Decided on the user's behalf";
|
|
5234
5277
|
var STOPPED = {
|
|
5235
5278
|
status: "timeout",
|
|
5236
5279
|
message: "The turn was stopped before this was answered."
|
|
@@ -5248,6 +5291,94 @@ var InteractionController = class {
|
|
|
5248
5291
|
* one-interaction-at-a-time guard in `request`).
|
|
5249
5292
|
*/
|
|
5250
5293
|
this.contexts = /* @__PURE__ */ new Map();
|
|
5294
|
+
/**
|
|
5295
|
+
* Told about every interaction this controller opens (GENC-1660), in registration order. Only
|
|
5296
|
+
* the root's are ever consulted: a sub-agent's request is opened by the root.
|
|
5297
|
+
*/
|
|
5298
|
+
this.interceptors = [];
|
|
5299
|
+
}
|
|
5300
|
+
/** See `ChatDriver.addInteractionInterceptor`. Returns a remover. */
|
|
5301
|
+
addInterceptor(interceptor) {
|
|
5302
|
+
if (!interceptor.id || interceptor.id === "user") {
|
|
5303
|
+
throw new Error(
|
|
5304
|
+
`addInteractionInterceptor: an interceptor needs an id other than 'user' (got "${interceptor.id}").`
|
|
5305
|
+
);
|
|
5306
|
+
}
|
|
5307
|
+
this.interceptors.push(interceptor);
|
|
5308
|
+
return () => {
|
|
5309
|
+
const index = this.interceptors.indexOf(interceptor);
|
|
5310
|
+
if (index >= 0) this.interceptors.splice(index, 1);
|
|
5311
|
+
};
|
|
5312
|
+
}
|
|
5313
|
+
/**
|
|
5314
|
+
* Offer a new interaction to the interceptors and record who ends up holding it.
|
|
5315
|
+
*
|
|
5316
|
+
* **Outermost first.** A decorator installs its interceptor on the driver it wraps when it is
|
|
5317
|
+
* constructed, and decorators are constructed innermost first, so registration order is
|
|
5318
|
+
* innermost first and the walk runs it backwards. The first claim wins; later interceptors still
|
|
5319
|
+
* see the interaction but their `hold` does nothing. A claim on a `userOnly` interaction is
|
|
5320
|
+
* refused.
|
|
5321
|
+
*
|
|
5322
|
+
* Runs before the interaction's message is appended, so the widget's first render already knows
|
|
5323
|
+
* its holder and nothing appears only to disappear.
|
|
5324
|
+
*/
|
|
5325
|
+
offer(handle, opened) {
|
|
5326
|
+
let claimant;
|
|
5327
|
+
for (const interceptor of [...this.interceptors].reverse()) {
|
|
5328
|
+
const hold = this.holdFor(handle, interceptor, () => claimant === interceptor);
|
|
5329
|
+
let answer;
|
|
5330
|
+
try {
|
|
5331
|
+
answer = interceptor.onInteractionOpened({ ...opened, ctx: handle.context, hold });
|
|
5332
|
+
} catch (e) {
|
|
5333
|
+
logger2.warn(`InteractionController: interceptor "${interceptor.id}" threw:`, e);
|
|
5334
|
+
continue;
|
|
5335
|
+
}
|
|
5336
|
+
if (answer !== "claim" || claimant) continue;
|
|
5337
|
+
if (opened.userOnly) {
|
|
5338
|
+
recordMetaEvent(this.deps.sessionKey, "interaction.claim-refused", {
|
|
5339
|
+
interactionId: opened.interactionId,
|
|
5340
|
+
claimant: interceptor.id,
|
|
5341
|
+
reason: "userOnly"
|
|
5342
|
+
});
|
|
5343
|
+
continue;
|
|
5344
|
+
}
|
|
5345
|
+
claimant = interceptor;
|
|
5346
|
+
handle.setHeldBy(interceptor.id);
|
|
5347
|
+
const pending = this.pending.get(opened.interactionId);
|
|
5348
|
+
if (pending) pending.claimant = interceptor;
|
|
5349
|
+
}
|
|
5350
|
+
return handle.context.heldBy;
|
|
5351
|
+
}
|
|
5352
|
+
/** The {@link InteractionHold} for one interceptor: it applies only while that one is the claimant. */
|
|
5353
|
+
holdFor(handle, interceptor, isClaimant) {
|
|
5354
|
+
return {
|
|
5355
|
+
set: (holder) => {
|
|
5356
|
+
if (!isClaimant()) return false;
|
|
5357
|
+
const next = holder === "user" ? "user" : interceptor.id;
|
|
5358
|
+
if (!handle.setHeldBy(next)) return false;
|
|
5359
|
+
recordMetaEvent(this.deps.sessionKey, "interaction.held", {
|
|
5360
|
+
interactionId: handle.context.interactionId,
|
|
5361
|
+
heldBy: next,
|
|
5362
|
+
claimant: interceptor.id
|
|
5363
|
+
});
|
|
5364
|
+
return true;
|
|
5365
|
+
},
|
|
5366
|
+
resolve: (result) => {
|
|
5367
|
+
const { interactionId } = handle.context;
|
|
5368
|
+
const pending = this.pending.get(interactionId);
|
|
5369
|
+
if (!isClaimant() || !pending || handle.context.heldBy !== interceptor.id) return false;
|
|
5370
|
+
pending.settledBy = interceptor.id;
|
|
5371
|
+
this.resolve(interactionId, result);
|
|
5372
|
+
return true;
|
|
5373
|
+
},
|
|
5374
|
+
recordDecision: (decision) => {
|
|
5375
|
+
if (!isClaimant()) return false;
|
|
5376
|
+
const pending = this.pending.get(handle.context.interactionId);
|
|
5377
|
+
if (!pending) return false;
|
|
5378
|
+
pending.decision = { choice: decision.choice, shownToUser: decision.shownToUser };
|
|
5379
|
+
return true;
|
|
5380
|
+
}
|
|
5381
|
+
};
|
|
5251
5382
|
}
|
|
5252
5383
|
/** See `ChatDriver.setHostInteractionRequester`. */
|
|
5253
5384
|
setHostRequester(fn, recordOutcome) {
|
|
@@ -5257,10 +5388,14 @@ var InteractionController = class {
|
|
|
5257
5388
|
/**
|
|
5258
5389
|
* See `ChatDriver.requestInteraction`. `onOpened` is told the id of the interaction this call
|
|
5259
5390
|
* opens, wherever it opens it, so the caller can later record its outcome and no other's.
|
|
5391
|
+
* `innerPath` is the owner path a sub-agent's request has gathered on its way up, and
|
|
5392
|
+
* `onSettled` is told who answered, wherever it resolves (GENC-1660).
|
|
5260
5393
|
*/
|
|
5261
|
-
request(componentName, data, options, onOpened) {
|
|
5394
|
+
request(componentName, data, options, onOpened, innerPath = [], onSettled) {
|
|
5395
|
+
const agent = this.deps.activeAgentName();
|
|
5396
|
+
const ownerPath = agent ? [agent, ...innerPath] : [...innerPath];
|
|
5262
5397
|
if (this.hostRequester) {
|
|
5263
|
-
return this.hostRequester(componentName, data, options, onOpened);
|
|
5398
|
+
return this.hostRequester(componentName, data, options, onOpened, ownerPath, onSettled);
|
|
5264
5399
|
}
|
|
5265
5400
|
if (this.pending.size > 0) {
|
|
5266
5401
|
throw new Error(
|
|
@@ -5276,6 +5411,7 @@ var InteractionController = class {
|
|
|
5276
5411
|
this.pending.set(interactionId, {
|
|
5277
5412
|
resolve,
|
|
5278
5413
|
reject,
|
|
5414
|
+
onSettled,
|
|
5279
5415
|
overrideId: chatInputDuringExecution ? interactionId : void 0,
|
|
5280
5416
|
// Resolve the interaction with a timeout result if the user does not
|
|
5281
5417
|
// respond in time. `resolve` runs the same teardown a normal resolution
|
|
@@ -5288,10 +5424,22 @@ var InteractionController = class {
|
|
|
5288
5424
|
});
|
|
5289
5425
|
}, timeoutMs) : void 0
|
|
5290
5426
|
});
|
|
5427
|
+
const handle = createInteractionContext(interactionId);
|
|
5428
|
+
this.contexts.set(interactionId, handle);
|
|
5429
|
+
const heldBy = this.offer(handle, {
|
|
5430
|
+
interactionId,
|
|
5431
|
+
componentName,
|
|
5432
|
+
data,
|
|
5433
|
+
ownerPath,
|
|
5434
|
+
userOnly: options?.userOnly === true
|
|
5435
|
+
});
|
|
5291
5436
|
recordMetaEvent(this.deps.sessionKey, "interaction.requested", {
|
|
5292
5437
|
interactionId,
|
|
5293
5438
|
component: componentName,
|
|
5294
|
-
agent: this.deps.activeAgentName()
|
|
5439
|
+
agent: this.deps.activeAgentName(),
|
|
5440
|
+
ownerPath,
|
|
5441
|
+
heldBy,
|
|
5442
|
+
...options?.userOnly ? { userOnly: true } : {}
|
|
5295
5443
|
});
|
|
5296
5444
|
this.deps.activityBus.publish("interaction-requested", void 0);
|
|
5297
5445
|
if (chatInputDuringExecution) {
|
|
@@ -5301,7 +5449,6 @@ var InteractionController = class {
|
|
|
5301
5449
|
})
|
|
5302
5450
|
);
|
|
5303
5451
|
}
|
|
5304
|
-
this.contexts.set(interactionId, createInteractionContext(interactionId));
|
|
5305
5452
|
this.deps.appendToHistory({
|
|
5306
5453
|
role: "assistant",
|
|
5307
5454
|
content: "",
|
|
@@ -5312,6 +5459,11 @@ var InteractionController = class {
|
|
|
5312
5459
|
...presentation ? { presentation } : {}
|
|
5313
5460
|
}
|
|
5314
5461
|
});
|
|
5462
|
+
const opened = this.pending.get(interactionId);
|
|
5463
|
+
if (opened) {
|
|
5464
|
+
opened.appended = true;
|
|
5465
|
+
if (opened.early) this.resolve(interactionId, opened.early.result);
|
|
5466
|
+
}
|
|
5315
5467
|
const signal = options?.signal;
|
|
5316
5468
|
if (signal) {
|
|
5317
5469
|
const onAbort = () => this.resolve(interactionId, STOPPED);
|
|
@@ -5337,6 +5489,10 @@ var InteractionController = class {
|
|
|
5337
5489
|
logger2.warn(`Interaction with ID ${interactionId} not found.`);
|
|
5338
5490
|
return;
|
|
5339
5491
|
}
|
|
5492
|
+
if (!interaction.appended) {
|
|
5493
|
+
interaction.early ??= { result };
|
|
5494
|
+
return;
|
|
5495
|
+
}
|
|
5340
5496
|
if (interaction.timeoutHandle) clearTimeout(interaction.timeoutHandle);
|
|
5341
5497
|
interaction.unlinkSignal?.();
|
|
5342
5498
|
this.deps.foldDiagnostics(
|
|
@@ -5344,20 +5500,50 @@ var InteractionController = class {
|
|
|
5344
5500
|
result?.diagnostics
|
|
5345
5501
|
);
|
|
5346
5502
|
const costUsd = typeof result?.costUsd === "number" && Number.isFinite(result.costUsd) && result.costUsd > 0 ? result.costUsd : void 0;
|
|
5503
|
+
const unanswered = result?.status === "timeout";
|
|
5504
|
+
const answeredBy = unanswered ? void 0 : interaction.settledBy ?? "user";
|
|
5505
|
+
const decision = answeredBy && answeredBy !== "user" ? {
|
|
5506
|
+
framing: interaction.claimant?.decisionFraming ?? DEFAULT_DECISION_FRAMING,
|
|
5507
|
+
...interaction.decision?.choice !== void 0 && {
|
|
5508
|
+
choice: interaction.decision.choice
|
|
5509
|
+
},
|
|
5510
|
+
...interaction.decision?.shownToUser !== void 0 && {
|
|
5511
|
+
shownToUser: interaction.decision.shownToUser
|
|
5512
|
+
}
|
|
5513
|
+
} : void 0;
|
|
5347
5514
|
const patched = this.deps.patchInteractionMessage(interactionId, (message) => ({
|
|
5348
5515
|
...message,
|
|
5349
|
-
interaction: {
|
|
5516
|
+
interaction: {
|
|
5517
|
+
...message.interaction,
|
|
5518
|
+
resolved: result,
|
|
5519
|
+
...answeredBy ? { answeredBy } : {}
|
|
5520
|
+
},
|
|
5350
5521
|
...costUsd != null ? { externalCostUsd: (message.externalCostUsd ?? 0) + costUsd } : {}
|
|
5351
5522
|
}));
|
|
5352
5523
|
if (patched) this.deps.notifyHistoryUpdated();
|
|
5353
5524
|
if (typeof result?.userMessage === "string" && result.userMessage.trim()) {
|
|
5354
|
-
this.deps.appendToHistory(
|
|
5525
|
+
this.deps.appendToHistory(
|
|
5526
|
+
answeredBy && answeredBy !== "user" ? {
|
|
5527
|
+
role: "assistant",
|
|
5528
|
+
content: result.userMessage,
|
|
5529
|
+
delegate: answeredBy,
|
|
5530
|
+
excludeFromProvider: true
|
|
5531
|
+
} : { role: "synthetic-user", content: result.userMessage }
|
|
5532
|
+
);
|
|
5355
5533
|
}
|
|
5356
5534
|
if (interaction.overrideId) {
|
|
5357
5535
|
this.deps.emit(new CustomEvent("interaction-stop", { detail: { interactionId } }));
|
|
5358
5536
|
}
|
|
5359
|
-
recordMetaEvent(this.deps.sessionKey, "interaction.resolved", {
|
|
5537
|
+
recordMetaEvent(this.deps.sessionKey, "interaction.resolved", {
|
|
5538
|
+
interactionId,
|
|
5539
|
+
...answeredBy ? { answeredBy } : {}
|
|
5540
|
+
});
|
|
5360
5541
|
this.deps.activityBus.publish("interaction-resolved", void 0);
|
|
5542
|
+
try {
|
|
5543
|
+
interaction.onSettled?.({ answeredBy, decision });
|
|
5544
|
+
} catch (e) {
|
|
5545
|
+
logger2.warn("InteractionController: onSettled threw:", e);
|
|
5546
|
+
}
|
|
5361
5547
|
interaction.resolve(result);
|
|
5362
5548
|
this.pending.delete(interactionId);
|
|
5363
5549
|
this.contexts.get(interactionId)?.dispose();
|
|
@@ -5756,6 +5942,18 @@ var budgetDetailOf = (e) => {
|
|
|
5756
5942
|
...e.otherVendorAvailable != null ? { otherVendorAvailable: e.otherVendorAvailable } : {}
|
|
5757
5943
|
};
|
|
5758
5944
|
};
|
|
5945
|
+
function withDelegatedDecisions(result, content, decisions, hasImages) {
|
|
5946
|
+
if (decisions.length === 0) return content;
|
|
5947
|
+
if (!hasImages && result !== null && typeof result === "object" && !Array.isArray(result)) {
|
|
5948
|
+
return JSON.stringify({ ...result, decidedOnUsersBehalf: decisions });
|
|
5949
|
+
}
|
|
5950
|
+
return [content, ...decisions.map(describeDelegatedDecision)].join("\n\n");
|
|
5951
|
+
}
|
|
5952
|
+
function describeDelegatedDecision(decision) {
|
|
5953
|
+
const choice = decision.choice !== void 0 ? `: "${decision.choice}"` : "";
|
|
5954
|
+
const shown = decision.shownToUser ? ` What the user was shown, as your words: "${decision.shownToUser}"` : " Nothing was shown to the user about it.";
|
|
5955
|
+
return `${decision.framing}${choice}.${shown}`;
|
|
5956
|
+
}
|
|
5759
5957
|
function inFlightToolCallsAt(history) {
|
|
5760
5958
|
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
5761
5959
|
const message = history[i];
|
|
@@ -5921,6 +6119,8 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
5921
6119
|
* on that one was reset by every handoff inside a single message.
|
|
5922
6120
|
*/
|
|
5923
6121
|
this.messageController = new AbortController();
|
|
6122
|
+
/** Capabilities provided on this driver by decorators (GENC-1660). Only the root's are used. */
|
|
6123
|
+
this.handlerCapabilities = /* @__PURE__ */ new Map();
|
|
5924
6124
|
/**
|
|
5925
6125
|
* Caches validated provider lookups per name within the current agent. Cleared
|
|
5926
6126
|
* by `installAgent` so each new agent's static/function-resolved names are
|
|
@@ -5944,9 +6144,17 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
5944
6144
|
* the point: two would let `getActiveAgent()` disagree between the outer and inner driver.
|
|
5945
6145
|
* `@internal` rather than private so that reach is legal without six delegating one-liners.
|
|
5946
6146
|
*
|
|
6147
|
+
* **This driver raises the lifecycle events for every transition of the session**, whoever made
|
|
6148
|
+
* it: itself, through {@link ChatDriver.activateAgent} or a release, or the orchestrator reaching
|
|
6149
|
+
* through (GENC-1485). Before, only the orchestrator dispatched them, so a host on a bare
|
|
6150
|
+
* `ChatDriver` never learned its flow owner.
|
|
6151
|
+
*
|
|
5947
6152
|
* @internal
|
|
5948
6153
|
*/
|
|
5949
|
-
this.agentSession = new AgentSession({
|
|
6154
|
+
this.agentSession = new AgentSession({
|
|
6155
|
+
sessionKey: () => this.sessionKey,
|
|
6156
|
+
onTransition: (t) => this.dispatchAgentTransition(t)
|
|
6157
|
+
});
|
|
5950
6158
|
/**
|
|
5951
6159
|
* A provider refusal was observed this turn, so the tool loop must not call the model again
|
|
5952
6160
|
* (GENC-1506).
|
|
@@ -6455,6 +6663,51 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
6455
6663
|
await this.agentSession.activate(config);
|
|
6456
6664
|
this.installAgent(config);
|
|
6457
6665
|
}
|
|
6666
|
+
/**
|
|
6667
|
+
* {@inheritDoc AiDriver.resumeRestoredFlow}
|
|
6668
|
+
*
|
|
6669
|
+
* This driver holds no list of agents, so it resumes only the `agent` it is handed (GENC-1660):
|
|
6670
|
+
* whoever runs a bare `ChatDriver` knows which agent that is, and after a reload only the name
|
|
6671
|
+
* survived. Without one it does nothing, as a driver without this member did before; given one
|
|
6672
|
+
* that is not the restored flow owner, it refuses.
|
|
6673
|
+
*
|
|
6674
|
+
* For headless callers: the shipped host restores and resumes through `OrchestratingDriver`,
|
|
6675
|
+
* and a bare `ChatDriver` does not answer `canRestoreFlowOwner`, so the host never plans a resume
|
|
6676
|
+
* for one.
|
|
6677
|
+
*
|
|
6678
|
+
* The orchestrator does not delegate here, because it installs a decorated config of its own;
|
|
6679
|
+
* this is the bare-driver path.
|
|
6680
|
+
*
|
|
6681
|
+
* @beta
|
|
6682
|
+
*/
|
|
6683
|
+
async resumeRestoredFlow(resumeNote, agent) {
|
|
6684
|
+
if (!agent) return void 0;
|
|
6685
|
+
const owner = this.agentSession.getFlowOwner();
|
|
6686
|
+
if (owner !== null && owner !== agent.name) {
|
|
6687
|
+
logger2.warn(
|
|
6688
|
+
`ChatDriver: resumeRestoredFlow was handed "${agent.name}", but the flow belongs to "${owner}" \u2014 not resuming.`
|
|
6689
|
+
);
|
|
6690
|
+
return void 0;
|
|
6691
|
+
}
|
|
6692
|
+
await this.activateAgent(agent);
|
|
6693
|
+
if (!resumeNote || agent.resumeOnRestore === false) return void 0;
|
|
6694
|
+
return this.continueFromHistory(void 0, { tailNote: resumeNote });
|
|
6695
|
+
}
|
|
6696
|
+
/**
|
|
6697
|
+
* Raise the host-facing lifecycle events for one session transition (GENC-1485), in the order
|
|
6698
|
+
* the orchestrator raised them before it stopped doing so itself: the flow lock first, then the
|
|
6699
|
+
* release, then the active agent.
|
|
6700
|
+
*/
|
|
6701
|
+
dispatchAgentTransition(t) {
|
|
6702
|
+
if (t.flowOwnerChangedTo !== void 0) {
|
|
6703
|
+
this.dispatchEvent(new CustomEvent("flow-owner-changed", { detail: t.flowOwnerChangedTo }));
|
|
6704
|
+
}
|
|
6705
|
+
const active = this.agentSession.getActive();
|
|
6706
|
+
if (!active && t.previousAgent) {
|
|
6707
|
+
this.dispatchEvent(new CustomEvent("agent-released", { detail: t.previousAgent }));
|
|
6708
|
+
}
|
|
6709
|
+
this.dispatchEvent(new CustomEvent("agent-changed", { detail: active }));
|
|
6710
|
+
}
|
|
6458
6711
|
/**
|
|
6459
6712
|
* {@inheritDoc AiDriver.getActiveAgent}
|
|
6460
6713
|
*
|
|
@@ -6490,6 +6743,14 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
6490
6743
|
setFlowOwner(name) {
|
|
6491
6744
|
this.agentSession.setFlowOwner(name);
|
|
6492
6745
|
}
|
|
6746
|
+
/**
|
|
6747
|
+
* {@inheritDoc AiDriver.getFlowOwner}
|
|
6748
|
+
*
|
|
6749
|
+
* @beta
|
|
6750
|
+
*/
|
|
6751
|
+
getFlowOwner() {
|
|
6752
|
+
return this.agentSession.getFlowOwner();
|
|
6753
|
+
}
|
|
6493
6754
|
/**
|
|
6494
6755
|
* Install `config` as the active agent **without firing lifecycle hooks**.
|
|
6495
6756
|
*
|
|
@@ -7063,8 +7324,84 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7063
7324
|
* and `presentation` to control whether the host wraps the widget in a chat
|
|
7064
7325
|
* bubble and/or shows the "Assistant" label.
|
|
7065
7326
|
*/
|
|
7066
|
-
async requestInteraction(componentName, data, options, onOpened) {
|
|
7067
|
-
return this.interactions.request(
|
|
7327
|
+
async requestInteraction(componentName, data, options, onOpened, innerPath, onSettled) {
|
|
7328
|
+
return this.interactions.request(
|
|
7329
|
+
componentName,
|
|
7330
|
+
data,
|
|
7331
|
+
options,
|
|
7332
|
+
onOpened,
|
|
7333
|
+
innerPath,
|
|
7334
|
+
onSettled
|
|
7335
|
+
);
|
|
7336
|
+
}
|
|
7337
|
+
/**
|
|
7338
|
+
* {@inheritDoc AiDriver.addInteractionInterceptor}
|
|
7339
|
+
*
|
|
7340
|
+
* Consulted only on the root driver, where every interaction opens: a sub-agent's request
|
|
7341
|
+
* travels up to its root, carrying its owner path, and is offered there once.
|
|
7342
|
+
*
|
|
7343
|
+
* @beta
|
|
7344
|
+
*/
|
|
7345
|
+
addInteractionInterceptor(interceptor) {
|
|
7346
|
+
return this.interactions.addInterceptor(interceptor);
|
|
7347
|
+
}
|
|
7348
|
+
/**
|
|
7349
|
+
* {@inheritDoc AiDriver.presentInteraction}
|
|
7350
|
+
*
|
|
7351
|
+
* @beta
|
|
7352
|
+
*/
|
|
7353
|
+
presentInteraction(componentName, data, options) {
|
|
7354
|
+
return this.requestInteraction(componentName, data, options);
|
|
7355
|
+
}
|
|
7356
|
+
/**
|
|
7357
|
+
* {@inheritDoc AiDriver.provideHandlerCapability}
|
|
7358
|
+
*
|
|
7359
|
+
* Throws if `name` is already provided: two decorators answering to one name would leave a
|
|
7360
|
+
* handler unable to tell which it is talking to.
|
|
7361
|
+
*
|
|
7362
|
+
* @beta
|
|
7363
|
+
*/
|
|
7364
|
+
provideHandlerCapability(name, factory) {
|
|
7365
|
+
if (this.handlerCapabilities.has(name)) {
|
|
7366
|
+
throw new Error(`provideHandlerCapability: "${name}" is already provided.`);
|
|
7367
|
+
}
|
|
7368
|
+
this.handlerCapabilities.set(name, factory);
|
|
7369
|
+
return () => {
|
|
7370
|
+
if (this.handlerCapabilities.get(name) === factory) this.handlerCapabilities.delete(name);
|
|
7371
|
+
};
|
|
7372
|
+
}
|
|
7373
|
+
/**
|
|
7374
|
+
* The capabilities this driver's handlers see, and who they see them as: the root's factories,
|
|
7375
|
+
* the root's flow owner, and the owner path from the root down to this driver's agent.
|
|
7376
|
+
*/
|
|
7377
|
+
capabilityScope() {
|
|
7378
|
+
const own = this.activeAgentName ? [this.activeAgentName] : [];
|
|
7379
|
+
if (!this.capabilityParent) {
|
|
7380
|
+
return {
|
|
7381
|
+
factories: this.handlerCapabilities,
|
|
7382
|
+
scope: { ownerPath: own, flowOwner: this.agentSession.getFlowOwner() }
|
|
7383
|
+
};
|
|
7384
|
+
}
|
|
7385
|
+
const up = this.capabilityParent.capabilityScope();
|
|
7386
|
+
return {
|
|
7387
|
+
factories: up.factories,
|
|
7388
|
+
scope: { ownerPath: [...up.scope.ownerPath, ...own], flowOwner: up.scope.flowOwner }
|
|
7389
|
+
};
|
|
7390
|
+
}
|
|
7391
|
+
/** Build `context.capabilities` for one handler call, or `undefined` when nothing is provided. */
|
|
7392
|
+
buildCapabilities() {
|
|
7393
|
+
const { factories, scope } = this.capabilityScope();
|
|
7394
|
+
if (factories.size === 0) return void 0;
|
|
7395
|
+
const capabilities2 = {};
|
|
7396
|
+
for (const [name, factory] of factories) {
|
|
7397
|
+
try {
|
|
7398
|
+
const value = factory(scope);
|
|
7399
|
+
if (value !== void 0) capabilities2[name] = value;
|
|
7400
|
+
} catch (e) {
|
|
7401
|
+
logger2.warn(`ChatDriver: handler capability "${name}" threw \u2014 leaving it out:`, e);
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7404
|
+
return capabilities2;
|
|
7068
7405
|
}
|
|
7069
7406
|
/**
|
|
7070
7407
|
* Record what came of an interaction, after it resolved, on its history message
|
|
@@ -7136,8 +7473,10 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7136
7473
|
this.history = [...messages];
|
|
7137
7474
|
this.notifyHistoryUpdated();
|
|
7138
7475
|
}
|
|
7139
|
-
async sendMessage(userInput, attachments) {
|
|
7476
|
+
async sendMessage(userInput, attachments, options) {
|
|
7140
7477
|
if (this.busy || !userInput.trim() && !attachments?.length) return { reason: "done" };
|
|
7478
|
+
const delegate = options?.delegate;
|
|
7479
|
+
const delegateFraming = delegate ? options?.delegateFraming ?? DEFAULT_DELEGATE_FRAMING : void 0;
|
|
7141
7480
|
this.busy = true;
|
|
7142
7481
|
this.beginTurn();
|
|
7143
7482
|
this.beginMessage();
|
|
@@ -7154,7 +7493,12 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7154
7493
|
this.aiServiceWall = void 0;
|
|
7155
7494
|
this.aiServiceNoticesShown.clear();
|
|
7156
7495
|
this.aiServiceNoticesQueued = [];
|
|
7157
|
-
this.appendToHistory({
|
|
7496
|
+
this.appendToHistory({
|
|
7497
|
+
role: "user",
|
|
7498
|
+
content: userInput,
|
|
7499
|
+
attachments,
|
|
7500
|
+
...delegate ? { delegate, delegateFraming } : {}
|
|
7501
|
+
});
|
|
7158
7502
|
this.turnStartedAt = Date.now();
|
|
7159
7503
|
recordMetaEvent(this.sessionKey, "turn.start", {
|
|
7160
7504
|
phase: "sendMessage",
|
|
@@ -7163,7 +7507,10 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7163
7507
|
this.activityBus.publish("tool-loop-start", void 0);
|
|
7164
7508
|
let result = { reason: "done" };
|
|
7165
7509
|
try {
|
|
7166
|
-
result = await this.runToolLoop(
|
|
7510
|
+
result = await this.runToolLoop(
|
|
7511
|
+
delegate ? frameDelegatedContent(userInput, delegateFraming) : userInput,
|
|
7512
|
+
attachments
|
|
7513
|
+
);
|
|
7167
7514
|
return result;
|
|
7168
7515
|
} catch (e) {
|
|
7169
7516
|
logger2.error("ChatDriver error:", e);
|
|
@@ -7216,13 +7563,25 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7216
7563
|
* kept on a seven-way fan-out, reporting ~2.3x under the real cost, with the
|
|
7217
7564
|
* error growing as the fan-out widens.
|
|
7218
7565
|
*/
|
|
7219
|
-
buildHandlerContext(activeToolCallId, traceCapture) {
|
|
7566
|
+
buildHandlerContext(activeToolCallId, traceCapture, decisions) {
|
|
7220
7567
|
let opened;
|
|
7221
7568
|
let recorded = false;
|
|
7569
|
+
const capabilities2 = this.buildCapabilities();
|
|
7222
7570
|
return {
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7571
|
+
...capabilities2 && { capabilities: capabilities2 },
|
|
7572
|
+
requestInteraction: (componentName, data, options) => this.requestInteraction(
|
|
7573
|
+
componentName,
|
|
7574
|
+
data,
|
|
7575
|
+
options,
|
|
7576
|
+
(interactionId) => {
|
|
7577
|
+
opened = interactionId;
|
|
7578
|
+
},
|
|
7579
|
+
void 0,
|
|
7580
|
+
// An interaction an interceptor answered is reported in THIS call's result (GENC-1660).
|
|
7581
|
+
(settled) => {
|
|
7582
|
+
if (settled.decision) decisions?.push(settled.decision);
|
|
7583
|
+
}
|
|
7584
|
+
),
|
|
7226
7585
|
recordInteractionOutcome: (outcome) => {
|
|
7227
7586
|
if (!opened) return false;
|
|
7228
7587
|
if (recorded) {
|
|
@@ -7341,11 +7700,12 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7341
7700
|
}
|
|
7342
7701
|
child.installAgent({ ...subConfig, primerHistory: effectivePrimer });
|
|
7343
7702
|
child.setHostInteractionRequester(
|
|
7344
|
-
(componentName, data, opts, onOpened) => this.requestInteraction(componentName, data, opts, onOpened),
|
|
7703
|
+
(componentName, data, opts, onOpened, ownerPath, onSettled) => this.requestInteraction(componentName, data, opts, onOpened, ownerPath, onSettled),
|
|
7345
7704
|
// An outcome goes where its interaction went.
|
|
7346
7705
|
(interactionId, outcome) => this.recordInteractionOutcome(interactionId, outcome)
|
|
7347
7706
|
);
|
|
7348
7707
|
child.hostAIServiceNotice = (code) => this.noticeAIServiceRefusal(code);
|
|
7708
|
+
child.capabilityParent = this;
|
|
7349
7709
|
const forwardTrace = (e) => {
|
|
7350
7710
|
this.dispatchEvent(
|
|
7351
7711
|
new CustomEvent("sub-agent-history-updated", {
|
|
@@ -8056,10 +8416,19 @@ ${tailBody}
|
|
|
8056
8416
|
}
|
|
8057
8417
|
const traceCapture = { traces: [] };
|
|
8058
8418
|
const capturedTrace = () => traceCapture.traces.length ? traceCapture.traces.flat() : void 0;
|
|
8419
|
+
const decisions = [];
|
|
8059
8420
|
try {
|
|
8060
|
-
const result = await handler(
|
|
8421
|
+
const result = await handler(
|
|
8422
|
+
tc.args,
|
|
8423
|
+
this.buildHandlerContext(tc.id, traceCapture, decisions)
|
|
8424
|
+
);
|
|
8061
8425
|
const resultImages = toolResultImages(result);
|
|
8062
|
-
const content =
|
|
8426
|
+
const content = withDelegatedDecisions(
|
|
8427
|
+
result,
|
|
8428
|
+
resultImages ? typeof result.content === "string" ? result.content : JSON.stringify({ ...result, attachments: void 0 }) : typeof result === "string" ? result : JSON.stringify(result),
|
|
8429
|
+
decisions,
|
|
8430
|
+
!!resultImages
|
|
8431
|
+
);
|
|
8063
8432
|
executedById.set(tc.id, {
|
|
8064
8433
|
toolCallId: tc.id,
|
|
8065
8434
|
content,
|
|
@@ -8259,6 +8628,189 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
8259
8628
|
}
|
|
8260
8629
|
};
|
|
8261
8630
|
|
|
8631
|
+
// src/components/ai-driver/ai-driver.ts
|
|
8632
|
+
var AI_DRIVER_EVENT_TYPES = Object.keys({
|
|
8633
|
+
"history-updated": true,
|
|
8634
|
+
"sub-agent-history-updated": true,
|
|
8635
|
+
"sub-agent-start": true,
|
|
8636
|
+
"sub-agent-stop": true,
|
|
8637
|
+
"interaction-start": true,
|
|
8638
|
+
"interaction-stop": true,
|
|
8639
|
+
"provider-changed": true,
|
|
8640
|
+
"external-cost-recorded": true,
|
|
8641
|
+
"agent-changed": true,
|
|
8642
|
+
"agent-released": true,
|
|
8643
|
+
"flow-owner-changed": true,
|
|
8644
|
+
"orchestrating-start": true,
|
|
8645
|
+
"orchestrating-stop": true
|
|
8646
|
+
});
|
|
8647
|
+
|
|
8648
|
+
// src/components/ai-driver/base-driver-decorator.ts
|
|
8649
|
+
var BaseDriverDecorator = class _BaseDriverDecorator extends EventTarget {
|
|
8650
|
+
constructor(inner) {
|
|
8651
|
+
super();
|
|
8652
|
+
this.inner = inner;
|
|
8653
|
+
/** Undoes this layer's hold on its inner driver; see {@link BaseDriverDecorator.detach}. */
|
|
8654
|
+
this.detachers = [];
|
|
8655
|
+
for (const type of AI_DRIVER_EVENT_TYPES) {
|
|
8656
|
+
const forward = (e) => {
|
|
8657
|
+
this.dispatchEvent(new CustomEvent(type, { detail: e.detail }));
|
|
8658
|
+
};
|
|
8659
|
+
inner.addEventListener(type, forward);
|
|
8660
|
+
this.onDetach(() => inner.removeEventListener(type, forward));
|
|
8661
|
+
}
|
|
8662
|
+
}
|
|
8663
|
+
/**
|
|
8664
|
+
* Let go of the inner driver WITHOUT disposing it: stop forwarding its events, and undo whatever
|
|
8665
|
+
* a subclass registered through {@link BaseDriverDecorator.onDetach} (its interceptors, its
|
|
8666
|
+
* capabilities, its own listeners). For dropping or replacing a decorator while the driver it
|
|
8667
|
+
* wrapped lives on — otherwise the old layer keeps re-dispatching every event and is never
|
|
8668
|
+
* collected. Idempotent. `dispose()` detaches too.
|
|
8669
|
+
*/
|
|
8670
|
+
detach() {
|
|
8671
|
+
for (const undo of this.detachers.splice(0)) {
|
|
8672
|
+
try {
|
|
8673
|
+
undo();
|
|
8674
|
+
} catch {
|
|
8675
|
+
}
|
|
8676
|
+
}
|
|
8677
|
+
}
|
|
8678
|
+
/** Register something {@link BaseDriverDecorator.detach} must undo. */
|
|
8679
|
+
onDetach(undo) {
|
|
8680
|
+
this.detachers.push(undo);
|
|
8681
|
+
}
|
|
8682
|
+
sendMessage(input, attachments, options) {
|
|
8683
|
+
return this.inner.sendMessage(input, attachments, options);
|
|
8684
|
+
}
|
|
8685
|
+
continueFromHistory(transientPrimer, options) {
|
|
8686
|
+
return this.inner.continueFromHistory(transientPrimer, options);
|
|
8687
|
+
}
|
|
8688
|
+
resolveInteraction(interactionId, result) {
|
|
8689
|
+
this.inner.resolveInteraction(interactionId, result);
|
|
8690
|
+
}
|
|
8691
|
+
recordExternalCost(interactionId, costUsd) {
|
|
8692
|
+
return this.inner.recordExternalCost?.(interactionId, costUsd) ?? false;
|
|
8693
|
+
}
|
|
8694
|
+
getInteractionContext(interactionId) {
|
|
8695
|
+
return this.inner.getInteractionContext?.(interactionId);
|
|
8696
|
+
}
|
|
8697
|
+
loadHistory(messages) {
|
|
8698
|
+
this.inner.loadHistory(messages);
|
|
8699
|
+
}
|
|
8700
|
+
primeRestoredAgentState(snapshots) {
|
|
8701
|
+
this.inner.primeRestoredAgentState(snapshots);
|
|
8702
|
+
}
|
|
8703
|
+
canRestoreFlowOwner(agentName) {
|
|
8704
|
+
return this.inner.canRestoreFlowOwner?.(agentName) ?? false;
|
|
8705
|
+
}
|
|
8706
|
+
canResumeRestoredFlow(agentName) {
|
|
8707
|
+
return this.inner.canResumeRestoredFlow?.(agentName) ?? false;
|
|
8708
|
+
}
|
|
8709
|
+
resumeRestoredFlow(resumeNote, agent) {
|
|
8710
|
+
return this.inner.resumeRestoredFlow?.(resumeNote, agent) ?? Promise.resolve(void 0);
|
|
8711
|
+
}
|
|
8712
|
+
getRawHistory() {
|
|
8713
|
+
return this.inner.getRawHistory?.() ?? [];
|
|
8714
|
+
}
|
|
8715
|
+
getHistoryForPersistence() {
|
|
8716
|
+
return this.inner.getHistoryForPersistence?.() ?? [...this.getRawHistory()];
|
|
8717
|
+
}
|
|
8718
|
+
getCondensePoliciesForPersistence(messages) {
|
|
8719
|
+
return this.inner.getCondensePoliciesForPersistence?.(messages) ?? [];
|
|
8720
|
+
}
|
|
8721
|
+
restoreCondensePolicies(entries) {
|
|
8722
|
+
return this.inner.restoreCondensePolicies?.(entries) ?? { restored: 0, awaitingReactivation: 0 };
|
|
8723
|
+
}
|
|
8724
|
+
getActiveAgent() {
|
|
8725
|
+
return this.inner.getActiveAgent();
|
|
8726
|
+
}
|
|
8727
|
+
setPinnedAgent(name) {
|
|
8728
|
+
this.inner.setPinnedAgent(name);
|
|
8729
|
+
}
|
|
8730
|
+
setFlowOwner(name) {
|
|
8731
|
+
this.inner.setFlowOwner(name);
|
|
8732
|
+
}
|
|
8733
|
+
getFlowOwner() {
|
|
8734
|
+
return this.inner.getFlowOwner?.() ?? null;
|
|
8735
|
+
}
|
|
8736
|
+
/**
|
|
8737
|
+
* Forwarded, so an interceptor installed on any layer lands on the driver that opens the
|
|
8738
|
+
* interactions. A decorator that answers interactions installs its own on `this.inner`.
|
|
8739
|
+
*/
|
|
8740
|
+
addInteractionInterceptor(interceptor) {
|
|
8741
|
+
return this.inner.addInteractionInterceptor?.(interceptor) ?? (() => void 0);
|
|
8742
|
+
}
|
|
8743
|
+
presentInteraction(componentName, data, options) {
|
|
8744
|
+
return this.inner.presentInteraction?.(componentName, data, options) ?? Promise.reject(new Error("presentInteraction: the inner driver cannot show interactions."));
|
|
8745
|
+
}
|
|
8746
|
+
provideHandlerCapability(name, factory) {
|
|
8747
|
+
return this.inner.provideHandlerCapability?.(name, factory) ?? (() => void 0);
|
|
8748
|
+
}
|
|
8749
|
+
getContextReserve() {
|
|
8750
|
+
return this.inner.getContextReserve?.();
|
|
8751
|
+
}
|
|
8752
|
+
getPreambleTokens() {
|
|
8753
|
+
return this.inner.getPreambleTokens?.();
|
|
8754
|
+
}
|
|
8755
|
+
setContextGuard(policy) {
|
|
8756
|
+
this.inner.setContextGuard?.(policy);
|
|
8757
|
+
}
|
|
8758
|
+
compact(options) {
|
|
8759
|
+
return this.inner.compact?.(options) ?? Promise.resolve(null);
|
|
8760
|
+
}
|
|
8761
|
+
getCompactionPlan(options) {
|
|
8762
|
+
return this.inner.getCompactionPlan?.(options) ?? null;
|
|
8763
|
+
}
|
|
8764
|
+
canCompact(options) {
|
|
8765
|
+
return this.inner.canCompact?.(options) ?? false;
|
|
8766
|
+
}
|
|
8767
|
+
isBusy() {
|
|
8768
|
+
return this.inner.isBusy();
|
|
8769
|
+
}
|
|
8770
|
+
getSuggestions(history, prompt2, count, allAgentInfo) {
|
|
8771
|
+
return this.inner.getSuggestions(history, prompt2, count, allAgentInfo);
|
|
8772
|
+
}
|
|
8773
|
+
getTurnSnapshots() {
|
|
8774
|
+
return this.inner.getTurnSnapshots?.() ?? [];
|
|
8775
|
+
}
|
|
8776
|
+
/**
|
|
8777
|
+
* The inner driver's entries. Diagnostics compose down the stack: a decorator with entries of
|
|
8778
|
+
* its own overrides this and appends them to `super.getExternalDiagnostics()`.
|
|
8779
|
+
*/
|
|
8780
|
+
getExternalDiagnostics() {
|
|
8781
|
+
return this.inner.getExternalDiagnostics?.() ?? [];
|
|
8782
|
+
}
|
|
8783
|
+
getActiveProviderName() {
|
|
8784
|
+
return this.inner.getActiveProviderName();
|
|
8785
|
+
}
|
|
8786
|
+
dispose() {
|
|
8787
|
+
const disposed = this.inner.dispose?.();
|
|
8788
|
+
this.detach();
|
|
8789
|
+
return disposed;
|
|
8790
|
+
}
|
|
8791
|
+
cancel() {
|
|
8792
|
+
this.inner.cancel?.();
|
|
8793
|
+
}
|
|
8794
|
+
/**
|
|
8795
|
+
* Every driver in the stack, outermost first: `driver`, then each inner driver down to the one
|
|
8796
|
+
* that is not a decorator.
|
|
8797
|
+
*
|
|
8798
|
+
* For diagnostics and labelling only: behaviour must go through `AiDriver` members, which every
|
|
8799
|
+
* layer forwards, never through a concrete class found here.
|
|
8800
|
+
*
|
|
8801
|
+
* @internal
|
|
8802
|
+
*/
|
|
8803
|
+
static layers(driver) {
|
|
8804
|
+
const layers = [driver];
|
|
8805
|
+
let current = driver;
|
|
8806
|
+
while (current instanceof _BaseDriverDecorator) {
|
|
8807
|
+
current = current.inner;
|
|
8808
|
+
layers.push(current);
|
|
8809
|
+
}
|
|
8810
|
+
return layers;
|
|
8811
|
+
}
|
|
8812
|
+
};
|
|
8813
|
+
|
|
8262
8814
|
// src/components/orchestrating-driver/orchestrating-driver.ts
|
|
8263
8815
|
var DEFAULT_MAX_HANDOFFS = 3;
|
|
8264
8816
|
var DEFAULT_CLASSIFIER_HISTORY_LENGTH = 4;
|
|
@@ -8321,9 +8873,9 @@ Politely let the user know what you can help with and invite them to rephrase th
|
|
|
8321
8873
|
|
|
8322
8874
|
${UNROUTABLE_INSTRUCTION}`;
|
|
8323
8875
|
}
|
|
8324
|
-
var OrchestratingDriver = class extends
|
|
8876
|
+
var OrchestratingDriver = class _OrchestratingDriver extends BaseDriverDecorator {
|
|
8325
8877
|
constructor(providerRegistry, agents, options = {}) {
|
|
8326
|
-
super();
|
|
8878
|
+
super(_OrchestratingDriver.createInner(providerRegistry, agents, options));
|
|
8327
8879
|
this.providerRegistry = providerRegistry;
|
|
8328
8880
|
this.agents = agents;
|
|
8329
8881
|
/**
|
|
@@ -8347,7 +8899,6 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8347
8899
|
this.maxHandoffs = options.maxHandoffs ?? DEFAULT_MAX_HANDOFFS;
|
|
8348
8900
|
this.classifierHistoryLength = options.classifierHistoryLength ?? DEFAULT_CLASSIFIER_HISTORY_LENGTH;
|
|
8349
8901
|
this.classifierRetries = options.classifierRetries ?? DEFAULT_CLASSIFIER_RETRIES;
|
|
8350
|
-
validateStaticAgentProviders(agents, providerRegistry);
|
|
8351
8902
|
this.specialists = agents.filter(isSpecialist).filter((a) => !a.excludeFromClassifier);
|
|
8352
8903
|
const fallbacks = agents.filter(isFallback);
|
|
8353
8904
|
if (fallbacks.length > 1) {
|
|
@@ -8357,51 +8908,10 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8357
8908
|
}
|
|
8358
8909
|
const rawFallback = fallbacks[0];
|
|
8359
8910
|
this.fallback = rawFallback ? { ...rawFallback, systemPrompt: buildFallbackSystemPrompt(rawFallback, this.specialists) } : void 0;
|
|
8360
|
-
|
|
8361
|
-
|
|
8362
|
-
|
|
8363
|
-
|
|
8364
|
-
sessionKey: this.sessionKey,
|
|
8365
|
-
activityBus: options.activityBus,
|
|
8366
|
-
budgetExhaustedMessage: options.budgetExhaustedMessage,
|
|
8367
|
-
aiServiceErrorMessages: options.aiServiceErrorMessages,
|
|
8368
|
-
// This driver releases after the turn so it can fire the host-facing events; see the option.
|
|
8369
|
-
deferAgentRelease: true
|
|
8370
|
-
});
|
|
8371
|
-
this.chatDriver.addEventListener("history-updated", (e) => {
|
|
8372
|
-
this.dispatchEvent(new CustomEvent("history-updated", { detail: e.detail }));
|
|
8373
|
-
});
|
|
8374
|
-
this.chatDriver.addEventListener("sub-agent-history-updated", (e) => {
|
|
8375
|
-
this.dispatchEvent(
|
|
8376
|
-
new CustomEvent("sub-agent-history-updated", { detail: e.detail })
|
|
8377
|
-
);
|
|
8378
|
-
});
|
|
8379
|
-
this.chatDriver.addEventListener("sub-agent-start", (e) => {
|
|
8380
|
-
this.dispatchEvent(new CustomEvent("sub-agent-start", { detail: e.detail }));
|
|
8381
|
-
});
|
|
8382
|
-
this.chatDriver.addEventListener("sub-agent-stop", (e) => {
|
|
8383
|
-
this.dispatchEvent(new CustomEvent("sub-agent-stop", { detail: e.detail }));
|
|
8384
|
-
});
|
|
8385
|
-
this.chatDriver.addEventListener("interaction-start", (e) => {
|
|
8386
|
-
this.dispatchEvent(
|
|
8387
|
-
new CustomEvent("interaction-start", { detail: e.detail })
|
|
8388
|
-
);
|
|
8389
|
-
});
|
|
8390
|
-
this.chatDriver.addEventListener("interaction-stop", (e) => {
|
|
8391
|
-
this.dispatchEvent(
|
|
8392
|
-
new CustomEvent("interaction-stop", { detail: e.detail })
|
|
8393
|
-
);
|
|
8394
|
-
});
|
|
8395
|
-
this.chatDriver.addEventListener("provider-changed", (e) => {
|
|
8396
|
-
this.dispatchEvent(
|
|
8397
|
-
new CustomEvent("provider-changed", { detail: e.detail })
|
|
8398
|
-
);
|
|
8399
|
-
});
|
|
8400
|
-
this.chatDriver.addEventListener("external-cost-recorded", (e) => {
|
|
8401
|
-
this.dispatchEvent(
|
|
8402
|
-
new CustomEvent("external-cost-recorded", { detail: e.detail })
|
|
8403
|
-
);
|
|
8404
|
-
});
|
|
8911
|
+
}
|
|
8912
|
+
/** The inner driver this one built and routes onto. */
|
|
8913
|
+
get chatDriver() {
|
|
8914
|
+
return this.inner;
|
|
8405
8915
|
}
|
|
8406
8916
|
/**
|
|
8407
8917
|
* The stateful-agent contract (§7.1): active agent, user pin, flow lock, restored state, and
|
|
@@ -8425,108 +8935,23 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8425
8935
|
get activeAgent() {
|
|
8426
8936
|
return this.agentSession.getActive();
|
|
8427
8937
|
}
|
|
8428
|
-
resolveInteraction(interactionId, result) {
|
|
8429
|
-
this.chatDriver.resolveInteraction(interactionId, result);
|
|
8430
|
-
}
|
|
8431
|
-
/** Delegated for the same reason as `resolveInteraction`: the history that owns the
|
|
8432
|
-
* interaction — and therefore the cost — lives on the wrapped driver, not here. */
|
|
8433
|
-
recordExternalCost(interactionId, costUsd) {
|
|
8434
|
-
return this.chatDriver.recordExternalCost(interactionId, costUsd);
|
|
8435
|
-
}
|
|
8436
|
-
getInteractionContext(interactionId) {
|
|
8437
|
-
return this.chatDriver.getInteractionContext(interactionId);
|
|
8438
|
-
}
|
|
8439
|
-
isBusy() {
|
|
8440
|
-
return this.chatDriver.isBusy();
|
|
8441
|
-
}
|
|
8442
|
-
/** Currently active provider name from the underlying ChatDriver. */
|
|
8443
|
-
getActiveProviderName() {
|
|
8444
|
-
return this.chatDriver.getActiveProviderName();
|
|
8445
|
-
}
|
|
8446
|
-
/**
|
|
8447
|
-
* Pins routing to a specific agent by name. While pinned, the classifier is
|
|
8448
|
-
* skipped and the continuation tool is hidden from the agent's tool list, so
|
|
8449
|
-
* the agent cannot quietly hand back to the orchestrator. Pass `null` to
|
|
8450
|
-
* return to automatic routing.
|
|
8451
|
-
*/
|
|
8452
|
-
setPinnedAgent(name) {
|
|
8453
|
-
this.agentSession.setPinned(name);
|
|
8454
|
-
}
|
|
8455
|
-
/**
|
|
8456
|
-
* Set the flow-owner lock — used to restore state from Redux on driver
|
|
8457
|
-
* reconstruction (agents-array changes, pop-in/pop-out). Not intended for
|
|
8458
|
-
* external callers to manipulate routing; that's what `setPinnedAgent` is
|
|
8459
|
-
* for.
|
|
8460
|
-
*/
|
|
8461
|
-
setFlowOwner(name) {
|
|
8462
|
-
this.agentSession.setFlowOwner(name);
|
|
8463
|
-
}
|
|
8464
|
-
loadHistory(messages) {
|
|
8465
|
-
this.chatDriver.loadHistory(messages);
|
|
8466
|
-
}
|
|
8467
|
-
getRawHistory() {
|
|
8468
|
-
return this.chatDriver.getHistory();
|
|
8469
|
-
}
|
|
8470
8938
|
/**
|
|
8471
|
-
*
|
|
8472
|
-
*
|
|
8473
|
-
*
|
|
8474
|
-
* **This forward is the whole feature, for any multi-agent host.** `AiDriver` declares
|
|
8475
|
-
* `getHistoryForPersistence` optional, and `SessionPersister` calls it through `?.`, falling back
|
|
8476
|
-
* to {@link OrchestratingDriver.getRawHistory}. This class does NOT extend `ChatDriver` — it
|
|
8477
|
-
* wraps one — so without an explicit forward the optional call resolves to `undefined` and every
|
|
8478
|
-
* host on an orchestrating driver silently keeps persisting the full payloads. Measured exactly
|
|
8479
|
-
* that way on a Create run: 10 `context.condensed` events fired, and the session still stored
|
|
8480
|
-
* the two largest `read_context` results at 26,907 and 23,060 chars. The method existed, the
|
|
8481
|
-
* tests passed, and the feature did nothing.
|
|
8482
|
-
*/
|
|
8483
|
-
getHistoryForPersistence() {
|
|
8484
|
-
return this.chatDriver.getHistoryForPersistence();
|
|
8485
|
-
}
|
|
8486
|
-
/**
|
|
8487
|
-
* Delegates to the inner {@link ChatDriver}, which owns the condensation registry and clocks
|
|
8488
|
-
* (GENC-1632). The forward matters for the same reason as `getHistoryForPersistence` above: the
|
|
8489
|
-
* persister reaches it through `?.`, so a missing forward would compile, pass the ChatDriver
|
|
8490
|
-
* tests, and do nothing for every orchestrated host — which is every host with an agents array.
|
|
8939
|
+
* Build the inner driver, after validating the agents' providers, so a bad config throws before
|
|
8940
|
+
* a driver exists to subscribe to the registry. Static because it runs before `super()`.
|
|
8491
8941
|
*/
|
|
8492
|
-
|
|
8493
|
-
|
|
8494
|
-
|
|
8495
|
-
|
|
8496
|
-
|
|
8497
|
-
|
|
8498
|
-
|
|
8499
|
-
|
|
8500
|
-
|
|
8501
|
-
|
|
8502
|
-
|
|
8503
|
-
|
|
8504
|
-
|
|
8505
|
-
this.chatDriver.setContextGuard(policy);
|
|
8506
|
-
}
|
|
8507
|
-
/** {@inheritDoc AiDriver.compact} */
|
|
8508
|
-
compact(options) {
|
|
8509
|
-
return this.chatDriver.compact(options);
|
|
8510
|
-
}
|
|
8511
|
-
/** {@inheritDoc AiDriver.canCompact} */
|
|
8512
|
-
canCompact(options) {
|
|
8513
|
-
return this.chatDriver.canCompact(options);
|
|
8514
|
-
}
|
|
8515
|
-
/** {@inheritDoc AiDriver.getPreambleTokens} */
|
|
8516
|
-
getPreambleTokens() {
|
|
8517
|
-
return this.chatDriver.getPreambleTokens();
|
|
8518
|
-
}
|
|
8519
|
-
/** {@inheritDoc AiDriver.getCompactionPlan} */
|
|
8520
|
-
getCompactionPlan(options) {
|
|
8521
|
-
return this.chatDriver.getCompactionPlan(options);
|
|
8522
|
-
}
|
|
8523
|
-
/** Delegates to the inner {@link ChatDriver} — turns are captured there. */
|
|
8524
|
-
getTurnSnapshots() {
|
|
8525
|
-
return this.chatDriver.getTurnSnapshots();
|
|
8526
|
-
}
|
|
8527
|
-
/** Delegates to the inner {@link ChatDriver} — interactions resolve there, so it holds the buffer. */
|
|
8528
|
-
getExternalDiagnostics() {
|
|
8529
|
-
return this.chatDriver.getExternalDiagnostics();
|
|
8942
|
+
static createInner(providerRegistry, agents, options = {}) {
|
|
8943
|
+
validateStaticAgentProviders(agents, providerRegistry);
|
|
8944
|
+
return new ChatDriver(providerRegistry, {
|
|
8945
|
+
maxToolIterations: options.maxToolIterations,
|
|
8946
|
+
maxFoldOperations: options.maxFoldOperations,
|
|
8947
|
+
maxTurnSnapshots: options.maxTurnSnapshots,
|
|
8948
|
+
sessionKey: options.sessionKey ?? "",
|
|
8949
|
+
activityBus: options.activityBus,
|
|
8950
|
+
budgetExhaustedMessage: options.budgetExhaustedMessage,
|
|
8951
|
+
aiServiceErrorMessages: options.aiServiceErrorMessages,
|
|
8952
|
+
// A release ends this driver's handoff loop, so it acts on the flag itself; see the option.
|
|
8953
|
+
deferAgentRelease: true
|
|
8954
|
+
});
|
|
8530
8955
|
}
|
|
8531
8956
|
async getSuggestions(history, prompt2, count, allAgentInfo) {
|
|
8532
8957
|
const pinned = this.resolvePinnedAgent();
|
|
@@ -8541,14 +8966,19 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8541
8966
|
}));
|
|
8542
8967
|
return this.chatDriver.getSuggestions(history, prompt2, count, agentInfo);
|
|
8543
8968
|
}
|
|
8544
|
-
async sendMessage(input, attachments) {
|
|
8969
|
+
async sendMessage(input, attachments, options) {
|
|
8970
|
+
const delegated = options?.delegate ? {
|
|
8971
|
+
delegate: options.delegate,
|
|
8972
|
+
delegateFraming: options.delegateFraming ?? DEFAULT_DELEGATE_FRAMING
|
|
8973
|
+
} : {};
|
|
8545
8974
|
try {
|
|
8546
|
-
return await this.runOrchestratedTurn(input, attachments);
|
|
8975
|
+
return await this.runOrchestratedTurn(input, attachments, options, delegated);
|
|
8547
8976
|
} catch (e) {
|
|
8548
8977
|
const pendingUserMessage = this.userMessageAppended ? void 0 : {
|
|
8549
8978
|
role: "user",
|
|
8550
8979
|
content: input,
|
|
8551
|
-
...attachments ? { attachments } : {}
|
|
8980
|
+
...attachments ? { attachments } : {},
|
|
8981
|
+
...delegated
|
|
8552
8982
|
};
|
|
8553
8983
|
if (e instanceof BudgetExhaustedError) {
|
|
8554
8984
|
return this.chatDriver.reportBudgetExhausted(e, pendingUserMessage);
|
|
@@ -8559,13 +8989,13 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8559
8989
|
throw e;
|
|
8560
8990
|
}
|
|
8561
8991
|
}
|
|
8562
|
-
async runOrchestratedTurn(input, attachments) {
|
|
8992
|
+
async runOrchestratedTurn(input, attachments, options, delegated) {
|
|
8563
8993
|
this.cancelled = false;
|
|
8564
8994
|
this.userMessageAppended = false;
|
|
8565
8995
|
const history = this.chatDriver.getHistory();
|
|
8566
8996
|
this.dispatchEvent(
|
|
8567
8997
|
new CustomEvent("history-updated", {
|
|
8568
|
-
detail: [...history, { role: "user", content: input, attachments }]
|
|
8998
|
+
detail: [...history, { role: "user", content: input, attachments, ...delegated }]
|
|
8569
8999
|
})
|
|
8570
9000
|
);
|
|
8571
9001
|
const pinned = this.resolvePinnedAgent();
|
|
@@ -8575,7 +9005,11 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8575
9005
|
this.recordRouting("pinned", pinned, { durationMs: 0 });
|
|
8576
9006
|
routed = { agent: pinned, decision: "pinned" };
|
|
8577
9007
|
} else {
|
|
8578
|
-
routed = await this.classify(
|
|
9008
|
+
routed = await this.classify(
|
|
9009
|
+
delegated.delegate ? frameDelegatedContent(input, delegated.delegateFraming) : input,
|
|
9010
|
+
history,
|
|
9011
|
+
this.activeAgent
|
|
9012
|
+
);
|
|
8579
9013
|
}
|
|
8580
9014
|
let currentAgent = routed.agent;
|
|
8581
9015
|
let isHandoff = false;
|
|
@@ -8602,7 +9036,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8602
9036
|
result = await this.chatDriver.continueFromHistory(contextPrimer);
|
|
8603
9037
|
} else {
|
|
8604
9038
|
this.userMessageAppended = true;
|
|
8605
|
-
result = await this.chatDriver.sendMessage(input, attachments);
|
|
9039
|
+
result = await this.chatDriver.sendMessage(input, attachments, options);
|
|
8606
9040
|
}
|
|
8607
9041
|
lastResult = result;
|
|
8608
9042
|
if (this.chatDriver.getAgentReleaseRequested()) {
|
|
@@ -8635,18 +9069,12 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8635
9069
|
this.chatDriver.reportStoppedBeforeTurn({
|
|
8636
9070
|
role: "user",
|
|
8637
9071
|
content: input,
|
|
8638
|
-
...attachments ? { attachments } : {}
|
|
9072
|
+
...attachments ? { attachments } : {},
|
|
9073
|
+
...delegated
|
|
8639
9074
|
});
|
|
8640
9075
|
}
|
|
8641
9076
|
return lastResult?.reason === "done" ? lastResult : { reason: "done" };
|
|
8642
9077
|
}
|
|
8643
|
-
async continueFromHistory(transientPrimer, options) {
|
|
8644
|
-
return this.chatDriver.continueFromHistory(transientPrimer, options);
|
|
8645
|
-
}
|
|
8646
|
-
/** {@inheritDoc AiDriver.primeRestoredAgentState} */
|
|
8647
|
-
primeRestoredAgentState(snapshots) {
|
|
8648
|
-
this.agentSession.primeRestoredStates(snapshots);
|
|
8649
|
-
}
|
|
8650
9078
|
/** {@inheritDoc AiDriver.canRestoreFlowOwner} */
|
|
8651
9079
|
canRestoreFlowOwner(agentName) {
|
|
8652
9080
|
return this.agents.some((a) => a.name === agentName);
|
|
@@ -8663,6 +9091,10 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8663
9091
|
* to classify, and a restored lock already says which agent owns the flow. `continueFromHistory`
|
|
8664
9092
|
* on its own would not do — it neither routes nor installs, so it would run on whatever agent
|
|
8665
9093
|
* happened to be installed (none, straight after a reload).
|
|
9094
|
+
*
|
|
9095
|
+
* Looks the owner up in its own agents list, so the `agent` argument is not needed and is
|
|
9096
|
+
* ignored; and does not delegate to the inner driver's resume, which would install the agent
|
|
9097
|
+
* undecorated.
|
|
8666
9098
|
*/
|
|
8667
9099
|
async resumeRestoredFlow(resumeNote) {
|
|
8668
9100
|
const owner = this.agentSession.getFlowOwner();
|
|
@@ -8699,15 +9131,8 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8699
9131
|
}
|
|
8700
9132
|
return winner ? { ...winner, agentName, basis: "reachable-max" } : void 0;
|
|
8701
9133
|
}
|
|
8702
|
-
/** {@inheritDoc AiDriver.getActiveAgent} */
|
|
8703
|
-
getActiveAgent() {
|
|
8704
|
-
return this.agentSession.getActive();
|
|
8705
|
-
}
|
|
8706
9134
|
async installAgent(agent) {
|
|
8707
|
-
const { previousAgent
|
|
8708
|
-
if (flowOwnerChangedTo !== void 0) {
|
|
8709
|
-
this.dispatchEvent(new CustomEvent("flow-owner-changed", { detail: flowOwnerChangedTo }));
|
|
8710
|
-
}
|
|
9135
|
+
const { previousAgent } = await this.agentSession.activate(agent);
|
|
8711
9136
|
const isTerminal = isFallback(agent) || this.agentSession.getPinned() !== null || this.agentSession.getFlowOwner() !== null || !!(agent.onActivate || agent.onDeactivate);
|
|
8712
9137
|
let agentToApply = agent;
|
|
8713
9138
|
if (!isTerminal) {
|
|
@@ -8730,12 +9155,11 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8730
9155
|
this.chatDriver.setProviderHistoryTransform((h) => transformHistoryForAgent(h, agent.name));
|
|
8731
9156
|
this.chatDriver.installAgent(agentToApply);
|
|
8732
9157
|
this.dispatchEvent(new CustomEvent("orchestrating-stop"));
|
|
8733
|
-
this.dispatchEvent(new CustomEvent("agent-changed", { detail: agent }));
|
|
8734
9158
|
}
|
|
8735
9159
|
/**
|
|
8736
|
-
* Release the current stateful agent: fire `onDeactivate
|
|
8737
|
-
*
|
|
8738
|
-
* unlocked state. Called automatically when a tool handler invokes
|
|
9160
|
+
* Release the current stateful agent: fire `onDeactivate` and clear the flow-owner lock. The
|
|
9161
|
+
* inner driver raises the events from the session's transition, so the host (and Redux) reflect
|
|
9162
|
+
* the unlocked state (GENC-1485). Called automatically when a tool handler invokes
|
|
8739
9163
|
* `context.releaseAgent`.
|
|
8740
9164
|
*
|
|
8741
9165
|
* The user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user
|
|
@@ -8744,15 +9168,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8744
9168
|
* routing through the classifier.
|
|
8745
9169
|
*/
|
|
8746
9170
|
async releaseActiveAgent() {
|
|
8747
|
-
|
|
8748
|
-
if (!released) return;
|
|
8749
|
-
if (released.flowOwnerChangedTo !== void 0) {
|
|
8750
|
-
this.dispatchEvent(
|
|
8751
|
-
new CustomEvent("flow-owner-changed", { detail: released.flowOwnerChangedTo })
|
|
8752
|
-
);
|
|
8753
|
-
}
|
|
8754
|
-
this.dispatchEvent(new CustomEvent("agent-released", { detail: released.previousAgent }));
|
|
8755
|
-
this.dispatchEvent(new CustomEvent("agent-changed", { detail: void 0 }));
|
|
9171
|
+
await this.agentSession.release();
|
|
8756
9172
|
}
|
|
8757
9173
|
/**
|
|
8758
9174
|
* Stop the current turn (user "stop" button). Cancels the inner chat driver's
|
|
@@ -8771,6 +9187,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8771
9187
|
async dispose() {
|
|
8772
9188
|
await this.agentSession.dispose();
|
|
8773
9189
|
await this.chatDriver.dispose();
|
|
9190
|
+
this.detach();
|
|
8774
9191
|
}
|
|
8775
9192
|
/**
|
|
8776
9193
|
* Record one routing decision on the debug timeline — what was chosen, why, what it cost, and
|
|
@@ -8812,7 +9229,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8812
9229
|
classifierTranscript(history) {
|
|
8813
9230
|
const lines = history.filter((m) => (m.role === "user" || m.role === "assistant") && reachesProvider(m)).filter((m) => !!m.content?.trim()).slice(-this.classifierHistoryLength).map((m) => {
|
|
8814
9231
|
const speaker = m.role === "user" ? "User" : "Assistant";
|
|
8815
|
-
const content = m.content ?? "";
|
|
9232
|
+
const content = m.role === "user" && m.delegate ? frameDelegatedContent(m.content ?? "", m.delegateFraming) : m.content ?? "";
|
|
8816
9233
|
const body = content.length > CLASSIFIER_MESSAGE_CHARS ? `${content.slice(0, CLASSIFIER_MESSAGE_CHARS)}\u2026` : content;
|
|
8817
9234
|
return `${speaker}: ${body}`;
|
|
8818
9235
|
});
|