@duetso/agent 0.1.80 → 0.1.82
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/package.json +1 -1
- package/dist/src/memory/observational.d.ts +8 -1
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +9 -3
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/session/session.d.ts +31 -13
- package/dist/src/session/session.d.ts.map +1 -1
- package/dist/src/session/session.js +63 -23
- package/dist/src/session/session.js.map +1 -1
- package/dist/src/tui/app.d.ts +25 -35
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +195 -2308
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/autocomplete-controller.d.ts +83 -0
- package/dist/src/tui/autocomplete-controller.d.ts.map +1 -0
- package/dist/src/tui/autocomplete-controller.js +308 -0
- package/dist/src/tui/autocomplete-controller.js.map +1 -0
- package/dist/src/tui/boot-screen.d.ts +64 -0
- package/dist/src/tui/boot-screen.d.ts.map +1 -0
- package/dist/src/tui/boot-screen.js +137 -0
- package/dist/src/tui/boot-screen.js.map +1 -0
- package/dist/src/tui/controllers.d.ts +39 -0
- package/dist/src/tui/controllers.d.ts.map +1 -0
- package/dist/src/tui/controllers.js +53 -0
- package/dist/src/tui/controllers.js.map +1 -0
- package/dist/src/tui/copy-controller.d.ts +75 -0
- package/dist/src/tui/copy-controller.d.ts.map +1 -0
- package/dist/src/tui/copy-controller.js +140 -0
- package/dist/src/tui/copy-controller.js.map +1 -0
- package/dist/src/tui/history-replay.d.ts +33 -0
- package/dist/src/tui/history-replay.d.ts.map +1 -0
- package/dist/src/tui/history-replay.js +47 -0
- package/dist/src/tui/history-replay.js.map +1 -0
- package/dist/src/tui/initial-prompt.d.ts +24 -0
- package/dist/src/tui/initial-prompt.d.ts.map +1 -0
- package/dist/src/tui/initial-prompt.js +29 -0
- package/dist/src/tui/initial-prompt.js.map +1 -0
- package/dist/src/tui/key-handlers.d.ts +49 -0
- package/dist/src/tui/key-handlers.d.ts.map +1 -0
- package/dist/src/tui/key-handlers.js +192 -0
- package/dist/src/tui/key-handlers.js.map +1 -0
- package/dist/src/tui/layout.d.ts +71 -0
- package/dist/src/tui/layout.d.ts.map +1 -0
- package/dist/src/tui/layout.js +245 -0
- package/dist/src/tui/layout.js.map +1 -0
- package/dist/src/tui/paste-controller.d.ts +90 -0
- package/dist/src/tui/paste-controller.d.ts.map +1 -0
- package/dist/src/tui/paste-controller.js +234 -0
- package/dist/src/tui/paste-controller.js.map +1 -0
- package/dist/src/tui/question-picker.d.ts +111 -0
- package/dist/src/tui/question-picker.d.ts.map +1 -0
- package/dist/src/tui/question-picker.js +308 -0
- package/dist/src/tui/question-picker.js.map +1 -0
- package/dist/src/tui/renderer-lifecycle.d.ts +20 -0
- package/dist/src/tui/renderer-lifecycle.d.ts.map +1 -0
- package/dist/src/tui/renderer-lifecycle.js +50 -0
- package/dist/src/tui/renderer-lifecycle.js.map +1 -0
- package/dist/src/tui/session-subscription.d.ts +30 -0
- package/dist/src/tui/session-subscription.d.ts.map +1 -0
- package/dist/src/tui/session-subscription.js +78 -0
- package/dist/src/tui/session-subscription.js.map +1 -0
- package/dist/src/tui/sidebar.d.ts +2 -2
- package/dist/src/tui/sidebar.d.ts.map +1 -1
- package/dist/src/tui/sidebar.js +1 -1
- package/dist/src/tui/sidebar.js.map +1 -1
- package/dist/src/tui/slash-commands.d.ts +23 -0
- package/dist/src/tui/slash-commands.d.ts.map +1 -0
- package/dist/src/tui/slash-commands.js +79 -0
- package/dist/src/tui/slash-commands.js.map +1 -0
- package/dist/src/tui/starter-section.d.ts +99 -0
- package/dist/src/tui/starter-section.d.ts.map +1 -0
- package/dist/src/tui/starter-section.js +254 -0
- package/dist/src/tui/starter-section.js.map +1 -0
- package/dist/src/tui/status-controller.d.ts +94 -0
- package/dist/src/tui/status-controller.d.ts.map +1 -0
- package/dist/src/tui/status-controller.js +186 -0
- package/dist/src/tui/status-controller.js.map +1 -0
- package/dist/src/tui/step-renderer.d.ts +55 -0
- package/dist/src/tui/step-renderer.d.ts.map +1 -0
- package/dist/src/tui/step-renderer.js +246 -0
- package/dist/src/tui/step-renderer.js.map +1 -0
- package/dist/src/tui/transcript-writer.d.ts +63 -0
- package/dist/src/tui/transcript-writer.d.ts.map +1 -0
- package/dist/src/tui/transcript-writer.js +130 -0
- package/dist/src/tui/transcript-writer.js.map +1 -0
- package/dist/src/turn-runner/turn-runner.d.ts +77 -1
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +150 -12
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/dist/src/turn-runner/usage-accounting.d.ts +18 -1
- package/dist/src/turn-runner/usage-accounting.d.ts.map +1 -1
- package/dist/src/turn-runner/usage-accounting.js +48 -27
- package/dist/src/turn-runner/usage-accounting.js.map +1 -1
- package/dist/src/types/protocol.d.ts +48 -17
- package/dist/src/types/protocol.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { TextRenderable, } from "@opentui/core";
|
|
2
|
+
import { COLORS } from "./theme.js";
|
|
3
|
+
/**
|
|
4
|
+
* Owns every write to the transcript ScrollBox plus the `/diag` key+selection
|
|
5
|
+
* event log. Centralizing here keeps the post-teardown try/catch that swallows
|
|
6
|
+
* "TextBuffer is destroyed" in exactly one place (v0.1.68 fix), and gives the
|
|
7
|
+
* status/hint controllers a single `isDestroyed()` source of truth.
|
|
8
|
+
*/
|
|
9
|
+
export class TranscriptWriter {
|
|
10
|
+
renderer;
|
|
11
|
+
transcript;
|
|
12
|
+
options;
|
|
13
|
+
destroyed = false;
|
|
14
|
+
hasRenderedAnyBlock = false;
|
|
15
|
+
keyDiagnostics = false;
|
|
16
|
+
log = [];
|
|
17
|
+
constructor(renderer, transcript, options) {
|
|
18
|
+
this.renderer = renderer;
|
|
19
|
+
this.transcript = transcript;
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
// ---- transcript writes ---------------------------------------------------
|
|
23
|
+
/** Append a single line. Empty content is ignored so callers can guard with
|
|
24
|
+
* raw input without producing blank rows; use `addLine` instead when a
|
|
25
|
+
* spacer is wanted and a renderable handle is needed. */
|
|
26
|
+
appendLine(content, fg) {
|
|
27
|
+
if (!content)
|
|
28
|
+
return;
|
|
29
|
+
if (this.destroyed)
|
|
30
|
+
return;
|
|
31
|
+
try {
|
|
32
|
+
const line = new TextRenderable(this.renderer, { content, fg });
|
|
33
|
+
this.transcript.add(line);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (isTextBufferDestroyedError(error)) {
|
|
37
|
+
this.handleBufferDestroyed();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Append a labelled block. `beginBlock()` inserts a blank separator before
|
|
44
|
+
* every block except the first so distinct steps stay visually distinct. */
|
|
45
|
+
appendBlock(label, body, fg) {
|
|
46
|
+
this.beginBlock();
|
|
47
|
+
const text = label ? `${label}\n${body}` : body;
|
|
48
|
+
for (const line of text.split("\n"))
|
|
49
|
+
this.appendLine(line, fg);
|
|
50
|
+
}
|
|
51
|
+
beginBlock() {
|
|
52
|
+
if (this.hasRenderedAnyBlock)
|
|
53
|
+
this.appendLine(" ", COLORS.hint);
|
|
54
|
+
this.hasRenderedAnyBlock = true;
|
|
55
|
+
}
|
|
56
|
+
/** Append a line and return the renderable handle. Used by the starter
|
|
57
|
+
* block (which destroys its own refs on dismiss) and substitutes a single
|
|
58
|
+
* space for empty content so the renderable still occupies a row. */
|
|
59
|
+
addLine(content, fg) {
|
|
60
|
+
const line = new TextRenderable(this.renderer, { content: content || " ", fg });
|
|
61
|
+
this.transcript.add(line);
|
|
62
|
+
return line;
|
|
63
|
+
}
|
|
64
|
+
// ---- transcript log ------------------------------------------------------
|
|
65
|
+
/** Records a user/agent message body in the parallel log read by `/copy`
|
|
66
|
+
* and the copy keystroke. Trims and drops empty payloads so noisy
|
|
67
|
+
* presentation-only writes (spacers, etc.) do not pollute the log. */
|
|
68
|
+
recordEntry(kind, text) {
|
|
69
|
+
const trimmed = text.trim();
|
|
70
|
+
if (!trimmed)
|
|
71
|
+
return;
|
|
72
|
+
this.log.push({ kind, text: trimmed });
|
|
73
|
+
}
|
|
74
|
+
entries() {
|
|
75
|
+
return this.log;
|
|
76
|
+
}
|
|
77
|
+
// ---- teardown ------------------------------------------------------------
|
|
78
|
+
/** Flipped from the renderer `destroy` handler. Subsequent transcript
|
|
79
|
+
* writes short-circuit instead of throwing from a destroyed buffer. */
|
|
80
|
+
markDestroyed() {
|
|
81
|
+
this.destroyed = true;
|
|
82
|
+
}
|
|
83
|
+
isDestroyed() {
|
|
84
|
+
return this.destroyed;
|
|
85
|
+
}
|
|
86
|
+
// ---- /diag key + selection logging ---------------------------------------
|
|
87
|
+
setKeyDiagnosticsEnabled(enabled) {
|
|
88
|
+
this.keyDiagnostics = enabled;
|
|
89
|
+
}
|
|
90
|
+
isKeyDiagnosticsEnabled() {
|
|
91
|
+
return this.keyDiagnostics;
|
|
92
|
+
}
|
|
93
|
+
logKey(label, key) {
|
|
94
|
+
if (!this.keyDiagnostics)
|
|
95
|
+
return;
|
|
96
|
+
const flags = [];
|
|
97
|
+
if (key.ctrl)
|
|
98
|
+
flags.push("ctrl");
|
|
99
|
+
if (key.shift)
|
|
100
|
+
flags.push("shift");
|
|
101
|
+
if (key.meta)
|
|
102
|
+
flags.push("meta");
|
|
103
|
+
if (key.super)
|
|
104
|
+
flags.push("super");
|
|
105
|
+
if (key.option)
|
|
106
|
+
flags.push("option");
|
|
107
|
+
const selection = this.options.getLastSelectionText();
|
|
108
|
+
this.appendBlock("[diag]", `${label} name=${JSON.stringify(key.name)} flags=[${flags.join(",")}] sequence=${JSON.stringify(key.sequence)} source=${key.source} | lastSelection=${selection.length}c rendererSel=${this.renderer.hasSelection ? "yes" : "no"}`, COLORS.hint);
|
|
109
|
+
}
|
|
110
|
+
logSelection(text) {
|
|
111
|
+
if (!this.keyDiagnostics)
|
|
112
|
+
return;
|
|
113
|
+
this.appendBlock("[diag]", `selection event: ${text.length} chars — ${JSON.stringify(text.slice(0, 80))}`, COLORS.hint);
|
|
114
|
+
}
|
|
115
|
+
handleBufferDestroyed() {
|
|
116
|
+
this.destroyed = true;
|
|
117
|
+
this.options.onBufferDestroyed?.();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* OpenTUI's TextBuffer throws a plain Error with this exact message from its
|
|
122
|
+
* `guard()` method when any setter is touched after destroy. We sniff the
|
|
123
|
+
* message to distinguish post-teardown races (swallow) from real bugs
|
|
124
|
+
* (rethrow). Exported so the status/hint controllers can share the same
|
|
125
|
+
* sniff without re-importing OpenTUI internals.
|
|
126
|
+
*/
|
|
127
|
+
export function isTextBufferDestroyedError(error) {
|
|
128
|
+
return error instanceof Error && error.message === "TextBuffer is destroyed";
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=transcript-writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transcript-writer.js","sourceRoot":"","sources":["../../../src/tui/transcript-writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAcpC;;;;;GAKG;AACH,MAAM,OAAO,gBAAgB;IAOR;IACA;IACA;IARX,SAAS,GAAG,KAAK,CAAC;IAClB,mBAAmB,GAAG,KAAK,CAAC;IAC5B,cAAc,GAAG,KAAK,CAAC;IACd,GAAG,GAAsB,EAAE,CAAC;IAE7C,YACmB,QAAqB,EACrB,UAA+B,EAC/B,OAAgC;QAFhC,aAAQ,GAAR,QAAQ,CAAa;QACrB,eAAU,GAAV,UAAU,CAAqB;QAC/B,YAAO,GAAP,OAAO,CAAyB;IAChD,CAAC;IAEJ,6EAA6E;IAE7E;;8DAE0D;IAC1D,UAAU,CAAC,OAAe,EAAE,EAAU;QACpC,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;YAChE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,0BAA0B,CAAC,KAAK,CAAC,EAAE,CAAC;gBACtC,IAAI,CAAC,qBAAqB,EAAE,CAAC;gBAC7B,OAAO;YACT,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;iFAC6E;IAC7E,WAAW,CAAC,KAAoB,EAAE,IAAY,EAAE,EAAU;QACxD,IAAI,CAAC,UAAU,EAAE,CAAC;QAClB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAChD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YAAE,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,UAAU;QACR,IAAI,IAAI,CAAC,mBAAmB;YAAE,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAClC,CAAC;IAED;;0EAEsE;IACtE,OAAO,CAAC,OAAe,EAAE,EAAU;QACjC,MAAM,IAAI,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6EAA6E;IAE7E;;2EAEuE;IACvE,WAAW,CAAC,IAA6B,EAAE,IAAY;QACrD,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,GAAG,CAAC;IAClB,CAAC;IAED,6EAA6E;IAE7E;4EACwE;IACxE,aAAa;QACX,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,6EAA6E;IAE7E,wBAAwB,CAAC,OAAgB;QACvC,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;IAChC,CAAC;IAED,uBAAuB;QACrB,OAAO,IAAI,CAAC,cAAc,CAAC;IAC7B,CAAC;IAED,MAAM,CAAC,KAAa,EAAE,GAAa;QACjC,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAO;QACjC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,IAAI,GAAG,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,GAAG,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,GAAG,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,GAAG,CAAC,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC;QACtD,IAAI,CAAC,WAAW,CACd,QAAQ,EACR,GAAG,KAAK,SAAS,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,GAAG,CAAC,MAAM,oBAAoB,SAAS,CAAC,MAAM,iBAAiB,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,EAClO,MAAM,CAAC,IAAI,CACZ,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAO;QACjC,IAAI,CAAC,WAAW,CACd,QAAQ,EACR,oBAAoB,IAAI,CAAC,MAAM,YAAY,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAC9E,MAAM,CAAC,IAAI,CACZ,CAAC;IACJ,CAAC;IAEO,qBAAqB;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC;IACrC,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAc;IACvD,OAAO,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,KAAK,yBAAyB,CAAC;AAC/E,CAAC"}
|
|
@@ -6,6 +6,7 @@ import { MemoryContextCache } from "../memory/store.js";
|
|
|
6
6
|
import type { TurnRunnerConfig } from "../types/config.js";
|
|
7
7
|
import type { TurnAgentFile, TurnAnswerCommand, TurnContextWindowUsage, TurnEditFollowUpQueueCommand, TurnEvent, TurnInterruptCommand, TurnMode, TurnPromptCommand, TurnState, TurnTokenUsage, TurnStartCommand, TurnTerminalEvent, TurnCommand, TurnOptions } from "../types/protocol.js";
|
|
8
8
|
import type { StateMachineAgentState } from "../types/state-machine.js";
|
|
9
|
+
import { type WireGuardHorizon } from "./wire-shaping.js";
|
|
9
10
|
import { type TurnRunnerControlResult } from "./tools.js";
|
|
10
11
|
import { SkillContext } from "./skill-context.js";
|
|
11
12
|
import { type StateAgentHandle } from "./state-machine-controller.js";
|
|
@@ -76,6 +77,21 @@ export declare class TurnRunner {
|
|
|
76
77
|
private started;
|
|
77
78
|
/** Aggregates model usage across parent agents, state agents, and memory work for one turn chain. */
|
|
78
79
|
private turnUsage?;
|
|
80
|
+
/**
|
|
81
|
+
* Snapshot of the latest parent assistant `message_end`'s bar fields.
|
|
82
|
+
* State-agent and terminal emissions reuse these so the bar/breakdown
|
|
83
|
+
* does not jitter mid-turn when only a state agent advanced cost.
|
|
84
|
+
*
|
|
85
|
+
* Always set by the time a state agent runs in production: the parent
|
|
86
|
+
* worker selects a state via a real LLM call, which emits `message_end`
|
|
87
|
+
* and populates this. Exposed as `protected` for test harnesses that
|
|
88
|
+
* stub `runAgentWorker` past the real parent path and need to seed the
|
|
89
|
+
* snapshot before driving a state-agent emission.
|
|
90
|
+
*/
|
|
91
|
+
protected lastParentUsageSnapshot?: {
|
|
92
|
+
effectiveContextWindow: number;
|
|
93
|
+
contextWindowUsage: TurnContextWindowUsage;
|
|
94
|
+
};
|
|
79
95
|
/** Ensures persisted memory hydrates once before the first turn that needs it. */
|
|
80
96
|
private memoryLoaded;
|
|
81
97
|
/** MCP servers connected during start(). Disposed on runner.dispose(). */
|
|
@@ -162,7 +178,56 @@ export declare class TurnRunner {
|
|
|
162
178
|
protected runAgentMode(state: TurnState, prompt: string, images?: ImageContent[]): Promise<TurnTerminalEvent>;
|
|
163
179
|
protected runAgentWorker(input: AgentWorkerInput): Promise<AgentWorkerResult>;
|
|
164
180
|
private setParentAgentRunning;
|
|
181
|
+
/**
|
|
182
|
+
* Records the parent worker's per-call usage into the running turn
|
|
183
|
+
* aggregate and emits a `usage` event so consumers see cost tick after
|
|
184
|
+
* each parent agent boundary. Recording at the worker boundary (instead
|
|
185
|
+
* of per `message_end`) keeps the same behavior when test harnesses
|
|
186
|
+
* stub `runAgentWorker` and supply `terminal.usage` directly without
|
|
187
|
+
* running a real pi `Agent`.
|
|
188
|
+
*/
|
|
165
189
|
private runAgentWorkerWithUsage;
|
|
190
|
+
/**
|
|
191
|
+
* Inspect the most recent assistant message after a parent prompt and,
|
|
192
|
+
* when it carries a provider context-overflow error, mutate the agent
|
|
193
|
+
* state and the sticky `wireGuardHorizon` so that a subsequent re-prompt
|
|
194
|
+
* sends roughly the newer half of observable history.
|
|
195
|
+
*
|
|
196
|
+
* Returns `true` only when recovery actually happened — meaning the
|
|
197
|
+
* horizon advanced past at least one observable message. Callers use
|
|
198
|
+
* the return value as a single-shot gate: invoke once after the first
|
|
199
|
+
* `agent.prompt(...)` and, on `true`, resume the turn via
|
|
200
|
+
* `agent.continue()` exactly once. The helper never inspects the
|
|
201
|
+
* failing message again, so a second overflow on the retry falls
|
|
202
|
+
* through naturally as a `failed` turn.
|
|
203
|
+
*
|
|
204
|
+
* Mutations on success:
|
|
205
|
+
* - pops the failure assistant message that pi-agent pushed for the
|
|
206
|
+
* overflow (an empty-text marker with `stopReason: "error"` and
|
|
207
|
+
* `errorMessage`) so the retry's transcript does not carry it.
|
|
208
|
+
* `agent.continue()`'s own run lifecycle resets `errorMessage`
|
|
209
|
+
* before the retry, so we do not clear it here;
|
|
210
|
+
* - advances `this.wireGuardHorizon.evictionHorizon` to the smallest
|
|
211
|
+
* value at which `findEvictionHorizon` reports the dispatched list
|
|
212
|
+
* shrinks to at most `floor(n / 2)` observable messages, where `n`
|
|
213
|
+
* excludes the failure message itself.
|
|
214
|
+
*
|
|
215
|
+
* Emits one informational `system` event describing the compaction so
|
|
216
|
+
* surfaces can show a notice. The message reports the *actual*
|
|
217
|
+
* post-eviction drop count (after `MIN_HISTORY_TAIL` clamping and
|
|
218
|
+
* orphan-head skipping), not the half-of-n target.
|
|
219
|
+
*
|
|
220
|
+
* Returns `false` when:
|
|
221
|
+
* - the parent agent did not fail (`errorMessage` is unset);
|
|
222
|
+
* - the failure is not a context overflow (rate limit, transport
|
|
223
|
+
* error, etc. — left as-is for the caller to surface);
|
|
224
|
+
* - no horizon advance is possible (e.g. the existing horizon already
|
|
225
|
+
* covers every evictable message, or the history is too short to
|
|
226
|
+
* satisfy `MIN_HISTORY_TAIL`). In those cases retrying with the
|
|
227
|
+
* same context would still overflow, so we accept the failure
|
|
228
|
+
* instead of paying a second pointless request.
|
|
229
|
+
*/
|
|
230
|
+
protected tryRecoverFromContextOverflow(agent: Agent): boolean;
|
|
166
231
|
protected updateMemoryAfterAgentRun(messages: AgentMessage[], options: TurnOptions | undefined): Promise<void>;
|
|
167
232
|
private refreshMemoryContextPack;
|
|
168
233
|
/**
|
|
@@ -173,7 +238,7 @@ export declare class TurnRunner {
|
|
|
173
238
|
resolveMemoryActorModel(options: TurnOptions | undefined): string;
|
|
174
239
|
protected createAgent(input: AgentConfigInput, onControlResult?: (result: TurnRunnerControlResult) => void): Agent;
|
|
175
240
|
protected createMemoryTransform(): (messages: AgentMessage[]) => Promise<AgentMessage[]>;
|
|
176
|
-
|
|
241
|
+
protected readonly wireGuardHorizon: WireGuardHorizon;
|
|
177
242
|
getSkills(): Promise<readonly Skill[]>;
|
|
178
243
|
/** System-prompt files (AGENTS.md by default) that resolved on disk for this session. */
|
|
179
244
|
getResolvedAgentFiles(): Promise<readonly TurnAgentFile[]>;
|
|
@@ -201,6 +266,17 @@ export declare class TurnRunner {
|
|
|
201
266
|
protected recordUsage(usage?: TurnTokenUsage | Usage): void;
|
|
202
267
|
protected emitAgentEvent(event: AgentEvent): void;
|
|
203
268
|
protected emitParentAgentEvent(event: AgentEvent): void;
|
|
269
|
+
/**
|
|
270
|
+
* Emit a `usage` event reflecting the latest `this.turnUsage`. Reuses the
|
|
271
|
+
* most recent parent context-window snapshot for the bar/breakdown so
|
|
272
|
+
* mid-turn ticks (parent worker finish, state-agent finish) surface cost
|
|
273
|
+
* without jittering the parent context fields. No-ops when no usage has
|
|
274
|
+
* been recorded yet or before the first parent emission — both are only
|
|
275
|
+
* possible during construction/teardown or in test harnesses; a real
|
|
276
|
+
* parent worker always sets the snapshot before its terminal usage is
|
|
277
|
+
* recorded.
|
|
278
|
+
*/
|
|
279
|
+
protected emitTurnUsage(): void;
|
|
204
280
|
/**
|
|
205
281
|
* Estimate the per-segment occupancy of the parent agent's input before
|
|
206
282
|
* reconciliation with provider `totalTokens`. System prompt and memory
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-runner.d.ts","sourceRoot":"","sources":["../../../src/turn-runner/turn-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"turn-runner.d.ts","sourceRoot":"","sources":["../../../src/turn-runner/turn-runner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,SAAS,EACf,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,KAAK,EACX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iCAAiC,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAgBlD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAMxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EACV,aAAa,EACb,iBAAiB,EACjB,sBAAsB,EACtB,4BAA4B,EAC5B,SAAS,EAET,oBAAoB,EACpB,QAAQ,EACR,iBAAiB,EAEjB,SAAS,EACT,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,WAAW,EAEZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAGxE,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAML,KAAK,uBAAuB,EAC7B,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,+BAA+B,CAAC;AAIvC,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAE1D,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,SAAS,CAAC;IACjB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,OAAO,EAAE,uBAAuB,CAAC;CAClC;AAUD;;;;;;;;GAQG;AACH,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,sBAAsB,EAC5B,WAAW,EAAE,MAAM,GAClB,sBAAsB,CAuDxB;AAED,qBAAa,UAAU;IAwDT,QAAQ,CAAC,MAAM,EAAE,gBAAgB;IAvD7C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+B;IAC7D,iFAAiF;IACjF,SAAS,CAAC,QAAQ,CAAC,MAAM,qBAA4B;IACrD,mEAAmE;IACnE,OAAO,CAAC,iBAAiB,CAAC,CAA0B;IACpD;;;;OAIG;IACH,OAAO,CAAC,WAAW,CAAC,CAAQ;IAC5B,2FAA2F;IAC3F,OAAO,CAAC,kBAAkB,CAAS;IACnC,2EAA2E;IAC3E,OAAO,CAAC,mBAAmB,CAA6C;IACxE,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAChE,qFAAqF;IACrF,OAAO,CAAC,qBAAqB,CAAC,CAA6B;IAC3D,sFAAsF;IACtF,OAAO,CAAC,mBAAmB,CAAC,CAAoB;IAChD;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB,CAAC,CAA6B;IACvD,sFAAsF;IACtF,OAAO,CAAC,KAAK,CAAC,CAAY;IAC1B,yEAAyE;IACzE,OAAO,CAAC,OAAO,CAAS;IACxB,qGAAqG;IACrG,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC;;;;;;;;;;OAUG;IACH,SAAS,CAAC,uBAAuB,CAAC,EAAE;QAClC,sBAAsB,EAAE,MAAM,CAAC;QAC/B,kBAAkB,EAAE,sBAAsB,CAAC;KAC5C,CAAC;IACF,kFAAkF;IAClF,OAAO,CAAC,YAAY,CAAS;IAC7B,0EAA0E;IAC1E,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,SAAS,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;gBAEzB,MAAM,EAAE,gBAAgB;IAQvC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAW9B,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAOhD,iBAAiB,CAAC,OAAO,EAAE,4BAA4B,GAAG,IAAI;IAK9D;;;;;;;OAOG;IACG,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,SAAS,CAAC;IAqBpD,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAsB9C,YAAY;YA2BZ,kBAAkB;IAWhC,OAAO,CAAC,6BAA6B;IAyBrC,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,sCAAsC;YAWhC,oCAAoC;YAyBpC,uBAAuB;IA8BrC,OAAO,CAAC,oBAAoB;IAe5B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,6BAA6B;IAiBrC,OAAO,CAAC,4BAA4B;IAMpC,OAAO,CAAC,sBAAsB;IAQ9B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,0BAA0B;IAKlC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAQ5B,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,iBAAiB;IAIzB,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,IAAI;IA4B/C,SAAS,CAAC,IAAI,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAMtC,OAAO,CAAC,0BAA0B;cAMlB,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;cAsB9D,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;cAU9D,IAAI,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAoBlD,OAAO,CAAC,+BAA+B;IAgCvC,OAAO,CAAC,0BAA0B;YAuBpB,uBAAuB;YA2BvB,8BAA8B;IA8C5C,SAAS,CAAC,sBAAsB,CAAC,KAAK,EAAE;QACtC,KAAK,EAAE,sBAAsB,CAAC;QAC9B,MAAM,EAAE,MAAM,CAAC;KAChB,GAAG,gBAAgB;IA6DpB,OAAO,CAAC,kBAAkB;IAO1B,QAAQ,IAAI,SAAS,GAAG,SAAS;IAKjC,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,gBAAgB;IAKxB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,cAAc;IAMtB,SAAS,CAAC,kBAAkB,IAAI,KAAK;IAOrC,OAAO,CAAC,qBAAqB;cAyBb,uCAAuC,CAAC,KAAK,EAAE;QAC7D,KAAK,EAAE,SAAS,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;QACxB,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;KAClC,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAYhB,gCAAgC;IA2D9C,SAAS,CAAC,WAAW,CACnB,IAAI,EAAE,QAAQ,EACd,WAAW,CAAC,EAAE,MAAM,GACnB;QACD,KAAK,EAAE,SAAS,EAAE,CAAC;KACpB;IA6CD,OAAO,CAAC,4BAA4B;cAMpB,YAAY,CAC1B,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,EAAE,GACtB,OAAO,CAAC,iBAAiB,CAAC;cAYb,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAyEnF,OAAO,CAAC,qBAAqB;IAK7B;;;;;;;OAOG;YACW,uBAAuB;IAOrC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACH,SAAS,CAAC,6BAA6B,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;cAiC9C,yBAAyB,CACvC,QAAQ,EAAE,YAAY,EAAE,EACxB,OAAO,EAAE,WAAW,GAAG,SAAS,GAC/B,OAAO,CAAC,IAAI,CAAC;YA4BF,wBAAwB;IAkBtC;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM;IAIjE,SAAS,CAAC,WAAW,CACnB,KAAK,EAAE,gBAAgB,EACvB,eAAe,CAAC,EAAE,CAAC,MAAM,EAAE,uBAAuB,KAAK,IAAI,GAC1D,KAAK;IA8BR,SAAS,CAAC,qBAAqB;IA0B/B,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAA0B;IAEzE,SAAS,IAAI,OAAO,CAAC,SAAS,KAAK,EAAE,CAAC;IAK5C,yFAAyF;IACnF,qBAAqB,IAAI,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC;IAKhE,oFAAoF;IAC9E,kBAAkB,IAAI,OAAO,CAAC,SAAS,cAAc,EAAE,CAAC;IAK9D,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;YAI/B,kBAAkB;IAIhC;;;;OAIG;YACW,yBAAyB;YAKzB,kBAAkB;IA+BhC,SAAS,CAAC,wCAAwC,CAAC,KAAK,CAAC,EAAE;QACzD,MAAM,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;QACnC,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;KAC3B,GAAG,MAAM;IAIV,OAAO,CAAC,eAAe;IAWvB;;;OAGG;IACH,kBAAkB,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW;IAY1E,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,KAAK,GAAG,IAAI;IAI3D,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IASjD,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAmBvD;;;;;;;;;OASG;IACH,SAAS,CAAC,aAAa,IAAI,IAAI;IAU/B;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,0BAA0B;;;;;;IAYpC;;;;;;;OAOG;IACH,SAAS,CAAC,sBAAsB,IAAI,MAAM;IAK1C;;;;;;OAMG;IACH,SAAS,CAAC,uBAAuB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM;CAIhE"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Agent, } from "@earendil-works/pi-agent-core";
|
|
2
|
-
import { getEnvApiKey } from "@earendil-works/pi-ai";
|
|
2
|
+
import { getEnvApiKey, isContextOverflow, } from "@earendil-works/pi-ai";
|
|
3
3
|
import dedent from "dedent";
|
|
4
4
|
import { assistantText } from "../core/serializer.js";
|
|
5
5
|
import { toXML } from "../lib/xml.js";
|
|
6
|
-
import { createObservationalContextTransform, DEFAULT_EFFECTIVE_CONTEXT, estimateTokens, resolveObservationalMemorySettings, updateObservationalMemory, } from "../memory/observational.js";
|
|
6
|
+
import { createObservationalContextTransform, DEFAULT_EFFECTIVE_CONTEXT, estimateTokens, resolveObservationalMemorySettings, stripObservationalContextMessages, updateObservationalMemory, } from "../memory/observational.js";
|
|
7
7
|
import { rebuildMemoryContextPack } from "../memory/context-pack.js";
|
|
8
8
|
import { createEmbeddingClient } from "../memory/embedding.js";
|
|
9
9
|
import { loadStoredMemory } from "../memory/storage.js";
|
|
@@ -11,7 +11,7 @@ import { MemoryContextCache } from "../memory/store.js";
|
|
|
11
11
|
import { DEFAULT_CLI_MEMORY_MODEL, DEFAULT_CLI_MODEL, resolveModelName, } from "../model-resolution/resolver.js";
|
|
12
12
|
import { agentEventToTurnEvents, agentMessageText } from "./agent-events.js";
|
|
13
13
|
import { createStateMachineSystemPromptLayer } from "./prompts.js";
|
|
14
|
-
import { calculateWireBytes, createInitialHorizon } from "./wire-shaping.js";
|
|
14
|
+
import { applyEvictionHorizon, calculateWireBytes, createInitialHorizon, findEvictionHorizon, } from "./wire-shaping.js";
|
|
15
15
|
import { createDefaultTurnRunnerTools, createTurnRunnerTools, formatCarriedTodosReminder, isTurnRunnerControlResult, } from "./tools.js";
|
|
16
16
|
import { connectMcpServers } from "./mcp.js";
|
|
17
17
|
import { SkillContext } from "./skill-context.js";
|
|
@@ -125,6 +125,18 @@ export class TurnRunner {
|
|
|
125
125
|
started = false;
|
|
126
126
|
/** Aggregates model usage across parent agents, state agents, and memory work for one turn chain. */
|
|
127
127
|
turnUsage;
|
|
128
|
+
/**
|
|
129
|
+
* Snapshot of the latest parent assistant `message_end`'s bar fields.
|
|
130
|
+
* State-agent and terminal emissions reuse these so the bar/breakdown
|
|
131
|
+
* does not jitter mid-turn when only a state agent advanced cost.
|
|
132
|
+
*
|
|
133
|
+
* Always set by the time a state agent runs in production: the parent
|
|
134
|
+
* worker selects a state via a real LLM call, which emits `message_end`
|
|
135
|
+
* and populates this. Exposed as `protected` for test harnesses that
|
|
136
|
+
* stub `runAgentWorker` past the real parent path and need to seed the
|
|
137
|
+
* snapshot before driving a state-agent emission.
|
|
138
|
+
*/
|
|
139
|
+
lastParentUsageSnapshot;
|
|
128
140
|
/** Ensures persisted memory hydrates once before the first turn that needs it. */
|
|
129
141
|
memoryLoaded = false;
|
|
130
142
|
/** MCP servers connected during start(). Disposed on runner.dispose(). */
|
|
@@ -215,7 +227,16 @@ export class TurnRunner {
|
|
|
215
227
|
terminal = await this.drainQueuedTurnCommands(terminal);
|
|
216
228
|
terminal = { ...terminal, state: this.snapshotState(terminal.state) };
|
|
217
229
|
if (this.turnUsage) {
|
|
218
|
-
terminal = {
|
|
230
|
+
terminal = {
|
|
231
|
+
...terminal,
|
|
232
|
+
usage: this.turnUsage,
|
|
233
|
+
...(this.lastParentUsageSnapshot
|
|
234
|
+
? {
|
|
235
|
+
effectiveContextWindow: this.lastParentUsageSnapshot.effectiveContextWindow,
|
|
236
|
+
contextWindowUsage: this.lastParentUsageSnapshot.contextWindowUsage,
|
|
237
|
+
}
|
|
238
|
+
: {}),
|
|
239
|
+
};
|
|
219
240
|
}
|
|
220
241
|
this.setState(terminal.state);
|
|
221
242
|
this.emit(terminal);
|
|
@@ -616,8 +637,6 @@ export class TurnRunner {
|
|
|
616
637
|
});
|
|
617
638
|
let unsubscribe;
|
|
618
639
|
const finish = () => {
|
|
619
|
-
const usage = usageFromMessages(agent.state.messages);
|
|
620
|
-
this.recordUsage(usage);
|
|
621
640
|
if (control.type === "ask_user_question") {
|
|
622
641
|
return { type: "ask", questions: control.questions };
|
|
623
642
|
}
|
|
@@ -641,6 +660,11 @@ export class TurnRunner {
|
|
|
641
660
|
return { type: "failed", error: String(error) };
|
|
642
661
|
}
|
|
643
662
|
finally {
|
|
663
|
+
// Record state-agent usage on every exit path — success, error, or
|
|
664
|
+
// interrupt — so partial work still flows into the turn aggregate
|
|
665
|
+
// and ticks the sidebar cost via the emitted `usage` event.
|
|
666
|
+
this.recordUsage(usageFromMessages(agent.state.messages));
|
|
667
|
+
this.emitTurnUsage();
|
|
644
668
|
unsubscribe?.();
|
|
645
669
|
}
|
|
646
670
|
},
|
|
@@ -876,6 +900,17 @@ export class TurnRunner {
|
|
|
876
900
|
let interruptedDuringPrompt;
|
|
877
901
|
try {
|
|
878
902
|
await agent.prompt(input.prompt, input.images);
|
|
903
|
+
// Single-shot recovery: if the provider rejected the first attempt
|
|
904
|
+
// with a context-overflow error, advance the sticky wire-shaping
|
|
905
|
+
// horizon so the next send carries roughly the newer half of
|
|
906
|
+
// history, then resume the same turn via `agent.continue()`.
|
|
907
|
+
// Continuing (rather than re-prompting) keeps the existing user
|
|
908
|
+
// message at the tail instead of appending a duplicate. A
|
|
909
|
+
// still-too-big second attempt falls through as `failed` — no
|
|
910
|
+
// further retries.
|
|
911
|
+
if (this.tryRecoverFromContextOverflow(agent)) {
|
|
912
|
+
await agent.continue();
|
|
913
|
+
}
|
|
879
914
|
}
|
|
880
915
|
catch (error) {
|
|
881
916
|
interruptedDuringPrompt = this.consumeInterruptedTerminal();
|
|
@@ -926,11 +961,89 @@ export class TurnRunner {
|
|
|
926
961
|
this.parentAgentRunning = running;
|
|
927
962
|
this.snapshotActiveAgentState();
|
|
928
963
|
}
|
|
964
|
+
/**
|
|
965
|
+
* Records the parent worker's per-call usage into the running turn
|
|
966
|
+
* aggregate and emits a `usage` event so consumers see cost tick after
|
|
967
|
+
* each parent agent boundary. Recording at the worker boundary (instead
|
|
968
|
+
* of per `message_end`) keeps the same behavior when test harnesses
|
|
969
|
+
* stub `runAgentWorker` and supply `terminal.usage` directly without
|
|
970
|
+
* running a real pi `Agent`.
|
|
971
|
+
*/
|
|
929
972
|
async runAgentWorkerWithUsage(input) {
|
|
930
973
|
const result = await this.runAgentWorker(input);
|
|
931
974
|
this.recordUsage(result.terminal.usage);
|
|
975
|
+
this.emitTurnUsage();
|
|
932
976
|
return result;
|
|
933
977
|
}
|
|
978
|
+
/**
|
|
979
|
+
* Inspect the most recent assistant message after a parent prompt and,
|
|
980
|
+
* when it carries a provider context-overflow error, mutate the agent
|
|
981
|
+
* state and the sticky `wireGuardHorizon` so that a subsequent re-prompt
|
|
982
|
+
* sends roughly the newer half of observable history.
|
|
983
|
+
*
|
|
984
|
+
* Returns `true` only when recovery actually happened — meaning the
|
|
985
|
+
* horizon advanced past at least one observable message. Callers use
|
|
986
|
+
* the return value as a single-shot gate: invoke once after the first
|
|
987
|
+
* `agent.prompt(...)` and, on `true`, resume the turn via
|
|
988
|
+
* `agent.continue()` exactly once. The helper never inspects the
|
|
989
|
+
* failing message again, so a second overflow on the retry falls
|
|
990
|
+
* through naturally as a `failed` turn.
|
|
991
|
+
*
|
|
992
|
+
* Mutations on success:
|
|
993
|
+
* - pops the failure assistant message that pi-agent pushed for the
|
|
994
|
+
* overflow (an empty-text marker with `stopReason: "error"` and
|
|
995
|
+
* `errorMessage`) so the retry's transcript does not carry it.
|
|
996
|
+
* `agent.continue()`'s own run lifecycle resets `errorMessage`
|
|
997
|
+
* before the retry, so we do not clear it here;
|
|
998
|
+
* - advances `this.wireGuardHorizon.evictionHorizon` to the smallest
|
|
999
|
+
* value at which `findEvictionHorizon` reports the dispatched list
|
|
1000
|
+
* shrinks to at most `floor(n / 2)` observable messages, where `n`
|
|
1001
|
+
* excludes the failure message itself.
|
|
1002
|
+
*
|
|
1003
|
+
* Emits one informational `system` event describing the compaction so
|
|
1004
|
+
* surfaces can show a notice. The message reports the *actual*
|
|
1005
|
+
* post-eviction drop count (after `MIN_HISTORY_TAIL` clamping and
|
|
1006
|
+
* orphan-head skipping), not the half-of-n target.
|
|
1007
|
+
*
|
|
1008
|
+
* Returns `false` when:
|
|
1009
|
+
* - the parent agent did not fail (`errorMessage` is unset);
|
|
1010
|
+
* - the failure is not a context overflow (rate limit, transport
|
|
1011
|
+
* error, etc. — left as-is for the caller to surface);
|
|
1012
|
+
* - no horizon advance is possible (e.g. the existing horizon already
|
|
1013
|
+
* covers every evictable message, or the history is too short to
|
|
1014
|
+
* satisfy `MIN_HISTORY_TAIL`). In those cases retrying with the
|
|
1015
|
+
* same context would still overflow, so we accept the failure
|
|
1016
|
+
* instead of paying a second pointless request.
|
|
1017
|
+
*/
|
|
1018
|
+
tryRecoverFromContextOverflow(agent) {
|
|
1019
|
+
if (!agent.state.errorMessage)
|
|
1020
|
+
return false;
|
|
1021
|
+
const messages = agent.state.messages;
|
|
1022
|
+
const lastMessage = messages[messages.length - 1];
|
|
1023
|
+
if (!lastMessage || lastMessage.role !== "assistant")
|
|
1024
|
+
return false;
|
|
1025
|
+
if (!isContextOverflow(lastMessage, agent.state.model.contextWindow))
|
|
1026
|
+
return false;
|
|
1027
|
+
// Exclude the failure assistant message from the half-history
|
|
1028
|
+
// calculation; only "real" prior turns should count toward the
|
|
1029
|
+
// observable population the cut targets.
|
|
1030
|
+
const observable = stripObservationalContextMessages(messages.slice(0, -1));
|
|
1031
|
+
const target = Math.floor(observable.length / 2);
|
|
1032
|
+
const previousHorizon = this.wireGuardHorizon.evictionHorizon;
|
|
1033
|
+
const newHorizon = findEvictionHorizon(observable, previousHorizon, (candidate) => candidate.length <= target);
|
|
1034
|
+
if (newHorizon === previousHorizon)
|
|
1035
|
+
return false;
|
|
1036
|
+
messages.pop();
|
|
1037
|
+
this.wireGuardHorizon.evictionHorizon = newHorizon;
|
|
1038
|
+
const remaining = applyEvictionHorizon(observable, newHorizon).length;
|
|
1039
|
+
const dropped = observable.length - remaining;
|
|
1040
|
+
this.emit({
|
|
1041
|
+
type: "system",
|
|
1042
|
+
level: "info",
|
|
1043
|
+
message: `Context overflow: dropped ${dropped} older message${dropped === 1 ? "" : "s"} and retrying.`,
|
|
1044
|
+
});
|
|
1045
|
+
return true;
|
|
1046
|
+
}
|
|
934
1047
|
async updateMemoryAfterAgentRun(messages, options) {
|
|
935
1048
|
if (this.config.memoryDbPath === undefined) {
|
|
936
1049
|
return;
|
|
@@ -1033,7 +1146,9 @@ export class TurnRunner {
|
|
|
1033
1146
|
}
|
|
1034
1147
|
// Sticky across all turns within this runner instance. Resets on
|
|
1035
1148
|
// session resume (new runner). Mutated in place by the memory transform
|
|
1036
|
-
// when either the token or byte budget triggers eviction
|
|
1149
|
+
// when either the token or byte budget triggers eviction, and by
|
|
1150
|
+
// `runAgentWorker` on a provider context-overflow error so the retry
|
|
1151
|
+
// sends the newer half of history.
|
|
1037
1152
|
wireGuardHorizon = createInitialHorizon();
|
|
1038
1153
|
async getSkills() {
|
|
1039
1154
|
await this.ensureSkillsLoaded();
|
|
@@ -1127,12 +1242,35 @@ export class TurnRunner {
|
|
|
1127
1242
|
this.emitAgentEvent(event);
|
|
1128
1243
|
if (event.type !== "message_end" || event.message.role !== "assistant")
|
|
1129
1244
|
return;
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1245
|
+
// Cache the parent's latest bar/breakdown so subsequent state-agent and
|
|
1246
|
+
// terminal emissions can reuse it without rescaling against a stale base.
|
|
1247
|
+
// Usage *recording* and `usage` event emission happen at worker / state-agent
|
|
1248
|
+
// boundaries (see `runAgentWorkerWithUsage` and `createStateAgentHandle`)
|
|
1249
|
+
// instead of per message, so test harnesses that stub `runAgentWorker`
|
|
1250
|
+
// — without ever firing `message_end` — still get correct accounting.
|
|
1251
|
+
this.lastParentUsageSnapshot = {
|
|
1134
1252
|
effectiveContextWindow: this.effectiveContextWindow(),
|
|
1135
|
-
contextWindowUsage: scaleContextWindowUsageToTotalTokens(
|
|
1253
|
+
contextWindowUsage: scaleContextWindowUsageToTotalTokens(this.estimateContextWindowUsage(), event.message.usage.totalTokens),
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
/**
|
|
1257
|
+
* Emit a `usage` event reflecting the latest `this.turnUsage`. Reuses the
|
|
1258
|
+
* most recent parent context-window snapshot for the bar/breakdown so
|
|
1259
|
+
* mid-turn ticks (parent worker finish, state-agent finish) surface cost
|
|
1260
|
+
* without jittering the parent context fields. No-ops when no usage has
|
|
1261
|
+
* been recorded yet or before the first parent emission — both are only
|
|
1262
|
+
* possible during construction/teardown or in test harnesses; a real
|
|
1263
|
+
* parent worker always sets the snapshot before its terminal usage is
|
|
1264
|
+
* recorded.
|
|
1265
|
+
*/
|
|
1266
|
+
emitTurnUsage() {
|
|
1267
|
+
if (!this.turnUsage || !this.lastParentUsageSnapshot)
|
|
1268
|
+
return;
|
|
1269
|
+
this.emit({
|
|
1270
|
+
type: "usage",
|
|
1271
|
+
usage: this.turnUsage,
|
|
1272
|
+
effectiveContextWindow: this.lastParentUsageSnapshot.effectiveContextWindow,
|
|
1273
|
+
contextWindowUsage: this.lastParentUsageSnapshot.contextWindowUsage,
|
|
1136
1274
|
});
|
|
1137
1275
|
}
|
|
1138
1276
|
/**
|