@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
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync,
|
|
1
|
+
import { lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, statSync, openSync, fsyncSync, closeSync } from "node:fs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import {
|
|
3
|
+
import { dirname, join, relative } from "node:path";
|
|
4
4
|
import { jaccardDistance, termSet } from "../memory-vector.js";
|
|
5
5
|
import { MAX_MEMORY_BYTES } from "../memory.js";
|
|
6
6
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
7
7
|
import { computeEntryRev, entryFromFile, isValidEntryId, parseEntryFile, serializeEntryFile } from "./frontmatter.js";
|
|
8
|
-
import { ControlPlaneCorruptError, CURSORS_FILE, atomicWriteFileSync, claimRootScope, deriveControlPlaneDir, ensureDirExists, enqueueMemoryAnnouncement, registerScope, registeredScopes, resolveMemoryEngineRoot, scopeDirName, } from "./layout.js";
|
|
8
|
+
import { ControlPlaneCorruptError, CURSORS_FILE, QUARANTINE_DIR, atomicWriteFileSync, quarantineAndTombstone, claimRootScope, deriveControlPlaneDir, ensureDirExists, enqueueMemoryAnnouncement, registerScope, registeredScopes, resolveMemoryEngineRoot, scopeDirName, writeAllSync, } from "./layout.js";
|
|
9
9
|
import { scanMemoryFileName, scanMemoryWrite } from "./scan.js";
|
|
10
10
|
export const MEMORY_INDEX_FILENAME = "MEMORY.md";
|
|
11
11
|
export const DEFAULT_MAX_ENTRY_DEPTH = 3;
|
|
@@ -18,6 +18,8 @@ export function scanEntryFiles(dir, opts = {}) {
|
|
|
18
18
|
names = readdirSync(d);
|
|
19
19
|
}
|
|
20
20
|
catch {
|
|
21
|
+
if (d !== dir)
|
|
22
|
+
opts.onSkip?.(d, "unreadable");
|
|
21
23
|
return;
|
|
22
24
|
}
|
|
23
25
|
for (const name of names.sort()) {
|
|
@@ -32,6 +34,7 @@ export function scanEntryFiles(dir, opts = {}) {
|
|
|
32
34
|
st = lstatSync(p);
|
|
33
35
|
}
|
|
34
36
|
catch {
|
|
37
|
+
opts.onSkip?.(p, "unreadable");
|
|
35
38
|
continue;
|
|
36
39
|
}
|
|
37
40
|
if (st.isSymbolicLink()) {
|
|
@@ -65,7 +68,6 @@ const TXN_LOCK_STALE_MS = 30_000;
|
|
|
65
68
|
const TXN_LOCK_WAIT_MS = 15_000;
|
|
66
69
|
const TXN_LOCK_STEAL_GRACE_MS = 250;
|
|
67
70
|
const SHADOW_DIR = "shadow";
|
|
68
|
-
const QUARANTINE_DIR = "quarantine";
|
|
69
71
|
export class FileMemoryEngineBackend {
|
|
70
72
|
directoryRoot;
|
|
71
73
|
controlPlaneRoot;
|
|
@@ -98,35 +100,6 @@ export class FileMemoryEngineBackend {
|
|
|
98
100
|
shadowPath(id) {
|
|
99
101
|
return join(this.controlPlaneRoot, SHADOW_DIR, `${id}.md`);
|
|
100
102
|
}
|
|
101
|
-
quarantineFile(path, content) {
|
|
102
|
-
const dest = join(this.controlPlaneRoot, QUARANTINE_DIR, `${Date.now()}-${basename(path)}`);
|
|
103
|
-
let captured;
|
|
104
|
-
let detail;
|
|
105
|
-
try {
|
|
106
|
-
ensureDirExists(dirname(dest));
|
|
107
|
-
writeFileSync(dest, content, "utf8");
|
|
108
|
-
captured = dest;
|
|
109
|
-
}
|
|
110
|
-
catch (err) {
|
|
111
|
-
detail = `quarantine copy failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
112
|
-
}
|
|
113
|
-
let removed = false;
|
|
114
|
-
try {
|
|
115
|
-
rmSync(path, { force: true });
|
|
116
|
-
removed = true;
|
|
117
|
-
}
|
|
118
|
-
catch (rmErr) {
|
|
119
|
-
try {
|
|
120
|
-
writeFileSync(path, "---\ndeleted: true\n---\n", "utf8");
|
|
121
|
-
removed = true;
|
|
122
|
-
detail = `${detail !== undefined ? `${detail}; ` : ""}delete failed (${rmErr instanceof Error ? rmErr.message : String(rmErr)}) — tombstoned in place`;
|
|
123
|
-
}
|
|
124
|
-
catch (clearErr) {
|
|
125
|
-
detail = `${detail !== undefined ? `${detail}; ` : ""}suspect file could not be deleted or cleared: ${clearErr instanceof Error ? clearErr.message : String(clearErr)}`;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return { ...(captured !== undefined ? { dest: captured } : {}), removed, ...(detail !== undefined ? { detail } : {}) };
|
|
129
|
-
}
|
|
130
103
|
loadLedger() {
|
|
131
104
|
if (this.ledger)
|
|
132
105
|
return this.ledger;
|
|
@@ -278,12 +251,14 @@ export class FileMemoryEngineBackend {
|
|
|
278
251
|
const ledger = sync ? this.loadLedger() : undefined;
|
|
279
252
|
let ledgerChanged = false;
|
|
280
253
|
const adoptedExternal = [];
|
|
254
|
+
const skippedUnreadable = [];
|
|
281
255
|
for (const f of files) {
|
|
282
256
|
let text;
|
|
283
257
|
try {
|
|
284
258
|
text = readFileSync(f.path, "utf8");
|
|
285
259
|
}
|
|
286
260
|
catch {
|
|
261
|
+
skippedUnreadable.push(relative(this.directoryRoot, f.path));
|
|
287
262
|
continue;
|
|
288
263
|
}
|
|
289
264
|
const parsed = parseEntryFile(text);
|
|
@@ -312,7 +287,7 @@ export class FileMemoryEngineBackend {
|
|
|
312
287
|
}
|
|
313
288
|
if (finding) {
|
|
314
289
|
this.inboundFindings.push(finding);
|
|
315
|
-
const q =
|
|
290
|
+
const q = quarantineAndTombstone(f.path, text, join(this.controlPlaneRoot, QUARANTINE_DIR), this.now);
|
|
316
291
|
const shadowText = committed !== undefined ? this.readCommittedShadow(entry.id) : undefined;
|
|
317
292
|
let restoredShadow = false;
|
|
318
293
|
if (shadowText !== undefined) {
|
|
@@ -378,6 +353,20 @@ export class FileMemoryEngineBackend {
|
|
|
378
353
|
catch {
|
|
379
354
|
}
|
|
380
355
|
}
|
|
356
|
+
if (skippedUnreadable.length > 0) {
|
|
357
|
+
try {
|
|
358
|
+
enqueueMemoryAnnouncement(this.controlPlaneRoot, {
|
|
359
|
+
kind: "external",
|
|
360
|
+
at: this.now(),
|
|
361
|
+
items: skippedUnreadable
|
|
362
|
+
.slice(0, 12)
|
|
363
|
+
.map((rel) => `committed memory entry could not be read and is MISSING from this session's memory: ${JSON.stringify(inlineUntrusted(rel))} — check the file (permissions/fs)`)
|
|
364
|
+
.concat(skippedUnreadable.length > 12 ? [`…and ${skippedUnreadable.length - 12} more unreadable entr(ies)`] : []),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
}
|
|
369
|
+
}
|
|
381
370
|
if (!sync && this.batchScan !== undefined)
|
|
382
371
|
this.batchScan.set(scope, entries);
|
|
383
372
|
return entries;
|
|
@@ -458,7 +447,7 @@ export class FileMemoryEngineBackend {
|
|
|
458
447
|
try {
|
|
459
448
|
const fd = openSync(ownerPath, "w", 0o600);
|
|
460
449
|
try {
|
|
461
|
-
|
|
450
|
+
writeAllSync(fd, token);
|
|
462
451
|
fsyncSync(fd);
|
|
463
452
|
}
|
|
464
453
|
finally {
|
|
@@ -584,7 +573,7 @@ export class FileMemoryEngineBackend {
|
|
|
584
573
|
mkdirSync(dirname(op.target), { recursive: true });
|
|
585
574
|
const fd = openSync(staged, "w", 0o600);
|
|
586
575
|
try {
|
|
587
|
-
|
|
576
|
+
writeAllSync(fd, op.content);
|
|
588
577
|
fsyncSync(fd);
|
|
589
578
|
}
|
|
590
579
|
finally {
|
|
@@ -23,6 +23,13 @@ export declare function scopeDirFor(memoryDir: string, controlDir: string, scope
|
|
|
23
23
|
export declare function canonicalize(p: string): string;
|
|
24
24
|
export declare function isContainedIn(root: string, child: string): boolean;
|
|
25
25
|
export declare function ensureDirExists(dir: string): void;
|
|
26
|
+
export declare const QUARANTINE_DIR = "quarantine";
|
|
27
|
+
export interface QuarantineOutcome {
|
|
28
|
+
dest?: string;
|
|
29
|
+
removed: boolean;
|
|
30
|
+
detail?: string;
|
|
31
|
+
}
|
|
32
|
+
export declare function quarantineAndTombstone(path: string, content: string, quarantineDir: string, now: () => number): QuarantineOutcome;
|
|
26
33
|
export declare const ANNOUNCEMENTS_FILE = "announcements.json";
|
|
27
34
|
export declare const MEMORY_ANNOUNCEMENTS_MAX = 20;
|
|
28
35
|
export declare const SCAN_FUSE_FILE = "scan-fuse.json";
|
|
@@ -39,5 +46,6 @@ export declare function readIndexRevs(controlDir: string): Record<string, string
|
|
|
39
46
|
export declare function writeIndexRevs(controlDir: string, revs: Record<string, string>): void;
|
|
40
47
|
export declare function scanFuseCount(controlDir: string, key: string): number;
|
|
41
48
|
export declare function clearScanFuse(controlDir: string, keys: Iterable<string>): void;
|
|
49
|
+
export declare function writeAllSync(fd: number, data: string): void;
|
|
42
50
|
export declare function atomicWriteFileSync(path: string, data: string): void;
|
|
43
51
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeSync } from "node:fs";
|
|
1
|
+
import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import { dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
4
|
+
import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path";
|
|
5
5
|
const SCOPES_FILE = "scopes.json";
|
|
6
6
|
export const CURSORS_FILE = "cursors.json";
|
|
7
7
|
export class ControlPlaneCorruptError extends Error {
|
|
@@ -190,6 +190,52 @@ export function ensureDirExists(dir) {
|
|
|
190
190
|
if (!existsSync(dir))
|
|
191
191
|
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
192
192
|
}
|
|
193
|
+
export const QUARANTINE_DIR = "quarantine";
|
|
194
|
+
const DELETED_TOMBSTONE = "---\ndeleted: true\n---\n";
|
|
195
|
+
let quarantineSeq = 0;
|
|
196
|
+
export function quarantineAndTombstone(path, content, quarantineDir, now) {
|
|
197
|
+
let captured;
|
|
198
|
+
let detail;
|
|
199
|
+
try {
|
|
200
|
+
ensureDirExists(quarantineDir);
|
|
201
|
+
const base = `${now()}-${basename(path)}`;
|
|
202
|
+
for (let attempt = 0; captured === undefined && attempt < 10; attempt++) {
|
|
203
|
+
const candidate = join(quarantineDir, attempt === 0 ? base : `${base}.${attempt}`);
|
|
204
|
+
try {
|
|
205
|
+
writeFileSync(candidate, content, { encoding: "utf8", flag: "wx" });
|
|
206
|
+
captured = candidate;
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
if (!(err instanceof Error && "code" in err && err.code === "EEXIST"))
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (captured === undefined) {
|
|
214
|
+
const last = join(quarantineDir, `${base}.${process.pid}.${quarantineSeq++}`);
|
|
215
|
+
writeFileSync(last, content, { encoding: "utf8", flag: "wx" });
|
|
216
|
+
captured = last;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (err) {
|
|
220
|
+
detail = `quarantine copy failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
221
|
+
}
|
|
222
|
+
let removed = false;
|
|
223
|
+
try {
|
|
224
|
+
rmSync(path, { force: true });
|
|
225
|
+
removed = true;
|
|
226
|
+
}
|
|
227
|
+
catch (rmErr) {
|
|
228
|
+
try {
|
|
229
|
+
writeFileSync(path, DELETED_TOMBSTONE, "utf8");
|
|
230
|
+
removed = true;
|
|
231
|
+
detail = `${detail !== undefined ? `${detail}; ` : ""}delete failed (${rmErr instanceof Error ? rmErr.message : String(rmErr)}) — tombstoned in place`;
|
|
232
|
+
}
|
|
233
|
+
catch (clearErr) {
|
|
234
|
+
detail = `${detail !== undefined ? `${detail}; ` : ""}suspect file could not be deleted or cleared: ${clearErr instanceof Error ? clearErr.message : String(clearErr)}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { ...(captured !== undefined ? { dest: captured } : {}), removed, ...(detail !== undefined ? { detail } : {}) };
|
|
238
|
+
}
|
|
193
239
|
export const ANNOUNCEMENTS_FILE = "announcements.json";
|
|
194
240
|
export const MEMORY_ANNOUNCEMENTS_MAX = 20;
|
|
195
241
|
export const SCAN_FUSE_FILE = "scan-fuse.json";
|
|
@@ -219,7 +265,7 @@ function acquireSidecarLock(lockDir, opts = {}) {
|
|
|
219
265
|
let fd;
|
|
220
266
|
try {
|
|
221
267
|
fd = openSync(join(lockDir, "owner"), "w");
|
|
222
|
-
|
|
268
|
+
writeAllSync(fd, token);
|
|
223
269
|
}
|
|
224
270
|
catch {
|
|
225
271
|
}
|
|
@@ -444,12 +490,22 @@ export function clearScanFuse(controlDir, keys) {
|
|
|
444
490
|
return changed ? counts : undefined;
|
|
445
491
|
});
|
|
446
492
|
}
|
|
493
|
+
export function writeAllSync(fd, data) {
|
|
494
|
+
const buf = Buffer.from(data, "utf8");
|
|
495
|
+
let written = 0;
|
|
496
|
+
while (written < buf.length) {
|
|
497
|
+
const n = writeSync(fd, buf, written, buf.length - written);
|
|
498
|
+
if (n <= 0)
|
|
499
|
+
throw new Error(`writeAllSync: write made no progress (${written}/${buf.length} bytes) — refusing to leave a truncated file`);
|
|
500
|
+
written += n;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
447
503
|
export function atomicWriteFileSync(path, data) {
|
|
448
504
|
ensureDirExists(dirname(path));
|
|
449
505
|
const tmp = join(dirname(path), `.tmp-${process.pid}-${Math.random().toString(36).slice(2, 10)}`);
|
|
450
506
|
const fd = openSync(tmp, "w", 0o600);
|
|
451
507
|
try {
|
|
452
|
-
|
|
508
|
+
writeAllSync(fd, data);
|
|
453
509
|
fsyncSync(fd);
|
|
454
510
|
}
|
|
455
511
|
finally {
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { canonicalize } from "./layout.js";
|
|
4
|
+
function pathSegmentsDualFamily(p) {
|
|
5
|
+
return p.split(/[\\/]/).filter(Boolean);
|
|
6
|
+
}
|
|
7
|
+
const CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
|
|
8
|
+
const foldPathCase = (s) => (CASE_INSENSITIVE_FS ? s.toLowerCase() : s);
|
|
3
9
|
export const SCOPE_SEGMENT_MAX_ENCODED = 64;
|
|
4
10
|
const SEGMENT_SAFE = /^[A-Za-z0-9._-]$/;
|
|
5
11
|
export function encodeScopeSegment(raw) {
|
|
@@ -117,9 +123,9 @@ export function assertScopeContractPlacement(args) {
|
|
|
117
123
|
const personal = args.parsedScopes.filter(isCentralAuthorityScope);
|
|
118
124
|
if (personal.length === 0)
|
|
119
125
|
return;
|
|
120
|
-
const
|
|
121
|
-
const
|
|
122
|
-
if (
|
|
126
|
+
const rootSegs = pathSegmentsDualFamily(canonicalize(resolve(args.repoRoot))).map(foldPathCase);
|
|
127
|
+
const dirSegs = pathSegmentsDualFamily(canonicalize(resolve(args.memoryDir))).map(foldPathCase);
|
|
128
|
+
if (rootSegs.length <= dirSegs.length && rootSegs.every((s, i) => s === dirSegs[i])) {
|
|
123
129
|
const e = new Error(`memory scope contract v2: central-authority scope(s) ${personal
|
|
124
130
|
.map((p) => `"${p.kind}"`)
|
|
125
131
|
.join(", ")} cannot materialize into an IN-REPO memory dir (${args.memoryDir}) — ` +
|
|
@@ -88,7 +88,7 @@ export interface MemorySessionHandle {
|
|
|
88
88
|
indexBaselineLines: number;
|
|
89
89
|
indexText: string;
|
|
90
90
|
}
|
|
91
|
-
export type HarvestRejectionCode = "outside_root" | "symlink" | "secret" | "injection" | "filename" | "too_large" | "file_cap" | "readonly_layer" | "stub_modified" | "nested_too_deep" | "quarantine_failed" | "invalid";
|
|
91
|
+
export type HarvestRejectionCode = "outside_root" | "symlink" | "secret" | "injection" | "filename" | "too_large" | "file_cap" | "readonly_layer" | "stub_modified" | "nested_too_deep" | "quarantine_failed" | "unreadable" | "invalid";
|
|
92
92
|
export interface HarvestRejection {
|
|
93
93
|
path: string;
|
|
94
94
|
code: HarvestRejectionCode;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { canonicalToolName } from "./tool-name-aliases.js";
|
|
2
1
|
export const BASH_GENERIC_PARAMS = new Set(["command", "timeout", "description", "run_in_background"]);
|
|
3
2
|
const DEFAULT_CAPS = {
|
|
4
3
|
maxRules: 256,
|
|
@@ -142,7 +141,7 @@ function compile(rules, caps, primaryFieldGeneric) {
|
|
|
142
141
|
continue;
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
|
-
const toolName =
|
|
144
|
+
const toolName = parsed.toolName;
|
|
146
145
|
if (toolName.startsWith("mcp__") && (parsed.ruleContent !== undefined || indexOfUnescaped(text, "(") !== -1)) {
|
|
147
146
|
bad(text, "unsupported.mcp_paren", "MCP rules do not support patterns in parentheses (CC parity); use the toolAxes system");
|
|
148
147
|
continue;
|
|
@@ -262,7 +261,7 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
262
261
|
const defaultAction = opts?.defaultAction ?? "allow";
|
|
263
262
|
return {
|
|
264
263
|
check(req) {
|
|
265
|
-
const entry = byTool.get(
|
|
264
|
+
const entry = byTool.get(req.toolName);
|
|
266
265
|
if (entry) {
|
|
267
266
|
if (entry.bare.deny) {
|
|
268
267
|
return { action: "deny", reason: ruleMessage("denied", entry.bare.deny.ruleText, entry.bare.deny.source) };
|
|
@@ -16,7 +16,6 @@ export declare class ActiveSkillScope {
|
|
|
16
16
|
active(): readonly ActiveSkillFrame[];
|
|
17
17
|
get size(): number;
|
|
18
18
|
}
|
|
19
|
-
export declare function isWithin(root: string, p: string): boolean;
|
|
20
19
|
export declare function createActiveSkillScopePolicy(opts: {
|
|
21
20
|
scope: ActiveSkillScope;
|
|
22
21
|
env: ExecutionEnv;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { canonicalizeTarget, fileArgPath } from "../../tools/fs/safety.js";
|
|
2
|
-
import {
|
|
2
|
+
import { PATH_WRITE_TOOLS, isWithin } from "./session-rule-policy.js";
|
|
3
3
|
import { parseSkillToolEntry, skillSpecifierRejection } from "../skill-tool-specifier.js";
|
|
4
4
|
export class ActiveSkillScope {
|
|
5
5
|
frames = [];
|
|
@@ -16,26 +16,11 @@ export class ActiveSkillScope {
|
|
|
16
16
|
return this.frames.length;
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
|
-
const PATH_WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit"]);
|
|
20
|
-
export function isWithin(root, p) {
|
|
21
|
-
const norm = (x) => {
|
|
22
|
-
let n = x.replace(/\\/g, "/");
|
|
23
|
-
if (/^[A-Za-z]:(\/|$)/.test(n))
|
|
24
|
-
n = n[0].toLowerCase() + n.slice(1);
|
|
25
|
-
return n;
|
|
26
|
-
};
|
|
27
|
-
const r = norm(root);
|
|
28
|
-
const c = norm(p);
|
|
29
|
-
if (c === r)
|
|
30
|
-
return true;
|
|
31
|
-
const base = r.endsWith("/") ? r : r + "/";
|
|
32
|
-
return c.startsWith(base);
|
|
33
|
-
}
|
|
34
19
|
export function createActiveSkillScopePolicy(opts) {
|
|
35
20
|
const { scope, env, rootPath, toolEffects } = opts;
|
|
36
21
|
return {
|
|
37
22
|
async check(req, signal) {
|
|
38
|
-
const toolName =
|
|
23
|
+
const toolName = req.toolName;
|
|
39
24
|
const frames = scope.active();
|
|
40
25
|
if (frames.length === 0)
|
|
41
26
|
return { action: "allow" };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AssistantMessage } from "../../internal/llm.js";
|
|
2
|
-
import type { TaskResult, TaskSpec } from "../types.js";
|
|
2
|
+
import type { NestedUsage, TaskResult, TaskSpec } from "../types.js";
|
|
3
3
|
export interface Stats {
|
|
4
4
|
turns: number;
|
|
5
5
|
tokens: number;
|
|
@@ -12,12 +12,7 @@ export interface Stats {
|
|
|
12
12
|
cacheHitRate?: number;
|
|
13
13
|
totalInputTokens: number;
|
|
14
14
|
costMicroUsd: number;
|
|
15
|
-
nested?:
|
|
16
|
-
tokens: number;
|
|
17
|
-
turns: number;
|
|
18
|
-
tasks: number;
|
|
19
|
-
costMicroUsd?: number;
|
|
20
|
-
};
|
|
15
|
+
nested?: NestedUsage;
|
|
21
16
|
memory?: {
|
|
22
17
|
tokens: number;
|
|
23
18
|
costMicroUsd: number;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { WALLTIME_CUTOFF_MESSAGE } from "../../brain/walltime.js";
|
|
1
|
+
import { isDegenerateCutMessage, isWalltimeCutMessage } from "../../brain/terminal-cause.js";
|
|
3
2
|
import { extractErrorCode, stripErrorCodePrefix } from "../../brain/errors.js";
|
|
4
3
|
export function errorCodeOf(err) {
|
|
5
4
|
let cur = err;
|
|
@@ -54,8 +53,8 @@ export function assembleResult(spec, sessionId, final, stats, flags) {
|
|
|
54
53
|
let checkpointToken;
|
|
55
54
|
let checkpointGate;
|
|
56
55
|
let workspaceRestoreMode;
|
|
57
|
-
const isDegenerate = final?.stopReason === "error" && final
|
|
58
|
-
const isWalltimeCutoff = final?.stopReason === "error" && final
|
|
56
|
+
const isDegenerate = final?.stopReason === "error" && isDegenerateCutMessage(final);
|
|
57
|
+
const isWalltimeCutoff = final?.stopReason === "error" && isWalltimeCutMessage(final);
|
|
59
58
|
if (flags.outputInvalid) {
|
|
60
59
|
status = "failed";
|
|
61
60
|
errorCode = "output.invalid";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type TracerHook } from "../trace.js";
|
|
2
|
+
import type { MaybeCompactOptions } from "../auto-compaction.js";
|
|
3
|
+
import type { TaskSpec } from "../types.js";
|
|
4
|
+
import type { Prepared } from "./prepare-task.js";
|
|
5
|
+
export declare function buildWorkingFileAttachments(spec: TaskSpec, prepared: Prepared): MaybeCompactOptions["workingFileAttachments"];
|
|
6
|
+
export declare function centerAdoptionOption(prepared: Prepared): Partial<Pick<MaybeCompactOptions, "centerAdoption">>;
|
|
7
|
+
export declare function emitInputTruncated(tracer: TracerHook | undefined, taskId: string): NonNullable<MaybeCompactOptions["onInputTruncated"]>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { emitTrace } from "../trace.js";
|
|
2
|
+
export function buildWorkingFileAttachments(spec, prepared) {
|
|
3
|
+
if (spec.compaction?.attachWorkingFiles === false || !prepared.readTaskFile)
|
|
4
|
+
return undefined;
|
|
5
|
+
return {
|
|
6
|
+
readFile: prepared.readTaskFile,
|
|
7
|
+
...(prepared.recentlyReadFiles ? { recentlyReadFiles: prepared.recentlyReadFiles } : {}),
|
|
8
|
+
...(prepared.normalizeAttachmentPath ? { normalizePath: prepared.normalizeAttachmentPath } : {}),
|
|
9
|
+
...(prepared.isDedupStubResult ? { isDedupStubResult: prepared.isDedupStubResult } : {}),
|
|
10
|
+
...(prepared.instructionSources
|
|
11
|
+
? { instructionSourcePaths: prepared.instructionSources.filter((s) => s.contentHash !== null).map((s) => s.path) }
|
|
12
|
+
: {}),
|
|
13
|
+
...(typeof spec.compaction?.attachWorkingFiles === "object" ? spec.compaction.attachWorkingFiles : undefined),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function centerAdoptionOption(prepared) {
|
|
17
|
+
const ca = prepared.centerCompactionCandidate?.();
|
|
18
|
+
return ca !== undefined ? { centerAdoption: ca } : {};
|
|
19
|
+
}
|
|
20
|
+
export function emitInputTruncated(tracer, taskId) {
|
|
21
|
+
return (info) => emitTrace(tracer, () => ({
|
|
22
|
+
kind: "compaction.input_truncated",
|
|
23
|
+
version: 1,
|
|
24
|
+
taskId,
|
|
25
|
+
label: info.label,
|
|
26
|
+
droppedChars: info.droppedChars,
|
|
27
|
+
keptChars: info.keptChars,
|
|
28
|
+
ts: Date.now(),
|
|
29
|
+
}));
|
|
30
|
+
}
|
|
@@ -12,12 +12,13 @@ import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
|
12
12
|
import { CacheBreakDetector, type ToolFingerprintInput } from "../cache-break-detector.js";
|
|
13
13
|
import { type OutputRef, type BlockedRef, type SkillListingEntry } from "./synthetic-tools.js";
|
|
14
14
|
import type { MemoryEngine } from "../memory-engine/engine.js";
|
|
15
|
+
import { type ToolManifestRow } from "../../prompt-assembly/tool-catalog.js";
|
|
15
16
|
import type { TaskNotificationPayload } from "../task-notification.js";
|
|
16
17
|
import { type CwdRef } from "../../tools/fs/index.js";
|
|
17
18
|
import type { Runner } from "./runtask.js";
|
|
18
19
|
import { type CheckpointGate, type CheckpointState, type CheckpointToken, type ResourceLedger, type ResourceLimitReason } from "../checkpoint-store.js";
|
|
19
|
-
import type { AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
|
|
20
|
-
import type { RunnerDeps, TaskEvent, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
|
|
20
|
+
import type { ActiveWorktreeSession, AgentMessage, AgentTool, ExecutionEnv } from "../../internal/harness.js";
|
|
21
|
+
import type { NestedUsageAccum, RunnerDeps, TaskEvent, TaskResult, TaskSpec, ToolActivity, ToolEffect } from "../types.js";
|
|
21
22
|
import type { RepairBundle } from "../../agents/repair-loop.js";
|
|
22
23
|
export declare const DEFAULT_IRREVERSIBLE_SCOPE = "irreversible";
|
|
23
24
|
export declare function checkpointScopeOf(spec: {
|
|
@@ -42,21 +43,11 @@ export interface Prepared {
|
|
|
42
43
|
hit: boolean;
|
|
43
44
|
};
|
|
44
45
|
blockedToolCalls: Set<string>;
|
|
45
|
-
nestedStats:
|
|
46
|
-
tokens: number;
|
|
47
|
-
turns: number;
|
|
48
|
-
tasks: number;
|
|
49
|
-
costMicroUsd: number;
|
|
50
|
-
};
|
|
46
|
+
nestedStats: NestedUsageAccum;
|
|
51
47
|
rewindNotes?: NonNullable<TaskResult["rewindNotes"]>;
|
|
52
48
|
cwdRef?: CwdRef;
|
|
53
49
|
worktreeSessionRef?: {
|
|
54
|
-
current?:
|
|
55
|
-
worktreeDir: string;
|
|
56
|
-
originalCwd: string;
|
|
57
|
-
baseSha: string;
|
|
58
|
-
entered?: boolean;
|
|
59
|
-
};
|
|
50
|
+
current?: ActiveWorktreeSession;
|
|
60
51
|
};
|
|
61
52
|
workspaceStateSettle?: {
|
|
62
53
|
rootCanonical: string;
|
|
@@ -89,15 +80,7 @@ export interface Prepared {
|
|
|
89
80
|
hash: string;
|
|
90
81
|
contentHash?: string;
|
|
91
82
|
}>;
|
|
92
|
-
tools?:
|
|
93
|
-
wireName: string;
|
|
94
|
-
aliases: string[];
|
|
95
|
-
contractId: string;
|
|
96
|
-
implementationRevision: string;
|
|
97
|
-
cardId: string;
|
|
98
|
-
shapeDigest: string;
|
|
99
|
-
wireSchemaDigest: string;
|
|
100
|
-
}>;
|
|
83
|
+
tools?: ToolManifestRow[];
|
|
101
84
|
snapshot?: {
|
|
102
85
|
cacheIdentity: string;
|
|
103
86
|
elements: import("../../prompt-assembly/turn-snapshot.js").CacheIdentityElements;
|