@sema-agent/core 5.6.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 +55 -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 +15 -10
- package/dist/core/checkpoint-store.js +13 -10
- 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 +267 -253
- package/dist/core/secret-env.d.ts +2 -0
- package/dist/core/secret-env.js +16 -4
- 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-notification.d.ts +1 -0
- package/dist/core/task-notification.js +3 -0
- 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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type QuestionAnswer } from "./ask-question.js";
|
|
1
2
|
import type { ReadEntry } from "../tools/fs/safety.js";
|
|
2
3
|
import type { RepairBundle } from "../agents/repair-loop.js";
|
|
3
4
|
import type { WorkspaceHandle } from "./remote-env.js";
|
|
@@ -9,7 +10,8 @@ export type CheckpointToken = string & {
|
|
|
9
10
|
readonly __brand: "CheckpointToken";
|
|
10
11
|
};
|
|
11
12
|
export declare function mintCheckpointToken(): CheckpointToken;
|
|
12
|
-
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">;
|
|
13
15
|
export interface SafetyAxis {
|
|
14
16
|
egress?: boolean;
|
|
15
17
|
irreversible?: boolean;
|
|
@@ -52,6 +54,7 @@ export type CheckpointGate = {
|
|
|
52
54
|
} | {
|
|
53
55
|
kind: "resource_limit";
|
|
54
56
|
reason: ResourceLimitReason;
|
|
57
|
+
resumeAfterMs?: number;
|
|
55
58
|
} | {
|
|
56
59
|
kind: "needs_review";
|
|
57
60
|
reason: string;
|
|
@@ -68,6 +71,7 @@ export type ResumeOutcome = {
|
|
|
68
71
|
decision: "allow" | "deny";
|
|
69
72
|
updatedInput?: unknown;
|
|
70
73
|
reason?: string;
|
|
74
|
+
answer?: QuestionAnswer;
|
|
71
75
|
} | {
|
|
72
76
|
gate: "resource_limit";
|
|
73
77
|
decision: "continue";
|
|
@@ -151,12 +155,12 @@ export type SerializedCheckpointState = {
|
|
|
151
155
|
export declare const CURRENT_CHECKPOINT_VERSION = 1;
|
|
152
156
|
export declare const RESOURCE_CHECKPOINT_VERSION = 2;
|
|
153
157
|
export declare const BINDING_CHECKPOINT_VERSION = 3;
|
|
154
|
-
export declare const
|
|
155
|
-
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION =
|
|
158
|
+
export declare const TOKEN_CHECKPOINT_VERSION = 5;
|
|
159
|
+
export declare const MAX_SUPPORTED_CHECKPOINT_VERSION = 5;
|
|
156
160
|
export declare function checkpointVersionOf(cp: Pick<Checkpoint, "version">): number;
|
|
157
161
|
export interface ResourceLedger {
|
|
158
162
|
totalBudgetMicroUsd?: number;
|
|
159
|
-
|
|
163
|
+
totalTokens?: number;
|
|
160
164
|
spentMicroUsd: number;
|
|
161
165
|
spentTokens: number;
|
|
162
166
|
spentTurns: number;
|
|
@@ -170,16 +174,17 @@ export declare function debitLedger(prior: ResourceLedger | undefined, slice: {
|
|
|
170
174
|
walltimeMs?: number;
|
|
171
175
|
}, total?: {
|
|
172
176
|
totalBudgetMicroUsd?: number;
|
|
173
|
-
|
|
177
|
+
totalTokens?: number;
|
|
174
178
|
}, opts?: {
|
|
175
179
|
countSlice?: boolean;
|
|
176
180
|
}): ResourceLedger;
|
|
177
181
|
export declare function remainingBudgetMicroUsd(ledger: ResourceLedger | undefined): number | undefined;
|
|
178
|
-
export declare function
|
|
182
|
+
export declare function remainingTokens(ledger: ResourceLedger | undefined): number | undefined;
|
|
179
183
|
export interface ResolvedOutcome {
|
|
180
184
|
boundCallId: string;
|
|
181
185
|
decision: "allow" | "deny" | "approve" | "reject" | "edit";
|
|
182
186
|
updatedInput?: unknown;
|
|
187
|
+
answer?: QuestionAnswer;
|
|
183
188
|
}
|
|
184
189
|
export type ReopenReason = "env_failed" | "tool_unavailable";
|
|
185
190
|
export interface ResolveExpectation {
|
|
@@ -239,12 +244,12 @@ export interface CheckpointSummary {
|
|
|
239
244
|
}
|
|
240
245
|
export declare function summarizeCheckpoint(cp: Checkpoint): CheckpointSummary;
|
|
241
246
|
export declare class CheckpointError extends Error {
|
|
242
|
-
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";
|
|
243
248
|
readonly detail?: {
|
|
244
|
-
field?: "boundCallId" | "boundInputHash";
|
|
249
|
+
field?: "boundCallId" | "boundInputHash" | "answer";
|
|
245
250
|
} | undefined;
|
|
246
|
-
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?: {
|
|
247
|
-
field?: "boundCallId" | "boundInputHash";
|
|
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?: {
|
|
252
|
+
field?: "boundCallId" | "boundInputHash" | "answer";
|
|
248
253
|
} | undefined);
|
|
249
254
|
}
|
|
250
255
|
export interface CheckpointStore {
|
|
@@ -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;
|
|
@@ -223,9 +223,12 @@ export function winnerFromOutcome(outcome) {
|
|
|
223
223
|
}
|
|
224
224
|
if (outcome.gate !== "policy_ask")
|
|
225
225
|
return undefined;
|
|
226
|
-
return
|
|
227
|
-
|
|
228
|
-
|
|
226
|
+
return {
|
|
227
|
+
boundCallId: outcome.boundCallId,
|
|
228
|
+
decision: outcome.decision,
|
|
229
|
+
...(outcome.updatedInput === undefined ? {} : { updatedInput: outcome.updatedInput }),
|
|
230
|
+
...(outcome.answer === undefined ? {} : { answer: outcome.answer }),
|
|
231
|
+
};
|
|
229
232
|
}
|
|
230
233
|
export function validatePendingSteer(steer) {
|
|
231
234
|
if (sanitizeUntrustedText(steer.text) !== steer.text) {
|
|
@@ -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[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
1
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, rmdirSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
@@ -36,6 +36,37 @@ function subtreeHasFiles(dir) {
|
|
|
36
36
|
}
|
|
37
37
|
return false;
|
|
38
38
|
}
|
|
39
|
+
function pruneFilelessDirTree(dir) {
|
|
40
|
+
let entries;
|
|
41
|
+
try {
|
|
42
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return !existsSync(dir);
|
|
46
|
+
}
|
|
47
|
+
for (const e of entries) {
|
|
48
|
+
if (!e.isDirectory())
|
|
49
|
+
return false;
|
|
50
|
+
if (!pruneFilelessDirTree(join(dir, e.name)))
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
rmdirSync(dir);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function tryAdoptByRename(fromDir, toDir) {
|
|
62
|
+
try {
|
|
63
|
+
renameSync(fromDir, toDir);
|
|
64
|
+
return { ok: true };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
return { ok: false, code: err.code, error: err };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
39
70
|
function migrateKeyedPartition(m) {
|
|
40
71
|
const disclose = (code, message) => {
|
|
41
72
|
try {
|
|
@@ -52,36 +83,26 @@ function migrateKeyedPartition(m) {
|
|
|
52
83
|
return;
|
|
53
84
|
if (!existsSync(m.fromDir))
|
|
54
85
|
return;
|
|
55
|
-
if (existsSync(m.toDir)) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
try {
|
|
61
|
-
rmSync(m.toDir, { recursive: true });
|
|
62
|
-
}
|
|
63
|
-
catch (err) {
|
|
64
|
-
const code = err.code;
|
|
65
|
-
disclose("memory.partition_adopt_failed", `could not clear the empty directory scaffolding at ${m.toDir} before adopting ${m.fromDir} (${code ?? String(err)}): ` +
|
|
66
|
-
`this session proceeds on ${m.toDir}; ${m.fromDir} still holds the prior memory — move it by hand.`);
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
86
|
+
if (existsSync(m.toDir) && subtreeHasFiles(m.toDir)) {
|
|
87
|
+
disclose("memory.partition_split", splitText);
|
|
88
|
+
return;
|
|
69
89
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
ensureDirExists(dirname(m.toDir));
|
|
91
|
+
let outcome = tryAdoptByRename(m.fromDir, m.toDir);
|
|
92
|
+
if (!outcome.ok && (outcome.code === "EEXIST" || outcome.code === "ENOTEMPTY") && !subtreeHasFiles(m.toDir) && pruneFilelessDirTree(m.toDir)) {
|
|
93
|
+
outcome = tryAdoptByRename(m.fromDir, m.toDir);
|
|
94
|
+
}
|
|
95
|
+
if (outcome.ok)
|
|
96
|
+
return;
|
|
97
|
+
const { code, error } = outcome;
|
|
98
|
+
if ((code === "ENOENT" || code === "EEXIST" || code === "ENOTEMPTY") && existsSync(m.toDir)) {
|
|
99
|
+
if (!existsSync(m.fromDir))
|
|
80
100
|
return;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
`${m.fromDir} still holds the prior memory — move it by hand.`);
|
|
101
|
+
disclose("memory.partition_split", splitText);
|
|
102
|
+
return;
|
|
84
103
|
}
|
|
104
|
+
disclose("memory.partition_adopt_failed", `could not adopt the memory partition ${m.fromDir} → ${m.toDir} (${code ?? String(error)}): this session proceeds on ${m.toDir}; ` +
|
|
105
|
+
`${m.fromDir} still holds the prior memory — move it by hand.`);
|
|
85
106
|
}
|
|
86
107
|
catch {
|
|
87
108
|
}
|
|
@@ -338,8 +359,13 @@ function sleepSync(ms) {
|
|
|
338
359
|
}
|
|
339
360
|
const LOCK_STALE_MS = 1_500;
|
|
340
361
|
const LOCK_WAIT_CAP_MS = 3_000;
|
|
341
|
-
|
|
342
|
-
|
|
362
|
+
const LOCK_SPIN_PAUSE_MS = 5;
|
|
363
|
+
const MAX_FAIL_OPEN_DEADLINE_ROUNDS = 3;
|
|
364
|
+
export function acquireSidecarLock(lockDir, opts = {}) {
|
|
365
|
+
const waitCapMs = opts.waitCapMs ?? LOCK_WAIT_CAP_MS;
|
|
366
|
+
const deadline = Date.now() + waitCapMs;
|
|
367
|
+
const mintToken = () => `${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
|
|
368
|
+
let deadlineRounds = 0;
|
|
343
369
|
const stealOnce = () => {
|
|
344
370
|
const tomb = `${lockDir}.stale-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
345
371
|
try {
|
|
@@ -354,7 +380,7 @@ function acquireSidecarLock(lockDir, opts = {}) {
|
|
|
354
380
|
for (;;) {
|
|
355
381
|
try {
|
|
356
382
|
mkdirSync(lockDir);
|
|
357
|
-
const token =
|
|
383
|
+
const token = mintToken();
|
|
358
384
|
let fd;
|
|
359
385
|
try {
|
|
360
386
|
fd = openSync(join(lockDir, "owner"), "w");
|
|
@@ -374,25 +400,24 @@ function acquireSidecarLock(lockDir, opts = {}) {
|
|
|
374
400
|
return token;
|
|
375
401
|
}
|
|
376
402
|
catch {
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
catch {
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
if (Date.now() > deadline) {
|
|
388
|
-
if (opts.onDeadline === "throw") {
|
|
389
|
-
throw new Error(`sidecar lock wait timed out (${LOCK_WAIT_CAP_MS}ms) at ${lockDir} — refusing to proceed without the lock (fail-closed sidecar)`);
|
|
390
|
-
}
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
const age = Date.now() - statSync(lockDir).mtimeMs;
|
|
406
|
+
if (age > LOCK_STALE_MS)
|
|
391
407
|
stealOnce();
|
|
392
|
-
|
|
408
|
+
}
|
|
409
|
+
catch {
|
|
410
|
+
}
|
|
411
|
+
if (Date.now() > deadline) {
|
|
412
|
+
if (opts.onDeadline === "throw") {
|
|
413
|
+
throw new Error(`sidecar lock wait timed out (${waitCapMs}ms) at ${lockDir} — refusing to proceed without the lock (fail-closed sidecar)`);
|
|
393
414
|
}
|
|
394
|
-
|
|
415
|
+
deadlineRounds += 1;
|
|
416
|
+
if (deadlineRounds > MAX_FAIL_OPEN_DEADLINE_ROUNDS)
|
|
417
|
+
return mintToken();
|
|
418
|
+
stealOnce();
|
|
395
419
|
}
|
|
420
|
+
sleepSync(LOCK_SPIN_PAUSE_MS);
|
|
396
421
|
}
|
|
397
422
|
}
|
|
398
423
|
function rollForwardSidecar(file, journal) {
|
|
@@ -30,14 +30,6 @@ export interface Stats {
|
|
|
30
30
|
}>;
|
|
31
31
|
};
|
|
32
32
|
mechanisms?: {
|
|
33
|
-
finalizeInjected?: true;
|
|
34
|
-
nudgesSent?: number;
|
|
35
|
-
capShrinks?: number;
|
|
36
|
-
callCutoffs?: number;
|
|
37
|
-
toolClamps?: number;
|
|
38
|
-
toolCuts?: number;
|
|
39
|
-
toolCutKills?: number;
|
|
40
|
-
pendingCutTools?: number;
|
|
41
33
|
finalVerifyInjected?: true;
|
|
42
34
|
finalVerifyInjections?: number;
|
|
43
35
|
attachmentsInjected?: number;
|
|
@@ -60,6 +52,7 @@ export interface Stats {
|
|
|
60
52
|
compactionMicroUsd: number;
|
|
61
53
|
};
|
|
62
54
|
}
|
|
55
|
+
export type BudgetAxis = "tokens" | "cost";
|
|
63
56
|
export interface ResultFlags {
|
|
64
57
|
threw: unknown;
|
|
65
58
|
model?: string;
|
|
@@ -70,6 +63,7 @@ export interface ResultFlags {
|
|
|
70
63
|
abortedForTurns: boolean;
|
|
71
64
|
abortedLive?: boolean;
|
|
72
65
|
budgetHit?: "exceeded" | "precall";
|
|
66
|
+
budgetAxis?: BudgetAxis;
|
|
73
67
|
blockedReason?: string;
|
|
74
68
|
conflict?: boolean;
|
|
75
69
|
outputInvalid?: boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isDegenerateCutMessage
|
|
1
|
+
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
2
2
|
import { extractErrorCode, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
3
3
|
export function errorCodeOf(err) {
|
|
4
4
|
let cur = err;
|
|
@@ -54,7 +54,6 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
54
54
|
let checkpointGate;
|
|
55
55
|
let workspaceRestoreMode;
|
|
56
56
|
const isDegenerate = final?.stopReason === "error" && isDegenerateCutMessage(final);
|
|
57
|
-
const isWalltimeCutoff = final?.stopReason === "error" && isWalltimeCutMessage(final);
|
|
58
57
|
if (flags.outputInvalid) {
|
|
59
58
|
status = "failed";
|
|
60
59
|
errorCode = "output.invalid";
|
|
@@ -66,19 +65,15 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
66
65
|
errorMessage = final?.errorMessage;
|
|
67
66
|
salvagedOutput = text.trim() || undefined;
|
|
68
67
|
}
|
|
69
|
-
else if (isWalltimeCutoff) {
|
|
70
|
-
status = "timeout";
|
|
71
|
-
errorCode = "limit.timeout";
|
|
72
|
-
errorMessage = final?.errorMessage;
|
|
73
|
-
salvagedOutput = text.trim() || undefined;
|
|
74
|
-
}
|
|
75
68
|
else if (flags.budgetHit) {
|
|
76
69
|
status = "failed";
|
|
77
|
-
errorCode = flags.
|
|
70
|
+
errorCode = flags.budgetAxis === "tokens" ? "limits.max_tokens_exceeded" : "limits.max_cost_exceeded";
|
|
71
|
+
const axisName = flags.budgetAxis === "tokens" ? "limits.maxTokens" : "limits.maxCostUsd";
|
|
78
72
|
errorMessage =
|
|
79
73
|
flags.budgetHit === "precall"
|
|
80
|
-
?
|
|
81
|
-
:
|
|
74
|
+
? `the estimated cost of the first call already exceeds ${axisName}; the task was not started`
|
|
75
|
+
: `cumulative usage exceeded ${axisName}`;
|
|
76
|
+
salvagedOutput = text.trim() || undefined;
|
|
82
77
|
}
|
|
83
78
|
else if (flags.suspendLoop) {
|
|
84
79
|
status = "failed";
|
|
@@ -86,15 +81,17 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
86
81
|
errorMessage = "task suspended too many times (resume/restart loop) — exceeded the suspend limit";
|
|
87
82
|
}
|
|
88
83
|
else if (flags.threw) {
|
|
89
|
-
status =
|
|
84
|
+
status = "failed";
|
|
90
85
|
errorMessage = flags.threw instanceof Error ? flags.threw.message : String(flags.threw);
|
|
91
86
|
errorCode = flags.abortedForTimeout
|
|
92
|
-
? "
|
|
87
|
+
? "limits.max_walltime_exceeded"
|
|
93
88
|
: flags.abortedForTurns
|
|
94
|
-
? "
|
|
89
|
+
? "limits.max_turns_exceeded"
|
|
95
90
|
: flags.conflict
|
|
96
91
|
? "conflict"
|
|
97
92
|
: errorCodeOf(flags.threw);
|
|
93
|
+
if (flags.abortedForTimeout || flags.abortedForTurns)
|
|
94
|
+
salvagedOutput = text.trim() || undefined;
|
|
98
95
|
}
|
|
99
96
|
else if (flags.blockedReason) {
|
|
100
97
|
status = "blocked";
|
|
@@ -114,9 +111,11 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
114
111
|
workspaceRestoreMode = flags.reviewRef.restoreMode;
|
|
115
112
|
}
|
|
116
113
|
else if (flags.abortedLive || final?.stopReason === "aborted") {
|
|
117
|
-
status =
|
|
118
|
-
errorCode = flags.abortedForTimeout ? "
|
|
114
|
+
status = "failed";
|
|
115
|
+
errorCode = flags.abortedForTimeout ? "limits.max_walltime_exceeded" : flags.abortedForTurns ? "limits.max_turns_exceeded" : undefined;
|
|
119
116
|
errorMessage = final?.errorMessage ?? (flags.abortedForTurns ? "max turns exceeded" : "run aborted");
|
|
117
|
+
if (flags.abortedForTimeout || flags.abortedForTurns)
|
|
118
|
+
salvagedOutput = text.trim() || undefined;
|
|
120
119
|
}
|
|
121
120
|
else if (!final) {
|
|
122
121
|
status = "failed";
|
|
@@ -8,7 +8,6 @@ import type { SessionStore } from "../session.js";
|
|
|
8
8
|
import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
|
|
9
9
|
import type { OnAsk, ToolPolicy } from "../tool-policy.js";
|
|
10
10
|
import { type ActiveSkillFrame } from "./active-skill-scope.js";
|
|
11
|
-
import { type CutKillRegistry } from "./cut-kill.js";
|
|
12
11
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
13
12
|
import { CacheBreakDetector, type ToolFingerprintInput } from "../cache-break-detector.js";
|
|
14
13
|
import { type BrainCallGuardrailRef } from "../../brain/timeout.js";
|
|
@@ -19,10 +18,23 @@ import type { TaskNotificationPayload } from "../task-notification.js";
|
|
|
19
18
|
import { type CwdRef } from "../../tools/fs/index.js";
|
|
20
19
|
import { type WorkflowSizeGuideline } from "../../orchestration/workflow-size-guideline.js";
|
|
21
20
|
import type { Runner } from "./runtask.js";
|
|
22
|
-
import { type CheckpointGate, type CheckpointState, type CheckpointToken, type ResourceLedger, type ResourceLimitReason } from "../checkpoint-store.js";
|
|
21
|
+
import { type CheckpointGate, type CheckpointState, type CheckpointToken, type ResourceLedger, type PlatformLimitReason, type ResourceLimitReason } from "../checkpoint-store.js";
|
|
23
22
|
import type { ActiveWorktreeSession, AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
|
|
24
|
-
import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
|
|
23
|
+
import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskLimits, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
|
|
25
24
|
import type { RepairBundle } from "../../agents/repair-loop.js";
|
|
25
|
+
export declare function resolveTaskLimits(limits: TaskLimits | undefined): TaskLimits | undefined;
|
|
26
|
+
export declare const ENV_LIFETIME_SUSPEND_MARGIN_MS = 60000;
|
|
27
|
+
export declare const USAGE_WINDOW_REAP_MARGIN_MS: number;
|
|
28
|
+
export interface UsageGovernance {
|
|
29
|
+
readonly key: string;
|
|
30
|
+
check(now: number): Promise<number | undefined>;
|
|
31
|
+
commit(cumulativeTokens: number, now: number): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
export declare function resolveEnvLifetimeExpiry(env: ExecutionEnv, observedAt: number | undefined): {
|
|
34
|
+
expiresAt: number;
|
|
35
|
+
} | {
|
|
36
|
+
unanchored: true;
|
|
37
|
+
} | undefined;
|
|
26
38
|
export declare const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
27
39
|
export declare function checkpointScopeOf(spec: {
|
|
28
40
|
durableApproval?: {
|
|
@@ -132,9 +144,17 @@ export interface Prepared {
|
|
|
132
144
|
turns: number;
|
|
133
145
|
walltimeMs: number;
|
|
134
146
|
}) => Promise<boolean>;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
147
|
+
suspendForPlatformLimit?: (reason: PlatformLimitReason, sliceSpend: {
|
|
148
|
+
costMicroUsd: number;
|
|
149
|
+
tokens: number;
|
|
150
|
+
turns: number;
|
|
151
|
+
walltimeMs: number;
|
|
152
|
+
}, hint?: {
|
|
153
|
+
resumeAfterMs: number;
|
|
154
|
+
}) => Promise<boolean>;
|
|
155
|
+
envLifetimeSuspendAt?: number;
|
|
156
|
+
usageGovernance?: UsageGovernance;
|
|
157
|
+
callIssuedAtRef: {
|
|
138
158
|
current?: number;
|
|
139
159
|
};
|
|
140
160
|
brainCallGuardrailRef: BrainCallGuardrailRef;
|