@caupulican/pi-adaptative 0.93.18 → 0.93.19
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/CHANGELOG.md +17 -0
- package/dist/core/agent-session.d.ts +1 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +22 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction-controller.d.ts +21 -0
- package/dist/core/compaction-controller.d.ts.map +1 -1
- package/dist/core/compaction-controller.js +149 -26
- package/dist/core/compaction-controller.js.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +1 -0
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-agent-control.d.ts +2 -0
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.js.map +1 -1
- package/dist/core/foreground-lifecycle-adapter.d.ts +27 -0
- package/dist/core/foreground-lifecycle-adapter.d.ts.map +1 -0
- package/dist/core/foreground-lifecycle-adapter.js +61 -0
- package/dist/core/foreground-lifecycle-adapter.js.map +1 -0
- package/dist/core/foreground-lifecycle-controller.d.ts +48 -0
- package/dist/core/foreground-lifecycle-controller.d.ts.map +1 -0
- package/dist/core/foreground-lifecycle-controller.js +404 -0
- package/dist/core/foreground-lifecycle-controller.js.map +1 -0
- package/dist/core/foreground-recovery-controller.d.ts +3 -0
- package/dist/core/foreground-recovery-controller.d.ts.map +1 -1
- package/dist/core/foreground-recovery-controller.js +15 -1
- package/dist/core/foreground-recovery-controller.js.map +1 -1
- package/dist/core/goals/goal-state.d.ts.map +1 -1
- package/dist/core/goals/goal-state.js +9 -2
- package/dist/core/goals/goal-state.js.map +1 -1
- package/dist/core/keybindings.d.ts +2 -2
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -2
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/memory/memory-manager.d.ts +22 -0
- package/dist/core/memory/memory-manager.d.ts.map +1 -1
- package/dist/core/memory/memory-manager.js +108 -49
- package/dist/core/memory/memory-manager.js.map +1 -1
- package/dist/core/model-router/session-buffer.d.ts +10 -2
- package/dist/core/model-router/session-buffer.d.ts.map +1 -1
- package/dist/core/model-router/session-buffer.js +35 -9
- package/dist/core/model-router/session-buffer.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +20 -2
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +70 -17
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/provider-request-runtime-controller.d.ts +12 -1
- package/dist/core/provider-request-runtime-controller.d.ts.map +1 -1
- package/dist/core/provider-request-runtime-controller.js +45 -2
- package/dist/core/provider-request-runtime-controller.js.map +1 -1
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +4 -2
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +1 -3
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +10 -20
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/tool-group.d.ts +3 -0
- package/dist/modes/interactive/components/tool-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-group.js +25 -0
- package/dist/modes/interactive/components/tool-group.js.map +1 -1
- package/dist/modes/interactive/components/transcript-overlay.d.ts +13 -0
- package/dist/modes/interactive/components/transcript-overlay.d.ts.map +1 -0
- package/dist/modes/interactive/components/transcript-overlay.js +35 -0
- package/dist/modes/interactive/components/transcript-overlay.js.map +1 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +7 -4
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.js +2 -1
- package/dist/modes/interactive/interactive-event-controller.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +9 -7
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +70 -99
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/runtime-status-controller.d.ts +40 -0
- package/dist/modes/interactive/runtime-status-controller.d.ts.map +1 -0
- package/dist/modes/interactive/runtime-status-controller.js +147 -0
- package/dist/modes/interactive/runtime-status-controller.js.map +1 -0
- package/dist/modes/interactive/runtime-status.d.ts +15 -0
- package/dist/modes/interactive/runtime-status.d.ts.map +1 -0
- package/dist/modes/interactive/runtime-status.js +18 -0
- package/dist/modes/interactive/runtime-status.js.map +1 -0
- package/docs/json.md +1 -1
- package/docs/keybindings.md +2 -1
- package/docs/session-format.md +59 -4
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { ForegroundLifecycleController } from "./foreground-lifecycle-controller.js";
|
|
2
|
+
/**
|
|
3
|
+
* Host-side adapter for the foreground lifecycle boundary.
|
|
4
|
+
*
|
|
5
|
+
* The lifecycle controller owns request/tool repair semantics. This adapter owns the coding-agent
|
|
6
|
+
* persistence wiring around it: atomic router batches, canonical message ids, and warnings emitted
|
|
7
|
+
* before the first session subscriber exists.
|
|
8
|
+
*/
|
|
9
|
+
export class ForegroundLifecycleAdapter {
|
|
10
|
+
constructor(agent, sessionManager, modelRouter) {
|
|
11
|
+
this.pendingWarnings = [];
|
|
12
|
+
this.sessionManager = sessionManager;
|
|
13
|
+
this.lifecycle = new ForegroundLifecycleController({
|
|
14
|
+
agent,
|
|
15
|
+
sessionManager,
|
|
16
|
+
modelRouter,
|
|
17
|
+
emitWarning: (message) => this.pendingWarnings.push(message),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
install() {
|
|
21
|
+
this.lifecycle.install();
|
|
22
|
+
}
|
|
23
|
+
start() {
|
|
24
|
+
this.install();
|
|
25
|
+
this.repair();
|
|
26
|
+
}
|
|
27
|
+
reload() {
|
|
28
|
+
this.resetForSessionReload();
|
|
29
|
+
this.repair();
|
|
30
|
+
}
|
|
31
|
+
repair() {
|
|
32
|
+
this.lifecycle.repair();
|
|
33
|
+
}
|
|
34
|
+
resetForSessionReload() {
|
|
35
|
+
this.lifecycle.resetForSessionReload();
|
|
36
|
+
}
|
|
37
|
+
appendMessage(message) {
|
|
38
|
+
const entryId = this.sessionManager.appendMessage(message);
|
|
39
|
+
this.lifecycle.onMessagePersisted(message, entryId);
|
|
40
|
+
return entryId;
|
|
41
|
+
}
|
|
42
|
+
appendMessageBatch(batch) {
|
|
43
|
+
const entryIds = this.sessionManager.appendMessageBatch(batch);
|
|
44
|
+
for (let index = 0; index < batch.length; index += 1) {
|
|
45
|
+
const item = batch[index];
|
|
46
|
+
if (item.kind === "message")
|
|
47
|
+
this.lifecycle.onMessagePersisted(item.message, entryIds[index]);
|
|
48
|
+
}
|
|
49
|
+
return entryIds;
|
|
50
|
+
}
|
|
51
|
+
drainWarnings() {
|
|
52
|
+
const warnings = this.pendingWarnings;
|
|
53
|
+
this.pendingWarnings = [];
|
|
54
|
+
return warnings;
|
|
55
|
+
}
|
|
56
|
+
emitPendingWarnings(emit) {
|
|
57
|
+
for (const warning of this.drainWarnings())
|
|
58
|
+
emit(warning);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=foreground-lifecycle-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreground-lifecycle-adapter.js","sourceRoot":"","sources":["../../src/core/foreground-lifecycle-adapter.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,6BAA6B,EAAE,MAAM,sCAAsC,CAAC;AAGrF;;;;;;GAMG;AACH,MAAM,OAAO,0BAA0B;IAKtC,YAAY,KAAY,EAAE,cAA8B,EAAE,WAAkC;QAFpF,oBAAe,GAAa,EAAE,CAAC;QAGtC,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,IAAI,6BAA6B,CAAC;YAClD,KAAK;YACL,cAAc;YACd,WAAW;YACX,WAAW,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;SAC5D,CAAC,CAAC;IACJ,CAAC;IAED,OAAO;QACN,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,OAAO,EAAE,CAAC;QACf,IAAI,CAAC,MAAM,EAAE,CAAC;IACf,CAAC;IAED,MAAM;QACL,IAAI,CAAC,qBAAqB,EAAE,CAAC;QAC7B,IAAI,CAAC,MAAM,EAAE,CAAC;IACf,CAAC;IAED,MAAM;QACL,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;IACzB,CAAC;IAED,qBAAqB;QACpB,IAAI,CAAC,SAAS,CAAC,qBAAqB,EAAE,CAAC;IACxC,CAAC;IAED,aAAa,CAAC,OAAgB;QAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC3D,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,kBAAkB,CAAC,KAA0C;QAC5D,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC/D,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAE,CAAC;YAC3B,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAE,CAAC,CAAC;QAChG,CAAC;QACD,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,aAAa;QACZ,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC;QACtC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,mBAAmB,CAAC,IAA+B;QAClD,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,aAAa,EAAE;YAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3D,CAAC;CACD","sourcesContent":["import type { Agent } from \"@caupulican/pi-agent-core\";\nimport type { SessionManager, SessionMessageBatchEntry } from \"@caupulican/pi-agent-core/session\";\nimport type { Message } from \"@caupulican/pi-ai\";\nimport { ForegroundLifecycleController } from \"./foreground-lifecycle-controller.ts\";\nimport type { ModelRouterController } from \"./model-router-controller.ts\";\n\n/**\n * Host-side adapter for the foreground lifecycle boundary.\n *\n * The lifecycle controller owns request/tool repair semantics. This adapter owns the coding-agent\n * persistence wiring around it: atomic router batches, canonical message ids, and warnings emitted\n * before the first session subscriber exists.\n */\nexport class ForegroundLifecycleAdapter {\n\tprivate readonly lifecycle: ForegroundLifecycleController;\n\tprivate readonly sessionManager: SessionManager;\n\tprivate pendingWarnings: string[] = [];\n\n\tconstructor(agent: Agent, sessionManager: SessionManager, modelRouter: ModelRouterController) {\n\t\tthis.sessionManager = sessionManager;\n\t\tthis.lifecycle = new ForegroundLifecycleController({\n\t\t\tagent,\n\t\t\tsessionManager,\n\t\t\tmodelRouter,\n\t\t\temitWarning: (message) => this.pendingWarnings.push(message),\n\t\t});\n\t}\n\n\tinstall(): void {\n\t\tthis.lifecycle.install();\n\t}\n\n\tstart(): void {\n\t\tthis.install();\n\t\tthis.repair();\n\t}\n\n\treload(): void {\n\t\tthis.resetForSessionReload();\n\t\tthis.repair();\n\t}\n\n\trepair(): void {\n\t\tthis.lifecycle.repair();\n\t}\n\n\tresetForSessionReload(): void {\n\t\tthis.lifecycle.resetForSessionReload();\n\t}\n\n\tappendMessage(message: Message): string {\n\t\tconst entryId = this.sessionManager.appendMessage(message);\n\t\tthis.lifecycle.onMessagePersisted(message, entryId);\n\t\treturn entryId;\n\t}\n\n\tappendMessageBatch(batch: readonly SessionMessageBatchEntry[]): string[] {\n\t\tconst entryIds = this.sessionManager.appendMessageBatch(batch);\n\t\tfor (let index = 0; index < batch.length; index += 1) {\n\t\t\tconst item = batch[index]!;\n\t\t\tif (item.kind === \"message\") this.lifecycle.onMessagePersisted(item.message, entryIds[index]!);\n\t\t}\n\t\treturn entryIds;\n\t}\n\n\tdrainWarnings(): string[] {\n\t\tconst warnings = this.pendingWarnings;\n\t\tthis.pendingWarnings = [];\n\t\treturn warnings;\n\t}\n\n\temitPendingWarnings(emit: (message: string) => void): void {\n\t\tfor (const warning of this.drainWarnings()) emit(warning);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Agent, AgentMessage } from "@caupulican/pi-agent-core";
|
|
2
|
+
import type { SessionManager } from "@caupulican/pi-agent-core/session";
|
|
3
|
+
import type { ModelRouterController } from "./model-router-controller.ts";
|
|
4
|
+
interface ForegroundLifecycleControllerDeps {
|
|
5
|
+
agent: Agent;
|
|
6
|
+
sessionManager: SessionManager;
|
|
7
|
+
modelRouter: ModelRouterController;
|
|
8
|
+
emitWarning(message: string): void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Foreground persistence boundary for provider requests and tool execution.
|
|
12
|
+
*
|
|
13
|
+
* Agent-core deliberately owns provider/tool ordering while coding-agent owns the session log. This
|
|
14
|
+
* controller is the adapter between those lifecycles: request snapshots are written before transport,
|
|
15
|
+
* cheap routed messages are committed before a prepared tool body starts, and canonical result message
|
|
16
|
+
* ids are linked to one bounded terminal record. It never persists request content, tool arguments, or
|
|
17
|
+
* tool results in lifecycle metadata.
|
|
18
|
+
*/
|
|
19
|
+
export declare class ForegroundLifecycleController {
|
|
20
|
+
private readonly deps;
|
|
21
|
+
private readonly persistedMessages;
|
|
22
|
+
private readonly startedTools;
|
|
23
|
+
private readonly pendingToolsByCall;
|
|
24
|
+
private readonly completedResultMessages;
|
|
25
|
+
constructor(deps: ForegroundLifecycleControllerDeps);
|
|
26
|
+
/** Install the two agent-core callbacks at the host-owned durability boundary. */
|
|
27
|
+
install(): void;
|
|
28
|
+
notePersistedMessage(message: AgentMessage, entryId: string): void;
|
|
29
|
+
/** Drop in-flight associations when the host swaps/reloads the active session branch. */
|
|
30
|
+
resetForSessionReload(): void;
|
|
31
|
+
private findPersistedMessageEntryId;
|
|
32
|
+
private requestReason;
|
|
33
|
+
private onProviderRequestSnapshot;
|
|
34
|
+
private appendMessage;
|
|
35
|
+
private onToolCallStart;
|
|
36
|
+
private toolKey;
|
|
37
|
+
private callKey;
|
|
38
|
+
/** Called after the canonical message entry has been appended by AgentSession. */
|
|
39
|
+
onMessagePersisted(message: AgentMessage, entryId: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Repair incomplete lifecycle records on construction/reload. The active branch is inspected first;
|
|
42
|
+
* duplicate, mismatched, or out-of-order records are left untouched and reported rather than guessed.
|
|
43
|
+
*/
|
|
44
|
+
repair(): string[];
|
|
45
|
+
private appendRepairResult;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=foreground-lifecycle-controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreground-lifecycle-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-lifecycle-controller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAGX,cAAc,EACd,MAAM,mCAAmC,CAAC;AAI3C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAa1E,UAAU,iCAAiC;IAC1C,KAAK,EAAE,KAAK,CAAC;IACb,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,qBAAqB,CAAC;IACnC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAuLD;;;;;;;;GAQG;AACH,qBAAa,6BAA6B;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoC;IACzD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAiC;IACnE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0C;IACvE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAkC;IACrE,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAAyB;IAEjE,YAAY,IAAI,EAAE,iCAAiC,EAElD;IAED,kFAAkF;IAClF,OAAO,IAAI,IAAI,CAGd;IAED,oBAAoB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjE;IAED,yFAAyF;IACzF,qBAAqB,IAAI,IAAI,CAG5B;IAED,OAAO,CAAC,2BAA2B;IAInC,OAAO,CAAC,aAAa;YAQP,yBAAyB;IA8BvC,OAAO,CAAC,aAAa,CAInB;YAEY,eAAe;IA+C7B,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,OAAO;IAIf,kFAAkF;IAClF,kBAAkB,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAwB/D;IAED;;;OAGG;IACH,MAAM,IAAI,MAAM,EAAE,CAkEjB;IAED,OAAO,CAAC,kBAAkB;CAoB1B"}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { sessionLifecycleToolIdentityKey } from "@caupulican/pi-agent-core/session";
|
|
3
|
+
const MAX_MESSAGE_ENTRY_IDS = 256;
|
|
4
|
+
const MAX_WARNING_LENGTH = 500;
|
|
5
|
+
const MAX_EXACT_FINGERPRINT_CHARS = 32 * 1024;
|
|
6
|
+
const MAX_FINGERPRINT_DEPTH = 16;
|
|
7
|
+
const MAX_FINGERPRINT_ITEMS = 256;
|
|
8
|
+
const MAX_FINGERPRINT_STRING_CHARS = 16 * 1024;
|
|
9
|
+
const MAX_PROVIDER_HISTORY_MESSAGES = 256;
|
|
10
|
+
const MAX_LIFECYCLE_ANCESTRY_STEPS = 4096;
|
|
11
|
+
function boundedWarning(message) {
|
|
12
|
+
const normalized = message
|
|
13
|
+
.replace(/[\u0000-\u001F\u007F]/g, " ")
|
|
14
|
+
.replace(/\s+/g, " ")
|
|
15
|
+
.trim();
|
|
16
|
+
return normalized.length > MAX_WARNING_LENGTH ? `${normalized.slice(0, MAX_WARNING_LENGTH)}…` : normalized;
|
|
17
|
+
}
|
|
18
|
+
function boundedObjectKeys(record) {
|
|
19
|
+
const keys = [];
|
|
20
|
+
let truncated = false;
|
|
21
|
+
for (const key in record) {
|
|
22
|
+
if (!Object.hasOwn(record, key))
|
|
23
|
+
continue;
|
|
24
|
+
if (keys.length >= MAX_FINGERPRINT_ITEMS) {
|
|
25
|
+
truncated = true;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
keys.push(key);
|
|
29
|
+
}
|
|
30
|
+
keys.sort();
|
|
31
|
+
return { keys, truncated };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Canonicalize only bounded provider metadata. The system/tool envelope is allowed to retain exact
|
|
35
|
+
* values within the explicit cap; history is represented by descriptors below so a disk-backed or
|
|
36
|
+
* very long context is never copied into a second full object graph just to fingerprint it.
|
|
37
|
+
*/
|
|
38
|
+
function canonicalizeBounded(value, budget, depth = 0) {
|
|
39
|
+
if (budget.remaining <= 0)
|
|
40
|
+
return "[truncated]";
|
|
41
|
+
if (value === null || typeof value === "boolean")
|
|
42
|
+
return value;
|
|
43
|
+
if (typeof value === "string") {
|
|
44
|
+
const limit = Math.min(MAX_FINGERPRINT_STRING_CHARS, budget.remaining);
|
|
45
|
+
budget.remaining -= Math.min(value.length, limit);
|
|
46
|
+
return value.length <= limit ? value : `${value.slice(0, limit)}…<${value.length}>`;
|
|
47
|
+
}
|
|
48
|
+
if (typeof value === "number")
|
|
49
|
+
return Number.isFinite(value) ? value : String(value);
|
|
50
|
+
if (typeof value === "bigint")
|
|
51
|
+
return value.toString();
|
|
52
|
+
if (typeof value === "undefined")
|
|
53
|
+
return "[undefined]";
|
|
54
|
+
if (typeof value === "function" || typeof value === "symbol")
|
|
55
|
+
return `[${typeof value}]`;
|
|
56
|
+
if (typeof value !== "object")
|
|
57
|
+
return String(value);
|
|
58
|
+
if (depth >= MAX_FINGERPRINT_DEPTH)
|
|
59
|
+
return `[depth:${depth}]`;
|
|
60
|
+
if (budget.seen.has(value))
|
|
61
|
+
return "[circular]";
|
|
62
|
+
budget.seen.add(value);
|
|
63
|
+
if (Array.isArray(value)) {
|
|
64
|
+
const result = [];
|
|
65
|
+
for (let index = 0; index < Math.min(value.length, MAX_FINGERPRINT_ITEMS); index += 1) {
|
|
66
|
+
result.push(canonicalizeBounded(value[index], budget, depth + 1));
|
|
67
|
+
if (budget.remaining <= 0)
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
if (value.length > result.length)
|
|
71
|
+
result.push(`[items:${value.length}]`);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
const record = value;
|
|
75
|
+
const { keys, truncated } = boundedObjectKeys(record);
|
|
76
|
+
const result = {};
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
const keyLimit = Math.min(key.length, budget.remaining, MAX_FINGERPRINT_STRING_CHARS);
|
|
79
|
+
const boundedKey = key.length <= keyLimit ? key : `${key.slice(0, keyLimit)}…<${key.length}>`;
|
|
80
|
+
budget.remaining -= Math.min(key.length, keyLimit);
|
|
81
|
+
result[boundedKey] = canonicalizeBounded(record[key], budget, depth + 1);
|
|
82
|
+
if (budget.remaining <= 0)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
if (truncated)
|
|
86
|
+
result["[keys]"] = `>${MAX_FINGERPRINT_ITEMS}`;
|
|
87
|
+
return result;
|
|
88
|
+
}
|
|
89
|
+
function fingerprint(value, maxChars = MAX_EXACT_FINGERPRINT_CHARS) {
|
|
90
|
+
const serialized = JSON.stringify(canonicalizeBounded(value, { remaining: maxChars, seen: new WeakSet() }));
|
|
91
|
+
return createHash("sha256").update(serialized.slice(0, maxChars)).digest("hex");
|
|
92
|
+
}
|
|
93
|
+
function contentDescriptor(content) {
|
|
94
|
+
if (typeof content === "string") {
|
|
95
|
+
return { kind: "text", length: content.length, fingerprint: fingerprint(content) };
|
|
96
|
+
}
|
|
97
|
+
if (!Array.isArray(content))
|
|
98
|
+
return { kind: typeof content };
|
|
99
|
+
return content.slice(0, MAX_FINGERPRINT_ITEMS).map((block) => {
|
|
100
|
+
if (!block || typeof block !== "object")
|
|
101
|
+
return { kind: typeof block };
|
|
102
|
+
const candidate = block;
|
|
103
|
+
const descriptor = { type: candidate.type ?? "unknown" };
|
|
104
|
+
for (const key of ["id", "name", "toolCallId", "toolName"]) {
|
|
105
|
+
if (typeof candidate[key] === "string")
|
|
106
|
+
descriptor[key] = candidate[key];
|
|
107
|
+
}
|
|
108
|
+
for (const key of ["text", "thinking", "arguments", "data"]) {
|
|
109
|
+
const field = candidate[key];
|
|
110
|
+
if (typeof field === "string") {
|
|
111
|
+
descriptor[`${key}Length`] = field.length;
|
|
112
|
+
descriptor[`${key}Fingerprint`] = fingerprint(field);
|
|
113
|
+
}
|
|
114
|
+
else if (field !== undefined) {
|
|
115
|
+
descriptor[`${key}Type`] = typeof field;
|
|
116
|
+
descriptor[`${key}Fingerprint`] = fingerprint(field);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return descriptor;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function providerHistoryMetadata(messages) {
|
|
123
|
+
const metadata = [];
|
|
124
|
+
const firstCount = Math.min(messages.length, Math.floor(MAX_PROVIDER_HISTORY_MESSAGES / 4));
|
|
125
|
+
const indexes = messages.length <= MAX_PROVIDER_HISTORY_MESSAGES
|
|
126
|
+
? Array.from({ length: messages.length }, (_, index) => index)
|
|
127
|
+
: [
|
|
128
|
+
...Array.from({ length: firstCount }, (_, index) => index),
|
|
129
|
+
...Array.from({ length: MAX_PROVIDER_HISTORY_MESSAGES - firstCount }, (_, index) => messages.length - (MAX_PROVIDER_HISTORY_MESSAGES - firstCount) + index),
|
|
130
|
+
];
|
|
131
|
+
for (const index of indexes) {
|
|
132
|
+
const message = messages[index];
|
|
133
|
+
if (!message || typeof message !== "object") {
|
|
134
|
+
metadata.push({ type: typeof message });
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const candidate = message;
|
|
138
|
+
const entry = {
|
|
139
|
+
role: candidate.role,
|
|
140
|
+
content: contentDescriptor(candidate.content),
|
|
141
|
+
};
|
|
142
|
+
for (const key of ["id", "callId", "toolCallId", "toolName", "provider", "model"]) {
|
|
143
|
+
if (typeof candidate[key] === "string")
|
|
144
|
+
entry[key] = candidate[key];
|
|
145
|
+
}
|
|
146
|
+
metadata.push(entry);
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
messageCount: messages.length,
|
|
150
|
+
omittedMessageCount: Math.max(0, messages.length - metadata.length),
|
|
151
|
+
messages: metadata,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function messageEntryIds(sessionManager) {
|
|
155
|
+
const ids = [];
|
|
156
|
+
let entry = sessionManager.getLeafEntry();
|
|
157
|
+
let steps = 0;
|
|
158
|
+
while (entry && ids.length < MAX_MESSAGE_ENTRY_IDS && steps < MAX_LIFECYCLE_ANCESTRY_STEPS) {
|
|
159
|
+
if (entry.type === "message")
|
|
160
|
+
ids.push(entry.id);
|
|
161
|
+
entry = entry.parentId === null ? undefined : sessionManager.getEntry(entry.parentId);
|
|
162
|
+
steps += 1;
|
|
163
|
+
}
|
|
164
|
+
return ids.reverse();
|
|
165
|
+
}
|
|
166
|
+
function latestRequestSnapshot(sessionManager) {
|
|
167
|
+
let entry = sessionManager.getLeafEntry();
|
|
168
|
+
let steps = 0;
|
|
169
|
+
while (entry && steps < MAX_LIFECYCLE_ANCESTRY_STEPS) {
|
|
170
|
+
if (entry.type === "request_snapshot")
|
|
171
|
+
return entry;
|
|
172
|
+
entry = entry.parentId === null ? undefined : sessionManager.getEntry(entry.parentId);
|
|
173
|
+
steps += 1;
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
function modelRef(model) {
|
|
178
|
+
return { api: model.api, provider: model.provider, modelId: model.id };
|
|
179
|
+
}
|
|
180
|
+
function outcomeForToolResult(result) {
|
|
181
|
+
return result.isError ? "error" : "success";
|
|
182
|
+
}
|
|
183
|
+
function isAmbiguousInspection(inspection) {
|
|
184
|
+
return inspection.refusalReasons.length > 0;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Foreground persistence boundary for provider requests and tool execution.
|
|
188
|
+
*
|
|
189
|
+
* Agent-core deliberately owns provider/tool ordering while coding-agent owns the session log. This
|
|
190
|
+
* controller is the adapter between those lifecycles: request snapshots are written before transport,
|
|
191
|
+
* cheap routed messages are committed before a prepared tool body starts, and canonical result message
|
|
192
|
+
* ids are linked to one bounded terminal record. It never persists request content, tool arguments, or
|
|
193
|
+
* tool results in lifecycle metadata.
|
|
194
|
+
*/
|
|
195
|
+
export class ForegroundLifecycleController {
|
|
196
|
+
constructor(deps) {
|
|
197
|
+
this.persistedMessages = new WeakMap();
|
|
198
|
+
this.startedTools = new Map();
|
|
199
|
+
this.pendingToolsByCall = new Map();
|
|
200
|
+
this.completedResultMessages = new WeakSet();
|
|
201
|
+
this.appendMessage = (message) => {
|
|
202
|
+
const entryId = this.deps.sessionManager.appendMessage(message);
|
|
203
|
+
this.notePersistedMessage(message, entryId);
|
|
204
|
+
return entryId;
|
|
205
|
+
};
|
|
206
|
+
this.deps = deps;
|
|
207
|
+
}
|
|
208
|
+
/** Install the two agent-core callbacks at the host-owned durability boundary. */
|
|
209
|
+
install() {
|
|
210
|
+
this.deps.agent.onProviderRequestSnapshot = (context, signal) => this.onProviderRequestSnapshot(context, signal);
|
|
211
|
+
this.deps.agent.onToolCallStart = (calls, signal) => this.onToolCallStart(calls, signal);
|
|
212
|
+
}
|
|
213
|
+
notePersistedMessage(message, entryId) {
|
|
214
|
+
this.persistedMessages.set(message, entryId);
|
|
215
|
+
}
|
|
216
|
+
/** Drop in-flight associations when the host swaps/reloads the active session branch. */
|
|
217
|
+
resetForSessionReload() {
|
|
218
|
+
this.startedTools.clear();
|
|
219
|
+
this.pendingToolsByCall.clear();
|
|
220
|
+
}
|
|
221
|
+
findPersistedMessageEntryId(message) {
|
|
222
|
+
return this.persistedMessages.get(message);
|
|
223
|
+
}
|
|
224
|
+
requestReason(model) {
|
|
225
|
+
const latest = latestRequestSnapshot(this.deps.sessionManager);
|
|
226
|
+
if (!latest)
|
|
227
|
+
return "initial";
|
|
228
|
+
return latest.api !== model.api || latest.provider !== model.provider || latest.modelId !== model.id
|
|
229
|
+
? "change"
|
|
230
|
+
: "resume";
|
|
231
|
+
}
|
|
232
|
+
async onProviderRequestSnapshot(context, signal) {
|
|
233
|
+
signal?.throwIfAborted();
|
|
234
|
+
const flushed = this.deps.modelRouter.commitSessionBufferPrefix();
|
|
235
|
+
for (const [message, entryId] of flushed)
|
|
236
|
+
this.notePersistedMessage(message, entryId);
|
|
237
|
+
const model = context.model;
|
|
238
|
+
const ref = modelRef(model);
|
|
239
|
+
const requestId = context.requestId;
|
|
240
|
+
this.deps.sessionManager.appendRequestSnapshot({
|
|
241
|
+
requestId,
|
|
242
|
+
reason: this.requestReason(model),
|
|
243
|
+
api: ref.api,
|
|
244
|
+
provider: ref.provider,
|
|
245
|
+
modelId: ref.modelId,
|
|
246
|
+
effectiveConfigFingerprint: fingerprint({
|
|
247
|
+
model: ref,
|
|
248
|
+
reasoning: context.reasoning,
|
|
249
|
+
maxTokens: context.maxTokens,
|
|
250
|
+
attempt: context.attempt,
|
|
251
|
+
}),
|
|
252
|
+
systemFingerprint: fingerprint(context.context.systemPrompt),
|
|
253
|
+
toolsFingerprint: fingerprint(context.context.tools ?? []),
|
|
254
|
+
historyFingerprint: fingerprint(providerHistoryMetadata(context.context.messages)),
|
|
255
|
+
messageEntryIds: messageEntryIds(this.deps.sessionManager),
|
|
256
|
+
});
|
|
257
|
+
signal?.throwIfAborted();
|
|
258
|
+
}
|
|
259
|
+
async onToolCallStart(calls, signal) {
|
|
260
|
+
if (calls.length === 0)
|
|
261
|
+
return;
|
|
262
|
+
signal?.throwIfAborted();
|
|
263
|
+
const requestId = calls[0].requestId;
|
|
264
|
+
if (calls.some((call) => call.requestId !== requestId)) {
|
|
265
|
+
throw new Error("Tool reservation wave contains multiple provider request identities");
|
|
266
|
+
}
|
|
267
|
+
const flushed = this.deps.modelRouter.commitSessionBuffer();
|
|
268
|
+
for (const [message, entryId] of flushed)
|
|
269
|
+
this.notePersistedMessage(message, entryId);
|
|
270
|
+
const assistantMessage = calls[0].assistantMessage;
|
|
271
|
+
const assistantMessageEntryId = this.findPersistedMessageEntryId(assistantMessage);
|
|
272
|
+
if (!assistantMessageEntryId) {
|
|
273
|
+
throw new Error("Foreground tool reservation rejected: assistant message is not canonically persisted.");
|
|
274
|
+
}
|
|
275
|
+
const identities = [];
|
|
276
|
+
for (const call of calls) {
|
|
277
|
+
const identity = {
|
|
278
|
+
requestId,
|
|
279
|
+
assistantMessageEntryId,
|
|
280
|
+
callId: call.callId,
|
|
281
|
+
toolName: call.toolName,
|
|
282
|
+
};
|
|
283
|
+
const key = this.toolKey(identity);
|
|
284
|
+
if (this.startedTools.has(key))
|
|
285
|
+
throw new Error(`Duplicate foreground tool reservation: ${call.callId}.`);
|
|
286
|
+
identities.push(identity);
|
|
287
|
+
}
|
|
288
|
+
this.deps.sessionManager.appendForegroundToolStarts(identities.map(({ requestId: identityRequestId, assistantMessageEntryId: messageEntryId, callId, toolName }) => ({
|
|
289
|
+
requestId: identityRequestId,
|
|
290
|
+
assistantMessageEntryId: messageEntryId,
|
|
291
|
+
callId,
|
|
292
|
+
toolName,
|
|
293
|
+
})));
|
|
294
|
+
for (const identity of identities)
|
|
295
|
+
this.startedTools.set(this.toolKey(identity), identity);
|
|
296
|
+
for (const identity of identities) {
|
|
297
|
+
const callKey = this.callKey(identity.callId, identity.toolName);
|
|
298
|
+
const pending = this.pendingToolsByCall.get(callKey) ?? new Set();
|
|
299
|
+
pending.add(this.toolKey(identity));
|
|
300
|
+
this.pendingToolsByCall.set(callKey, pending);
|
|
301
|
+
}
|
|
302
|
+
signal?.throwIfAborted();
|
|
303
|
+
}
|
|
304
|
+
toolKey(identity) {
|
|
305
|
+
return [identity.requestId, identity.assistantMessageEntryId, identity.callId, identity.toolName].join("\u0000");
|
|
306
|
+
}
|
|
307
|
+
callKey(callId, toolName) {
|
|
308
|
+
return `${callId}\u0000${toolName}`;
|
|
309
|
+
}
|
|
310
|
+
/** Called after the canonical message entry has been appended by AgentSession. */
|
|
311
|
+
onMessagePersisted(message, entryId) {
|
|
312
|
+
this.notePersistedMessage(message, entryId);
|
|
313
|
+
if (message.role !== "toolResult")
|
|
314
|
+
return;
|
|
315
|
+
if (this.completedResultMessages.has(message))
|
|
316
|
+
return;
|
|
317
|
+
const result = message;
|
|
318
|
+
const pending = this.pendingToolsByCall.get(this.callKey(result.toolCallId, result.toolName));
|
|
319
|
+
if (!pending || pending.size !== 1)
|
|
320
|
+
return;
|
|
321
|
+
const key = pending.values().next().value;
|
|
322
|
+
const identity = this.startedTools.get(key);
|
|
323
|
+
if (!identity)
|
|
324
|
+
return;
|
|
325
|
+
const metadata = result.isError
|
|
326
|
+
? { resultMessageEntryId: entryId, errorKind: result.errorKind ?? "tool_failure" }
|
|
327
|
+
: { resultMessageEntryId: entryId };
|
|
328
|
+
this.deps.sessionManager.appendForegroundToolTerminal(identity.requestId, identity.assistantMessageEntryId, identity.callId, identity.toolName, outcomeForToolResult(result), metadata);
|
|
329
|
+
this.completedResultMessages.add(message);
|
|
330
|
+
this.startedTools.delete(key);
|
|
331
|
+
this.pendingToolsByCall.delete(this.callKey(result.toolCallId, result.toolName));
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Repair incomplete lifecycle records on construction/reload. The active branch is inspected first;
|
|
335
|
+
* duplicate, mismatched, or out-of-order records are left untouched and reported rather than guessed.
|
|
336
|
+
*/
|
|
337
|
+
repair() {
|
|
338
|
+
const inspection = this.deps.sessionManager.inspectSessionLifecycle();
|
|
339
|
+
if (isAmbiguousInspection(inspection)) {
|
|
340
|
+
const warning = boundedWarning("Session lifecycle repair refused: duplicate, mismatched, or out-of-order records require manual review.");
|
|
341
|
+
this.deps.emitWarning(warning);
|
|
342
|
+
return [warning];
|
|
343
|
+
}
|
|
344
|
+
const plan = this.deps.sessionManager.planSessionLifecycleRepair();
|
|
345
|
+
const index = this.deps.sessionManager.getSessionLifecycleIndex();
|
|
346
|
+
const warnings = [];
|
|
347
|
+
for (const closer of plan.toolClosers) {
|
|
348
|
+
const call = index.assistantToolCalls.find((candidate) => candidate.assistantMessageEntryId === closer.assistantMessageEntryId &&
|
|
349
|
+
candidate.callId === closer.callId &&
|
|
350
|
+
candidate.requestId === closer.requestId);
|
|
351
|
+
if (!call) {
|
|
352
|
+
warnings.push(boundedWarning(`Session lifecycle repair skipped tool ${closer.callId}: assistant call is missing.`));
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
const synthetic = this.appendRepairResult(closer.toolName, closer.callId, closer.code);
|
|
356
|
+
if (closer.sourceEntryId && closer.requestId !== undefined) {
|
|
357
|
+
this.deps.sessionManager.appendForegroundToolTerminal(closer.requestId, closer.assistantMessageEntryId, closer.callId, closer.toolName, "error", { resultMessageEntryId: synthetic, errorKind: "tool_failure" });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const promotion of plan.terminalPromotions) {
|
|
361
|
+
const record = index.toolsByIdentity.get(sessionLifecycleToolIdentityKey(promotion.requestId, promotion.assistantMessageEntryId, promotion.callId));
|
|
362
|
+
// A canonical immediate result has no foreground start and must remain terminal-free.
|
|
363
|
+
if (!record?.start)
|
|
364
|
+
continue;
|
|
365
|
+
if (promotion.requestId === undefined)
|
|
366
|
+
continue;
|
|
367
|
+
this.deps.sessionManager.appendForegroundToolTerminal(promotion.requestId, promotion.assistantMessageEntryId, promotion.callId, promotion.toolName, promotion.outcome, {
|
|
368
|
+
resultMessageEntryId: promotion.resultMessageEntryId,
|
|
369
|
+
...(promotion.errorKind === undefined ? {} : { errorKind: promotion.errorKind }),
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
for (const closer of plan.compactionClosers) {
|
|
373
|
+
this.deps.sessionManager.appendCompactionEnd(closer.compactionId, "interrupted", {
|
|
374
|
+
error: "Compaction was interrupted before its terminal outcome was recorded.",
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
if (plan.toolClosers.length > 0 || plan.terminalPromotions.length > 0 || plan.compactionClosers.length > 0) {
|
|
378
|
+
this.deps.agent.state.messages = this.deps.sessionManager.buildSessionContext().messages;
|
|
379
|
+
}
|
|
380
|
+
for (const warning of warnings)
|
|
381
|
+
this.deps.emitWarning(warning);
|
|
382
|
+
return warnings;
|
|
383
|
+
}
|
|
384
|
+
appendRepairResult(toolName, callId, code) {
|
|
385
|
+
const message = {
|
|
386
|
+
role: "toolResult",
|
|
387
|
+
toolCallId: callId,
|
|
388
|
+
toolName,
|
|
389
|
+
content: [
|
|
390
|
+
{
|
|
391
|
+
type: "text",
|
|
392
|
+
text: code === "TOOL_NOT_STARTED"
|
|
393
|
+
? "The harness recorded that this tool call was never started. Treat it as not run and decide whether to retry."
|
|
394
|
+
: "The harness recorded that the tool outcome is unknown after interruption. Inspect the workspace before retrying or claiming completion.",
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
isError: true,
|
|
398
|
+
errorKind: "tool_failure",
|
|
399
|
+
timestamp: Date.now(),
|
|
400
|
+
};
|
|
401
|
+
return this.appendMessage(message);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=foreground-lifecycle-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"foreground-lifecycle-controller.js","sourceRoot":"","sources":["../../src/core/foreground-lifecycle-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAOzC,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AAKpF,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,2BAA2B,GAAG,EAAE,GAAG,IAAI,CAAC;AAC9C,MAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AAClC,MAAM,4BAA4B,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/C,MAAM,6BAA6B,GAAG,GAAG,CAAC;AAC1C,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAkB1C,SAAS,cAAc,CAAC,OAAe;IACtC,MAAM,UAAU,GAAG,OAAO;SACxB,OAAO,CAAC,wBAAwB,EAAE,GAAG,CAAC;SACtC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;IACT,OAAO,UAAU,CAAC,MAAM,GAAG,kBAAkB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC;AAC5G,CAAC;AAOD,SAAS,iBAAiB,CAAC,MAA+B;IACzD,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;YAAE,SAAS;QAC1C,IAAI,IAAI,CAAC,MAAM,IAAI,qBAAqB,EAAE,CAAC;YAC1C,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM;QACP,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC;IACD,IAAI,CAAC,IAAI,EAAE,CAAC;IACZ,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;AAC5B,CAAC;AAED;;;;GAIG;AACH,SAAS,mBAAmB,CAAC,KAAc,EAAE,MAAyB,EAAE,KAAK,GAAG,CAAC;IAChF,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC;QAAE,OAAO,aAAa,CAAC;IAChD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAC/D,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,4BAA4B,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC;QACvE,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;QAClD,OAAO,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC;IACrF,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACrF,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IACvD,IAAI,OAAO,KAAK,KAAK,WAAW;QAAE,OAAO,aAAa,CAAC;IACvD,IAAI,OAAO,KAAK,KAAK,UAAU,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,OAAO,KAAK,GAAG,CAAC;IACzF,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACpD,IAAI,KAAK,IAAI,qBAAqB;QAAE,OAAO,UAAU,KAAK,GAAG,CAAC;IAC9D,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;QAAE,OAAO,YAAY,CAAC;IAChD,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAc,EAAE,CAAC;QAC7B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,qBAAqB,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;YACvF,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;YAClE,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC;gBAAE,MAAM;QAClC,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;YAAE,MAAM,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QACzE,OAAO,MAAM,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,MAAM,GAA4B,EAAE,CAAC;IAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;QACtF,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,GAAG,CAAC,MAAM,GAAG,CAAC;QAC9F,MAAM,CAAC,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,UAAU,CAAC,GAAG,mBAAmB,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QACzE,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC;YAAE,MAAM;IAClC,CAAC;IACD,IAAI,SAAS;QAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAI,qBAAqB,EAAE,CAAC;IAC9D,OAAO,MAAM,CAAC;AACf,CAAC;AAED,SAAS,WAAW,CAAC,KAAc,EAAE,QAAQ,GAAG,2BAA2B;IAC1E,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IAC5G,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACjF,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAgB;IAC1C,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IACpF,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,OAAO,OAAO,EAAE,CAAC;IAC7D,OAAO,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC5D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,EAAE,CAAC;QACvE,MAAM,SAAS,GAAG,KAAgC,CAAC;QACnD,MAAM,UAAU,GAA4B,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,IAAI,SAAS,EAAE,CAAC;QAClF,KAAK,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,CAAU,EAAE,CAAC;YACrE,IAAI,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,UAAU,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QAC1E,CAAC;QACD,KAAK,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAU,EAAE,CAAC;YACtE,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAC/B,UAAU,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;gBAC1C,UAAU,CAAC,GAAG,GAAG,aAAa,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YACtD,CAAC;iBAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBAChC,UAAU,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,OAAO,KAAK,CAAC;gBACxC,UAAU,CAAC,GAAG,GAAG,aAAa,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;YACtD,CAAC;QACF,CAAC;QACD,OAAO,UAAU,CAAC;IACnB,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,QAA4B;IAC5D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,6BAA6B,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5F,MAAM,OAAO,GACZ,QAAQ,CAAC,MAAM,IAAI,6BAA6B;QAC/C,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC;QAC9D,CAAC,CAAC;YACA,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC;YAC1D,GAAG,KAAK,CAAC,IAAI,CACZ,EAAE,MAAM,EAAE,6BAA6B,GAAG,UAAU,EAAE,EACtD,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,6BAA6B,GAAG,UAAU,CAAC,GAAG,KAAK,CACpF;SACD,CAAC;IACL,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,OAAO,EAAE,CAAC,CAAC;YACxC,SAAS;QACV,CAAC;QACD,MAAM,SAAS,GAAG,OAAkC,CAAC;QACrD,MAAM,KAAK,GAA4B;YACtC,IAAI,EAAE,SAAS,CAAC,IAAI;YACpB,OAAO,EAAE,iBAAiB,CAAC,SAAS,CAAC,OAAO,CAAC;SAC7C,CAAC;QACF,KAAK,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,CAAU,EAAE,CAAC;YAC5F,IAAI,OAAO,SAAS,CAAC,GAAG,CAAC,KAAK,QAAQ;gBAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;QACrE,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;IACD,OAAO;QACN,YAAY,EAAE,QAAQ,CAAC,MAAM;QAC7B,mBAAmB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QACnE,QAAQ,EAAE,QAAQ;KAClB,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CAAC,cAA8B;IACtD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,IAAI,KAAK,GAAG,cAAc,CAAC,YAAY,EAAE,CAAC;IAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,IAAI,GAAG,CAAC,MAAM,GAAG,qBAAqB,IAAI,KAAK,GAAG,4BAA4B,EAAE,CAAC;QAC5F,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACjD,KAAK,GAAG,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtF,KAAK,IAAI,CAAC,CAAC;IACZ,CAAC;IACD,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC;AACtB,CAAC;AAED,SAAS,qBAAqB,CAAC,cAA8B;IAC5D,IAAI,KAAK,GAAG,cAAc,CAAC,YAAY,EAAE,CAAC;IAC1C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,IAAI,KAAK,GAAG,4BAA4B,EAAE,CAAC;QACtD,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB;YAAE,OAAO,KAAK,CAAC;QACpD,KAAK,GAAG,KAAK,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QACtF,KAAK,IAAI,CAAC,CAAC;IACZ,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAiB;IAClC,OAAO,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;AACxE,CAAC;AAED,SAAS,oBAAoB,CAAC,MAAyB;IACtD,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7C,CAAC;AAED,SAAS,qBAAqB,CAAC,UAAsC;IACpE,OAAO,UAAU,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,6BAA6B;IAOzC,YAAY,IAAuC;QALlC,sBAAiB,GAAG,IAAI,OAAO,EAAkB,CAAC;QAClD,iBAAY,GAAG,IAAI,GAAG,EAA+B,CAAC;QACtD,uBAAkB,GAAG,IAAI,GAAG,EAAuB,CAAC;QACpD,4BAAuB,GAAG,IAAI,OAAO,EAAU,CAAC;QAgEzD,kBAAa,GAAkB,CAAC,OAAO,EAAE,EAAE;YAClD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAChE,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC5C,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC;QAjED,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;IAED,kFAAkF;IAClF,OAAO;QACN,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,yBAAyB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,yBAAyB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACjH,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1F,CAAC;IAED,oBAAoB,CAAC,OAAqB,EAAE,OAAe;QAC1D,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,yFAAyF;IACzF,qBAAqB;QACpB,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;IACjC,CAAC;IAEO,2BAA2B,CAAC,OAAqB;QACxD,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5C,CAAC;IAEO,aAAa,CAAC,KAAiB;QACtC,MAAM,MAAM,GAAG,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC/D,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,OAAO,MAAM,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,QAAQ,KAAK,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,EAAE;YACnG,CAAC,CAAC,QAAQ;YACV,CAAC,CAAC,QAAQ,CAAC;IACb,CAAC;IAEO,KAAK,CAAC,yBAAyB,CACtC,OAAuC,EACvC,MAAoB;QAEpB,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,yBAAyB,EAAE,CAAC;QAClE,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,OAAO;YAAE,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtF,MAAM,KAAK,GAAG,OAAO,CAAC,KAAmB,CAAC;QAC1C,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;QAC5B,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACpC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,qBAAqB,CAAC;YAC9C,SAAS;YACT,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;YACjC,GAAG,EAAE,GAAG,CAAC,GAAG;YACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,OAAO,EAAE,GAAG,CAAC,OAAO;YACpB,0BAA0B,EAAE,WAAW,CAAC;gBACvC,KAAK,EAAE,GAAG;gBACV,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,OAAO,EAAE,OAAO,CAAC,OAAO;aACxB,CAAC;YACF,iBAAiB,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC;YAC5D,gBAAgB,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1D,kBAAkB,EAAE,WAAW,CAAC,uBAAuB,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAClF,eAAe,EAAE,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;SAC1D,CAAC,CAAC;QACH,MAAM,EAAE,cAAc,EAAE,CAAC;IAC1B,CAAC;IAQO,KAAK,CAAC,eAAe,CAAC,KAAsC,EAAE,MAAoB;QACzF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAC/B,MAAM,EAAE,cAAc,EAAE,CAAC;QACzB,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,SAAS,CAAC;QACtC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,CAAC,EAAE,CAAC;YACxD,MAAM,IAAI,KAAK,CAAC,qEAAqE,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,mBAAmB,EAAE,CAAC;QAC5D,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,OAAO;YAAE,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACtF,MAAM,gBAAgB,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,gBAAgB,CAAC;QACpD,MAAM,uBAAuB,GAAG,IAAI,CAAC,2BAA2B,CAAC,gBAAgB,CAAC,CAAC;QACnF,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,uFAAuF,CAAC,CAAC;QAC1G,CAAC;QACD,MAAM,UAAU,GAA0B,EAAE,CAAC;QAC7C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAwB;gBACrC,SAAS;gBACT,uBAAuB;gBACvB,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACvB,CAAC;YACF,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YAC1G,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC3B,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,0BAA0B,CAClD,UAAU,CAAC,GAAG,CACb,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;YACjG,SAAS,EAAE,iBAAiB;YAC5B,uBAAuB,EAAE,cAAc;YACvC,MAAM;YACN,QAAQ;SACR,CAAC,CACF,CACD,CAAC;QACF,KAAK,MAAM,QAAQ,IAAI,UAAU;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC3F,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACjE,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;YAC1E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;YACpC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC/C,CAAC;QACD,MAAM,EAAE,cAAc,EAAE,CAAC;IAC1B,CAAC;IAEO,OAAO,CACd,QAAoG;QAEpG,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,uBAAuB,EAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClH,CAAC;IAEO,OAAO,CAAC,MAAc,EAAE,QAAgB;QAC/C,OAAO,GAAG,MAAM,SAAS,QAAQ,EAAE,CAAC;IACrC,CAAC;IAED,kFAAkF;IAClF,kBAAkB,CAAC,OAAqB,EAAE,OAAe;QACxD,IAAI,CAAC,oBAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY;YAAE,OAAO;QAC1C,IAAI,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO;QACtD,MAAM,MAAM,GAAG,OAA4B,CAAC;QAC5C,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC9F,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO;QAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAe,CAAC;QACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ;YAAE,OAAO;QACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO;YAC9B,CAAC,CAAC,EAAE,oBAAoB,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,cAAc,EAAE;YAClF,CAAC,CAAC,EAAE,oBAAoB,EAAE,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,4BAA4B,CACpD,QAAQ,CAAC,SAAS,EAClB,QAAQ,CAAC,uBAAuB,EAChC,QAAQ,CAAC,MAAM,EACf,QAAQ,CAAC,QAAQ,EACjB,oBAAoB,CAAC,MAAM,CAAC,EAC5B,QAAQ,CACR,CAAC;QACF,IAAI,CAAC,uBAAuB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC1C,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClF,CAAC;IAED;;;OAGG;IACH,MAAM;QACL,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,uBAAuB,EAAE,CAAC;QACtE,IAAI,qBAAqB,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,cAAc,CAC7B,yGAAyG,CACzG,CAAC;YACF,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,0BAA0B,EAAE,CAAC;QACnE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,wBAAwB,EAAE,CAAC;QAClE,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,KAAK,CAAC,kBAAkB,CAAC,IAAI,CACzC,CAAC,SAAS,EAAE,EAAE,CACb,SAAS,CAAC,uBAAuB,KAAK,MAAM,CAAC,uBAAuB;gBACpE,SAAS,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;gBAClC,SAAS,CAAC,SAAS,KAAK,MAAM,CAAC,SAAS,CACzC,CAAC;YACF,IAAI,CAAC,IAAI,EAAE,CAAC;gBACX,QAAQ,CAAC,IAAI,CACZ,cAAc,CAAC,yCAAyC,MAAM,CAAC,MAAM,8BAA8B,CAAC,CACpG,CAAC;gBACF,SAAS;YACV,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;YACvF,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC5D,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,4BAA4B,CACpD,MAAM,CAAC,SAAS,EAChB,MAAM,CAAC,uBAAuB,EAC9B,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,QAAQ,EACf,OAAO,EACP,EAAE,oBAAoB,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,EAAE,CAC9D,CAAC;YACH,CAAC;QACF,CAAC;QACD,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,kBAAkB,EAAE,CAAC;YACjD,MAAM,MAAM,GAAG,KAAK,CAAC,eAAe,CAAC,GAAG,CACvC,+BAA+B,CAAC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC,uBAAuB,EAAE,SAAS,CAAC,MAAM,CAAC,CACzG,CAAC;YACF,sFAAsF;YACtF,IAAI,CAAC,MAAM,EAAE,KAAK;gBAAE,SAAS;YAC7B,IAAI,SAAS,CAAC,SAAS,KAAK,SAAS;gBAAE,SAAS;YAChD,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,4BAA4B,CACpD,SAAS,CAAC,SAAS,EACnB,SAAS,CAAC,uBAAuB,EACjC,SAAS,CAAC,MAAM,EAChB,SAAS,CAAC,QAAQ,EAClB,SAAS,CAAC,OAAO,EACjB;gBACC,oBAAoB,EAAE,SAAS,CAAC,oBAAoB;gBACpD,GAAG,CAAC,SAAS,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,EAAE,CAAC;aAChF,CACD,CAAC;QACH,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC7C,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,mBAAmB,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,EAAE;gBAChF,KAAK,EAAE,sEAAsE;aAC7E,CAAC,CAAC;QACJ,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5G,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,mBAAmB,EAAE,CAAC,QAAQ,CAAC;QAC1F,CAAC;QACD,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAC/D,OAAO,QAAQ,CAAC;IACjB,CAAC;IAEO,kBAAkB,CAAC,QAAgB,EAAE,MAAc,EAAE,IAAY;QACxE,MAAM,OAAO,GAAsB;YAClC,IAAI,EAAE,YAAY;YAClB,UAAU,EAAE,MAAM;YAClB,QAAQ;YACR,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAM;oBACZ,IAAI,EACH,IAAI,KAAK,kBAAkB;wBAC1B,CAAC,CAAC,8GAA8G;wBAChH,CAAC,CAAC,yIAAyI;iBAC7I;aACD;YACD,OAAO,EAAE,IAAI;YACb,SAAS,EAAE,cAAc;YACzB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACrB,CAAC;QACF,OAAO,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;CACD","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Agent, AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type {\n\tRequestSnapshotEntry,\n\tSessionLifecycleInspection,\n\tSessionManager,\n} from \"@caupulican/pi-agent-core/session\";\nimport { sessionLifecycleToolIdentityKey } from \"@caupulican/pi-agent-core/session\";\nimport type { ProviderRequestSnapshotContext, ToolCallStartContext } from \"@caupulican/pi-agent-core/types\";\nimport type { Api, Message, Model, ToolResultMessage } from \"@caupulican/pi-ai\";\nimport type { ModelRouterController } from \"./model-router-controller.ts\";\n\nconst MAX_MESSAGE_ENTRY_IDS = 256;\nconst MAX_WARNING_LENGTH = 500;\nconst MAX_EXACT_FINGERPRINT_CHARS = 32 * 1024;\nconst MAX_FINGERPRINT_DEPTH = 16;\nconst MAX_FINGERPRINT_ITEMS = 256;\nconst MAX_FINGERPRINT_STRING_CHARS = 16 * 1024;\nconst MAX_PROVIDER_HISTORY_MESSAGES = 256;\nconst MAX_LIFECYCLE_ANCESTRY_STEPS = 4096;\n\ntype AppendMessage = (message: Message) => string;\n\ninterface ForegroundLifecycleControllerDeps {\n\tagent: Agent;\n\tsessionManager: SessionManager;\n\tmodelRouter: ModelRouterController;\n\temitWarning(message: string): void;\n}\n\ninterface StartedToolIdentity {\n\trequestId: string;\n\tassistantMessageEntryId: string;\n\tcallId: string;\n\ttoolName: string;\n}\n\nfunction boundedWarning(message: string): string {\n\tconst normalized = message\n\t\t.replace(/[\\u0000-\\u001F\\u007F]/g, \" \")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\treturn normalized.length > MAX_WARNING_LENGTH ? `${normalized.slice(0, MAX_WARNING_LENGTH)}…` : normalized;\n}\n\ninterface FingerprintBudget {\n\tremaining: number;\n\tseen: WeakSet<object>;\n}\n\nfunction boundedObjectKeys(record: Record<string, unknown>): { keys: string[]; truncated: boolean } {\n\tconst keys: string[] = [];\n\tlet truncated = false;\n\tfor (const key in record) {\n\t\tif (!Object.hasOwn(record, key)) continue;\n\t\tif (keys.length >= MAX_FINGERPRINT_ITEMS) {\n\t\t\ttruncated = true;\n\t\t\tbreak;\n\t\t}\n\t\tkeys.push(key);\n\t}\n\tkeys.sort();\n\treturn { keys, truncated };\n}\n\n/**\n * Canonicalize only bounded provider metadata. The system/tool envelope is allowed to retain exact\n * values within the explicit cap; history is represented by descriptors below so a disk-backed or\n * very long context is never copied into a second full object graph just to fingerprint it.\n */\nfunction canonicalizeBounded(value: unknown, budget: FingerprintBudget, depth = 0): unknown {\n\tif (budget.remaining <= 0) return \"[truncated]\";\n\tif (value === null || typeof value === \"boolean\") return value;\n\tif (typeof value === \"string\") {\n\t\tconst limit = Math.min(MAX_FINGERPRINT_STRING_CHARS, budget.remaining);\n\t\tbudget.remaining -= Math.min(value.length, limit);\n\t\treturn value.length <= limit ? value : `${value.slice(0, limit)}…<${value.length}>`;\n\t}\n\tif (typeof value === \"number\") return Number.isFinite(value) ? value : String(value);\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (typeof value === \"undefined\") return \"[undefined]\";\n\tif (typeof value === \"function\" || typeof value === \"symbol\") return `[${typeof value}]`;\n\tif (typeof value !== \"object\") return String(value);\n\tif (depth >= MAX_FINGERPRINT_DEPTH) return `[depth:${depth}]`;\n\tif (budget.seen.has(value)) return \"[circular]\";\n\tbudget.seen.add(value);\n\tif (Array.isArray(value)) {\n\t\tconst result: unknown[] = [];\n\t\tfor (let index = 0; index < Math.min(value.length, MAX_FINGERPRINT_ITEMS); index += 1) {\n\t\t\tresult.push(canonicalizeBounded(value[index], budget, depth + 1));\n\t\t\tif (budget.remaining <= 0) break;\n\t\t}\n\t\tif (value.length > result.length) result.push(`[items:${value.length}]`);\n\t\treturn result;\n\t}\n\tconst record = value as Record<string, unknown>;\n\tconst { keys, truncated } = boundedObjectKeys(record);\n\tconst result: Record<string, unknown> = {};\n\tfor (const key of keys) {\n\t\tconst keyLimit = Math.min(key.length, budget.remaining, MAX_FINGERPRINT_STRING_CHARS);\n\t\tconst boundedKey = key.length <= keyLimit ? key : `${key.slice(0, keyLimit)}…<${key.length}>`;\n\t\tbudget.remaining -= Math.min(key.length, keyLimit);\n\t\tresult[boundedKey] = canonicalizeBounded(record[key], budget, depth + 1);\n\t\tif (budget.remaining <= 0) break;\n\t}\n\tif (truncated) result[\"[keys]\"] = `>${MAX_FINGERPRINT_ITEMS}`;\n\treturn result;\n}\n\nfunction fingerprint(value: unknown, maxChars = MAX_EXACT_FINGERPRINT_CHARS): string {\n\tconst serialized = JSON.stringify(canonicalizeBounded(value, { remaining: maxChars, seen: new WeakSet() }));\n\treturn createHash(\"sha256\").update(serialized.slice(0, maxChars)).digest(\"hex\");\n}\n\nfunction contentDescriptor(content: unknown): unknown {\n\tif (typeof content === \"string\") {\n\t\treturn { kind: \"text\", length: content.length, fingerprint: fingerprint(content) };\n\t}\n\tif (!Array.isArray(content)) return { kind: typeof content };\n\treturn content.slice(0, MAX_FINGERPRINT_ITEMS).map((block) => {\n\t\tif (!block || typeof block !== \"object\") return { kind: typeof block };\n\t\tconst candidate = block as Record<string, unknown>;\n\t\tconst descriptor: Record<string, unknown> = { type: candidate.type ?? \"unknown\" };\n\t\tfor (const key of [\"id\", \"name\", \"toolCallId\", \"toolName\"] as const) {\n\t\t\tif (typeof candidate[key] === \"string\") descriptor[key] = candidate[key];\n\t\t}\n\t\tfor (const key of [\"text\", \"thinking\", \"arguments\", \"data\"] as const) {\n\t\t\tconst field = candidate[key];\n\t\t\tif (typeof field === \"string\") {\n\t\t\t\tdescriptor[`${key}Length`] = field.length;\n\t\t\t\tdescriptor[`${key}Fingerprint`] = fingerprint(field);\n\t\t\t} else if (field !== undefined) {\n\t\t\t\tdescriptor[`${key}Type`] = typeof field;\n\t\t\t\tdescriptor[`${key}Fingerprint`] = fingerprint(field);\n\t\t\t}\n\t\t}\n\t\treturn descriptor;\n\t});\n}\n\nfunction providerHistoryMetadata(messages: readonly unknown[]): unknown {\n\tconst metadata: unknown[] = [];\n\tconst firstCount = Math.min(messages.length, Math.floor(MAX_PROVIDER_HISTORY_MESSAGES / 4));\n\tconst indexes =\n\t\tmessages.length <= MAX_PROVIDER_HISTORY_MESSAGES\n\t\t\t? Array.from({ length: messages.length }, (_, index) => index)\n\t\t\t: [\n\t\t\t\t\t...Array.from({ length: firstCount }, (_, index) => index),\n\t\t\t\t\t...Array.from(\n\t\t\t\t\t\t{ length: MAX_PROVIDER_HISTORY_MESSAGES - firstCount },\n\t\t\t\t\t\t(_, index) => messages.length - (MAX_PROVIDER_HISTORY_MESSAGES - firstCount) + index,\n\t\t\t\t\t),\n\t\t\t\t];\n\tfor (const index of indexes) {\n\t\tconst message = messages[index];\n\t\tif (!message || typeof message !== \"object\") {\n\t\t\tmetadata.push({ type: typeof message });\n\t\t\tcontinue;\n\t\t}\n\t\tconst candidate = message as Record<string, unknown>;\n\t\tconst entry: Record<string, unknown> = {\n\t\t\trole: candidate.role,\n\t\t\tcontent: contentDescriptor(candidate.content),\n\t\t};\n\t\tfor (const key of [\"id\", \"callId\", \"toolCallId\", \"toolName\", \"provider\", \"model\"] as const) {\n\t\t\tif (typeof candidate[key] === \"string\") entry[key] = candidate[key];\n\t\t}\n\t\tmetadata.push(entry);\n\t}\n\treturn {\n\t\tmessageCount: messages.length,\n\t\tomittedMessageCount: Math.max(0, messages.length - metadata.length),\n\t\tmessages: metadata,\n\t};\n}\n\nfunction messageEntryIds(sessionManager: SessionManager): string[] {\n\tconst ids: string[] = [];\n\tlet entry = sessionManager.getLeafEntry();\n\tlet steps = 0;\n\twhile (entry && ids.length < MAX_MESSAGE_ENTRY_IDS && steps < MAX_LIFECYCLE_ANCESTRY_STEPS) {\n\t\tif (entry.type === \"message\") ids.push(entry.id);\n\t\tentry = entry.parentId === null ? undefined : sessionManager.getEntry(entry.parentId);\n\t\tsteps += 1;\n\t}\n\treturn ids.reverse();\n}\n\nfunction latestRequestSnapshot(sessionManager: SessionManager): RequestSnapshotEntry | undefined {\n\tlet entry = sessionManager.getLeafEntry();\n\tlet steps = 0;\n\twhile (entry && steps < MAX_LIFECYCLE_ANCESTRY_STEPS) {\n\t\tif (entry.type === \"request_snapshot\") return entry;\n\t\tentry = entry.parentId === null ? undefined : sessionManager.getEntry(entry.parentId);\n\t\tsteps += 1;\n\t}\n\treturn undefined;\n}\n\nfunction modelRef(model: Model<Api>): { api: string; provider: string; modelId: string } {\n\treturn { api: model.api, provider: model.provider, modelId: model.id };\n}\n\nfunction outcomeForToolResult(result: ToolResultMessage): \"success\" | \"error\" {\n\treturn result.isError ? \"error\" : \"success\";\n}\n\nfunction isAmbiguousInspection(inspection: SessionLifecycleInspection): boolean {\n\treturn inspection.refusalReasons.length > 0;\n}\n\n/**\n * Foreground persistence boundary for provider requests and tool execution.\n *\n * Agent-core deliberately owns provider/tool ordering while coding-agent owns the session log. This\n * controller is the adapter between those lifecycles: request snapshots are written before transport,\n * cheap routed messages are committed before a prepared tool body starts, and canonical result message\n * ids are linked to one bounded terminal record. It never persists request content, tool arguments, or\n * tool results in lifecycle metadata.\n */\nexport class ForegroundLifecycleController {\n\tprivate readonly deps: ForegroundLifecycleControllerDeps;\n\tprivate readonly persistedMessages = new WeakMap<object, string>();\n\tprivate readonly startedTools = new Map<string, StartedToolIdentity>();\n\tprivate readonly pendingToolsByCall = new Map<string, Set<string>>();\n\tprivate readonly completedResultMessages = new WeakSet<object>();\n\n\tconstructor(deps: ForegroundLifecycleControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\t/** Install the two agent-core callbacks at the host-owned durability boundary. */\n\tinstall(): void {\n\t\tthis.deps.agent.onProviderRequestSnapshot = (context, signal) => this.onProviderRequestSnapshot(context, signal);\n\t\tthis.deps.agent.onToolCallStart = (calls, signal) => this.onToolCallStart(calls, signal);\n\t}\n\n\tnotePersistedMessage(message: AgentMessage, entryId: string): void {\n\t\tthis.persistedMessages.set(message, entryId);\n\t}\n\n\t/** Drop in-flight associations when the host swaps/reloads the active session branch. */\n\tresetForSessionReload(): void {\n\t\tthis.startedTools.clear();\n\t\tthis.pendingToolsByCall.clear();\n\t}\n\n\tprivate findPersistedMessageEntryId(message: AgentMessage): string | undefined {\n\t\treturn this.persistedMessages.get(message);\n\t}\n\n\tprivate requestReason(model: Model<Api>): \"initial\" | \"resume\" | \"change\" {\n\t\tconst latest = latestRequestSnapshot(this.deps.sessionManager);\n\t\tif (!latest) return \"initial\";\n\t\treturn latest.api !== model.api || latest.provider !== model.provider || latest.modelId !== model.id\n\t\t\t? \"change\"\n\t\t\t: \"resume\";\n\t}\n\n\tprivate async onProviderRequestSnapshot(\n\t\tcontext: ProviderRequestSnapshotContext,\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tsignal?.throwIfAborted();\n\t\tconst flushed = this.deps.modelRouter.commitSessionBufferPrefix();\n\t\tfor (const [message, entryId] of flushed) this.notePersistedMessage(message, entryId);\n\t\tconst model = context.model as Model<Api>;\n\t\tconst ref = modelRef(model);\n\t\tconst requestId = context.requestId;\n\t\tthis.deps.sessionManager.appendRequestSnapshot({\n\t\t\trequestId,\n\t\t\treason: this.requestReason(model),\n\t\t\tapi: ref.api,\n\t\t\tprovider: ref.provider,\n\t\t\tmodelId: ref.modelId,\n\t\t\teffectiveConfigFingerprint: fingerprint({\n\t\t\t\tmodel: ref,\n\t\t\t\treasoning: context.reasoning,\n\t\t\t\tmaxTokens: context.maxTokens,\n\t\t\t\tattempt: context.attempt,\n\t\t\t}),\n\t\t\tsystemFingerprint: fingerprint(context.context.systemPrompt),\n\t\t\ttoolsFingerprint: fingerprint(context.context.tools ?? []),\n\t\t\thistoryFingerprint: fingerprint(providerHistoryMetadata(context.context.messages)),\n\t\t\tmessageEntryIds: messageEntryIds(this.deps.sessionManager),\n\t\t});\n\t\tsignal?.throwIfAborted();\n\t}\n\n\tprivate appendMessage: AppendMessage = (message) => {\n\t\tconst entryId = this.deps.sessionManager.appendMessage(message);\n\t\tthis.notePersistedMessage(message, entryId);\n\t\treturn entryId;\n\t};\n\n\tprivate async onToolCallStart(calls: readonly ToolCallStartContext[], signal?: AbortSignal): Promise<void> {\n\t\tif (calls.length === 0) return;\n\t\tsignal?.throwIfAborted();\n\t\tconst requestId = calls[0]!.requestId;\n\t\tif (calls.some((call) => call.requestId !== requestId)) {\n\t\t\tthrow new Error(\"Tool reservation wave contains multiple provider request identities\");\n\t\t}\n\n\t\tconst flushed = this.deps.modelRouter.commitSessionBuffer();\n\t\tfor (const [message, entryId] of flushed) this.notePersistedMessage(message, entryId);\n\t\tconst assistantMessage = calls[0]!.assistantMessage;\n\t\tconst assistantMessageEntryId = this.findPersistedMessageEntryId(assistantMessage);\n\t\tif (!assistantMessageEntryId) {\n\t\t\tthrow new Error(\"Foreground tool reservation rejected: assistant message is not canonically persisted.\");\n\t\t}\n\t\tconst identities: StartedToolIdentity[] = [];\n\t\tfor (const call of calls) {\n\t\t\tconst identity: StartedToolIdentity = {\n\t\t\t\trequestId,\n\t\t\t\tassistantMessageEntryId,\n\t\t\t\tcallId: call.callId,\n\t\t\t\ttoolName: call.toolName,\n\t\t\t};\n\t\t\tconst key = this.toolKey(identity);\n\t\t\tif (this.startedTools.has(key)) throw new Error(`Duplicate foreground tool reservation: ${call.callId}.`);\n\t\t\tidentities.push(identity);\n\t\t}\n\t\tthis.deps.sessionManager.appendForegroundToolStarts(\n\t\t\tidentities.map(\n\t\t\t\t({ requestId: identityRequestId, assistantMessageEntryId: messageEntryId, callId, toolName }) => ({\n\t\t\t\t\trequestId: identityRequestId,\n\t\t\t\t\tassistantMessageEntryId: messageEntryId,\n\t\t\t\t\tcallId,\n\t\t\t\t\ttoolName,\n\t\t\t\t}),\n\t\t\t),\n\t\t);\n\t\tfor (const identity of identities) this.startedTools.set(this.toolKey(identity), identity);\n\t\tfor (const identity of identities) {\n\t\t\tconst callKey = this.callKey(identity.callId, identity.toolName);\n\t\t\tconst pending = this.pendingToolsByCall.get(callKey) ?? new Set<string>();\n\t\t\tpending.add(this.toolKey(identity));\n\t\t\tthis.pendingToolsByCall.set(callKey, pending);\n\t\t}\n\t\tsignal?.throwIfAborted();\n\t}\n\n\tprivate toolKey(\n\t\tidentity: Pick<StartedToolIdentity, \"requestId\" | \"assistantMessageEntryId\" | \"callId\" | \"toolName\">,\n\t): string {\n\t\treturn [identity.requestId, identity.assistantMessageEntryId, identity.callId, identity.toolName].join(\"\\u0000\");\n\t}\n\n\tprivate callKey(callId: string, toolName: string): string {\n\t\treturn `${callId}\\u0000${toolName}`;\n\t}\n\n\t/** Called after the canonical message entry has been appended by AgentSession. */\n\tonMessagePersisted(message: AgentMessage, entryId: string): void {\n\t\tthis.notePersistedMessage(message, entryId);\n\t\tif (message.role !== \"toolResult\") return;\n\t\tif (this.completedResultMessages.has(message)) return;\n\t\tconst result = message as ToolResultMessage;\n\t\tconst pending = this.pendingToolsByCall.get(this.callKey(result.toolCallId, result.toolName));\n\t\tif (!pending || pending.size !== 1) return;\n\t\tconst key = pending.values().next().value as string;\n\t\tconst identity = this.startedTools.get(key);\n\t\tif (!identity) return;\n\t\tconst metadata = result.isError\n\t\t\t? { resultMessageEntryId: entryId, errorKind: result.errorKind ?? \"tool_failure\" }\n\t\t\t: { resultMessageEntryId: entryId };\n\t\tthis.deps.sessionManager.appendForegroundToolTerminal(\n\t\t\tidentity.requestId,\n\t\t\tidentity.assistantMessageEntryId,\n\t\t\tidentity.callId,\n\t\t\tidentity.toolName,\n\t\t\toutcomeForToolResult(result),\n\t\t\tmetadata,\n\t\t);\n\t\tthis.completedResultMessages.add(message);\n\t\tthis.startedTools.delete(key);\n\t\tthis.pendingToolsByCall.delete(this.callKey(result.toolCallId, result.toolName));\n\t}\n\n\t/**\n\t * Repair incomplete lifecycle records on construction/reload. The active branch is inspected first;\n\t * duplicate, mismatched, or out-of-order records are left untouched and reported rather than guessed.\n\t */\n\trepair(): string[] {\n\t\tconst inspection = this.deps.sessionManager.inspectSessionLifecycle();\n\t\tif (isAmbiguousInspection(inspection)) {\n\t\t\tconst warning = boundedWarning(\n\t\t\t\t\"Session lifecycle repair refused: duplicate, mismatched, or out-of-order records require manual review.\",\n\t\t\t);\n\t\t\tthis.deps.emitWarning(warning);\n\t\t\treturn [warning];\n\t\t}\n\t\tconst plan = this.deps.sessionManager.planSessionLifecycleRepair();\n\t\tconst index = this.deps.sessionManager.getSessionLifecycleIndex();\n\t\tconst warnings: string[] = [];\n\t\tfor (const closer of plan.toolClosers) {\n\t\t\tconst call = index.assistantToolCalls.find(\n\t\t\t\t(candidate) =>\n\t\t\t\t\tcandidate.assistantMessageEntryId === closer.assistantMessageEntryId &&\n\t\t\t\t\tcandidate.callId === closer.callId &&\n\t\t\t\t\tcandidate.requestId === closer.requestId,\n\t\t\t);\n\t\t\tif (!call) {\n\t\t\t\twarnings.push(\n\t\t\t\t\tboundedWarning(`Session lifecycle repair skipped tool ${closer.callId}: assistant call is missing.`),\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst synthetic = this.appendRepairResult(closer.toolName, closer.callId, closer.code);\n\t\t\tif (closer.sourceEntryId && closer.requestId !== undefined) {\n\t\t\t\tthis.deps.sessionManager.appendForegroundToolTerminal(\n\t\t\t\t\tcloser.requestId,\n\t\t\t\t\tcloser.assistantMessageEntryId,\n\t\t\t\t\tcloser.callId,\n\t\t\t\t\tcloser.toolName,\n\t\t\t\t\t\"error\",\n\t\t\t\t\t{ resultMessageEntryId: synthetic, errorKind: \"tool_failure\" },\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tfor (const promotion of plan.terminalPromotions) {\n\t\t\tconst record = index.toolsByIdentity.get(\n\t\t\t\tsessionLifecycleToolIdentityKey(promotion.requestId, promotion.assistantMessageEntryId, promotion.callId),\n\t\t\t);\n\t\t\t// A canonical immediate result has no foreground start and must remain terminal-free.\n\t\t\tif (!record?.start) continue;\n\t\t\tif (promotion.requestId === undefined) continue;\n\t\t\tthis.deps.sessionManager.appendForegroundToolTerminal(\n\t\t\t\tpromotion.requestId,\n\t\t\t\tpromotion.assistantMessageEntryId,\n\t\t\t\tpromotion.callId,\n\t\t\t\tpromotion.toolName,\n\t\t\t\tpromotion.outcome,\n\t\t\t\t{\n\t\t\t\t\tresultMessageEntryId: promotion.resultMessageEntryId,\n\t\t\t\t\t...(promotion.errorKind === undefined ? {} : { errorKind: promotion.errorKind }),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t\tfor (const closer of plan.compactionClosers) {\n\t\t\tthis.deps.sessionManager.appendCompactionEnd(closer.compactionId, \"interrupted\", {\n\t\t\t\terror: \"Compaction was interrupted before its terminal outcome was recorded.\",\n\t\t\t});\n\t\t}\n\t\tif (plan.toolClosers.length > 0 || plan.terminalPromotions.length > 0 || plan.compactionClosers.length > 0) {\n\t\t\tthis.deps.agent.state.messages = this.deps.sessionManager.buildSessionContext().messages;\n\t\t}\n\t\tfor (const warning of warnings) this.deps.emitWarning(warning);\n\t\treturn warnings;\n\t}\n\n\tprivate appendRepairResult(toolName: string, callId: string, code: string): string {\n\t\tconst message: ToolResultMessage = {\n\t\t\trole: \"toolResult\",\n\t\t\ttoolCallId: callId,\n\t\t\ttoolName,\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext:\n\t\t\t\t\t\tcode === \"TOOL_NOT_STARTED\"\n\t\t\t\t\t\t\t? \"The harness recorded that this tool call was never started. Treat it as not run and decide whether to retry.\"\n\t\t\t\t\t\t\t: \"The harness recorded that the tool outcome is unknown after interruption. Inspect the workspace before retrying or claiming completion.\",\n\t\t\t\t},\n\t\t\t],\n\t\t\tisError: true,\n\t\t\terrorKind: \"tool_failure\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\t\treturn this.appendMessage(message);\n\t}\n}\n"]}
|
|
@@ -61,6 +61,9 @@ export declare class ForegroundRecoveryController {
|
|
|
61
61
|
ownsSubmission(lease: ForegroundSubmissionLease | undefined): boolean;
|
|
62
62
|
releaseSubmission(lease: ForegroundSubmissionLease): void;
|
|
63
63
|
runAgentPrompt(messages: AgentMessage | AgentMessage[], submissionLease?: ForegroundSubmissionLease): Promise<void>;
|
|
64
|
+
/** Continue from the already canonical agent history without replaying the original user message. */
|
|
65
|
+
runAgentContinuation(submissionLease?: ForegroundSubmissionLease): Promise<void>;
|
|
66
|
+
private runAgentExecution;
|
|
64
67
|
wakeIdleWaiters(): void;
|
|
65
68
|
waitForIdle(): Promise<void>;
|
|
66
69
|
shutdown(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"foreground-recovery-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-recovery-controller.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"foreground-recovery-controller.d.ts","sourceRoot":"","sources":["../../src/core/foreground-recovery-controller.ts"],"names":[],"mappings":"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"}
|