@sema-agent/core 5.7.0 → 5.8.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 +37 -0
- package/dist/agents/cascade.d.ts +1 -1
- package/dist/agents/subagent.d.ts +4 -7
- package/dist/agents/subagent.js +10 -19
- package/dist/agents/teacher.js +3 -3
- package/dist/agents/team.d.ts +1 -1
- package/dist/agents/team.js +1 -1
- package/dist/agents/verify.js +1 -1
- package/dist/brain/anthropic.js +24 -35
- package/dist/brain/openai.js +22 -33
- package/dist/brain/stream-engine.d.ts +0 -2
- package/dist/brain/stream-engine.js +5 -47
- package/dist/brain/stream-shared.d.ts +0 -10
- package/dist/brain/stream-shared.js +0 -23
- package/dist/brain/terminal-cause.d.ts +0 -1
- package/dist/brain/terminal-cause.js +0 -3
- package/dist/brain/timeout.d.ts +4 -2
- package/dist/brain/timeout.js +4 -14
- package/dist/config/catalog.d.ts +3 -11
- package/dist/config/catalog.js +30 -69
- package/dist/config/defaults.d.ts +0 -3
- package/dist/config/defaults.js +0 -3
- package/dist/core/auto-compaction.d.ts +2 -11
- package/dist/core/auto-compaction.js +3 -67
- package/dist/core/checkpoint-store.d.ts +10 -8
- package/dist/core/checkpoint-store.js +7 -7
- package/dist/core/memory-engine/file-backend.js +20 -17
- package/dist/core/memory-engine/layout.d.ts +5 -0
- package/dist/core/memory-engine/layout.js +72 -47
- package/dist/core/runner/assemble-result.d.ts +2 -8
- package/dist/core/runner/assemble-result.js +15 -16
- package/dist/core/runner/prepare-task.d.ts +26 -6
- package/dist/core/runner/prepare-task.js +203 -141
- package/dist/core/runner/runtask.d.ts +0 -2
- package/dist/core/runner/runtask.js +200 -244
- package/dist/core/session-policy-store.d.ts +5 -0
- package/dist/core/session-policy-store.js +3 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +2 -2
- package/dist/core/task-registry-agent.d.ts +2 -0
- package/dist/core/task-registry-agent.js +33 -1
- package/dist/core/task-registry.d.ts +2 -0
- package/dist/core/task-registry.js +9 -1
- package/dist/core/tool-errors.js +10 -2
- package/dist/core/trace.d.ts +1 -8
- package/dist/core/types.d.ts +24 -30
- package/dist/core/usage-window-store.d.ts +39 -0
- package/dist/core/usage-window-store.js +115 -0
- package/dist/engine/execution-env/node-execution-env.js +0 -22
- package/dist/engine/harness/agent-harness.d.ts +0 -7
- package/dist/engine/harness/agent-harness.js +13 -53
- package/dist/engine/harness/types.d.ts +2 -15
- package/dist/engine/llm/types.d.ts +1 -2
- package/dist/engine/loop/agent-loop.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +5 -171
- package/dist/engine/loop/types.d.ts +0 -18
- package/dist/index.d.ts +10 -5
- package/dist/index.js +8 -4
- package/dist/orchestration/goal.js +1 -5
- package/dist/orchestration/run-workflow-tool.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.js +1 -2
- package/dist/orchestration/workflow-governance.d.ts +6 -6
- package/dist/orchestration/workflow-governance.js +33 -47
- package/dist/prompt-assembly/event-registry.js +2 -3
- package/dist/stores/cc/lockfile.js +9 -11
- package/dist/stores/cc/mailbox-store.js +49 -38
- package/dist/stores/file/index.d.ts +3 -0
- package/dist/stores/file/index.js +4 -0
- package/dist/stores/file/usage-window-store.d.ts +9 -0
- package/dist/stores/file/usage-window-store.js +86 -0
- package/dist/tools/fs/fs-bash.d.ts +1 -10
- package/dist/tools/fs/fs-bash.js +20 -60
- package/dist/tools/fs/index.d.ts +0 -2
- package/dist/tools/fs/index.js +1 -2
- package/package.json +1 -4
- package/dist/bin/sema-tb.d.ts +0 -31
- package/dist/bin/sema-tb.js +0 -448
- package/dist/bin/tb-env.d.ts +0 -2
- package/dist/bin/tb-env.js +0 -17
- package/dist/brain/walltime.d.ts +0 -1
- package/dist/brain/walltime.js +0 -1
- package/dist/core/runner/call-cap.d.ts +0 -67
- package/dist/core/runner/call-cap.js +0 -145
- package/dist/core/runner/cut-kill.d.ts +0 -10
- package/dist/core/runner/cut-kill.js +0 -37
|
@@ -6,13 +6,3 @@ export interface RepetitionPoll {
|
|
|
6
6
|
readonly spared: RepetitionEvent[];
|
|
7
7
|
}
|
|
8
8
|
export declare function createRepetitionPoll(): RepetitionPoll;
|
|
9
|
-
export interface WalltimeGate {
|
|
10
|
-
onDeadline(): boolean;
|
|
11
|
-
shouldDropLine(): boolean;
|
|
12
|
-
readonly cut: boolean;
|
|
13
|
-
}
|
|
14
|
-
export declare function createWalltimeGate(deps: {
|
|
15
|
-
callDeadlineMs: number | undefined;
|
|
16
|
-
isDegenerate: () => boolean;
|
|
17
|
-
cancel: () => void;
|
|
18
|
-
}): WalltimeGate;
|
|
@@ -39,26 +39,3 @@ export function createRepetitionPoll() {
|
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
-
export function createWalltimeGate(deps) {
|
|
43
|
-
const { callDeadlineMs, isDegenerate, cancel } = deps;
|
|
44
|
-
let walltimeCut = false;
|
|
45
|
-
return {
|
|
46
|
-
get cut() {
|
|
47
|
-
return walltimeCut;
|
|
48
|
-
},
|
|
49
|
-
onDeadline() {
|
|
50
|
-
if (isDegenerate())
|
|
51
|
-
return false;
|
|
52
|
-
walltimeCut = true;
|
|
53
|
-
return true;
|
|
54
|
-
},
|
|
55
|
-
shouldDropLine() {
|
|
56
|
-
if (callDeadlineMs !== undefined && !walltimeCut && !isDegenerate() && Date.now() >= callDeadlineMs) {
|
|
57
|
-
walltimeCut = true;
|
|
58
|
-
cancel();
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
return walltimeCut;
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
import type { AssistantMessage } from "../internal/llm.js";
|
|
2
2
|
export type TerminalCauseCarrier = Pick<AssistantMessage, "errorKind">;
|
|
3
3
|
export declare function isDegenerateCutMessage(message: TerminalCauseCarrier): boolean;
|
|
4
|
-
export declare function isWalltimeCutMessage(message: TerminalCauseCarrier): boolean;
|
package/dist/brain/timeout.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { StreamFn } from "../internal/llm.js";
|
|
2
2
|
export declare function resolveStallTimeoutMs(value: number | undefined, knob: string): number | undefined;
|
|
3
|
+
export declare const STALL_CONNECT_MS = 30000;
|
|
4
|
+
export declare const STALL_FIRST_TOKEN_MS = 120000;
|
|
5
|
+
export declare const STALL_IDLE_MS = 90000;
|
|
3
6
|
export interface BrainTimeoutConfig {
|
|
4
7
|
connectTimeoutMs?: number;
|
|
5
8
|
firstTokenTimeoutMs?: number;
|
|
6
9
|
idleTimeoutMs?: number;
|
|
7
10
|
}
|
|
8
|
-
export declare function createConnectController(connectTimeoutMs: number | undefined, outerSignal: AbortSignal | undefined
|
|
11
|
+
export declare function createConnectController(connectTimeoutMs: number | undefined, outerSignal: AbortSignal | undefined): {
|
|
9
12
|
signal: AbortSignal;
|
|
10
13
|
timedOut: () => boolean;
|
|
11
|
-
deadlineHit: () => boolean;
|
|
12
14
|
clearTimer: () => void;
|
|
13
15
|
dispose: () => void;
|
|
14
16
|
};
|
package/dist/brain/timeout.js
CHANGED
|
@@ -8,10 +8,12 @@ export function resolveStallTimeoutMs(value, knob) {
|
|
|
8
8
|
}
|
|
9
9
|
return value;
|
|
10
10
|
}
|
|
11
|
-
export
|
|
11
|
+
export const STALL_CONNECT_MS = 30_000;
|
|
12
|
+
export const STALL_FIRST_TOKEN_MS = 120_000;
|
|
13
|
+
export const STALL_IDLE_MS = 90_000;
|
|
14
|
+
export function createConnectController(connectTimeoutMs, outerSignal) {
|
|
12
15
|
const ac = new AbortController();
|
|
13
16
|
let timedOut = false;
|
|
14
|
-
let deadlineHit = false;
|
|
15
17
|
const onAbort = () => ac.abort(outerSignal?.reason);
|
|
16
18
|
if (outerSignal) {
|
|
17
19
|
if (outerSignal.aborted)
|
|
@@ -26,26 +28,14 @@ export function createConnectController(connectTimeoutMs, outerSignal, deadlineA
|
|
|
26
28
|
ac.abort();
|
|
27
29
|
}, connectTimeoutMs);
|
|
28
30
|
}
|
|
29
|
-
let deadlineTimer;
|
|
30
|
-
if (deadlineAtMs !== undefined) {
|
|
31
|
-
deadlineTimer = setTimeout(() => {
|
|
32
|
-
deadlineHit = true;
|
|
33
|
-
ac.abort();
|
|
34
|
-
}, Math.max(0, deadlineAtMs - Date.now()));
|
|
35
|
-
deadlineTimer.unref?.();
|
|
36
|
-
}
|
|
37
31
|
const clearTimer = () => {
|
|
38
32
|
if (timer)
|
|
39
33
|
clearTimeout(timer);
|
|
40
34
|
timer = undefined;
|
|
41
|
-
if (deadlineTimer)
|
|
42
|
-
clearTimeout(deadlineTimer);
|
|
43
|
-
deadlineTimer = undefined;
|
|
44
35
|
};
|
|
45
36
|
return {
|
|
46
37
|
signal: ac.signal,
|
|
47
38
|
timedOut: () => timedOut,
|
|
48
|
-
deadlineHit: () => deadlineHit,
|
|
49
39
|
clearTimer,
|
|
50
40
|
dispose: () => {
|
|
51
41
|
clearTimer();
|
package/dist/config/catalog.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TaskLimits } from "../core/types.js";
|
|
1
2
|
export declare const CONFIG_CATALOG_VERSION = "sema-config@1";
|
|
2
3
|
export interface ConfigKnob {
|
|
3
4
|
key: string;
|
|
@@ -24,18 +25,9 @@ export interface EffectiveConfigField {
|
|
|
24
25
|
}
|
|
25
26
|
export declare const MAX_OVERRIDE_DECLARATIONS = 32;
|
|
26
27
|
export declare function resolveEffectiveConfig(spec: {
|
|
27
|
-
limits?:
|
|
28
|
-
maxTurns?: number;
|
|
29
|
-
timeoutSec?: number;
|
|
30
|
-
maxOutputTokens?: number;
|
|
31
|
-
deadlineNudge?: false | object;
|
|
32
|
-
callCapByDeadline?: false;
|
|
33
|
-
gracefulFinalize?: false;
|
|
34
|
-
};
|
|
35
|
-
maxCostUsd?: number;
|
|
36
|
-
maxTokens?: number;
|
|
28
|
+
limits?: Pick<TaskLimits, "maxTurns" | "maxWalltimeMs" | "maxOutputTokens" | "maxCostUsd" | "maxTokens">;
|
|
37
29
|
resourceSuspend?: {
|
|
38
|
-
|
|
30
|
+
totalTokens?: number;
|
|
39
31
|
totalBudgetUsd?: number;
|
|
40
32
|
};
|
|
41
33
|
configOverrides?: readonly ConfigOverrideDeclaration[];
|
package/dist/config/catalog.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, SESSION_DEFAULT_TTL_DAYS } from "./defaults.js";
|
|
2
2
|
export const CONFIG_CATALOG_VERSION = "sema-config@1";
|
|
3
3
|
export function describeConfigCatalog() {
|
|
4
4
|
const knobs = [
|
|
5
5
|
{
|
|
6
6
|
key: "limits.maxTurns",
|
|
7
|
-
recommended:
|
|
7
|
+
recommended: null,
|
|
8
8
|
unit: "turns",
|
|
9
|
-
kind: "
|
|
10
|
-
summary: "
|
|
11
|
-
precedence: "spec.limits.maxTurns >
|
|
9
|
+
kind: "budget",
|
|
10
|
+
summary: "Per-slice turn ceiling. Unset = unbounded (design/164 retired the engine-side net): a task that needs a turn bound states one. 0 = the explicit unbounded sentinel.",
|
|
11
|
+
precedence: "spec.limits.maxTurns > unset",
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
|
-
key: "limits.
|
|
14
|
+
key: "limits.maxWalltimeMs",
|
|
15
15
|
recommended: null,
|
|
16
|
-
unit: "
|
|
16
|
+
unit: "ms",
|
|
17
17
|
kind: "budget",
|
|
18
|
-
summary: "
|
|
19
|
-
precedence: "spec.limits.
|
|
18
|
+
summary: "Per-slice ACTIVE wall-clock window for the task (suspended time does not count; a resume opens a fresh window). Unset = unbounded, which is the recommendation — bound work by tokens, not by time.",
|
|
19
|
+
precedence: "spec.limits.maxWalltimeMs > unset",
|
|
20
20
|
},
|
|
21
21
|
{
|
|
22
22
|
key: "limits.maxOutputTokens",
|
|
@@ -27,65 +27,35 @@ export function describeConfigCatalog() {
|
|
|
27
27
|
precedence: "spec.limits.maxOutputTokens > model.maxTokens > provider default",
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
|
-
key: "limits.
|
|
31
|
-
recommended: true,
|
|
32
|
-
kind: "behavior",
|
|
33
|
-
summary: "Two one-shot convergence reminders (~80%/~95% of timeoutSec). Auto-ON when timeoutSec is set; no effect without it.",
|
|
34
|
-
precedence: "spec.limits.deadlineNudge > derived(timeoutSec set)",
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
key: "limits.callCapByDeadline",
|
|
38
|
-
recommended: true,
|
|
39
|
-
kind: "behavior",
|
|
40
|
-
summary: "Shrink each call's max_tokens to what remaining wall-clock can absorb (shrink-only). Auto-ON when timeoutSec is set.",
|
|
41
|
-
precedence: "spec.limits.callCapByDeadline > derived(timeoutSec set)",
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
key: "limits.gracefulFinalize",
|
|
45
|
-
recommended: true,
|
|
46
|
-
kind: "behavior",
|
|
47
|
-
summary: "One-shot finalize turn + write-out cushion + soft execution deadline when remaining time no longer fits a full cycle. Auto-ON when timeoutSec is set.",
|
|
48
|
-
precedence: "spec.limits.gracefulFinalize > derived(timeoutSec set)",
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
key: "budget.maxCostUsd",
|
|
30
|
+
key: "limits.maxCostUsd",
|
|
52
31
|
recommended: null,
|
|
53
32
|
unit: "usd",
|
|
54
33
|
kind: "budget",
|
|
55
|
-
summary: "
|
|
56
|
-
precedence: "spec.maxCostUsd > unset",
|
|
34
|
+
summary: "Per-slice spend ceiling (hard-cancels the in-flight stream by default when crossed). Unset = unbounded.",
|
|
35
|
+
precedence: "spec.limits.maxCostUsd > unset",
|
|
57
36
|
},
|
|
58
37
|
{
|
|
59
|
-
key: "
|
|
38
|
+
key: "limits.maxTokens",
|
|
60
39
|
recommended: null,
|
|
61
40
|
unit: "tokens",
|
|
62
41
|
kind: "budget",
|
|
63
|
-
summary: "
|
|
64
|
-
precedence: "spec.maxTokens > unset",
|
|
42
|
+
summary: "Per-slice token budget — the PRIMARY axis. Unset = unbounded.",
|
|
43
|
+
precedence: "spec.limits.maxTokens > unset",
|
|
65
44
|
},
|
|
66
45
|
{
|
|
67
|
-
key: "
|
|
46
|
+
key: "limits.approachNotice",
|
|
68
47
|
recommended: null,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
precedence: "spec.resources.totalWalltimeSec (first slice) > unset",
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
key: "delegation.forkMaxTurns",
|
|
76
|
-
recommended: FORK_DEFAULT_MAX_TURNS,
|
|
77
|
-
unit: "turns",
|
|
78
|
-
kind: "safety-net",
|
|
79
|
-
summary: "Default turn cap for the Agent fork arm (child inherits the parent's world; tighter net than the global default).",
|
|
80
|
-
precedence: "agent definition limits > fork default",
|
|
48
|
+
kind: "behavior",
|
|
49
|
+
summary: "Advisory limit-approach reminders (one at ~80% of the tightest armed axis, one at ~95%). Unset = ON whenever an axis is armed; false = off; { at } overrides the thresholds.",
|
|
50
|
+
precedence: "spec.limits.approachNotice > on-when-any-axis-armed",
|
|
81
51
|
},
|
|
82
52
|
{
|
|
83
|
-
key: "
|
|
84
|
-
recommended:
|
|
85
|
-
unit: "
|
|
86
|
-
kind: "
|
|
87
|
-
summary: "
|
|
88
|
-
precedence: "
|
|
53
|
+
key: "budget.totalTokens",
|
|
54
|
+
recommended: null,
|
|
55
|
+
unit: "tokens",
|
|
56
|
+
kind: "budget",
|
|
57
|
+
summary: "Cross-slice token allocation for resource-suspend tasks. First slice only; later slices read the frozen ledger. Unset = per-slice windows only.",
|
|
58
|
+
precedence: "spec.resourceSuspend.totalTokens (first slice) > unset",
|
|
89
59
|
},
|
|
90
60
|
{
|
|
91
61
|
key: "delegation.retainTtlMs",
|
|
@@ -119,28 +89,19 @@ const MAX_DECLARATION_CHARS = 200;
|
|
|
119
89
|
const sanitizeDeclarationText = (s) => s.replace(/[\u0000-\u0008\u000b-\u001f\u007f]/g, "").slice(0, MAX_DECLARATION_CHARS);
|
|
120
90
|
export function resolveEffectiveConfig(spec, ctx) {
|
|
121
91
|
const l = spec.limits;
|
|
122
|
-
const hasDeadline = l?.timeoutSec !== undefined;
|
|
123
92
|
const pick = (key, specVal, dflt) => specVal !== undefined ? { key, value: specVal, provenance: "spec" } : { key, value: dflt, provenance: "default" };
|
|
124
|
-
const deadlineSwitch = (key, v) => v === false
|
|
125
|
-
? { key, value: false, provenance: "spec" }
|
|
126
|
-
: v !== undefined
|
|
127
|
-
? { key, value: hasDeadline, provenance: "spec" }
|
|
128
|
-
: { key, value: hasDeadline, provenance: "derived" };
|
|
129
93
|
const maxOutputTokens = l?.maxOutputTokens !== undefined
|
|
130
94
|
? { key: "limits.maxOutputTokens", value: l.maxOutputTokens, provenance: "spec" }
|
|
131
95
|
: ctx?.modelMaxTokens !== undefined
|
|
132
96
|
? { key: "limits.maxOutputTokens", value: ctx.modelMaxTokens, provenance: "derived" }
|
|
133
97
|
: { key: "limits.maxOutputTokens", value: null, provenance: "default" };
|
|
134
98
|
const fields = [
|
|
135
|
-
pick("limits.maxTurns", l?.maxTurns,
|
|
136
|
-
pick("limits.
|
|
99
|
+
pick("limits.maxTurns", l?.maxTurns, null),
|
|
100
|
+
pick("limits.maxWalltimeMs", l?.maxWalltimeMs, null),
|
|
137
101
|
maxOutputTokens,
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
pick("budget.maxCostUsd", spec.maxCostUsd, null),
|
|
142
|
-
pick("budget.maxTokens", spec.maxTokens, null),
|
|
143
|
-
pick("budget.totalWalltimeSec", spec.resourceSuspend?.totalWalltimeSec, null),
|
|
102
|
+
pick("limits.maxCostUsd", l?.maxCostUsd, null),
|
|
103
|
+
pick("limits.maxTokens", l?.maxTokens, null),
|
|
104
|
+
pick("budget.totalTokens", spec.resourceSuspend?.totalTokens, null),
|
|
144
105
|
pick("budget.totalBudgetUsd", spec.resourceSuspend?.totalBudgetUsd, null),
|
|
145
106
|
];
|
|
146
107
|
for (const d of (spec.configOverrides ?? []).slice(0, MAX_OVERRIDE_DECLARATIONS)) {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export declare const DEFAULT_MAX_TURNS = 1000;
|
|
2
|
-
export declare const FORK_DEFAULT_MAX_TURNS = 200;
|
|
3
|
-
export declare const SESSION_BG_DEFAULT_TIMEOUT_SEC: number;
|
|
4
1
|
export declare const RETAIN_DEFAULT_TTL_MS: number;
|
|
5
2
|
export declare const RETAIN_DEFAULT_MAX = 16;
|
|
6
3
|
export declare const SESSION_DEFAULT_TTL_DAYS = 7;
|
package/dist/config/defaults.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export const DEFAULT_MAX_TURNS = 1000;
|
|
2
|
-
export const FORK_DEFAULT_MAX_TURNS = 200;
|
|
3
|
-
export const SESSION_BG_DEFAULT_TIMEOUT_SEC = 30 * 60;
|
|
4
1
|
export const RETAIN_DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
5
2
|
export const RETAIN_DEFAULT_MAX = 16;
|
|
6
3
|
export const SESSION_DEFAULT_TTL_DAYS = 7;
|
|
@@ -6,16 +6,14 @@ import type { Brain } from "./types.js";
|
|
|
6
6
|
export declare const DEFAULT_COMPACTION_INSTRUCTIONS: string;
|
|
7
7
|
export declare const COMPACTION_INSTRUCTIONS_MAX_CHARS = 2048;
|
|
8
8
|
export declare function sanitizeCompactionInstructions(text: string): string;
|
|
9
|
-
export declare const COMPACTION_THROUGHPUT_PRIOR_TOKENS_PER_MS = 3;
|
|
10
9
|
export type CompactionPhaseDurations = Omit<Extract<TraceEvent, {
|
|
11
10
|
kind: "compaction.phase_timings";
|
|
12
11
|
}>, "kind" | "version" | "taskId" | "ts" | "durationMs">;
|
|
13
|
-
export declare function isCompactionWalltimeAbort(err: unknown): boolean;
|
|
14
12
|
export declare function isCompactionManualCancel(err: unknown): boolean;
|
|
15
13
|
export interface CompactionWindowSafetyInfo {
|
|
16
14
|
kind: "fallback" | "clamp_disclosure";
|
|
17
15
|
truncationRatio: number;
|
|
18
|
-
reason?: "budget" | "
|
|
16
|
+
reason?: "budget" | "tolerance";
|
|
19
17
|
contentTokens: number;
|
|
20
18
|
headroomTokens?: number;
|
|
21
19
|
estCostMicroUsd?: number;
|
|
@@ -51,13 +49,6 @@ export interface MaybeCompactOptions {
|
|
|
51
49
|
capMicroUsd: number;
|
|
52
50
|
mainInputPer1M?: number;
|
|
53
51
|
};
|
|
54
|
-
walltime?: {
|
|
55
|
-
deadlineMs: number;
|
|
56
|
-
cushionMs: () => number;
|
|
57
|
-
throughputRef: {
|
|
58
|
-
ewmaTokensPerMs?: number;
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
52
|
onWindowSafety?: (info: CompactionWindowSafetyInfo) => void;
|
|
62
53
|
minTokens?: number;
|
|
63
54
|
force?: boolean;
|
|
@@ -131,7 +122,7 @@ export declare function maybeCompact(opts: MaybeCompactOptions): Promise<{
|
|
|
131
122
|
modelFallback?: true;
|
|
132
123
|
fallbackReason?: "window";
|
|
133
124
|
clampedRatio?: number;
|
|
134
|
-
clampReason?: "budget" | "
|
|
125
|
+
clampReason?: "budget" | "tolerance";
|
|
135
126
|
}>;
|
|
136
127
|
export declare function nextTrimForceBackoff(prev: boolean, comp: {
|
|
137
128
|
compacted: boolean;
|
|
@@ -22,13 +22,7 @@ const WORKING_FILE_BREAKOUT_TAGS = ["working-file", "summary"];
|
|
|
22
22
|
function escapeWorkingFilePathAttr(path) {
|
|
23
23
|
return path.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/[\r\n]+/g, " ");
|
|
24
24
|
}
|
|
25
|
-
export const COMPACTION_THROUGHPUT_PRIOR_TOKENS_PER_MS = 3;
|
|
26
|
-
const COMPACTION_THROUGHPUT_EWMA_ALPHA = 0.3;
|
|
27
|
-
const COMPACTION_WALLTIME_ABORT_FLAG = "semaCompactionWalltimeAbort";
|
|
28
25
|
const COMPACTION_MANUAL_CANCEL_FLAG = "semaCompactionManualCancel";
|
|
29
|
-
export function isCompactionWalltimeAbort(err) {
|
|
30
|
-
return (typeof err === "object" && err !== null && err[COMPACTION_WALLTIME_ABORT_FLAG] === true);
|
|
31
|
-
}
|
|
32
26
|
export function isCompactionManualCancel(err) {
|
|
33
27
|
return (typeof err === "object" && err !== null && err[COMPACTION_MANUAL_CANCEL_FLAG] === true);
|
|
34
28
|
}
|
|
@@ -180,7 +174,7 @@ export async function maybeCompact(opts) {
|
|
|
180
174
|
const wantsWindowSafety = opts.compactionModel !== undefined &&
|
|
181
175
|
summarizationCharsCapacity(opts.compactionModel) < summarizationCharsCapacity(opts.model);
|
|
182
176
|
let dry;
|
|
183
|
-
if (wantsWindowSafety
|
|
177
|
+
if (wantsWindowSafety) {
|
|
184
178
|
dry = dryRunSummarizationClamp(prep.value, summaryModel, effectiveInstructions);
|
|
185
179
|
}
|
|
186
180
|
const contentTokens = dry !== undefined ? Math.ceil((dry.conversationChars + (prep.value.previousSummary?.length ?? 0)) / cpt) : 0;
|
|
@@ -201,13 +195,6 @@ export async function maybeCompact(opts) {
|
|
|
201
195
|
gate = "budget";
|
|
202
196
|
}
|
|
203
197
|
}
|
|
204
|
-
if (gate === undefined && opts.walltime !== undefined) {
|
|
205
|
-
const remainingMs = opts.walltime.deadlineMs - opts.walltime.cushionMs() - Date.now();
|
|
206
|
-
const throughput = opts.walltime.throughputRef.ewmaTokensPerMs ?? COMPACTION_THROUGHPUT_PRIOR_TOKENS_PER_MS;
|
|
207
|
-
if (remainingMs < contentTokens / Math.max(throughput, 1e-6)) {
|
|
208
|
-
gate = "walltime";
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
198
|
if (gate === undefined) {
|
|
212
199
|
summaryModel = opts.model;
|
|
213
200
|
modelFallback = true;
|
|
@@ -245,35 +232,7 @@ export async function maybeCompact(opts) {
|
|
|
245
232
|
}
|
|
246
233
|
}
|
|
247
234
|
const auth = await opts.getApiKeyAndHeaders?.(summaryModel);
|
|
248
|
-
|
|
249
|
-
let softTimer;
|
|
250
|
-
let softAbort;
|
|
251
|
-
let onOuterAbort;
|
|
252
|
-
let firstAbortCause;
|
|
253
|
-
const latchCallerCause = () => {
|
|
254
|
-
firstAbortCause ??= opts.manualCancelSignal?.aborted === true ? "manual" : "caller";
|
|
255
|
-
};
|
|
256
|
-
if (opts.walltime !== undefined) {
|
|
257
|
-
softAbort = new AbortController();
|
|
258
|
-
onOuterAbort = () => {
|
|
259
|
-
latchCallerCause();
|
|
260
|
-
softAbort?.abort();
|
|
261
|
-
};
|
|
262
|
-
if (opts.signal?.aborted) {
|
|
263
|
-
latchCallerCause();
|
|
264
|
-
softAbort.abort();
|
|
265
|
-
}
|
|
266
|
-
else if (opts.signal) {
|
|
267
|
-
opts.signal.addEventListener("abort", onOuterAbort, { once: true });
|
|
268
|
-
}
|
|
269
|
-
const softMs = opts.walltime.deadlineMs - opts.walltime.cushionMs() - Date.now();
|
|
270
|
-
softTimer = setTimeout(() => {
|
|
271
|
-
firstAbortCause ??= "walltime";
|
|
272
|
-
softAbort?.abort();
|
|
273
|
-
}, Math.max(0, softMs));
|
|
274
|
-
softTimer.unref?.();
|
|
275
|
-
summarySignal = softAbort.signal;
|
|
276
|
-
}
|
|
235
|
+
const summarySignal = opts.signal;
|
|
277
236
|
const runtime = brainToRuntime(opts.brain);
|
|
278
237
|
let res;
|
|
279
238
|
summaryStartAt = Date.now();
|
|
@@ -284,23 +243,9 @@ export async function maybeCompact(opts) {
|
|
|
284
243
|
}
|
|
285
244
|
finally {
|
|
286
245
|
summaryEndAt = Date.now();
|
|
287
|
-
if (softTimer !== undefined)
|
|
288
|
-
clearTimeout(softTimer);
|
|
289
|
-
if (onOuterAbort !== undefined)
|
|
290
|
-
opts.signal?.removeEventListener("abort", onOuterAbort);
|
|
291
246
|
}
|
|
292
247
|
if (!res.ok) {
|
|
293
|
-
|
|
294
|
-
softAbort.signal.aborted &&
|
|
295
|
-
(firstAbortCause !== undefined ? firstAbortCause === "walltime" : opts.signal?.aborted !== true);
|
|
296
|
-
if (walltimeWon) {
|
|
297
|
-
if (typeof res.error !== "object" || res.error === null) {
|
|
298
|
-
throw Object.assign(new Error(String(res.error)), { [COMPACTION_WALLTIME_ABORT_FLAG]: true });
|
|
299
|
-
}
|
|
300
|
-
res.error[COMPACTION_WALLTIME_ABORT_FLAG] = true;
|
|
301
|
-
}
|
|
302
|
-
if (!walltimeWon &&
|
|
303
|
-
opts.manualCancelSignal?.aborted === true &&
|
|
248
|
+
if (opts.manualCancelSignal?.aborted === true &&
|
|
304
249
|
res.error instanceof CompactionError &&
|
|
305
250
|
res.error.code === "aborted") {
|
|
306
251
|
res.error[COMPACTION_MANUAL_CANCEL_FLAG] = true;
|
|
@@ -315,15 +260,6 @@ export async function maybeCompact(opts) {
|
|
|
315
260
|
throw new Error(`compaction produced an oversized summary (≈${Math.ceil(summary.length / cpt)} tokens vs ` +
|
|
316
261
|
`${res.value.tokensBefore} before compaction; history left untouched)`);
|
|
317
262
|
}
|
|
318
|
-
if (opts.walltime !== undefined && contentTokens > 0) {
|
|
319
|
-
const passMs = Math.max(1, Date.now() - compactStartAt);
|
|
320
|
-
const sample = contentTokens / passMs;
|
|
321
|
-
const prev = opts.walltime.throughputRef.ewmaTokensPerMs;
|
|
322
|
-
opts.walltime.throughputRef.ewmaTokensPerMs =
|
|
323
|
-
prev === undefined
|
|
324
|
-
? sample
|
|
325
|
-
: COMPACTION_THROUGHPUT_EWMA_ALPHA * sample + (1 - COMPACTION_THROUGHPUT_EWMA_ALPHA) * prev;
|
|
326
|
-
}
|
|
327
263
|
firstKeptEntryId = res.value.firstKeptEntryId;
|
|
328
264
|
summaryTokensBefore = res.value.tokensBefore;
|
|
329
265
|
details = res.value.details;
|
|
@@ -10,7 +10,8 @@ export type CheckpointToken = string & {
|
|
|
10
10
|
readonly __brand: "CheckpointToken";
|
|
11
11
|
};
|
|
12
12
|
export declare function mintCheckpointToken(): CheckpointToken;
|
|
13
|
-
export type ResourceLimitReason = "
|
|
13
|
+
export type ResourceLimitReason = "budget_tokens" | "budget_cost" | "walltime" | "turns" | "preempt" | "env_lifetime" | "usage_window";
|
|
14
|
+
export type PlatformLimitReason = Extract<ResourceLimitReason, "env_lifetime" | "usage_window">;
|
|
14
15
|
export interface SafetyAxis {
|
|
15
16
|
egress?: boolean;
|
|
16
17
|
irreversible?: boolean;
|
|
@@ -53,6 +54,7 @@ export type CheckpointGate = {
|
|
|
53
54
|
} | {
|
|
54
55
|
kind: "resource_limit";
|
|
55
56
|
reason: ResourceLimitReason;
|
|
57
|
+
resumeAfterMs?: number;
|
|
56
58
|
} | {
|
|
57
59
|
kind: "needs_review";
|
|
58
60
|
reason: string;
|
|
@@ -153,12 +155,12 @@ export type SerializedCheckpointState = {
|
|
|
153
155
|
export declare const CURRENT_CHECKPOINT_VERSION = 1;
|
|
154
156
|
export declare const RESOURCE_CHECKPOINT_VERSION = 2;
|
|
155
157
|
export declare const BINDING_CHECKPOINT_VERSION = 3;
|
|
156
|
-
export declare const
|
|
157
|
-
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION =
|
|
158
|
+
export declare const TOKEN_CHECKPOINT_VERSION = 5;
|
|
159
|
+
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION = 5;
|
|
158
160
|
export declare function checkpointVersionOf(cp: Pick<Checkpoint, "version">): number;
|
|
159
161
|
export interface ResourceLedger {
|
|
160
162
|
totalBudgetMicroUsd?: number;
|
|
161
|
-
|
|
163
|
+
totalTokens?: number;
|
|
162
164
|
spentMicroUsd: number;
|
|
163
165
|
spentTokens: number;
|
|
164
166
|
spentTurns: number;
|
|
@@ -172,12 +174,12 @@ export declare function debitLedger(prior: ResourceLedger | undefined, slice: {
|
|
|
172
174
|
walltimeMs?: number;
|
|
173
175
|
}, total?: {
|
|
174
176
|
totalBudgetMicroUsd?: number;
|
|
175
|
-
|
|
177
|
+
totalTokens?: number;
|
|
176
178
|
}, opts?: {
|
|
177
179
|
countSlice?: boolean;
|
|
178
180
|
}): ResourceLedger;
|
|
179
181
|
export declare function remainingBudgetMicroUsd(ledger: ResourceLedger | undefined): number | undefined;
|
|
180
|
-
export declare function
|
|
182
|
+
export declare function remainingTokens(ledger: ResourceLedger | undefined): number | undefined;
|
|
181
183
|
export interface ResolvedOutcome {
|
|
182
184
|
boundCallId: string;
|
|
183
185
|
decision: "allow" | "deny" | "approve" | "reject" | "edit";
|
|
@@ -242,11 +244,11 @@ export interface CheckpointSummary {
|
|
|
242
244
|
}
|
|
243
245
|
export declare function summarizeCheckpoint(cp: Checkpoint): CheckpointSummary;
|
|
244
246
|
export declare class CheckpointError extends Error {
|
|
245
|
-
readonly code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found" | "checkpoint.gate_mismatch" | "checkpoint.resume_aborted" | "checkpoint.invalid_outcome" | "checkpoint.unsupported_version" | "checkpoint.reopen_revote" | "checkpoint.reopened_concurrently" | "checkpoint.reopen_failed" | "steering.invalid_content" | "wake.gate_pending" | "wake.nothing_to_deliver" | "resume.parent_constraint_missing" | "resume.parent_constraint_mismatch";
|
|
247
|
+
readonly code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found" | "checkpoint.gate_mismatch" | "checkpoint.resume_aborted" | "checkpoint.invalid_outcome" | "checkpoint.unsupported_version" | "checkpoint.walltime_axis_retired" | "checkpoint.reopen_revote" | "checkpoint.reopened_concurrently" | "checkpoint.reopen_failed" | "steering.invalid_content" | "wake.gate_pending" | "wake.nothing_to_deliver" | "resume.parent_constraint_missing" | "resume.parent_constraint_mismatch";
|
|
246
248
|
readonly detail?: {
|
|
247
249
|
field?: "boundCallId" | "boundInputHash" | "answer";
|
|
248
250
|
} | undefined;
|
|
249
|
-
constructor(code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found" | "checkpoint.gate_mismatch" | "checkpoint.resume_aborted" | "checkpoint.invalid_outcome" | "checkpoint.unsupported_version" | "checkpoint.reopen_revote" | "checkpoint.reopened_concurrently" | "checkpoint.reopen_failed" | "steering.invalid_content" | "wake.gate_pending" | "wake.nothing_to_deliver" | "resume.parent_constraint_missing" | "resume.parent_constraint_mismatch", message: string, detail?: {
|
|
251
|
+
constructor(code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found" | "checkpoint.gate_mismatch" | "checkpoint.resume_aborted" | "checkpoint.invalid_outcome" | "checkpoint.unsupported_version" | "checkpoint.walltime_axis_retired" | "checkpoint.reopen_revote" | "checkpoint.reopened_concurrently" | "checkpoint.reopen_failed" | "steering.invalid_content" | "wake.gate_pending" | "wake.nothing_to_deliver" | "resume.parent_constraint_missing" | "resume.parent_constraint_mismatch", message: string, detail?: {
|
|
250
252
|
field?: "boundCallId" | "boundInputHash" | "answer";
|
|
251
253
|
} | undefined);
|
|
252
254
|
}
|
|
@@ -126,8 +126,8 @@ export function buildRiskDescriptor(input) {
|
|
|
126
126
|
export const CURRENT_CHECKPOINT_VERSION = 1;
|
|
127
127
|
export const RESOURCE_CHECKPOINT_VERSION = 2;
|
|
128
128
|
export const BINDING_CHECKPOINT_VERSION = 3;
|
|
129
|
-
export const
|
|
130
|
-
export const MAX_SUPPORTED_CHECKPOINT_VERSION =
|
|
129
|
+
export const TOKEN_CHECKPOINT_VERSION = 5;
|
|
130
|
+
export const MAX_SUPPORTED_CHECKPOINT_VERSION = 5;
|
|
131
131
|
export function checkpointVersionOf(cp) {
|
|
132
132
|
return cp.version ?? 0;
|
|
133
133
|
}
|
|
@@ -140,7 +140,7 @@ function finiteSpend(v) {
|
|
|
140
140
|
export function debitLedger(prior, slice, total, opts) {
|
|
141
141
|
return {
|
|
142
142
|
totalBudgetMicroUsd: finiteBound(prior?.totalBudgetMicroUsd) ?? finiteBound(total?.totalBudgetMicroUsd),
|
|
143
|
-
|
|
143
|
+
totalTokens: finiteBound(prior?.totalTokens) ?? finiteBound(total?.totalTokens),
|
|
144
144
|
spentMicroUsd: (finiteSpend(prior?.spentMicroUsd) ?? 0) + Math.max(0, finiteSpend(slice.costMicroUsd) ?? 0),
|
|
145
145
|
spentTokens: (finiteSpend(prior?.spentTokens) ?? 0) + Math.max(0, finiteSpend(slice.tokens) ?? 0),
|
|
146
146
|
spentTurns: (finiteSpend(prior?.spentTurns) ?? 0) + Math.max(0, finiteSpend(slice.turns) ?? 0),
|
|
@@ -157,14 +157,14 @@ export function remainingBudgetMicroUsd(ledger) {
|
|
|
157
157
|
return 0;
|
|
158
158
|
return Math.max(0, total - (spent ?? 0));
|
|
159
159
|
}
|
|
160
|
-
export function
|
|
161
|
-
const total = ledger?.
|
|
160
|
+
export function remainingTokens(ledger) {
|
|
161
|
+
const total = ledger?.totalTokens;
|
|
162
162
|
if (total === undefined || !Number.isFinite(total))
|
|
163
163
|
return undefined;
|
|
164
|
-
const spent = ledger?.
|
|
164
|
+
const spent = ledger?.spentTokens;
|
|
165
165
|
if (spent !== undefined && !Number.isFinite(spent))
|
|
166
166
|
return 0;
|
|
167
|
-
return Math.max(0, total
|
|
167
|
+
return Math.max(0, total - (spent ?? 0));
|
|
168
168
|
}
|
|
169
169
|
export function summarizeCheckpoint(cp) {
|
|
170
170
|
const riskDescriptor = cp.gate.kind === "human" || cp.gate.kind === "irreversible_ask" ? cp.gate.riskDescriptor : undefined;
|
|
@@ -487,28 +487,31 @@ export class FileMemoryEngineBackend {
|
|
|
487
487
|
mtimeMs = statSync(lockDir).mtimeMs;
|
|
488
488
|
}
|
|
489
489
|
catch {
|
|
490
|
-
|
|
490
|
+
mtimeMs = undefined;
|
|
491
491
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
statSync(ownerPath);
|
|
498
|
-
tokenPresent = true;
|
|
499
|
-
}
|
|
500
|
-
catch {
|
|
501
|
-
}
|
|
502
|
-
if (tokenPresent) {
|
|
503
|
-
const tomb = join(this.controlPlaneRoot, `txn.lock.stale-${randomUUID()}`);
|
|
492
|
+
if (mtimeMs !== undefined) {
|
|
493
|
+
const staleAt = mtimeMs + staleMs;
|
|
494
|
+
deadline = Math.max(deadline, staleAt + stealGraceMs);
|
|
495
|
+
if (Date.now() > staleAt) {
|
|
496
|
+
let tokenPresent = false;
|
|
504
497
|
try {
|
|
505
|
-
|
|
498
|
+
statSync(ownerPath);
|
|
499
|
+
tokenPresent = true;
|
|
506
500
|
}
|
|
507
501
|
catch {
|
|
508
|
-
continue;
|
|
509
502
|
}
|
|
510
|
-
|
|
511
|
-
|
|
503
|
+
if (tokenPresent) {
|
|
504
|
+
const tomb = join(this.controlPlaneRoot, `txn.lock.stale-${randomUUID()}`);
|
|
505
|
+
let stolen = false;
|
|
506
|
+
try {
|
|
507
|
+
renameSync(lockDir, tomb);
|
|
508
|
+
stolen = true;
|
|
509
|
+
}
|
|
510
|
+
catch {
|
|
511
|
+
}
|
|
512
|
+
if (stolen)
|
|
513
|
+
rmSync(tomb, { recursive: true, force: true });
|
|
514
|
+
}
|
|
512
515
|
}
|
|
513
516
|
}
|
|
514
517
|
if (Date.now() > deadline) {
|
|
@@ -41,6 +41,11 @@ export declare const ANNOUNCEMENTS_FILE = "announcements.json";
|
|
|
41
41
|
export declare const MEMORY_ANNOUNCEMENTS_MAX = 20;
|
|
42
42
|
export declare const SCAN_FUSE_FILE = "scan-fuse.json";
|
|
43
43
|
export declare const SCAN_FUSE_THRESHOLD = 3;
|
|
44
|
+
export interface SidecarLockOptions {
|
|
45
|
+
onDeadline?: "proceed" | "throw";
|
|
46
|
+
waitCapMs?: number;
|
|
47
|
+
}
|
|
48
|
+
export declare function acquireSidecarLock(lockDir: string, opts?: SidecarLockOptions): string;
|
|
44
49
|
interface AnnouncementsRecord {
|
|
45
50
|
folded: number;
|
|
46
51
|
queue: MemoryAnnouncement[];
|