@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.cjs
CHANGED
|
@@ -4109,6 +4109,10 @@ var META_EVENT_IMPORTANCE = {
|
|
|
4109
4109
|
"interaction.outcome": "normal",
|
|
4110
4110
|
// An outcome that could not be recorded: the widget goes on showing only the answer.
|
|
4111
4111
|
"interaction.outcome-dropped": "high",
|
|
4112
|
+
// A claimant handing a pending interaction to the person, or taking it back.
|
|
4113
|
+
"interaction.held": "normal",
|
|
4114
|
+
// A claim on a `userOnly` interaction, refused. Expected when an interceptor claims broadly.
|
|
4115
|
+
"interaction.claim-refused": "low",
|
|
4112
4116
|
"external-diagnostics.folded": "normal",
|
|
4113
4117
|
"session.cleared": "normal",
|
|
4114
4118
|
"session.switched": "low",
|
|
@@ -4320,6 +4324,14 @@ function projectCompaction(history, cut, summaryTokens = COMPACTION_SUMMARY_TOKE
|
|
|
4320
4324
|
};
|
|
4321
4325
|
}
|
|
4322
4326
|
|
|
4327
|
+
// src/utils/delegated-message.ts
|
|
4328
|
+
var DEFAULT_DELEGATE_FRAMING = "Sent on the user's behalf.";
|
|
4329
|
+
function frameDelegatedContent(content, framing) {
|
|
4330
|
+
return `(${framing ?? DEFAULT_DELEGATE_FRAMING})
|
|
4331
|
+
|
|
4332
|
+
${content}`;
|
|
4333
|
+
}
|
|
4334
|
+
|
|
4323
4335
|
// src/utils/logger.ts
|
|
4324
4336
|
var logger2 = createLogger("ai-assistant");
|
|
4325
4337
|
|
|
@@ -4371,7 +4383,9 @@ Additional guidance: "${prompt2}"` : "";
|
|
|
4371
4383
|
}
|
|
4372
4384
|
const conversationContext = history.filter(
|
|
4373
4385
|
(m) => reachesProvider(m) && (m.role === "user" || m.role === "assistant") && !m.toolCalls?.length && !m.thinking && !!m.content?.trim()
|
|
4374
|
-
).slice(-HISTORY_WINDOW).map(
|
|
4386
|
+
).slice(-HISTORY_WINDOW).map(
|
|
4387
|
+
(m) => m.role === "user" ? `User: ${m.delegate ? frameDelegatedContent(m.content ?? "", m.delegateFraming) : m.content}` : `Assistant: ${m.content}`
|
|
4388
|
+
).join("\n");
|
|
4375
4389
|
return `<conversation_history>
|
|
4376
4390
|
${conversationContext}
|
|
4377
4391
|
</conversation_history>
|
|
@@ -4761,7 +4775,8 @@ function renderMessageForSummary(m) {
|
|
|
4761
4775
|
case "compacted-summary":
|
|
4762
4776
|
return `[Earlier summary] ${m.content}`;
|
|
4763
4777
|
case "user":
|
|
4764
|
-
|
|
4778
|
+
if (!m.content) return "";
|
|
4779
|
+
return `User: ${m.delegate ? frameDelegatedContent(m.content, m.delegateFraming) : m.content}`;
|
|
4765
4780
|
case "assistant": {
|
|
4766
4781
|
if (m.category === "reasoning") {
|
|
4767
4782
|
return m.content ? `Thinking: ${m.content}` : "";
|
|
@@ -4793,6 +4808,9 @@ Summarize the conversation above so it can be dropped from context while preserv
|
|
|
4793
4808
|
}
|
|
4794
4809
|
function normalizeForProvider(history) {
|
|
4795
4810
|
return history.map((m) => {
|
|
4811
|
+
if (m.role === "user" && m.delegate) {
|
|
4812
|
+
return { ...m, content: frameDelegatedContent(m.content ?? "", m.delegateFraming) };
|
|
4813
|
+
}
|
|
4796
4814
|
if (m.role !== "compacted-summary") return m;
|
|
4797
4815
|
return {
|
|
4798
4816
|
...m,
|
|
@@ -5007,6 +5025,7 @@ var AgentSession = class {
|
|
|
5007
5025
|
transition.flowOwnerChangedTo = agent.name;
|
|
5008
5026
|
}
|
|
5009
5027
|
this.active = agent;
|
|
5028
|
+
if (switched) this.reportTransition(transition);
|
|
5010
5029
|
return transition;
|
|
5011
5030
|
}
|
|
5012
5031
|
/**
|
|
@@ -5028,8 +5047,21 @@ var AgentSession = class {
|
|
|
5028
5047
|
this.flowOwner = null;
|
|
5029
5048
|
transition.flowOwnerChangedTo = null;
|
|
5030
5049
|
}
|
|
5050
|
+
this.reportTransition(transition);
|
|
5031
5051
|
return transition;
|
|
5032
5052
|
}
|
|
5053
|
+
/**
|
|
5054
|
+
* Hand a transition to the owner. A throwing listener is logged rather than propagated, for the
|
|
5055
|
+
* same reason a throwing hook is: the transition has already happened, and failing the caller
|
|
5056
|
+
* would leave it believing it had not.
|
|
5057
|
+
*/
|
|
5058
|
+
reportTransition(transition) {
|
|
5059
|
+
try {
|
|
5060
|
+
this.deps.onTransition?.(transition);
|
|
5061
|
+
} catch (e) {
|
|
5062
|
+
logger2.warn(`${WARN_PREFIX}: onTransition threw:`, e);
|
|
5063
|
+
}
|
|
5064
|
+
}
|
|
5033
5065
|
/**
|
|
5034
5066
|
* Fire `onDeactivate` on the active agent and abort pending lifecycle work, for session
|
|
5035
5067
|
* teardown, so machines release resources cleanly. The session is not reusable afterwards: the
|
|
@@ -5255,8 +5287,12 @@ function createInteractionContext(interactionId) {
|
|
|
5255
5287
|
const listeners = /* @__PURE__ */ new Set();
|
|
5256
5288
|
const disposers = /* @__PURE__ */ new Set();
|
|
5257
5289
|
let disposed = false;
|
|
5290
|
+
let heldBy = "user";
|
|
5258
5291
|
const context = {
|
|
5259
5292
|
interactionId,
|
|
5293
|
+
get heldBy() {
|
|
5294
|
+
return heldBy;
|
|
5295
|
+
},
|
|
5260
5296
|
state,
|
|
5261
5297
|
once(key, factory) {
|
|
5262
5298
|
if (onceCache.has(key)) return onceCache.get(key);
|
|
@@ -5308,11 +5344,18 @@ function createInteractionContext(interactionId) {
|
|
|
5308
5344
|
disposers.clear();
|
|
5309
5345
|
listeners.clear();
|
|
5310
5346
|
onceCache.clear();
|
|
5347
|
+
},
|
|
5348
|
+
setHeldBy(holder) {
|
|
5349
|
+
if (disposed || holder === heldBy) return false;
|
|
5350
|
+
heldBy = holder;
|
|
5351
|
+
context.notify();
|
|
5352
|
+
return true;
|
|
5311
5353
|
}
|
|
5312
5354
|
};
|
|
5313
5355
|
}
|
|
5314
5356
|
|
|
5315
5357
|
// src/components/chat-driver/interaction-controller.ts
|
|
5358
|
+
var DEFAULT_DECISION_FRAMING = "Decided on the user's behalf";
|
|
5316
5359
|
var STOPPED = {
|
|
5317
5360
|
status: "timeout",
|
|
5318
5361
|
message: "The turn was stopped before this was answered."
|
|
@@ -5330,6 +5373,94 @@ var InteractionController = class {
|
|
|
5330
5373
|
* one-interaction-at-a-time guard in `request`).
|
|
5331
5374
|
*/
|
|
5332
5375
|
this.contexts = /* @__PURE__ */ new Map();
|
|
5376
|
+
/**
|
|
5377
|
+
* Told about every interaction this controller opens (GENC-1660), in registration order. Only
|
|
5378
|
+
* the root's are ever consulted: a sub-agent's request is opened by the root.
|
|
5379
|
+
*/
|
|
5380
|
+
this.interceptors = [];
|
|
5381
|
+
}
|
|
5382
|
+
/** See `ChatDriver.addInteractionInterceptor`. Returns a remover. */
|
|
5383
|
+
addInterceptor(interceptor) {
|
|
5384
|
+
if (!interceptor.id || interceptor.id === "user") {
|
|
5385
|
+
throw new Error(
|
|
5386
|
+
`addInteractionInterceptor: an interceptor needs an id other than 'user' (got "${interceptor.id}").`
|
|
5387
|
+
);
|
|
5388
|
+
}
|
|
5389
|
+
this.interceptors.push(interceptor);
|
|
5390
|
+
return () => {
|
|
5391
|
+
const index = this.interceptors.indexOf(interceptor);
|
|
5392
|
+
if (index >= 0) this.interceptors.splice(index, 1);
|
|
5393
|
+
};
|
|
5394
|
+
}
|
|
5395
|
+
/**
|
|
5396
|
+
* Offer a new interaction to the interceptors and record who ends up holding it.
|
|
5397
|
+
*
|
|
5398
|
+
* **Outermost first.** A decorator installs its interceptor on the driver it wraps when it is
|
|
5399
|
+
* constructed, and decorators are constructed innermost first, so registration order is
|
|
5400
|
+
* innermost first and the walk runs it backwards. The first claim wins; later interceptors still
|
|
5401
|
+
* see the interaction but their `hold` does nothing. A claim on a `userOnly` interaction is
|
|
5402
|
+
* refused.
|
|
5403
|
+
*
|
|
5404
|
+
* Runs before the interaction's message is appended, so the widget's first render already knows
|
|
5405
|
+
* its holder and nothing appears only to disappear.
|
|
5406
|
+
*/
|
|
5407
|
+
offer(handle, opened) {
|
|
5408
|
+
let claimant;
|
|
5409
|
+
for (const interceptor of [...this.interceptors].reverse()) {
|
|
5410
|
+
const hold = this.holdFor(handle, interceptor, () => claimant === interceptor);
|
|
5411
|
+
let answer;
|
|
5412
|
+
try {
|
|
5413
|
+
answer = interceptor.onInteractionOpened({ ...opened, ctx: handle.context, hold });
|
|
5414
|
+
} catch (e) {
|
|
5415
|
+
logger2.warn(`InteractionController: interceptor "${interceptor.id}" threw:`, e);
|
|
5416
|
+
continue;
|
|
5417
|
+
}
|
|
5418
|
+
if (answer !== "claim" || claimant) continue;
|
|
5419
|
+
if (opened.userOnly) {
|
|
5420
|
+
recordMetaEvent(this.deps.sessionKey, "interaction.claim-refused", {
|
|
5421
|
+
interactionId: opened.interactionId,
|
|
5422
|
+
claimant: interceptor.id,
|
|
5423
|
+
reason: "userOnly"
|
|
5424
|
+
});
|
|
5425
|
+
continue;
|
|
5426
|
+
}
|
|
5427
|
+
claimant = interceptor;
|
|
5428
|
+
handle.setHeldBy(interceptor.id);
|
|
5429
|
+
const pending = this.pending.get(opened.interactionId);
|
|
5430
|
+
if (pending) pending.claimant = interceptor;
|
|
5431
|
+
}
|
|
5432
|
+
return handle.context.heldBy;
|
|
5433
|
+
}
|
|
5434
|
+
/** The {@link InteractionHold} for one interceptor: it applies only while that one is the claimant. */
|
|
5435
|
+
holdFor(handle, interceptor, isClaimant) {
|
|
5436
|
+
return {
|
|
5437
|
+
set: (holder) => {
|
|
5438
|
+
if (!isClaimant()) return false;
|
|
5439
|
+
const next = holder === "user" ? "user" : interceptor.id;
|
|
5440
|
+
if (!handle.setHeldBy(next)) return false;
|
|
5441
|
+
recordMetaEvent(this.deps.sessionKey, "interaction.held", {
|
|
5442
|
+
interactionId: handle.context.interactionId,
|
|
5443
|
+
heldBy: next,
|
|
5444
|
+
claimant: interceptor.id
|
|
5445
|
+
});
|
|
5446
|
+
return true;
|
|
5447
|
+
},
|
|
5448
|
+
resolve: (result) => {
|
|
5449
|
+
const { interactionId } = handle.context;
|
|
5450
|
+
const pending = this.pending.get(interactionId);
|
|
5451
|
+
if (!isClaimant() || !pending || handle.context.heldBy !== interceptor.id) return false;
|
|
5452
|
+
pending.settledBy = interceptor.id;
|
|
5453
|
+
this.resolve(interactionId, result);
|
|
5454
|
+
return true;
|
|
5455
|
+
},
|
|
5456
|
+
recordDecision: (decision) => {
|
|
5457
|
+
if (!isClaimant()) return false;
|
|
5458
|
+
const pending = this.pending.get(handle.context.interactionId);
|
|
5459
|
+
if (!pending) return false;
|
|
5460
|
+
pending.decision = { choice: decision.choice, shownToUser: decision.shownToUser };
|
|
5461
|
+
return true;
|
|
5462
|
+
}
|
|
5463
|
+
};
|
|
5333
5464
|
}
|
|
5334
5465
|
/** See `ChatDriver.setHostInteractionRequester`. */
|
|
5335
5466
|
setHostRequester(fn, recordOutcome) {
|
|
@@ -5339,10 +5470,14 @@ var InteractionController = class {
|
|
|
5339
5470
|
/**
|
|
5340
5471
|
* See `ChatDriver.requestInteraction`. `onOpened` is told the id of the interaction this call
|
|
5341
5472
|
* opens, wherever it opens it, so the caller can later record its outcome and no other's.
|
|
5473
|
+
* `innerPath` is the owner path a sub-agent's request has gathered on its way up, and
|
|
5474
|
+
* `onSettled` is told who answered, wherever it resolves (GENC-1660).
|
|
5342
5475
|
*/
|
|
5343
|
-
request(componentName, data, options, onOpened) {
|
|
5476
|
+
request(componentName, data, options, onOpened, innerPath = [], onSettled) {
|
|
5477
|
+
const agent = this.deps.activeAgentName();
|
|
5478
|
+
const ownerPath = agent ? [agent, ...innerPath] : [...innerPath];
|
|
5344
5479
|
if (this.hostRequester) {
|
|
5345
|
-
return this.hostRequester(componentName, data, options, onOpened);
|
|
5480
|
+
return this.hostRequester(componentName, data, options, onOpened, ownerPath, onSettled);
|
|
5346
5481
|
}
|
|
5347
5482
|
if (this.pending.size > 0) {
|
|
5348
5483
|
throw new Error(
|
|
@@ -5358,6 +5493,7 @@ var InteractionController = class {
|
|
|
5358
5493
|
this.pending.set(interactionId, {
|
|
5359
5494
|
resolve,
|
|
5360
5495
|
reject,
|
|
5496
|
+
onSettled,
|
|
5361
5497
|
overrideId: chatInputDuringExecution ? interactionId : void 0,
|
|
5362
5498
|
// Resolve the interaction with a timeout result if the user does not
|
|
5363
5499
|
// respond in time. `resolve` runs the same teardown a normal resolution
|
|
@@ -5370,10 +5506,22 @@ var InteractionController = class {
|
|
|
5370
5506
|
});
|
|
5371
5507
|
}, timeoutMs) : void 0
|
|
5372
5508
|
});
|
|
5509
|
+
const handle = createInteractionContext(interactionId);
|
|
5510
|
+
this.contexts.set(interactionId, handle);
|
|
5511
|
+
const heldBy = this.offer(handle, {
|
|
5512
|
+
interactionId,
|
|
5513
|
+
componentName,
|
|
5514
|
+
data,
|
|
5515
|
+
ownerPath,
|
|
5516
|
+
userOnly: options?.userOnly === true
|
|
5517
|
+
});
|
|
5373
5518
|
recordMetaEvent(this.deps.sessionKey, "interaction.requested", {
|
|
5374
5519
|
interactionId,
|
|
5375
5520
|
component: componentName,
|
|
5376
|
-
agent: this.deps.activeAgentName()
|
|
5521
|
+
agent: this.deps.activeAgentName(),
|
|
5522
|
+
ownerPath,
|
|
5523
|
+
heldBy,
|
|
5524
|
+
...options?.userOnly ? { userOnly: true } : {}
|
|
5377
5525
|
});
|
|
5378
5526
|
this.deps.activityBus.publish("interaction-requested", void 0);
|
|
5379
5527
|
if (chatInputDuringExecution) {
|
|
@@ -5383,7 +5531,6 @@ var InteractionController = class {
|
|
|
5383
5531
|
})
|
|
5384
5532
|
);
|
|
5385
5533
|
}
|
|
5386
|
-
this.contexts.set(interactionId, createInteractionContext(interactionId));
|
|
5387
5534
|
this.deps.appendToHistory({
|
|
5388
5535
|
role: "assistant",
|
|
5389
5536
|
content: "",
|
|
@@ -5394,6 +5541,11 @@ var InteractionController = class {
|
|
|
5394
5541
|
...presentation ? { presentation } : {}
|
|
5395
5542
|
}
|
|
5396
5543
|
});
|
|
5544
|
+
const opened = this.pending.get(interactionId);
|
|
5545
|
+
if (opened) {
|
|
5546
|
+
opened.appended = true;
|
|
5547
|
+
if (opened.early) this.resolve(interactionId, opened.early.result);
|
|
5548
|
+
}
|
|
5397
5549
|
const signal = options?.signal;
|
|
5398
5550
|
if (signal) {
|
|
5399
5551
|
const onAbort = () => this.resolve(interactionId, STOPPED);
|
|
@@ -5419,6 +5571,10 @@ var InteractionController = class {
|
|
|
5419
5571
|
logger2.warn(`Interaction with ID ${interactionId} not found.`);
|
|
5420
5572
|
return;
|
|
5421
5573
|
}
|
|
5574
|
+
if (!interaction.appended) {
|
|
5575
|
+
interaction.early ??= { result };
|
|
5576
|
+
return;
|
|
5577
|
+
}
|
|
5422
5578
|
if (interaction.timeoutHandle) clearTimeout(interaction.timeoutHandle);
|
|
5423
5579
|
interaction.unlinkSignal?.();
|
|
5424
5580
|
this.deps.foldDiagnostics(
|
|
@@ -5426,20 +5582,50 @@ var InteractionController = class {
|
|
|
5426
5582
|
result?.diagnostics
|
|
5427
5583
|
);
|
|
5428
5584
|
const costUsd = typeof result?.costUsd === "number" && Number.isFinite(result.costUsd) && result.costUsd > 0 ? result.costUsd : void 0;
|
|
5585
|
+
const unanswered = result?.status === "timeout";
|
|
5586
|
+
const answeredBy = unanswered ? void 0 : interaction.settledBy ?? "user";
|
|
5587
|
+
const decision = answeredBy && answeredBy !== "user" ? {
|
|
5588
|
+
framing: interaction.claimant?.decisionFraming ?? DEFAULT_DECISION_FRAMING,
|
|
5589
|
+
...interaction.decision?.choice !== void 0 && {
|
|
5590
|
+
choice: interaction.decision.choice
|
|
5591
|
+
},
|
|
5592
|
+
...interaction.decision?.shownToUser !== void 0 && {
|
|
5593
|
+
shownToUser: interaction.decision.shownToUser
|
|
5594
|
+
}
|
|
5595
|
+
} : void 0;
|
|
5429
5596
|
const patched = this.deps.patchInteractionMessage(interactionId, (message) => ({
|
|
5430
5597
|
...message,
|
|
5431
|
-
interaction: {
|
|
5598
|
+
interaction: {
|
|
5599
|
+
...message.interaction,
|
|
5600
|
+
resolved: result,
|
|
5601
|
+
...answeredBy ? { answeredBy } : {}
|
|
5602
|
+
},
|
|
5432
5603
|
...costUsd != null ? { externalCostUsd: (message.externalCostUsd ?? 0) + costUsd } : {}
|
|
5433
5604
|
}));
|
|
5434
5605
|
if (patched) this.deps.notifyHistoryUpdated();
|
|
5435
5606
|
if (typeof result?.userMessage === "string" && result.userMessage.trim()) {
|
|
5436
|
-
this.deps.appendToHistory(
|
|
5607
|
+
this.deps.appendToHistory(
|
|
5608
|
+
answeredBy && answeredBy !== "user" ? {
|
|
5609
|
+
role: "assistant",
|
|
5610
|
+
content: result.userMessage,
|
|
5611
|
+
delegate: answeredBy,
|
|
5612
|
+
excludeFromProvider: true
|
|
5613
|
+
} : { role: "synthetic-user", content: result.userMessage }
|
|
5614
|
+
);
|
|
5437
5615
|
}
|
|
5438
5616
|
if (interaction.overrideId) {
|
|
5439
5617
|
this.deps.emit(new CustomEvent("interaction-stop", { detail: { interactionId } }));
|
|
5440
5618
|
}
|
|
5441
|
-
recordMetaEvent(this.deps.sessionKey, "interaction.resolved", {
|
|
5619
|
+
recordMetaEvent(this.deps.sessionKey, "interaction.resolved", {
|
|
5620
|
+
interactionId,
|
|
5621
|
+
...answeredBy ? { answeredBy } : {}
|
|
5622
|
+
});
|
|
5442
5623
|
this.deps.activityBus.publish("interaction-resolved", void 0);
|
|
5624
|
+
try {
|
|
5625
|
+
interaction.onSettled?.({ answeredBy, decision });
|
|
5626
|
+
} catch (e) {
|
|
5627
|
+
logger2.warn("InteractionController: onSettled threw:", e);
|
|
5628
|
+
}
|
|
5443
5629
|
interaction.resolve(result);
|
|
5444
5630
|
this.pending.delete(interactionId);
|
|
5445
5631
|
this.contexts.get(interactionId)?.dispose();
|
|
@@ -5838,6 +6024,18 @@ var budgetDetailOf = (e) => {
|
|
|
5838
6024
|
...e.otherVendorAvailable != null ? { otherVendorAvailable: e.otherVendorAvailable } : {}
|
|
5839
6025
|
};
|
|
5840
6026
|
};
|
|
6027
|
+
function withDelegatedDecisions(result, content, decisions, hasImages) {
|
|
6028
|
+
if (decisions.length === 0) return content;
|
|
6029
|
+
if (!hasImages && result !== null && typeof result === "object" && !Array.isArray(result)) {
|
|
6030
|
+
return JSON.stringify({ ...result, decidedOnUsersBehalf: decisions });
|
|
6031
|
+
}
|
|
6032
|
+
return [content, ...decisions.map(describeDelegatedDecision)].join("\n\n");
|
|
6033
|
+
}
|
|
6034
|
+
function describeDelegatedDecision(decision) {
|
|
6035
|
+
const choice = decision.choice !== void 0 ? `: "${decision.choice}"` : "";
|
|
6036
|
+
const shown = decision.shownToUser ? ` What the user was shown, as your words: "${decision.shownToUser}"` : " Nothing was shown to the user about it.";
|
|
6037
|
+
return `${decision.framing}${choice}.${shown}`;
|
|
6038
|
+
}
|
|
5841
6039
|
function inFlightToolCallsAt(history) {
|
|
5842
6040
|
for (let i = history.length - 1; i >= 0; i -= 1) {
|
|
5843
6041
|
const message = history[i];
|
|
@@ -6003,6 +6201,8 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
6003
6201
|
* on that one was reset by every handoff inside a single message.
|
|
6004
6202
|
*/
|
|
6005
6203
|
this.messageController = new AbortController();
|
|
6204
|
+
/** Capabilities provided on this driver by decorators (GENC-1660). Only the root's are used. */
|
|
6205
|
+
this.handlerCapabilities = /* @__PURE__ */ new Map();
|
|
6006
6206
|
/**
|
|
6007
6207
|
* Caches validated provider lookups per name within the current agent. Cleared
|
|
6008
6208
|
* by `installAgent` so each new agent's static/function-resolved names are
|
|
@@ -6026,9 +6226,17 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
6026
6226
|
* the point: two would let `getActiveAgent()` disagree between the outer and inner driver.
|
|
6027
6227
|
* `@internal` rather than private so that reach is legal without six delegating one-liners.
|
|
6028
6228
|
*
|
|
6229
|
+
* **This driver raises the lifecycle events for every transition of the session**, whoever made
|
|
6230
|
+
* it: itself, through {@link ChatDriver.activateAgent} or a release, or the orchestrator reaching
|
|
6231
|
+
* through (GENC-1485). Before, only the orchestrator dispatched them, so a host on a bare
|
|
6232
|
+
* `ChatDriver` never learned its flow owner.
|
|
6233
|
+
*
|
|
6029
6234
|
* @internal
|
|
6030
6235
|
*/
|
|
6031
|
-
this.agentSession = new AgentSession({
|
|
6236
|
+
this.agentSession = new AgentSession({
|
|
6237
|
+
sessionKey: () => this.sessionKey,
|
|
6238
|
+
onTransition: (t) => this.dispatchAgentTransition(t)
|
|
6239
|
+
});
|
|
6032
6240
|
/**
|
|
6033
6241
|
* A provider refusal was observed this turn, so the tool loop must not call the model again
|
|
6034
6242
|
* (GENC-1506).
|
|
@@ -6537,6 +6745,51 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
6537
6745
|
await this.agentSession.activate(config);
|
|
6538
6746
|
this.installAgent(config);
|
|
6539
6747
|
}
|
|
6748
|
+
/**
|
|
6749
|
+
* {@inheritDoc AiDriver.resumeRestoredFlow}
|
|
6750
|
+
*
|
|
6751
|
+
* This driver holds no list of agents, so it resumes only the `agent` it is handed (GENC-1660):
|
|
6752
|
+
* whoever runs a bare `ChatDriver` knows which agent that is, and after a reload only the name
|
|
6753
|
+
* survived. Without one it does nothing, as a driver without this member did before; given one
|
|
6754
|
+
* that is not the restored flow owner, it refuses.
|
|
6755
|
+
*
|
|
6756
|
+
* For headless callers: the shipped host restores and resumes through `OrchestratingDriver`,
|
|
6757
|
+
* and a bare `ChatDriver` does not answer `canRestoreFlowOwner`, so the host never plans a resume
|
|
6758
|
+
* for one.
|
|
6759
|
+
*
|
|
6760
|
+
* The orchestrator does not delegate here, because it installs a decorated config of its own;
|
|
6761
|
+
* this is the bare-driver path.
|
|
6762
|
+
*
|
|
6763
|
+
* @beta
|
|
6764
|
+
*/
|
|
6765
|
+
async resumeRestoredFlow(resumeNote, agent) {
|
|
6766
|
+
if (!agent) return void 0;
|
|
6767
|
+
const owner = this.agentSession.getFlowOwner();
|
|
6768
|
+
if (owner !== null && owner !== agent.name) {
|
|
6769
|
+
logger2.warn(
|
|
6770
|
+
`ChatDriver: resumeRestoredFlow was handed "${agent.name}", but the flow belongs to "${owner}" \u2014 not resuming.`
|
|
6771
|
+
);
|
|
6772
|
+
return void 0;
|
|
6773
|
+
}
|
|
6774
|
+
await this.activateAgent(agent);
|
|
6775
|
+
if (!resumeNote || agent.resumeOnRestore === false) return void 0;
|
|
6776
|
+
return this.continueFromHistory(void 0, { tailNote: resumeNote });
|
|
6777
|
+
}
|
|
6778
|
+
/**
|
|
6779
|
+
* Raise the host-facing lifecycle events for one session transition (GENC-1485), in the order
|
|
6780
|
+
* the orchestrator raised them before it stopped doing so itself: the flow lock first, then the
|
|
6781
|
+
* release, then the active agent.
|
|
6782
|
+
*/
|
|
6783
|
+
dispatchAgentTransition(t) {
|
|
6784
|
+
if (t.flowOwnerChangedTo !== void 0) {
|
|
6785
|
+
this.dispatchEvent(new CustomEvent("flow-owner-changed", { detail: t.flowOwnerChangedTo }));
|
|
6786
|
+
}
|
|
6787
|
+
const active = this.agentSession.getActive();
|
|
6788
|
+
if (!active && t.previousAgent) {
|
|
6789
|
+
this.dispatchEvent(new CustomEvent("agent-released", { detail: t.previousAgent }));
|
|
6790
|
+
}
|
|
6791
|
+
this.dispatchEvent(new CustomEvent("agent-changed", { detail: active }));
|
|
6792
|
+
}
|
|
6540
6793
|
/**
|
|
6541
6794
|
* {@inheritDoc AiDriver.getActiveAgent}
|
|
6542
6795
|
*
|
|
@@ -6572,6 +6825,14 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
6572
6825
|
setFlowOwner(name) {
|
|
6573
6826
|
this.agentSession.setFlowOwner(name);
|
|
6574
6827
|
}
|
|
6828
|
+
/**
|
|
6829
|
+
* {@inheritDoc AiDriver.getFlowOwner}
|
|
6830
|
+
*
|
|
6831
|
+
* @beta
|
|
6832
|
+
*/
|
|
6833
|
+
getFlowOwner() {
|
|
6834
|
+
return this.agentSession.getFlowOwner();
|
|
6835
|
+
}
|
|
6575
6836
|
/**
|
|
6576
6837
|
* Install `config` as the active agent **without firing lifecycle hooks**.
|
|
6577
6838
|
*
|
|
@@ -7145,8 +7406,84 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7145
7406
|
* and `presentation` to control whether the host wraps the widget in a chat
|
|
7146
7407
|
* bubble and/or shows the "Assistant" label.
|
|
7147
7408
|
*/
|
|
7148
|
-
async requestInteraction(componentName, data, options, onOpened) {
|
|
7149
|
-
return this.interactions.request(
|
|
7409
|
+
async requestInteraction(componentName, data, options, onOpened, innerPath, onSettled) {
|
|
7410
|
+
return this.interactions.request(
|
|
7411
|
+
componentName,
|
|
7412
|
+
data,
|
|
7413
|
+
options,
|
|
7414
|
+
onOpened,
|
|
7415
|
+
innerPath,
|
|
7416
|
+
onSettled
|
|
7417
|
+
);
|
|
7418
|
+
}
|
|
7419
|
+
/**
|
|
7420
|
+
* {@inheritDoc AiDriver.addInteractionInterceptor}
|
|
7421
|
+
*
|
|
7422
|
+
* Consulted only on the root driver, where every interaction opens: a sub-agent's request
|
|
7423
|
+
* travels up to its root, carrying its owner path, and is offered there once.
|
|
7424
|
+
*
|
|
7425
|
+
* @beta
|
|
7426
|
+
*/
|
|
7427
|
+
addInteractionInterceptor(interceptor) {
|
|
7428
|
+
return this.interactions.addInterceptor(interceptor);
|
|
7429
|
+
}
|
|
7430
|
+
/**
|
|
7431
|
+
* {@inheritDoc AiDriver.presentInteraction}
|
|
7432
|
+
*
|
|
7433
|
+
* @beta
|
|
7434
|
+
*/
|
|
7435
|
+
presentInteraction(componentName, data, options) {
|
|
7436
|
+
return this.requestInteraction(componentName, data, options);
|
|
7437
|
+
}
|
|
7438
|
+
/**
|
|
7439
|
+
* {@inheritDoc AiDriver.provideHandlerCapability}
|
|
7440
|
+
*
|
|
7441
|
+
* Throws if `name` is already provided: two decorators answering to one name would leave a
|
|
7442
|
+
* handler unable to tell which it is talking to.
|
|
7443
|
+
*
|
|
7444
|
+
* @beta
|
|
7445
|
+
*/
|
|
7446
|
+
provideHandlerCapability(name, factory) {
|
|
7447
|
+
if (this.handlerCapabilities.has(name)) {
|
|
7448
|
+
throw new Error(`provideHandlerCapability: "${name}" is already provided.`);
|
|
7449
|
+
}
|
|
7450
|
+
this.handlerCapabilities.set(name, factory);
|
|
7451
|
+
return () => {
|
|
7452
|
+
if (this.handlerCapabilities.get(name) === factory) this.handlerCapabilities.delete(name);
|
|
7453
|
+
};
|
|
7454
|
+
}
|
|
7455
|
+
/**
|
|
7456
|
+
* The capabilities this driver's handlers see, and who they see them as: the root's factories,
|
|
7457
|
+
* the root's flow owner, and the owner path from the root down to this driver's agent.
|
|
7458
|
+
*/
|
|
7459
|
+
capabilityScope() {
|
|
7460
|
+
const own = this.activeAgentName ? [this.activeAgentName] : [];
|
|
7461
|
+
if (!this.capabilityParent) {
|
|
7462
|
+
return {
|
|
7463
|
+
factories: this.handlerCapabilities,
|
|
7464
|
+
scope: { ownerPath: own, flowOwner: this.agentSession.getFlowOwner() }
|
|
7465
|
+
};
|
|
7466
|
+
}
|
|
7467
|
+
const up = this.capabilityParent.capabilityScope();
|
|
7468
|
+
return {
|
|
7469
|
+
factories: up.factories,
|
|
7470
|
+
scope: { ownerPath: [...up.scope.ownerPath, ...own], flowOwner: up.scope.flowOwner }
|
|
7471
|
+
};
|
|
7472
|
+
}
|
|
7473
|
+
/** Build `context.capabilities` for one handler call, or `undefined` when nothing is provided. */
|
|
7474
|
+
buildCapabilities() {
|
|
7475
|
+
const { factories, scope } = this.capabilityScope();
|
|
7476
|
+
if (factories.size === 0) return void 0;
|
|
7477
|
+
const capabilities2 = {};
|
|
7478
|
+
for (const [name, factory] of factories) {
|
|
7479
|
+
try {
|
|
7480
|
+
const value = factory(scope);
|
|
7481
|
+
if (value !== void 0) capabilities2[name] = value;
|
|
7482
|
+
} catch (e) {
|
|
7483
|
+
logger2.warn(`ChatDriver: handler capability "${name}" threw \u2014 leaving it out:`, e);
|
|
7484
|
+
}
|
|
7485
|
+
}
|
|
7486
|
+
return capabilities2;
|
|
7150
7487
|
}
|
|
7151
7488
|
/**
|
|
7152
7489
|
* Record what came of an interaction, after it resolved, on its history message
|
|
@@ -7218,8 +7555,10 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7218
7555
|
this.history = [...messages];
|
|
7219
7556
|
this.notifyHistoryUpdated();
|
|
7220
7557
|
}
|
|
7221
|
-
async sendMessage(userInput, attachments) {
|
|
7558
|
+
async sendMessage(userInput, attachments, options) {
|
|
7222
7559
|
if (this.busy || !userInput.trim() && !attachments?.length) return { reason: "done" };
|
|
7560
|
+
const delegate = options?.delegate;
|
|
7561
|
+
const delegateFraming = delegate ? options?.delegateFraming ?? DEFAULT_DELEGATE_FRAMING : void 0;
|
|
7223
7562
|
this.busy = true;
|
|
7224
7563
|
this.beginTurn();
|
|
7225
7564
|
this.beginMessage();
|
|
@@ -7236,7 +7575,12 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7236
7575
|
this.aiServiceWall = void 0;
|
|
7237
7576
|
this.aiServiceNoticesShown.clear();
|
|
7238
7577
|
this.aiServiceNoticesQueued = [];
|
|
7239
|
-
this.appendToHistory({
|
|
7578
|
+
this.appendToHistory({
|
|
7579
|
+
role: "user",
|
|
7580
|
+
content: userInput,
|
|
7581
|
+
attachments,
|
|
7582
|
+
...delegate ? { delegate, delegateFraming } : {}
|
|
7583
|
+
});
|
|
7240
7584
|
this.turnStartedAt = Date.now();
|
|
7241
7585
|
recordMetaEvent(this.sessionKey, "turn.start", {
|
|
7242
7586
|
phase: "sendMessage",
|
|
@@ -7245,7 +7589,10 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7245
7589
|
this.activityBus.publish("tool-loop-start", void 0);
|
|
7246
7590
|
let result = { reason: "done" };
|
|
7247
7591
|
try {
|
|
7248
|
-
result = await this.runToolLoop(
|
|
7592
|
+
result = await this.runToolLoop(
|
|
7593
|
+
delegate ? frameDelegatedContent(userInput, delegateFraming) : userInput,
|
|
7594
|
+
attachments
|
|
7595
|
+
);
|
|
7249
7596
|
return result;
|
|
7250
7597
|
} catch (e) {
|
|
7251
7598
|
logger2.error("ChatDriver error:", e);
|
|
@@ -7298,13 +7645,25 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7298
7645
|
* kept on a seven-way fan-out, reporting ~2.3x under the real cost, with the
|
|
7299
7646
|
* error growing as the fan-out widens.
|
|
7300
7647
|
*/
|
|
7301
|
-
buildHandlerContext(activeToolCallId, traceCapture) {
|
|
7648
|
+
buildHandlerContext(activeToolCallId, traceCapture, decisions) {
|
|
7302
7649
|
let opened;
|
|
7303
7650
|
let recorded = false;
|
|
7651
|
+
const capabilities2 = this.buildCapabilities();
|
|
7304
7652
|
return {
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7653
|
+
...capabilities2 && { capabilities: capabilities2 },
|
|
7654
|
+
requestInteraction: (componentName, data, options) => this.requestInteraction(
|
|
7655
|
+
componentName,
|
|
7656
|
+
data,
|
|
7657
|
+
options,
|
|
7658
|
+
(interactionId) => {
|
|
7659
|
+
opened = interactionId;
|
|
7660
|
+
},
|
|
7661
|
+
void 0,
|
|
7662
|
+
// An interaction an interceptor answered is reported in THIS call's result (GENC-1660).
|
|
7663
|
+
(settled) => {
|
|
7664
|
+
if (settled.decision) decisions?.push(settled.decision);
|
|
7665
|
+
}
|
|
7666
|
+
),
|
|
7308
7667
|
recordInteractionOutcome: (outcome) => {
|
|
7309
7668
|
if (!opened) return false;
|
|
7310
7669
|
if (recorded) {
|
|
@@ -7423,11 +7782,12 @@ var ChatDriver = class _ChatDriver extends EventTarget {
|
|
|
7423
7782
|
}
|
|
7424
7783
|
child.installAgent({ ...subConfig, primerHistory: effectivePrimer });
|
|
7425
7784
|
child.setHostInteractionRequester(
|
|
7426
|
-
(componentName, data, opts, onOpened) => this.requestInteraction(componentName, data, opts, onOpened),
|
|
7785
|
+
(componentName, data, opts, onOpened, ownerPath, onSettled) => this.requestInteraction(componentName, data, opts, onOpened, ownerPath, onSettled),
|
|
7427
7786
|
// An outcome goes where its interaction went.
|
|
7428
7787
|
(interactionId, outcome) => this.recordInteractionOutcome(interactionId, outcome)
|
|
7429
7788
|
);
|
|
7430
7789
|
child.hostAIServiceNotice = (code) => this.noticeAIServiceRefusal(code);
|
|
7790
|
+
child.capabilityParent = this;
|
|
7431
7791
|
const forwardTrace = (e) => {
|
|
7432
7792
|
this.dispatchEvent(
|
|
7433
7793
|
new CustomEvent("sub-agent-history-updated", {
|
|
@@ -8138,10 +8498,19 @@ ${tailBody}
|
|
|
8138
8498
|
}
|
|
8139
8499
|
const traceCapture = { traces: [] };
|
|
8140
8500
|
const capturedTrace = () => traceCapture.traces.length ? traceCapture.traces.flat() : void 0;
|
|
8501
|
+
const decisions = [];
|
|
8141
8502
|
try {
|
|
8142
|
-
const result = await handler(
|
|
8503
|
+
const result = await handler(
|
|
8504
|
+
tc.args,
|
|
8505
|
+
this.buildHandlerContext(tc.id, traceCapture, decisions)
|
|
8506
|
+
);
|
|
8143
8507
|
const resultImages = toolResultImages(result);
|
|
8144
|
-
const content =
|
|
8508
|
+
const content = withDelegatedDecisions(
|
|
8509
|
+
result,
|
|
8510
|
+
resultImages ? typeof result.content === "string" ? result.content : JSON.stringify({ ...result, attachments: void 0 }) : typeof result === "string" ? result : JSON.stringify(result),
|
|
8511
|
+
decisions,
|
|
8512
|
+
!!resultImages
|
|
8513
|
+
);
|
|
8145
8514
|
executedById.set(tc.id, {
|
|
8146
8515
|
toolCallId: tc.id,
|
|
8147
8516
|
content,
|
|
@@ -8341,6 +8710,189 @@ RECOVERY: this tool failed once \u2014 you may retry it, or take a different val
|
|
|
8341
8710
|
}
|
|
8342
8711
|
};
|
|
8343
8712
|
|
|
8713
|
+
// src/components/ai-driver/ai-driver.ts
|
|
8714
|
+
var AI_DRIVER_EVENT_TYPES = Object.keys({
|
|
8715
|
+
"history-updated": true,
|
|
8716
|
+
"sub-agent-history-updated": true,
|
|
8717
|
+
"sub-agent-start": true,
|
|
8718
|
+
"sub-agent-stop": true,
|
|
8719
|
+
"interaction-start": true,
|
|
8720
|
+
"interaction-stop": true,
|
|
8721
|
+
"provider-changed": true,
|
|
8722
|
+
"external-cost-recorded": true,
|
|
8723
|
+
"agent-changed": true,
|
|
8724
|
+
"agent-released": true,
|
|
8725
|
+
"flow-owner-changed": true,
|
|
8726
|
+
"orchestrating-start": true,
|
|
8727
|
+
"orchestrating-stop": true
|
|
8728
|
+
});
|
|
8729
|
+
|
|
8730
|
+
// src/components/ai-driver/base-driver-decorator.ts
|
|
8731
|
+
var BaseDriverDecorator = class _BaseDriverDecorator extends EventTarget {
|
|
8732
|
+
constructor(inner) {
|
|
8733
|
+
super();
|
|
8734
|
+
this.inner = inner;
|
|
8735
|
+
/** Undoes this layer's hold on its inner driver; see {@link BaseDriverDecorator.detach}. */
|
|
8736
|
+
this.detachers = [];
|
|
8737
|
+
for (const type of AI_DRIVER_EVENT_TYPES) {
|
|
8738
|
+
const forward = (e) => {
|
|
8739
|
+
this.dispatchEvent(new CustomEvent(type, { detail: e.detail }));
|
|
8740
|
+
};
|
|
8741
|
+
inner.addEventListener(type, forward);
|
|
8742
|
+
this.onDetach(() => inner.removeEventListener(type, forward));
|
|
8743
|
+
}
|
|
8744
|
+
}
|
|
8745
|
+
/**
|
|
8746
|
+
* Let go of the inner driver WITHOUT disposing it: stop forwarding its events, and undo whatever
|
|
8747
|
+
* a subclass registered through {@link BaseDriverDecorator.onDetach} (its interceptors, its
|
|
8748
|
+
* capabilities, its own listeners). For dropping or replacing a decorator while the driver it
|
|
8749
|
+
* wrapped lives on — otherwise the old layer keeps re-dispatching every event and is never
|
|
8750
|
+
* collected. Idempotent. `dispose()` detaches too.
|
|
8751
|
+
*/
|
|
8752
|
+
detach() {
|
|
8753
|
+
for (const undo of this.detachers.splice(0)) {
|
|
8754
|
+
try {
|
|
8755
|
+
undo();
|
|
8756
|
+
} catch {
|
|
8757
|
+
}
|
|
8758
|
+
}
|
|
8759
|
+
}
|
|
8760
|
+
/** Register something {@link BaseDriverDecorator.detach} must undo. */
|
|
8761
|
+
onDetach(undo) {
|
|
8762
|
+
this.detachers.push(undo);
|
|
8763
|
+
}
|
|
8764
|
+
sendMessage(input, attachments, options) {
|
|
8765
|
+
return this.inner.sendMessage(input, attachments, options);
|
|
8766
|
+
}
|
|
8767
|
+
continueFromHistory(transientPrimer, options) {
|
|
8768
|
+
return this.inner.continueFromHistory(transientPrimer, options);
|
|
8769
|
+
}
|
|
8770
|
+
resolveInteraction(interactionId, result) {
|
|
8771
|
+
this.inner.resolveInteraction(interactionId, result);
|
|
8772
|
+
}
|
|
8773
|
+
recordExternalCost(interactionId, costUsd) {
|
|
8774
|
+
return this.inner.recordExternalCost?.(interactionId, costUsd) ?? false;
|
|
8775
|
+
}
|
|
8776
|
+
getInteractionContext(interactionId) {
|
|
8777
|
+
return this.inner.getInteractionContext?.(interactionId);
|
|
8778
|
+
}
|
|
8779
|
+
loadHistory(messages) {
|
|
8780
|
+
this.inner.loadHistory(messages);
|
|
8781
|
+
}
|
|
8782
|
+
primeRestoredAgentState(snapshots) {
|
|
8783
|
+
this.inner.primeRestoredAgentState(snapshots);
|
|
8784
|
+
}
|
|
8785
|
+
canRestoreFlowOwner(agentName) {
|
|
8786
|
+
return this.inner.canRestoreFlowOwner?.(agentName) ?? false;
|
|
8787
|
+
}
|
|
8788
|
+
canResumeRestoredFlow(agentName) {
|
|
8789
|
+
return this.inner.canResumeRestoredFlow?.(agentName) ?? false;
|
|
8790
|
+
}
|
|
8791
|
+
resumeRestoredFlow(resumeNote, agent) {
|
|
8792
|
+
return this.inner.resumeRestoredFlow?.(resumeNote, agent) ?? Promise.resolve(void 0);
|
|
8793
|
+
}
|
|
8794
|
+
getRawHistory() {
|
|
8795
|
+
return this.inner.getRawHistory?.() ?? [];
|
|
8796
|
+
}
|
|
8797
|
+
getHistoryForPersistence() {
|
|
8798
|
+
return this.inner.getHistoryForPersistence?.() ?? [...this.getRawHistory()];
|
|
8799
|
+
}
|
|
8800
|
+
getCondensePoliciesForPersistence(messages) {
|
|
8801
|
+
return this.inner.getCondensePoliciesForPersistence?.(messages) ?? [];
|
|
8802
|
+
}
|
|
8803
|
+
restoreCondensePolicies(entries) {
|
|
8804
|
+
return this.inner.restoreCondensePolicies?.(entries) ?? { restored: 0, awaitingReactivation: 0 };
|
|
8805
|
+
}
|
|
8806
|
+
getActiveAgent() {
|
|
8807
|
+
return this.inner.getActiveAgent();
|
|
8808
|
+
}
|
|
8809
|
+
setPinnedAgent(name) {
|
|
8810
|
+
this.inner.setPinnedAgent(name);
|
|
8811
|
+
}
|
|
8812
|
+
setFlowOwner(name) {
|
|
8813
|
+
this.inner.setFlowOwner(name);
|
|
8814
|
+
}
|
|
8815
|
+
getFlowOwner() {
|
|
8816
|
+
return this.inner.getFlowOwner?.() ?? null;
|
|
8817
|
+
}
|
|
8818
|
+
/**
|
|
8819
|
+
* Forwarded, so an interceptor installed on any layer lands on the driver that opens the
|
|
8820
|
+
* interactions. A decorator that answers interactions installs its own on `this.inner`.
|
|
8821
|
+
*/
|
|
8822
|
+
addInteractionInterceptor(interceptor) {
|
|
8823
|
+
return this.inner.addInteractionInterceptor?.(interceptor) ?? (() => void 0);
|
|
8824
|
+
}
|
|
8825
|
+
presentInteraction(componentName, data, options) {
|
|
8826
|
+
return this.inner.presentInteraction?.(componentName, data, options) ?? Promise.reject(new Error("presentInteraction: the inner driver cannot show interactions."));
|
|
8827
|
+
}
|
|
8828
|
+
provideHandlerCapability(name, factory) {
|
|
8829
|
+
return this.inner.provideHandlerCapability?.(name, factory) ?? (() => void 0);
|
|
8830
|
+
}
|
|
8831
|
+
getContextReserve() {
|
|
8832
|
+
return this.inner.getContextReserve?.();
|
|
8833
|
+
}
|
|
8834
|
+
getPreambleTokens() {
|
|
8835
|
+
return this.inner.getPreambleTokens?.();
|
|
8836
|
+
}
|
|
8837
|
+
setContextGuard(policy) {
|
|
8838
|
+
this.inner.setContextGuard?.(policy);
|
|
8839
|
+
}
|
|
8840
|
+
compact(options) {
|
|
8841
|
+
return this.inner.compact?.(options) ?? Promise.resolve(null);
|
|
8842
|
+
}
|
|
8843
|
+
getCompactionPlan(options) {
|
|
8844
|
+
return this.inner.getCompactionPlan?.(options) ?? null;
|
|
8845
|
+
}
|
|
8846
|
+
canCompact(options) {
|
|
8847
|
+
return this.inner.canCompact?.(options) ?? false;
|
|
8848
|
+
}
|
|
8849
|
+
isBusy() {
|
|
8850
|
+
return this.inner.isBusy();
|
|
8851
|
+
}
|
|
8852
|
+
getSuggestions(history, prompt2, count, allAgentInfo) {
|
|
8853
|
+
return this.inner.getSuggestions(history, prompt2, count, allAgentInfo);
|
|
8854
|
+
}
|
|
8855
|
+
getTurnSnapshots() {
|
|
8856
|
+
return this.inner.getTurnSnapshots?.() ?? [];
|
|
8857
|
+
}
|
|
8858
|
+
/**
|
|
8859
|
+
* The inner driver's entries. Diagnostics compose down the stack: a decorator with entries of
|
|
8860
|
+
* its own overrides this and appends them to `super.getExternalDiagnostics()`.
|
|
8861
|
+
*/
|
|
8862
|
+
getExternalDiagnostics() {
|
|
8863
|
+
return this.inner.getExternalDiagnostics?.() ?? [];
|
|
8864
|
+
}
|
|
8865
|
+
getActiveProviderName() {
|
|
8866
|
+
return this.inner.getActiveProviderName();
|
|
8867
|
+
}
|
|
8868
|
+
dispose() {
|
|
8869
|
+
const disposed = this.inner.dispose?.();
|
|
8870
|
+
this.detach();
|
|
8871
|
+
return disposed;
|
|
8872
|
+
}
|
|
8873
|
+
cancel() {
|
|
8874
|
+
this.inner.cancel?.();
|
|
8875
|
+
}
|
|
8876
|
+
/**
|
|
8877
|
+
* Every driver in the stack, outermost first: `driver`, then each inner driver down to the one
|
|
8878
|
+
* that is not a decorator.
|
|
8879
|
+
*
|
|
8880
|
+
* For diagnostics and labelling only: behaviour must go through `AiDriver` members, which every
|
|
8881
|
+
* layer forwards, never through a concrete class found here.
|
|
8882
|
+
*
|
|
8883
|
+
* @internal
|
|
8884
|
+
*/
|
|
8885
|
+
static layers(driver) {
|
|
8886
|
+
const layers = [driver];
|
|
8887
|
+
let current = driver;
|
|
8888
|
+
while (current instanceof _BaseDriverDecorator) {
|
|
8889
|
+
current = current.inner;
|
|
8890
|
+
layers.push(current);
|
|
8891
|
+
}
|
|
8892
|
+
return layers;
|
|
8893
|
+
}
|
|
8894
|
+
};
|
|
8895
|
+
|
|
8344
8896
|
// src/components/orchestrating-driver/orchestrating-driver.ts
|
|
8345
8897
|
var DEFAULT_MAX_HANDOFFS = 3;
|
|
8346
8898
|
var DEFAULT_CLASSIFIER_HISTORY_LENGTH = 4;
|
|
@@ -8403,9 +8955,9 @@ Politely let the user know what you can help with and invite them to rephrase th
|
|
|
8403
8955
|
|
|
8404
8956
|
${UNROUTABLE_INSTRUCTION}`;
|
|
8405
8957
|
}
|
|
8406
|
-
var OrchestratingDriver = class extends
|
|
8958
|
+
var OrchestratingDriver = class _OrchestratingDriver extends BaseDriverDecorator {
|
|
8407
8959
|
constructor(providerRegistry, agents, options = {}) {
|
|
8408
|
-
super();
|
|
8960
|
+
super(_OrchestratingDriver.createInner(providerRegistry, agents, options));
|
|
8409
8961
|
this.providerRegistry = providerRegistry;
|
|
8410
8962
|
this.agents = agents;
|
|
8411
8963
|
/**
|
|
@@ -8429,7 +8981,6 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8429
8981
|
this.maxHandoffs = options.maxHandoffs ?? DEFAULT_MAX_HANDOFFS;
|
|
8430
8982
|
this.classifierHistoryLength = options.classifierHistoryLength ?? DEFAULT_CLASSIFIER_HISTORY_LENGTH;
|
|
8431
8983
|
this.classifierRetries = options.classifierRetries ?? DEFAULT_CLASSIFIER_RETRIES;
|
|
8432
|
-
validateStaticAgentProviders(agents, providerRegistry);
|
|
8433
8984
|
this.specialists = agents.filter(isSpecialist).filter((a) => !a.excludeFromClassifier);
|
|
8434
8985
|
const fallbacks = agents.filter(isFallback);
|
|
8435
8986
|
if (fallbacks.length > 1) {
|
|
@@ -8439,51 +8990,10 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8439
8990
|
}
|
|
8440
8991
|
const rawFallback = fallbacks[0];
|
|
8441
8992
|
this.fallback = rawFallback ? { ...rawFallback, systemPrompt: buildFallbackSystemPrompt(rawFallback, this.specialists) } : void 0;
|
|
8442
|
-
|
|
8443
|
-
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
sessionKey: this.sessionKey,
|
|
8447
|
-
activityBus: options.activityBus,
|
|
8448
|
-
budgetExhaustedMessage: options.budgetExhaustedMessage,
|
|
8449
|
-
aiServiceErrorMessages: options.aiServiceErrorMessages,
|
|
8450
|
-
// This driver releases after the turn so it can fire the host-facing events; see the option.
|
|
8451
|
-
deferAgentRelease: true
|
|
8452
|
-
});
|
|
8453
|
-
this.chatDriver.addEventListener("history-updated", (e) => {
|
|
8454
|
-
this.dispatchEvent(new CustomEvent("history-updated", { detail: e.detail }));
|
|
8455
|
-
});
|
|
8456
|
-
this.chatDriver.addEventListener("sub-agent-history-updated", (e) => {
|
|
8457
|
-
this.dispatchEvent(
|
|
8458
|
-
new CustomEvent("sub-agent-history-updated", { detail: e.detail })
|
|
8459
|
-
);
|
|
8460
|
-
});
|
|
8461
|
-
this.chatDriver.addEventListener("sub-agent-start", (e) => {
|
|
8462
|
-
this.dispatchEvent(new CustomEvent("sub-agent-start", { detail: e.detail }));
|
|
8463
|
-
});
|
|
8464
|
-
this.chatDriver.addEventListener("sub-agent-stop", (e) => {
|
|
8465
|
-
this.dispatchEvent(new CustomEvent("sub-agent-stop", { detail: e.detail }));
|
|
8466
|
-
});
|
|
8467
|
-
this.chatDriver.addEventListener("interaction-start", (e) => {
|
|
8468
|
-
this.dispatchEvent(
|
|
8469
|
-
new CustomEvent("interaction-start", { detail: e.detail })
|
|
8470
|
-
);
|
|
8471
|
-
});
|
|
8472
|
-
this.chatDriver.addEventListener("interaction-stop", (e) => {
|
|
8473
|
-
this.dispatchEvent(
|
|
8474
|
-
new CustomEvent("interaction-stop", { detail: e.detail })
|
|
8475
|
-
);
|
|
8476
|
-
});
|
|
8477
|
-
this.chatDriver.addEventListener("provider-changed", (e) => {
|
|
8478
|
-
this.dispatchEvent(
|
|
8479
|
-
new CustomEvent("provider-changed", { detail: e.detail })
|
|
8480
|
-
);
|
|
8481
|
-
});
|
|
8482
|
-
this.chatDriver.addEventListener("external-cost-recorded", (e) => {
|
|
8483
|
-
this.dispatchEvent(
|
|
8484
|
-
new CustomEvent("external-cost-recorded", { detail: e.detail })
|
|
8485
|
-
);
|
|
8486
|
-
});
|
|
8993
|
+
}
|
|
8994
|
+
/** The inner driver this one built and routes onto. */
|
|
8995
|
+
get chatDriver() {
|
|
8996
|
+
return this.inner;
|
|
8487
8997
|
}
|
|
8488
8998
|
/**
|
|
8489
8999
|
* The stateful-agent contract (§7.1): active agent, user pin, flow lock, restored state, and
|
|
@@ -8507,108 +9017,23 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8507
9017
|
get activeAgent() {
|
|
8508
9018
|
return this.agentSession.getActive();
|
|
8509
9019
|
}
|
|
8510
|
-
resolveInteraction(interactionId, result) {
|
|
8511
|
-
this.chatDriver.resolveInteraction(interactionId, result);
|
|
8512
|
-
}
|
|
8513
|
-
/** Delegated for the same reason as `resolveInteraction`: the history that owns the
|
|
8514
|
-
* interaction — and therefore the cost — lives on the wrapped driver, not here. */
|
|
8515
|
-
recordExternalCost(interactionId, costUsd) {
|
|
8516
|
-
return this.chatDriver.recordExternalCost(interactionId, costUsd);
|
|
8517
|
-
}
|
|
8518
|
-
getInteractionContext(interactionId) {
|
|
8519
|
-
return this.chatDriver.getInteractionContext(interactionId);
|
|
8520
|
-
}
|
|
8521
|
-
isBusy() {
|
|
8522
|
-
return this.chatDriver.isBusy();
|
|
8523
|
-
}
|
|
8524
|
-
/** Currently active provider name from the underlying ChatDriver. */
|
|
8525
|
-
getActiveProviderName() {
|
|
8526
|
-
return this.chatDriver.getActiveProviderName();
|
|
8527
|
-
}
|
|
8528
|
-
/**
|
|
8529
|
-
* Pins routing to a specific agent by name. While pinned, the classifier is
|
|
8530
|
-
* skipped and the continuation tool is hidden from the agent's tool list, so
|
|
8531
|
-
* the agent cannot quietly hand back to the orchestrator. Pass `null` to
|
|
8532
|
-
* return to automatic routing.
|
|
8533
|
-
*/
|
|
8534
|
-
setPinnedAgent(name) {
|
|
8535
|
-
this.agentSession.setPinned(name);
|
|
8536
|
-
}
|
|
8537
|
-
/**
|
|
8538
|
-
* Set the flow-owner lock — used to restore state from Redux on driver
|
|
8539
|
-
* reconstruction (agents-array changes, pop-in/pop-out). Not intended for
|
|
8540
|
-
* external callers to manipulate routing; that's what `setPinnedAgent` is
|
|
8541
|
-
* for.
|
|
8542
|
-
*/
|
|
8543
|
-
setFlowOwner(name) {
|
|
8544
|
-
this.agentSession.setFlowOwner(name);
|
|
8545
|
-
}
|
|
8546
|
-
loadHistory(messages) {
|
|
8547
|
-
this.chatDriver.loadHistory(messages);
|
|
8548
|
-
}
|
|
8549
|
-
getRawHistory() {
|
|
8550
|
-
return this.chatDriver.getHistory();
|
|
8551
|
-
}
|
|
8552
9020
|
/**
|
|
8553
|
-
*
|
|
8554
|
-
*
|
|
8555
|
-
*
|
|
8556
|
-
* **This forward is the whole feature, for any multi-agent host.** `AiDriver` declares
|
|
8557
|
-
* `getHistoryForPersistence` optional, and `SessionPersister` calls it through `?.`, falling back
|
|
8558
|
-
* to {@link OrchestratingDriver.getRawHistory}. This class does NOT extend `ChatDriver` — it
|
|
8559
|
-
* wraps one — so without an explicit forward the optional call resolves to `undefined` and every
|
|
8560
|
-
* host on an orchestrating driver silently keeps persisting the full payloads. Measured exactly
|
|
8561
|
-
* that way on a Create run: 10 `context.condensed` events fired, and the session still stored
|
|
8562
|
-
* the two largest `read_context` results at 26,907 and 23,060 chars. The method existed, the
|
|
8563
|
-
* tests passed, and the feature did nothing.
|
|
8564
|
-
*/
|
|
8565
|
-
getHistoryForPersistence() {
|
|
8566
|
-
return this.chatDriver.getHistoryForPersistence();
|
|
8567
|
-
}
|
|
8568
|
-
/**
|
|
8569
|
-
* Delegates to the inner {@link ChatDriver}, which owns the condensation registry and clocks
|
|
8570
|
-
* (GENC-1632). The forward matters for the same reason as `getHistoryForPersistence` above: the
|
|
8571
|
-
* persister reaches it through `?.`, so a missing forward would compile, pass the ChatDriver
|
|
8572
|
-
* tests, and do nothing for every orchestrated host — which is every host with an agents array.
|
|
9021
|
+
* Build the inner driver, after validating the agents' providers, so a bad config throws before
|
|
9022
|
+
* a driver exists to subscribe to the registry. Static because it runs before `super()`.
|
|
8573
9023
|
*/
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
this.chatDriver.setContextGuard(policy);
|
|
8588
|
-
}
|
|
8589
|
-
/** {@inheritDoc AiDriver.compact} */
|
|
8590
|
-
compact(options) {
|
|
8591
|
-
return this.chatDriver.compact(options);
|
|
8592
|
-
}
|
|
8593
|
-
/** {@inheritDoc AiDriver.canCompact} */
|
|
8594
|
-
canCompact(options) {
|
|
8595
|
-
return this.chatDriver.canCompact(options);
|
|
8596
|
-
}
|
|
8597
|
-
/** {@inheritDoc AiDriver.getPreambleTokens} */
|
|
8598
|
-
getPreambleTokens() {
|
|
8599
|
-
return this.chatDriver.getPreambleTokens();
|
|
8600
|
-
}
|
|
8601
|
-
/** {@inheritDoc AiDriver.getCompactionPlan} */
|
|
8602
|
-
getCompactionPlan(options) {
|
|
8603
|
-
return this.chatDriver.getCompactionPlan(options);
|
|
8604
|
-
}
|
|
8605
|
-
/** Delegates to the inner {@link ChatDriver} — turns are captured there. */
|
|
8606
|
-
getTurnSnapshots() {
|
|
8607
|
-
return this.chatDriver.getTurnSnapshots();
|
|
8608
|
-
}
|
|
8609
|
-
/** Delegates to the inner {@link ChatDriver} — interactions resolve there, so it holds the buffer. */
|
|
8610
|
-
getExternalDiagnostics() {
|
|
8611
|
-
return this.chatDriver.getExternalDiagnostics();
|
|
9024
|
+
static createInner(providerRegistry, agents, options = {}) {
|
|
9025
|
+
validateStaticAgentProviders(agents, providerRegistry);
|
|
9026
|
+
return new ChatDriver(providerRegistry, {
|
|
9027
|
+
maxToolIterations: options.maxToolIterations,
|
|
9028
|
+
maxFoldOperations: options.maxFoldOperations,
|
|
9029
|
+
maxTurnSnapshots: options.maxTurnSnapshots,
|
|
9030
|
+
sessionKey: options.sessionKey ?? "",
|
|
9031
|
+
activityBus: options.activityBus,
|
|
9032
|
+
budgetExhaustedMessage: options.budgetExhaustedMessage,
|
|
9033
|
+
aiServiceErrorMessages: options.aiServiceErrorMessages,
|
|
9034
|
+
// A release ends this driver's handoff loop, so it acts on the flag itself; see the option.
|
|
9035
|
+
deferAgentRelease: true
|
|
9036
|
+
});
|
|
8612
9037
|
}
|
|
8613
9038
|
async getSuggestions(history, prompt2, count, allAgentInfo) {
|
|
8614
9039
|
const pinned = this.resolvePinnedAgent();
|
|
@@ -8623,14 +9048,19 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8623
9048
|
}));
|
|
8624
9049
|
return this.chatDriver.getSuggestions(history, prompt2, count, agentInfo);
|
|
8625
9050
|
}
|
|
8626
|
-
async sendMessage(input, attachments) {
|
|
9051
|
+
async sendMessage(input, attachments, options) {
|
|
9052
|
+
const delegated = options?.delegate ? {
|
|
9053
|
+
delegate: options.delegate,
|
|
9054
|
+
delegateFraming: options.delegateFraming ?? DEFAULT_DELEGATE_FRAMING
|
|
9055
|
+
} : {};
|
|
8627
9056
|
try {
|
|
8628
|
-
return await this.runOrchestratedTurn(input, attachments);
|
|
9057
|
+
return await this.runOrchestratedTurn(input, attachments, options, delegated);
|
|
8629
9058
|
} catch (e) {
|
|
8630
9059
|
const pendingUserMessage = this.userMessageAppended ? void 0 : {
|
|
8631
9060
|
role: "user",
|
|
8632
9061
|
content: input,
|
|
8633
|
-
...attachments ? { attachments } : {}
|
|
9062
|
+
...attachments ? { attachments } : {},
|
|
9063
|
+
...delegated
|
|
8634
9064
|
};
|
|
8635
9065
|
if (e instanceof BudgetExhaustedError) {
|
|
8636
9066
|
return this.chatDriver.reportBudgetExhausted(e, pendingUserMessage);
|
|
@@ -8641,13 +9071,13 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8641
9071
|
throw e;
|
|
8642
9072
|
}
|
|
8643
9073
|
}
|
|
8644
|
-
async runOrchestratedTurn(input, attachments) {
|
|
9074
|
+
async runOrchestratedTurn(input, attachments, options, delegated) {
|
|
8645
9075
|
this.cancelled = false;
|
|
8646
9076
|
this.userMessageAppended = false;
|
|
8647
9077
|
const history = this.chatDriver.getHistory();
|
|
8648
9078
|
this.dispatchEvent(
|
|
8649
9079
|
new CustomEvent("history-updated", {
|
|
8650
|
-
detail: [...history, { role: "user", content: input, attachments }]
|
|
9080
|
+
detail: [...history, { role: "user", content: input, attachments, ...delegated }]
|
|
8651
9081
|
})
|
|
8652
9082
|
);
|
|
8653
9083
|
const pinned = this.resolvePinnedAgent();
|
|
@@ -8657,7 +9087,11 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8657
9087
|
this.recordRouting("pinned", pinned, { durationMs: 0 });
|
|
8658
9088
|
routed = { agent: pinned, decision: "pinned" };
|
|
8659
9089
|
} else {
|
|
8660
|
-
routed = await this.classify(
|
|
9090
|
+
routed = await this.classify(
|
|
9091
|
+
delegated.delegate ? frameDelegatedContent(input, delegated.delegateFraming) : input,
|
|
9092
|
+
history,
|
|
9093
|
+
this.activeAgent
|
|
9094
|
+
);
|
|
8661
9095
|
}
|
|
8662
9096
|
let currentAgent = routed.agent;
|
|
8663
9097
|
let isHandoff = false;
|
|
@@ -8684,7 +9118,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8684
9118
|
result = await this.chatDriver.continueFromHistory(contextPrimer);
|
|
8685
9119
|
} else {
|
|
8686
9120
|
this.userMessageAppended = true;
|
|
8687
|
-
result = await this.chatDriver.sendMessage(input, attachments);
|
|
9121
|
+
result = await this.chatDriver.sendMessage(input, attachments, options);
|
|
8688
9122
|
}
|
|
8689
9123
|
lastResult = result;
|
|
8690
9124
|
if (this.chatDriver.getAgentReleaseRequested()) {
|
|
@@ -8717,18 +9151,12 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8717
9151
|
this.chatDriver.reportStoppedBeforeTurn({
|
|
8718
9152
|
role: "user",
|
|
8719
9153
|
content: input,
|
|
8720
|
-
...attachments ? { attachments } : {}
|
|
9154
|
+
...attachments ? { attachments } : {},
|
|
9155
|
+
...delegated
|
|
8721
9156
|
});
|
|
8722
9157
|
}
|
|
8723
9158
|
return lastResult?.reason === "done" ? lastResult : { reason: "done" };
|
|
8724
9159
|
}
|
|
8725
|
-
async continueFromHistory(transientPrimer, options) {
|
|
8726
|
-
return this.chatDriver.continueFromHistory(transientPrimer, options);
|
|
8727
|
-
}
|
|
8728
|
-
/** {@inheritDoc AiDriver.primeRestoredAgentState} */
|
|
8729
|
-
primeRestoredAgentState(snapshots) {
|
|
8730
|
-
this.agentSession.primeRestoredStates(snapshots);
|
|
8731
|
-
}
|
|
8732
9160
|
/** {@inheritDoc AiDriver.canRestoreFlowOwner} */
|
|
8733
9161
|
canRestoreFlowOwner(agentName) {
|
|
8734
9162
|
return this.agents.some((a) => a.name === agentName);
|
|
@@ -8745,6 +9173,10 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8745
9173
|
* to classify, and a restored lock already says which agent owns the flow. `continueFromHistory`
|
|
8746
9174
|
* on its own would not do — it neither routes nor installs, so it would run on whatever agent
|
|
8747
9175
|
* happened to be installed (none, straight after a reload).
|
|
9176
|
+
*
|
|
9177
|
+
* Looks the owner up in its own agents list, so the `agent` argument is not needed and is
|
|
9178
|
+
* ignored; and does not delegate to the inner driver's resume, which would install the agent
|
|
9179
|
+
* undecorated.
|
|
8748
9180
|
*/
|
|
8749
9181
|
async resumeRestoredFlow(resumeNote) {
|
|
8750
9182
|
const owner = this.agentSession.getFlowOwner();
|
|
@@ -8781,15 +9213,8 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8781
9213
|
}
|
|
8782
9214
|
return winner ? { ...winner, agentName, basis: "reachable-max" } : void 0;
|
|
8783
9215
|
}
|
|
8784
|
-
/** {@inheritDoc AiDriver.getActiveAgent} */
|
|
8785
|
-
getActiveAgent() {
|
|
8786
|
-
return this.agentSession.getActive();
|
|
8787
|
-
}
|
|
8788
9216
|
async installAgent(agent) {
|
|
8789
|
-
const { previousAgent
|
|
8790
|
-
if (flowOwnerChangedTo !== void 0) {
|
|
8791
|
-
this.dispatchEvent(new CustomEvent("flow-owner-changed", { detail: flowOwnerChangedTo }));
|
|
8792
|
-
}
|
|
9217
|
+
const { previousAgent } = await this.agentSession.activate(agent);
|
|
8793
9218
|
const isTerminal = isFallback(agent) || this.agentSession.getPinned() !== null || this.agentSession.getFlowOwner() !== null || !!(agent.onActivate || agent.onDeactivate);
|
|
8794
9219
|
let agentToApply = agent;
|
|
8795
9220
|
if (!isTerminal) {
|
|
@@ -8812,12 +9237,11 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8812
9237
|
this.chatDriver.setProviderHistoryTransform((h) => transformHistoryForAgent(h, agent.name));
|
|
8813
9238
|
this.chatDriver.installAgent(agentToApply);
|
|
8814
9239
|
this.dispatchEvent(new CustomEvent("orchestrating-stop"));
|
|
8815
|
-
this.dispatchEvent(new CustomEvent("agent-changed", { detail: agent }));
|
|
8816
9240
|
}
|
|
8817
9241
|
/**
|
|
8818
|
-
* Release the current stateful agent: fire `onDeactivate
|
|
8819
|
-
*
|
|
8820
|
-
* unlocked state. Called automatically when a tool handler invokes
|
|
9242
|
+
* Release the current stateful agent: fire `onDeactivate` and clear the flow-owner lock. The
|
|
9243
|
+
* inner driver raises the events from the session's transition, so the host (and Redux) reflect
|
|
9244
|
+
* the unlocked state (GENC-1485). Called automatically when a tool handler invokes
|
|
8821
9245
|
* `context.releaseAgent`.
|
|
8822
9246
|
*
|
|
8823
9247
|
* The user-pin (`pinnedAgentName`) is intentionally *not* cleared — a user
|
|
@@ -8826,15 +9250,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8826
9250
|
* routing through the classifier.
|
|
8827
9251
|
*/
|
|
8828
9252
|
async releaseActiveAgent() {
|
|
8829
|
-
|
|
8830
|
-
if (!released) return;
|
|
8831
|
-
if (released.flowOwnerChangedTo !== void 0) {
|
|
8832
|
-
this.dispatchEvent(
|
|
8833
|
-
new CustomEvent("flow-owner-changed", { detail: released.flowOwnerChangedTo })
|
|
8834
|
-
);
|
|
8835
|
-
}
|
|
8836
|
-
this.dispatchEvent(new CustomEvent("agent-released", { detail: released.previousAgent }));
|
|
8837
|
-
this.dispatchEvent(new CustomEvent("agent-changed", { detail: void 0 }));
|
|
9253
|
+
await this.agentSession.release();
|
|
8838
9254
|
}
|
|
8839
9255
|
/**
|
|
8840
9256
|
* Stop the current turn (user "stop" button). Cancels the inner chat driver's
|
|
@@ -8853,6 +9269,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8853
9269
|
async dispose() {
|
|
8854
9270
|
await this.agentSession.dispose();
|
|
8855
9271
|
await this.chatDriver.dispose();
|
|
9272
|
+
this.detach();
|
|
8856
9273
|
}
|
|
8857
9274
|
/**
|
|
8858
9275
|
* Record one routing decision on the debug timeline — what was chosen, why, what it cost, and
|
|
@@ -8894,7 +9311,7 @@ var OrchestratingDriver = class extends EventTarget {
|
|
|
8894
9311
|
classifierTranscript(history) {
|
|
8895
9312
|
const lines = history.filter((m) => (m.role === "user" || m.role === "assistant") && reachesProvider(m)).filter((m) => !!m.content?.trim()).slice(-this.classifierHistoryLength).map((m) => {
|
|
8896
9313
|
const speaker = m.role === "user" ? "User" : "Assistant";
|
|
8897
|
-
const content = m.content ?? "";
|
|
9314
|
+
const content = m.role === "user" && m.delegate ? frameDelegatedContent(m.content ?? "", m.delegateFraming) : m.content ?? "";
|
|
8898
9315
|
const body = content.length > CLASSIFIER_MESSAGE_CHARS ? `${content.slice(0, CLASSIFIER_MESSAGE_CHARS)}\u2026` : content;
|
|
8899
9316
|
return `${speaker}: ${body}`;
|
|
8900
9317
|
});
|