@sema-agent/core 3.0.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2345 -0
- package/dist/agents/cascade.d.ts +2 -1
- package/dist/agents/cascade.js +31 -32
- package/dist/agents/cumulative-stats.d.ts +29 -0
- package/dist/agents/cumulative-stats.js +29 -0
- package/dist/agents/observer.d.ts +4 -0
- package/dist/agents/observer.js +11 -10
- package/dist/agents/repair-loop.js +27 -19
- package/dist/agents/retain-ledger.d.ts +2 -0
- package/dist/agents/retain-ledger.js +6 -5
- package/dist/agents/roster-store.js +3 -5
- package/dist/agents/subagent.js +66 -64
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +21 -24
- package/dist/agents/verify.js +15 -14
- package/dist/brain/anthropic.js +24 -21
- package/dist/brain/openai.js +24 -21
- package/dist/brain/stream-engine.js +5 -2
- package/dist/brain/stream-shared.d.ts +1 -0
- package/dist/brain/stream-shared.js +14 -0
- package/dist/brain/terminal-cause.d.ts +4 -0
- package/dist/brain/terminal-cause.js +12 -0
- package/dist/brain/tool-call-id.d.ts +1 -0
- package/dist/brain/tool-call-id.js +3 -0
- package/dist/brain/tool-call-repair.js +2 -1
- package/dist/core/auto-compaction.js +16 -25
- package/dist/core/background-agent-store.d.ts +22 -22
- package/dist/core/background-agent-store.js +19 -23
- package/dist/core/checkpoint-store.d.ts +6 -13
- package/dist/core/checkpoint-store.js +11 -5
- package/dist/core/file-snapshot-store.d.ts +8 -0
- package/dist/core/file-snapshot-store.js +4 -3
- package/dist/core/hooks.d.ts +3 -1
- package/dist/core/hooks.js +8 -18
- package/dist/core/lsp.js +4 -3
- package/dist/core/mailbox-store.d.ts +7 -10
- package/dist/core/mcp.d.ts +7 -10
- package/dist/core/mcp.js +7 -3
- package/dist/core/memory-engine/engine.d.ts +0 -1
- package/dist/core/memory-engine/engine.js +24 -34
- package/dist/core/memory-engine/file-backend.d.ts +1 -2
- package/dist/core/memory-engine/file-backend.js +25 -36
- package/dist/core/memory-engine/layout.d.ts +8 -0
- package/dist/core/memory-engine/layout.js +60 -4
- package/dist/core/memory-engine/scope-contract.js +9 -3
- package/dist/core/memory-engine/types.d.ts +1 -1
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +1 -15
- package/dist/core/runner/assemble-result.d.ts +2 -7
- package/dist/core/runner/assemble-result.js +3 -4
- package/dist/core/runner/compaction-call-options.d.ts +7 -0
- package/dist/core/runner/compaction-call-options.js +30 -0
- package/dist/core/runner/prepare-task.d.ts +6 -23
- package/dist/core/runner/prepare-task.js +20 -53
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +65 -156
- package/dist/core/runner/session-rule-policy.js +4 -1
- package/dist/core/runner/teardown-bounded.d.ts +7 -0
- package/dist/core/runner/teardown-bounded.js +36 -0
- package/dist/core/runner/tool-disclosure.d.ts +2 -5
- package/dist/core/runner/turn-attachments.d.ts +5 -15
- package/dist/core/runner/turn-attachments.js +1 -1
- package/dist/core/safe-notify.d.ts +17 -0
- package/dist/core/safe-notify.js +59 -0
- package/dist/core/sensitive-path-policy.js +2 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/stub-env.d.ts +2 -6
- package/dist/core/surrogate-safe-slice.d.ts +4 -0
- package/dist/core/surrogate-safe-slice.js +18 -0
- package/dist/core/task-registry-monitor.js +8 -13
- package/dist/core/task-registry-shared.d.ts +22 -0
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +1 -0
- package/dist/core/task-registry.js +40 -57
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.js +2 -1
- package/dist/core/tool-policy.js +10 -5
- package/dist/core/tool-result-store.d.ts +1 -0
- package/dist/core/tool-result-store.js +17 -1
- package/dist/core/trace.d.ts +2 -9
- package/dist/core/types.d.ts +15 -12
- package/dist/core/untrusted-egress.js +7 -5
- package/dist/core/workflow-journal-store.d.ts +9 -20
- package/dist/core/workflow-run-store.d.ts +11 -10
- package/dist/core/workflow-run-store.js +22 -16
- package/dist/engine/compaction/utils.js +4 -2
- package/dist/engine/execution-env/node-execution-env.d.ts +2 -24
- package/dist/engine/harness/agent-harness.d.ts +1 -0
- package/dist/engine/harness/agent-harness.js +13 -33
- package/dist/engine/harness/types.d.ts +29 -67
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +11 -4
- package/dist/engine/loop/types.d.ts +11 -9
- package/dist/engine/session/memory-repo.d.ts +2 -6
- package/dist/engine/session/session.d.ts +5 -30
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -1
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/orchestration/run-spec.js +7 -4
- package/dist/orchestration/run-workflow-tool.js +3 -35
- package/dist/orchestration/workflow.d.ts +3 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/stores/file/background-agent-store.d.ts +3 -12
- package/dist/stores/file/background-agent-store.js +3 -41
- package/dist/stores/file/checkpoint-store.js +6 -6
- package/dist/stores/file/mailbox-store.d.ts +2 -6
- package/dist/stores/file/session-policy-store.js +15 -1
- package/dist/stores/file/session-store.d.ts +2 -6
- package/dist/stores/file/workflow-journal-store.d.ts +3 -11
- package/dist/stores/file/workflow-run-store.d.ts +3 -7
- package/dist/stores/file/workflow-run-store.js +4 -17
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -0
- package/dist/tools/fs/bash-readonly-classifier.js +60 -25
- package/dist/tools/fs/fs-bash.js +324 -96
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/notebook.d.ts +2 -5
- package/dist/tools/fs/safety.d.ts +3 -0
- package/dist/tools/fs/safety.js +43 -14
- package/dist/tools/fs/search.js +5 -5
- package/dist/tools/monitor.d.ts +1 -0
- package/dist/tools/monitor.js +7 -5
- package/dist/tools/worktree.d.ts +2 -7
- package/dist/tools/worktree.js +3 -1
- package/package.json +6 -2
package/dist/agents/cascade.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface CascadeAttempt {
|
|
|
29
29
|
index: number;
|
|
30
30
|
model: ModelRef;
|
|
31
31
|
passed: boolean;
|
|
32
|
-
costMicroUsd
|
|
32
|
+
costMicroUsd?: number;
|
|
33
33
|
status: TaskResult["status"];
|
|
34
34
|
errorCode?: string;
|
|
35
35
|
gateError?: string;
|
|
@@ -40,6 +40,7 @@ export interface CascadeAttempt {
|
|
|
40
40
|
}
|
|
41
41
|
export interface CascadeRunResult extends TaskResult {
|
|
42
42
|
cascadeOutcome: "passed" | "exhausted";
|
|
43
|
+
costCeilingUnenforceable?: true;
|
|
43
44
|
escalated: boolean;
|
|
44
45
|
finalRung: number;
|
|
45
46
|
attempts: CascadeAttempt[];
|
package/dist/agents/cascade.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { releaseSession } from "./session-util.js";
|
|
2
2
|
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
3
|
+
import { buildCumulativeStats } from "./cumulative-stats.js";
|
|
4
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
5
|
+
const CASCADE_ON_RUNG_SITE = "cascade.onRung";
|
|
3
6
|
function createDefaultGate(spec) {
|
|
4
7
|
const requiresStructured = spec.outputSchema != null;
|
|
5
8
|
return (result) => result.status === "completed" && (!requiresStructured || result.structuredOutput !== undefined);
|
|
@@ -14,37 +17,33 @@ export async function runCascade(runner, spec, config) {
|
|
|
14
17
|
const gate = config.gate ?? createDefaultGate(spec);
|
|
15
18
|
const startedAt = Date.now();
|
|
16
19
|
const deadlineAt = config.totalTimeoutMs != null ? startedAt + config.totalTimeoutMs : undefined;
|
|
20
|
+
const notifier = createSafeNotifier();
|
|
17
21
|
const attempts = [];
|
|
18
22
|
let totalCost = 0;
|
|
23
|
+
let ceilingCostKnown = true;
|
|
19
24
|
let ownTokens = 0, ownTurns = 0, ownPrompt = 0, ownCached = 0, ownOutput = 0, ownCostMicro = 0;
|
|
20
25
|
let ownTotalInput = 0;
|
|
21
26
|
let ownCacheWrite = 0, ownCacheWriteLong = 0;
|
|
22
27
|
let nestedTokens = 0, nestedTurns = 0, nestedTasks = 0, nestedCostMicro = 0;
|
|
23
28
|
let ownLlmRootMicro = 0, ownCompactionMicro = 0;
|
|
29
|
+
let anyRungUnpriced = false;
|
|
24
30
|
let hrCount = 0, hrWaitMs = 0;
|
|
25
31
|
const hrGates = [];
|
|
26
|
-
const cumulativeStats = (baseStats) => ({
|
|
27
|
-
...baseStats,
|
|
32
|
+
const cumulativeStats = (baseStats) => buildCumulativeStats(baseStats, {
|
|
28
33
|
tokens: ownTokens,
|
|
29
34
|
turns: ownTurns,
|
|
30
|
-
promptTokens: ownPrompt
|
|
31
|
-
totalInputTokens: ownTotalInput
|
|
32
|
-
cachedTokens: ownCached
|
|
33
|
-
outputTokens: ownOutput
|
|
34
|
-
cacheWriteTokens: ownCacheWrite
|
|
35
|
-
cacheWriteTokensLong: ownCacheWriteLong
|
|
36
|
-
cacheHitRate: ownTotalInput > 0 ? Math.min(1, ownCached / ownTotalInput) : undefined,
|
|
35
|
+
promptTokens: ownPrompt,
|
|
36
|
+
totalInputTokens: ownTotalInput,
|
|
37
|
+
cachedTokens: ownCached,
|
|
38
|
+
outputTokens: ownOutput,
|
|
39
|
+
cacheWriteTokens: ownCacheWrite,
|
|
40
|
+
cacheWriteTokensLong: ownCacheWriteLong,
|
|
37
41
|
costMicroUsd: ownCostMicro,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
nestedSubagentMicroUsd: nestedCostMicro,
|
|
44
|
-
memoryConsolidationMicroUsd: 0,
|
|
45
|
-
compactionMicroUsd: ownCompactionMicro,
|
|
46
|
-
},
|
|
47
|
-
humanReview: hrCount > 0 ? { count: hrCount, totalWaitMs: hrWaitMs, gates: hrGates } : undefined,
|
|
42
|
+
anyLegUnpriced: anyRungUnpriced,
|
|
43
|
+
llmRootMicroUsd: ownLlmRootMicro,
|
|
44
|
+
compactionMicroUsd: ownCompactionMicro,
|
|
45
|
+
nested: { tokens: nestedTokens, turns: nestedTurns, tasks: nestedTasks, costMicroUsd: nestedCostMicro },
|
|
46
|
+
humanReview: { count: hrCount, totalWaitMs: hrWaitMs, gates: hrGates },
|
|
48
47
|
});
|
|
49
48
|
let lastResult;
|
|
50
49
|
let passedRung = -1;
|
|
@@ -52,8 +51,9 @@ export async function runCascade(runner, spec, config) {
|
|
|
52
51
|
for (let i = 0; i < maxRungs; i++) {
|
|
53
52
|
if (deadlineAt !== undefined && Date.now() >= deadlineAt)
|
|
54
53
|
break;
|
|
55
|
-
if (i > 0 && config.costCeilingMicroUsd != null && totalCost >= config.costCeilingMicroUsd)
|
|
54
|
+
if (i > 0 && config.costCeilingMicroUsd != null && (totalCost >= config.costCeilingMicroUsd || !ceilingCostKnown)) {
|
|
56
55
|
break;
|
|
56
|
+
}
|
|
57
57
|
const rung = ladder[i];
|
|
58
58
|
let deadlineFired = false;
|
|
59
59
|
const rungAbort = new AbortController();
|
|
@@ -80,8 +80,11 @@ export async function runCascade(runner, spec, config) {
|
|
|
80
80
|
}
|
|
81
81
|
lastResult = result;
|
|
82
82
|
const s = result.stats;
|
|
83
|
-
const
|
|
84
|
-
|
|
83
|
+
const rungCostKnown = s.costMicroUsd !== undefined && (s.nested === undefined || s.nested.costMicroUsd !== undefined);
|
|
84
|
+
const rungCost = rungCostKnown ? (s.costMicroUsd ?? 0) + (s.nested?.costMicroUsd ?? 0) : undefined;
|
|
85
|
+
totalCost += rungCost ?? 0;
|
|
86
|
+
if (!rungCostKnown)
|
|
87
|
+
ceilingCostKnown = false;
|
|
85
88
|
ownTokens += s.tokens;
|
|
86
89
|
ownTurns += s.turns;
|
|
87
90
|
ownPrompt += s.promptTokens ?? 0;
|
|
@@ -91,6 +94,8 @@ export async function runCascade(runner, spec, config) {
|
|
|
91
94
|
ownCacheWrite += s.cacheWriteTokens ?? 0;
|
|
92
95
|
ownCacheWriteLong += s.cacheWriteTokensLong ?? 0;
|
|
93
96
|
ownCostMicro += s.costMicroUsd ?? 0;
|
|
97
|
+
if (s.costMicroUsd === undefined)
|
|
98
|
+
anyRungUnpriced = true;
|
|
94
99
|
if (s.costBreakdown) {
|
|
95
100
|
ownLlmRootMicro += s.costBreakdown.llmRootMicroUsd;
|
|
96
101
|
ownCompactionMicro += s.costBreakdown.compactionMicroUsd;
|
|
@@ -113,6 +118,7 @@ export async function runCascade(runner, spec, config) {
|
|
|
113
118
|
...mapped,
|
|
114
119
|
stats: cumulativeStats(result.stats),
|
|
115
120
|
cascadeOutcome: "exhausted",
|
|
121
|
+
...(config.costCeilingMicroUsd != null && !ceilingCostKnown ? { costCeilingUnenforceable: true } : {}),
|
|
116
122
|
escalated: i > 0,
|
|
117
123
|
finalRung: i,
|
|
118
124
|
attempts,
|
|
@@ -128,11 +134,7 @@ export async function runCascade(runner, spec, config) {
|
|
|
128
134
|
errorCode: result.errorCode,
|
|
129
135
|
...(deadlineFired ? { timedOut: true } : { cancelled: true }),
|
|
130
136
|
});
|
|
131
|
-
|
|
132
|
-
config.onRung?.({ index: i, model: rung.model, passed: false, result });
|
|
133
|
-
}
|
|
134
|
-
catch {
|
|
135
|
-
}
|
|
137
|
+
notifier.notify(() => config.onRung?.({ index: i, model: rung.model, passed: false, result }), CASCADE_ON_RUNG_SITE);
|
|
136
138
|
await releaseSession(runner, result.sessionId);
|
|
137
139
|
break;
|
|
138
140
|
}
|
|
@@ -167,11 +169,7 @@ export async function runCascade(runner, spec, config) {
|
|
|
167
169
|
diagnostics,
|
|
168
170
|
...(statusRejected ? { statusRejected: true } : {}),
|
|
169
171
|
});
|
|
170
|
-
|
|
171
|
-
config.onRung?.({ index: i, model: rung.model, passed, result });
|
|
172
|
-
}
|
|
173
|
-
catch {
|
|
174
|
-
}
|
|
172
|
+
notifier.notify(() => config.onRung?.({ index: i, model: rung.model, passed, result }), CASCADE_ON_RUNG_SITE);
|
|
175
173
|
}
|
|
176
174
|
finally {
|
|
177
175
|
await releaseSession(runner, result.sessionId);
|
|
@@ -187,6 +185,7 @@ export async function runCascade(runner, spec, config) {
|
|
|
187
185
|
...base,
|
|
188
186
|
stats: cumulativeStats(base.stats),
|
|
189
187
|
cascadeOutcome: passedRung >= 0 ? "passed" : "exhausted",
|
|
188
|
+
...(config.costCeilingMicroUsd != null && !ceilingCostKnown ? { costCeilingUnenforceable: true } : {}),
|
|
190
189
|
escalated: finalRung > 0,
|
|
191
190
|
finalRung,
|
|
192
191
|
attempts,
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { TaskResult } from "../core/types.js";
|
|
2
|
+
export interface CumulativeNestedTotals {
|
|
3
|
+
tokens: number;
|
|
4
|
+
turns: number;
|
|
5
|
+
tasks: number;
|
|
6
|
+
costMicroUsd: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CumulativeHumanReviewTotals {
|
|
9
|
+
count: number;
|
|
10
|
+
totalWaitMs: number;
|
|
11
|
+
gates: NonNullable<TaskResult["stats"]["humanReview"]>["gates"];
|
|
12
|
+
}
|
|
13
|
+
export interface CumulativeStatsAccumulator {
|
|
14
|
+
tokens: number;
|
|
15
|
+
turns: number;
|
|
16
|
+
promptTokens: number;
|
|
17
|
+
totalInputTokens: number;
|
|
18
|
+
cachedTokens: number;
|
|
19
|
+
outputTokens: number;
|
|
20
|
+
cacheWriteTokens: number;
|
|
21
|
+
cacheWriteTokensLong: number;
|
|
22
|
+
costMicroUsd: number | undefined;
|
|
23
|
+
anyLegUnpriced: boolean;
|
|
24
|
+
llmRootMicroUsd: number;
|
|
25
|
+
compactionMicroUsd: number;
|
|
26
|
+
nested: CumulativeNestedTotals;
|
|
27
|
+
humanReview: CumulativeHumanReviewTotals;
|
|
28
|
+
}
|
|
29
|
+
export declare function buildCumulativeStats(base: TaskResult["stats"], acc: CumulativeStatsAccumulator): TaskResult["stats"];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function buildCumulativeStats(base, acc) {
|
|
2
|
+
return {
|
|
3
|
+
...base,
|
|
4
|
+
tokens: acc.tokens,
|
|
5
|
+
turns: acc.turns,
|
|
6
|
+
promptTokens: acc.promptTokens || undefined,
|
|
7
|
+
totalInputTokens: acc.totalInputTokens || undefined,
|
|
8
|
+
cachedTokens: acc.cachedTokens || undefined,
|
|
9
|
+
outputTokens: acc.outputTokens || undefined,
|
|
10
|
+
cacheWriteTokens: acc.cacheWriteTokens || undefined,
|
|
11
|
+
cacheWriteTokensLong: acc.cacheWriteTokensLong || undefined,
|
|
12
|
+
cacheHitRate: acc.totalInputTokens > 0 ? Math.min(1, acc.cachedTokens / acc.totalInputTokens) : undefined,
|
|
13
|
+
costMicroUsd: acc.anyLegUnpriced ? undefined : acc.costMicroUsd,
|
|
14
|
+
nested: acc.nested.tokens > 0 || acc.nested.tasks > 0
|
|
15
|
+
? { tokens: acc.nested.tokens, turns: acc.nested.turns, tasks: acc.nested.tasks, costMicroUsd: acc.nested.costMicroUsd || undefined }
|
|
16
|
+
: undefined,
|
|
17
|
+
costBreakdown: acc.anyLegUnpriced
|
|
18
|
+
? undefined
|
|
19
|
+
: {
|
|
20
|
+
llmRootMicroUsd: acc.llmRootMicroUsd,
|
|
21
|
+
nestedSubagentMicroUsd: acc.nested.costMicroUsd,
|
|
22
|
+
memoryConsolidationMicroUsd: 0,
|
|
23
|
+
compactionMicroUsd: acc.compactionMicroUsd,
|
|
24
|
+
},
|
|
25
|
+
humanReview: acc.humanReview.count > 0
|
|
26
|
+
? { count: acc.humanReview.count, totalWaitMs: acc.humanReview.totalWaitMs, gates: acc.humanReview.gates }
|
|
27
|
+
: undefined,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -63,7 +63,9 @@ export declare class ObserverDigestTap {
|
|
|
63
63
|
constructor(flush: (activity: ObserverDigestEvent[]) => void);
|
|
64
64
|
record(e: TaskEvent): void;
|
|
65
65
|
private flushText;
|
|
66
|
+
private readonly notifier;
|
|
66
67
|
private safeFlush;
|
|
68
|
+
get flushFailures(): number;
|
|
67
69
|
flushSegment(): void;
|
|
68
70
|
finish(reason: string): void;
|
|
69
71
|
}
|
|
@@ -112,7 +114,9 @@ export declare class ObserverPairing {
|
|
|
112
114
|
});
|
|
113
115
|
enqueueSegment(activity: ObserverDigestEvent[], trigger?: string): void;
|
|
114
116
|
private pumping?;
|
|
117
|
+
private readonly notifier;
|
|
115
118
|
private safeOnError;
|
|
119
|
+
get onErrorFailures(): number;
|
|
116
120
|
drain(): Promise<void>;
|
|
117
121
|
private pump;
|
|
118
122
|
private deliverBatch;
|
package/dist/agents/observer.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { defineTool } from "../core/tools.js";
|
|
3
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
3
4
|
export const OBSERVER_EVENT_TAGS = ["tool-call", "user-message", "tool-result", "turn-ended"];
|
|
4
5
|
const TAG_ESCAPE_RE = new RegExp(`<(?=/?(?:${OBSERVER_EVENT_TAGS.join("|")})(?:[>\\s/]|$))`, "gi");
|
|
5
6
|
export function escapeObserverTags(text) {
|
|
@@ -181,12 +182,12 @@ export class ObserverDigestTap {
|
|
|
181
182
|
this.pending.push({ type: "assistant_text", text: this.textBuf });
|
|
182
183
|
this.textBuf = "";
|
|
183
184
|
}
|
|
185
|
+
notifier = createSafeNotifier();
|
|
184
186
|
safeFlush(slice) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}
|
|
187
|
+
this.notifier.notify(() => this.flush(slice), "observer.digestTap.flush");
|
|
188
|
+
}
|
|
189
|
+
get flushFailures() {
|
|
190
|
+
return this.notifier.failuresAt("observer.digestTap.flush");
|
|
190
191
|
}
|
|
191
192
|
flushSegment() {
|
|
192
193
|
if (this.finished)
|
|
@@ -270,12 +271,12 @@ export class ObserverPairing {
|
|
|
270
271
|
}).catch(() => undefined);
|
|
271
272
|
}
|
|
272
273
|
pumping;
|
|
274
|
+
notifier = createSafeNotifier();
|
|
273
275
|
safeOnError(err) {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
276
|
+
this.notifier.notify(() => this.onError?.(err), "observer.pairing.onError");
|
|
277
|
+
}
|
|
278
|
+
get onErrorFailures() {
|
|
279
|
+
return this.notifier.failuresAt("observer.pairing.onError");
|
|
279
280
|
}
|
|
280
281
|
async drain() {
|
|
281
282
|
while (this.pumping) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isDurablePause } from "./suspend-guard.js";
|
|
2
2
|
import { delimitUntrusted, sanitizeUntrustedText } from "../core/untrusted-text.js";
|
|
3
3
|
import { assertOracleIsolation } from "../core/oracle-isolation.js";
|
|
4
|
+
import { buildCumulativeStats } from "./cumulative-stats.js";
|
|
4
5
|
export function isolationPermitsAutoAccept(verdict) {
|
|
5
6
|
return verdict.protected && verdict.isolationClass === "out_of_process";
|
|
6
7
|
}
|
|
@@ -78,7 +79,10 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
78
79
|
else if (config.workerEnv !== undefined && config.workerEnv === config.graderEnv) {
|
|
79
80
|
return unprotectedResult("graderEnv must not be the worker env");
|
|
80
81
|
}
|
|
81
|
-
const maxAttempts = Math.max(1, Math.floor(config.maxAttempts));
|
|
82
|
+
const maxAttempts = Number.isFinite(config.maxAttempts) ? Math.max(1, Math.floor(config.maxAttempts)) : 1;
|
|
83
|
+
if (!Number.isFinite(config.maxAttempts)) {
|
|
84
|
+
config.onWarn?.(new Error(`repair-loop: maxAttempts must be a finite number (got ${config.maxAttempts}) — falling back to 1 attempt`));
|
|
85
|
+
}
|
|
82
86
|
const bundle = config.resumeBundle
|
|
83
87
|
? { ...config.resumeBundle, diagnostics: [...config.resumeBundle.diagnostics], rejectedHypotheses: [...config.resumeBundle.rejectedHypotheses] }
|
|
84
88
|
: freshBundle();
|
|
@@ -89,7 +93,10 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
89
93
|
let ownTokens = 0, ownTurns = 0, ownPrompt = 0, ownTotalInput = 0, ownCached = 0, ownOutput = 0;
|
|
90
94
|
let ownCacheWrite = 0, ownCacheWriteLong = 0, ownCostMicro = 0;
|
|
91
95
|
let ownLlmRootMicro = 0, ownCompactionMicro = 0;
|
|
96
|
+
let anyAttemptUnpriced = false;
|
|
92
97
|
let nestedTokens = 0, nestedTurns = 0, nestedTasks = 0, nestedCostMicro = 0;
|
|
98
|
+
let hrCount = 0, hrWaitMs = 0;
|
|
99
|
+
const hrGates = [];
|
|
93
100
|
let oracleCostMicroUsd = 0;
|
|
94
101
|
const accumulate = (s) => {
|
|
95
102
|
ownTokens += s.tokens;
|
|
@@ -101,10 +108,17 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
101
108
|
ownCacheWrite += s.cacheWriteTokens ?? 0;
|
|
102
109
|
ownCacheWriteLong += s.cacheWriteTokensLong ?? 0;
|
|
103
110
|
ownCostMicro += s.costMicroUsd ?? 0;
|
|
111
|
+
if (s.costMicroUsd === undefined)
|
|
112
|
+
anyAttemptUnpriced = true;
|
|
104
113
|
if (s.costBreakdown) {
|
|
105
114
|
ownLlmRootMicro += s.costBreakdown.llmRootMicroUsd;
|
|
106
115
|
ownCompactionMicro += s.costBreakdown.compactionMicroUsd;
|
|
107
116
|
}
|
|
117
|
+
if (s.humanReview) {
|
|
118
|
+
hrCount += s.humanReview.count;
|
|
119
|
+
hrWaitMs += s.humanReview.totalWaitMs;
|
|
120
|
+
hrGates.push(...s.humanReview.gates);
|
|
121
|
+
}
|
|
108
122
|
if (s.nested) {
|
|
109
123
|
nestedTokens += s.nested.tokens;
|
|
110
124
|
nestedTurns += s.nested.turns;
|
|
@@ -112,27 +126,21 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
112
126
|
nestedCostMicro += s.nested.costMicroUsd ?? 0;
|
|
113
127
|
}
|
|
114
128
|
};
|
|
115
|
-
const cumulativeStats = (base) => ({
|
|
116
|
-
...base,
|
|
129
|
+
const cumulativeStats = (base) => buildCumulativeStats(base, {
|
|
117
130
|
tokens: ownTokens,
|
|
118
131
|
turns: ownTurns,
|
|
119
|
-
promptTokens: ownPrompt
|
|
120
|
-
totalInputTokens: ownTotalInput
|
|
121
|
-
cachedTokens: ownCached
|
|
122
|
-
outputTokens: ownOutput
|
|
123
|
-
cacheWriteTokens: ownCacheWrite
|
|
124
|
-
cacheWriteTokensLong: ownCacheWriteLong
|
|
125
|
-
cacheHitRate: ownTotalInput > 0 ? Math.min(1, ownCached / ownTotalInput) : undefined,
|
|
132
|
+
promptTokens: ownPrompt,
|
|
133
|
+
totalInputTokens: ownTotalInput,
|
|
134
|
+
cachedTokens: ownCached,
|
|
135
|
+
outputTokens: ownOutput,
|
|
136
|
+
cacheWriteTokens: ownCacheWrite,
|
|
137
|
+
cacheWriteTokensLong: ownCacheWriteLong,
|
|
126
138
|
costMicroUsd: ownCostMicro,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
nestedSubagentMicroUsd: nestedCostMicro,
|
|
133
|
-
memoryConsolidationMicroUsd: base.costBreakdown?.memoryConsolidationMicroUsd ?? 0,
|
|
134
|
-
compactionMicroUsd: ownCompactionMicro,
|
|
135
|
-
},
|
|
139
|
+
anyLegUnpriced: anyAttemptUnpriced,
|
|
140
|
+
llmRootMicroUsd: ownLlmRootMicro,
|
|
141
|
+
compactionMicroUsd: ownCompactionMicro,
|
|
142
|
+
nested: { tokens: nestedTokens, turns: nestedTurns, tasks: nestedTasks, costMicroUsd: nestedCostMicro },
|
|
143
|
+
humanReview: { count: hrCount, totalWaitMs: hrWaitMs, gates: hrGates },
|
|
136
144
|
});
|
|
137
145
|
let restartedOnce = false;
|
|
138
146
|
let restartUsed = false;
|
|
@@ -40,12 +40,14 @@ export declare class SubagentRetainLedger {
|
|
|
40
40
|
private readonly hooks;
|
|
41
41
|
private failedEvictions;
|
|
42
42
|
private lastFailure;
|
|
43
|
+
private readonly notifier;
|
|
43
44
|
constructor(config: true | {
|
|
44
45
|
ttlMs?: number;
|
|
45
46
|
max?: number;
|
|
46
47
|
}, hooks?: RetainLedgerHooks);
|
|
47
48
|
get evictionFailureCount(): number;
|
|
48
49
|
get lastEvictionError(): Error | undefined;
|
|
50
|
+
get evictionSinkFailureCount(): number;
|
|
49
51
|
noteDetachedFailure(trigger: RetainEvictionTrigger, error: unknown, parentToolCallId?: string): void;
|
|
50
52
|
private evictDetached;
|
|
51
53
|
disposeAllDetached(trigger: RetainEvictionTrigger): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { delimitUntrusted } from "../core/untrusted-text.js";
|
|
2
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
2
3
|
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX } from "../config/defaults.js";
|
|
3
4
|
export const SUBAGENT_RESUME_CAP = 8;
|
|
4
5
|
export class SubagentRetainLedger {
|
|
@@ -11,6 +12,7 @@ export class SubagentRetainLedger {
|
|
|
11
12
|
hooks;
|
|
12
13
|
failedEvictions = 0;
|
|
13
14
|
lastFailure;
|
|
15
|
+
notifier = createSafeNotifier();
|
|
14
16
|
constructor(config, hooks) {
|
|
15
17
|
const cfg = config === true ? {} : config;
|
|
16
18
|
this.ttlMs = cfg.ttlMs ?? RETAIN_DEFAULT_TTL_MS;
|
|
@@ -23,15 +25,14 @@ export class SubagentRetainLedger {
|
|
|
23
25
|
get lastEvictionError() {
|
|
24
26
|
return this.lastFailure;
|
|
25
27
|
}
|
|
28
|
+
get evictionSinkFailureCount() {
|
|
29
|
+
return this.notifier.failuresAt("retainLedger.onEvictionError");
|
|
30
|
+
}
|
|
26
31
|
noteDetachedFailure(trigger, error, parentToolCallId) {
|
|
27
32
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
28
33
|
this.failedEvictions += 1;
|
|
29
34
|
this.lastFailure = err;
|
|
30
|
-
|
|
31
|
-
this.hooks?.onEvictionError?.({ ...(parentToolCallId !== undefined ? { parentToolCallId } : {}), trigger, error: err });
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
}
|
|
35
|
+
this.notifier.notify(() => this.hooks?.onEvictionError?.({ ...(parentToolCallId !== undefined ? { parentToolCallId } : {}), trigger, error: err }), "retainLedger.onEvictionError");
|
|
35
36
|
}
|
|
36
37
|
evictDetached(parentToolCallId, trigger) {
|
|
37
38
|
void this.evict(parentToolCallId).catch((e) => this.noteDetachedFailure(trigger, e, parentToolCallId));
|
|
@@ -2,6 +2,7 @@ import { mkdirSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { atomicWriteFile } from "../stores/file/fs-atomic.js";
|
|
3
3
|
import { dirname } from "node:path";
|
|
4
4
|
import { normalizeAgentName } from "../core/task-registry.js";
|
|
5
|
+
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
5
6
|
export function entryAccessible(e, access) {
|
|
6
7
|
if (e.scope === undefined || access.scope === undefined || access.scope !== e.scope)
|
|
7
8
|
return false;
|
|
@@ -35,15 +36,12 @@ function capped(entries, opts) {
|
|
|
35
36
|
const max = opts?.maxEntries;
|
|
36
37
|
if (max === undefined || entries.length <= max)
|
|
37
38
|
return entries;
|
|
39
|
+
const notifier = createSafeNotifier();
|
|
38
40
|
const byAge = [...entries].sort((a, b) => a.createdAt - b.createdAt);
|
|
39
41
|
const dropCount = Math.max(0, entries.length - Math.max(0, max));
|
|
40
42
|
const dropped = new Set(byAge.slice(0, dropCount));
|
|
41
43
|
for (const e of dropped) {
|
|
42
|
-
|
|
43
|
-
opts?.onEvicted?.(e);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
}
|
|
44
|
+
notifier.notify(() => opts?.onEvicted?.(e), "rosterStore.onEvicted");
|
|
47
45
|
}
|
|
48
46
|
return entries.filter((e) => !dropped.has(e));
|
|
49
47
|
}
|