@sema-agent/core 3.0.0 → 5.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 +2382 -0
- package/dist/agents/cascade.d.ts +3 -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.d.ts +2 -0
- package/dist/agents/repair-loop.js +48 -24
- package/dist/agents/retain-ledger.d.ts +2 -0
- package/dist/agents/retain-ledger.js +6 -5
- package/dist/agents/roster-store.d.ts +1 -0
- package/dist/agents/roster-store.js +3 -5
- package/dist/agents/send-message-tool.d.ts +1 -0
- package/dist/agents/send-message-tool.js +2 -0
- package/dist/agents/subagent.d.ts +3 -1
- package/dist/agents/subagent.js +79 -94
- package/dist/agents/teacher.d.ts +1 -9
- package/dist/agents/teacher.js +21 -24
- package/dist/agents/tool-filter.js +6 -7
- package/dist/agents/verify.d.ts +1 -0
- 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/arg-summary.d.ts +21 -1
- package/dist/core/arg-summary.js +61 -14
- package/dist/core/auto-compaction.d.ts +1 -0
- 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/fs-write-gate-policy.js +2 -3
- package/dist/core/hooks.d.ts +4 -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 -9
- 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/permission-rules.js +2 -3
- package/dist/core/runner/active-skill-scope.d.ts +0 -1
- package/dist/core/runner/active-skill-scope.js +2 -17
- 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 +36 -61
- package/dist/core/runner/runtask.d.ts +1 -0
- package/dist/core/runner/runtask.js +84 -172
- package/dist/core/runner/session-rule-policy.js +7 -5
- 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 +4 -4
- package/dist/core/session-reconcile.js +1 -2
- package/dist/core/skill-tool-specifier.js +2 -3
- package/dist/core/skills-directory.js +2 -3
- 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 +23 -1
- package/dist/core/task-registry-shared.js +16 -0
- package/dist/core/task-registry.d.ts +6 -1
- package/dist/core/task-registry.js +52 -87
- package/dist/core/task-tool-shape.d.ts +0 -2
- package/dist/core/task-tool-shape.js +2 -5
- package/dist/core/tool-errors.js +5 -4
- package/dist/core/tool-name-aliases.d.ts +1 -2
- package/dist/core/tool-name-aliases.js +42 -59
- package/dist/core/tool-policy.js +21 -17
- 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 +9 -9
- package/dist/core/types.d.ts +15 -12
- package/dist/core/untrusted-egress.d.ts +4 -2
- package/dist/core/untrusted-egress.js +28 -14
- 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/execution-env/node-execution-env.js +1 -1
- 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 +12 -14
- 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 +6 -3
- package/dist/index.js +4 -3
- package/dist/internal/harness-types.d.ts +3 -1
- package/dist/orchestration/run-spec.js +6 -4
- package/dist/orchestration/run-workflow-tool.js +3 -36
- package/dist/orchestration/workflow-governance.js +2 -1
- package/dist/orchestration/workflow.d.ts +4 -11
- package/dist/orchestration/workflow.js +164 -204
- package/dist/prompt-assembly/packs/sema-default.js +1 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +10 -9
- package/dist/prompt-assembly/turn-snapshot.d.ts +2 -5
- package/dist/prompts/default.js +1 -3
- package/dist/prompts/simple-sections.d.ts +0 -1
- package/dist/prompts/simple-sections.js +0 -1
- 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 +330 -104
- package/dist/tools/fs/fs-pdf.d.ts +2 -12
- package/dist/tools/fs/fs-shared.js +2 -2
- package/dist/tools/fs/fs-write.js +0 -1
- 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
|
@@ -9,6 +9,7 @@ export interface CascadeRung {
|
|
|
9
9
|
overrides?: Partial<Pick<TaskSpec, "limits" | "degrade" | "systemPrompt">>;
|
|
10
10
|
}
|
|
11
11
|
export interface CascadeConfig {
|
|
12
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
12
13
|
ladder: CascadeRung[];
|
|
13
14
|
gate?: (result: TaskResult, rung: {
|
|
14
15
|
index: number;
|
|
@@ -29,7 +30,7 @@ export interface CascadeAttempt {
|
|
|
29
30
|
index: number;
|
|
30
31
|
model: ModelRef;
|
|
31
32
|
passed: boolean;
|
|
32
|
-
costMicroUsd
|
|
33
|
+
costMicroUsd?: number;
|
|
33
34
|
status: TaskResult["status"];
|
|
34
35
|
errorCode?: string;
|
|
35
36
|
gateError?: string;
|
|
@@ -40,6 +41,7 @@ export interface CascadeAttempt {
|
|
|
40
41
|
}
|
|
41
42
|
export interface CascadeRunResult extends TaskResult {
|
|
42
43
|
cascadeOutcome: "passed" | "exhausted";
|
|
44
|
+
costCeilingUnenforceable?: true;
|
|
43
45
|
escalated: boolean;
|
|
44
46
|
finalRung: number;
|
|
45
47
|
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(config.onNotifyError !== undefined ? { onError: config.onNotifyError } : undefined);
|
|
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,18 +79,31 @@ 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();
|
|
85
89
|
const startedAt = Date.now();
|
|
86
|
-
|
|
90
|
+
const seededSpend = config.resumeBundle?.spentMicroUsd;
|
|
91
|
+
const seededActiveMs = config.resumeBundle?.activeElapsedMs;
|
|
92
|
+
const priorActiveMs = typeof seededActiveMs === "number" && Number.isFinite(seededActiveMs) ? Math.max(0, seededActiveMs) : 0;
|
|
93
|
+
let spend = typeof seededSpend === "number" && Number.isFinite(seededSpend) ? Math.max(0, seededSpend) : 0;
|
|
94
|
+
const stampAccount = () => {
|
|
95
|
+
bundle.spentMicroUsd = spend;
|
|
96
|
+
bundle.activeElapsedMs = priorActiveMs + (Date.now() - startedAt);
|
|
97
|
+
};
|
|
87
98
|
let lastResult;
|
|
88
99
|
let lastOracle;
|
|
89
100
|
let ownTokens = 0, ownTurns = 0, ownPrompt = 0, ownTotalInput = 0, ownCached = 0, ownOutput = 0;
|
|
90
101
|
let ownCacheWrite = 0, ownCacheWriteLong = 0, ownCostMicro = 0;
|
|
91
102
|
let ownLlmRootMicro = 0, ownCompactionMicro = 0;
|
|
103
|
+
let anyAttemptUnpriced = false;
|
|
92
104
|
let nestedTokens = 0, nestedTurns = 0, nestedTasks = 0, nestedCostMicro = 0;
|
|
105
|
+
let hrCount = 0, hrWaitMs = 0;
|
|
106
|
+
const hrGates = [];
|
|
93
107
|
let oracleCostMicroUsd = 0;
|
|
94
108
|
const accumulate = (s) => {
|
|
95
109
|
ownTokens += s.tokens;
|
|
@@ -101,10 +115,17 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
101
115
|
ownCacheWrite += s.cacheWriteTokens ?? 0;
|
|
102
116
|
ownCacheWriteLong += s.cacheWriteTokensLong ?? 0;
|
|
103
117
|
ownCostMicro += s.costMicroUsd ?? 0;
|
|
118
|
+
if (s.costMicroUsd === undefined)
|
|
119
|
+
anyAttemptUnpriced = true;
|
|
104
120
|
if (s.costBreakdown) {
|
|
105
121
|
ownLlmRootMicro += s.costBreakdown.llmRootMicroUsd;
|
|
106
122
|
ownCompactionMicro += s.costBreakdown.compactionMicroUsd;
|
|
107
123
|
}
|
|
124
|
+
if (s.humanReview) {
|
|
125
|
+
hrCount += s.humanReview.count;
|
|
126
|
+
hrWaitMs += s.humanReview.totalWaitMs;
|
|
127
|
+
hrGates.push(...s.humanReview.gates);
|
|
128
|
+
}
|
|
108
129
|
if (s.nested) {
|
|
109
130
|
nestedTokens += s.nested.tokens;
|
|
110
131
|
nestedTurns += s.nested.turns;
|
|
@@ -112,27 +133,21 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
112
133
|
nestedCostMicro += s.nested.costMicroUsd ?? 0;
|
|
113
134
|
}
|
|
114
135
|
};
|
|
115
|
-
const cumulativeStats = (base) => ({
|
|
116
|
-
...base,
|
|
136
|
+
const cumulativeStats = (base) => buildCumulativeStats(base, {
|
|
117
137
|
tokens: ownTokens,
|
|
118
138
|
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,
|
|
139
|
+
promptTokens: ownPrompt,
|
|
140
|
+
totalInputTokens: ownTotalInput,
|
|
141
|
+
cachedTokens: ownCached,
|
|
142
|
+
outputTokens: ownOutput,
|
|
143
|
+
cacheWriteTokens: ownCacheWrite,
|
|
144
|
+
cacheWriteTokensLong: ownCacheWriteLong,
|
|
126
145
|
costMicroUsd: ownCostMicro,
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
nestedSubagentMicroUsd: nestedCostMicro,
|
|
133
|
-
memoryConsolidationMicroUsd: base.costBreakdown?.memoryConsolidationMicroUsd ?? 0,
|
|
134
|
-
compactionMicroUsd: ownCompactionMicro,
|
|
135
|
-
},
|
|
146
|
+
anyLegUnpriced: anyAttemptUnpriced,
|
|
147
|
+
llmRootMicroUsd: ownLlmRootMicro,
|
|
148
|
+
compactionMicroUsd: ownCompactionMicro,
|
|
149
|
+
nested: { tokens: nestedTokens, turns: nestedTurns, tasks: nestedTasks, costMicroUsd: nestedCostMicro },
|
|
150
|
+
humanReview: { count: hrCount, totalWaitMs: hrWaitMs, gates: hrGates },
|
|
136
151
|
});
|
|
137
152
|
let restartedOnce = false;
|
|
138
153
|
let restartUsed = false;
|
|
@@ -140,11 +155,11 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
140
155
|
const { sessionId: _drop, ...rest } = implSpec;
|
|
141
156
|
return { ...rest, objective: `${implSpec.objective}\n\n${repairObjective(bundle)}` };
|
|
142
157
|
};
|
|
143
|
-
const exhaustedBeforeDispatch = () => ({
|
|
158
|
+
const exhaustedBeforeDispatch = (message, errorCode) => ({
|
|
144
159
|
taskId: implSpec.taskId ?? "",
|
|
145
160
|
status: "failed",
|
|
146
|
-
result: `repair loop: attempt budget already spent (attemptCount ${bundle.attemptCount} >= maxAttempts ${maxAttempts}) — no attempt dispatched`,
|
|
147
|
-
errorCode: "repair.attempts_exhausted",
|
|
161
|
+
result: message ?? `repair loop: attempt budget already spent (attemptCount ${bundle.attemptCount} >= maxAttempts ${maxAttempts}) — no attempt dispatched`,
|
|
162
|
+
errorCode: errorCode ?? "repair.attempts_exhausted",
|
|
148
163
|
sessionId: implSpec.sessionId ?? "",
|
|
149
164
|
stats: { tokens: 0, turns: 0, costMicroUsd: 0 },
|
|
150
165
|
terminal: "gave_up",
|
|
@@ -156,6 +171,13 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
156
171
|
while (true) {
|
|
157
172
|
if (!restartedOnce && bundle.attemptCount >= maxAttempts)
|
|
158
173
|
return exhaustedBeforeDispatch();
|
|
174
|
+
if (config.costCeilingMicroUsd != null && spend >= config.costCeilingMicroUsd) {
|
|
175
|
+
return exhaustedBeforeDispatch(`repair loop: cost ceiling already spent (carried ${spend} >= ceiling ${config.costCeilingMicroUsd} micro-USD) — no attempt dispatched`, "repair.budget_exhausted");
|
|
176
|
+
}
|
|
177
|
+
if (config.totalTimeoutMs != null && priorActiveMs + (Date.now() - startedAt) >= config.totalTimeoutMs) {
|
|
178
|
+
return exhaustedBeforeDispatch(`repair loop: active time budget already spent (carried ${priorActiveMs + (Date.now() - startedAt)}ms >= ${config.totalTimeoutMs}ms) — no attempt dispatched`, "repair.budget_exhausted");
|
|
179
|
+
}
|
|
180
|
+
stampAccount();
|
|
159
181
|
const isRepair = bundle.attemptCount > 0 && lastResult !== undefined && !restartedOnce;
|
|
160
182
|
const isSeededResume = !restartedOnce &&
|
|
161
183
|
lastResult === undefined &&
|
|
@@ -174,6 +196,7 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
174
196
|
restartedOnce = false;
|
|
175
197
|
const result = await runner.runTask(spec, { repairBundle: bundle });
|
|
176
198
|
spend += (result.stats.costMicroUsd ?? 0) + (result.stats.nested?.costMicroUsd ?? 0);
|
|
199
|
+
stampAccount();
|
|
177
200
|
accumulate(result.stats);
|
|
178
201
|
lastResult = result;
|
|
179
202
|
bundle.attemptCount += 1;
|
|
@@ -202,6 +225,7 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
202
225
|
const oracle = await config.oracle(config.graderEnv, result.result);
|
|
203
226
|
oracleCostMicroUsd += oracle.costMicroUsd ?? 0;
|
|
204
227
|
spend += oracle.costMicroUsd ?? 0;
|
|
228
|
+
stampAccount();
|
|
205
229
|
lastOracle = oracle;
|
|
206
230
|
bundle.oracleTier = oracle.tier;
|
|
207
231
|
const projected = terminalForTier(oracle);
|
|
@@ -222,7 +246,7 @@ export async function runRepairLoop(runner, implSpec, config) {
|
|
|
222
246
|
if (bundle.diagnostics.length < 3)
|
|
223
247
|
bundle.diagnostics.push(oracle.trace);
|
|
224
248
|
}
|
|
225
|
-
const overTime = config.totalTimeoutMs != null && Date.now() - startedAt >= config.totalTimeoutMs;
|
|
249
|
+
const overTime = config.totalTimeoutMs != null && priorActiveMs + (Date.now() - startedAt) >= config.totalTimeoutMs;
|
|
226
250
|
const overCost = config.costCeilingMicroUsd != null && spend >= config.costCeilingMicroUsd;
|
|
227
251
|
if (bundle.attemptCount >= maxAttempts || overTime || overCost) {
|
|
228
252
|
if (bundle.attemptCount >= maxAttempts && !overTime && !overCost && lastResult !== undefined && !restartUsed) {
|
|
@@ -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));
|
|
@@ -23,6 +23,7 @@ export interface RosterStore {
|
|
|
23
23
|
releaseAgent(agentId: string): void | Promise<void>;
|
|
24
24
|
}
|
|
25
25
|
export interface RosterGcOptions {
|
|
26
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
26
27
|
maxAgeMs?: number;
|
|
27
28
|
maxEntries?: number;
|
|
28
29
|
onEvicted?: (entry: RosterEntry) => void;
|
|
@@ -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(opts?.onNotifyError !== undefined ? { onError: opts.onNotifyError } : undefined);
|
|
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
|
}
|
|
@@ -36,6 +36,7 @@ export interface SendMessageToolOptions {
|
|
|
36
36
|
content: string;
|
|
37
37
|
details?: unknown;
|
|
38
38
|
}>;
|
|
39
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
39
40
|
enrichCtx?: ToolCtxEnricher;
|
|
40
41
|
}
|
|
41
42
|
export declare const SEND_MESSAGE_SUMMARY_MAX = 200;
|
|
@@ -525,6 +525,8 @@ export function createSendMessageTool(opts) {
|
|
|
525
525
|
...(row.parentTaskId !== undefined ? { rowParentTaskId: row.parentTaskId } : {}),
|
|
526
526
|
...(row.parentSessionId !== undefined ? { rowParentSessionId: row.parentSessionId } : {}),
|
|
527
527
|
...(row.rootSessionId !== undefined ? { rowRootSessionId: row.rootSessionId } : {}),
|
|
528
|
+
...(row.createdAt !== undefined ? { rowSpawnedAt: row.createdAt } : {}),
|
|
529
|
+
...(opts.onNotifyError !== undefined ? { onNotifyError: opts.onNotifyError } : {}),
|
|
528
530
|
...(opts.notify ? { currentParentNotify: opts.notify } : {}),
|
|
529
531
|
});
|
|
530
532
|
const fromPrefix = senderIsChild ? `(message from teammate "${senderLabel}")\n` : "";
|
|
@@ -10,7 +10,6 @@ export type { SubagentStep, SubagentEditedFile } from "./subagent-steps.js";
|
|
|
10
10
|
export declare function notifyResultField(result: string | undefined): string | undefined;
|
|
11
11
|
export declare function inheritedManifestScopeFor(snapshot: readonly unknown[] | undefined): RunInternals["inheritedManifestScope"];
|
|
12
12
|
export declare const DEFAULT_SUBAGENT_TOOL_NAME = "Agent";
|
|
13
|
-
export declare const LEGACY_SUBAGENT_TOOL_NAME = "Task";
|
|
14
13
|
export declare const EXTRA_TOOLS_MAX_FACTORY_CALLS_PER_TREE = 64;
|
|
15
14
|
export declare const DEFAULT_SUBAGENT_MAX_DEPTH = 3;
|
|
16
15
|
export declare const EXTRA_TOOLS_FAILED_NOTE = "note: extraTools evaluation failed \u2014 the injected tool set was skipped for this spawn.";
|
|
@@ -76,6 +75,8 @@ export declare function createSubagentResume(deps: {
|
|
|
76
75
|
rowParentTaskId?: string;
|
|
77
76
|
rowParentSessionId?: string;
|
|
78
77
|
rowRootSessionId?: string;
|
|
78
|
+
rowSpawnedAt?: number;
|
|
79
|
+
onNotifyError?: (failure: import("../core/safe-notify.js").SafeNotifyFailure) => void;
|
|
79
80
|
}): (content: string) => Promise<string>;
|
|
80
81
|
export interface SubagentToolOptions {
|
|
81
82
|
runner: Runner;
|
|
@@ -109,6 +110,7 @@ export interface SubagentToolOptions {
|
|
|
109
110
|
onObserverError?: (err: unknown, info: {
|
|
110
111
|
observedAgent?: string;
|
|
111
112
|
observerAgent?: string;
|
|
113
|
+
site?: string;
|
|
112
114
|
}) => void;
|
|
113
115
|
extraTools?: (ctx: SubagentSpawnContext) => ToolSpec[] | Promise<ToolSpec[]>;
|
|
114
116
|
onExtraToolsError?: (err: unknown, info: {
|