@sema-agent/core 7.7.0 → 7.9.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 +88 -0
- package/dist/agents/agent-transcript-tool.js +2 -2
- package/dist/agents/list-agents-tool.js +2 -3
- package/dist/agents/observer.js +2 -2
- package/dist/agents/send-message-tool.js +2 -1
- package/dist/agents/subagent-steps.js +5 -4
- package/dist/agents/subagent.d.ts +1 -1
- package/dist/agents/subagent.js +2 -3
- package/dist/core/ask-origin.d.ts +12 -1
- package/dist/core/ask-origin.js +5 -1
- package/dist/core/ask-question.js +2 -2
- package/dist/core/checkpoint-store.d.ts +64 -24
- package/dist/core/context-edit.d.ts +0 -22
- package/dist/core/context-edit.js +2 -11
- package/dist/core/engine-notice.d.ts +6 -0
- package/dist/core/fs-write-gate-policy.d.ts +7 -1
- package/dist/core/fs-write-gate-policy.js +7 -7
- package/dist/core/gate-fold.js +2 -2
- package/dist/core/gate-lanes.js +53 -26
- package/dist/core/gate-outcome.d.ts +8 -4
- package/dist/core/gate-outcome.js +2 -1
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +6 -0
- package/dist/core/hooks.d.ts +64 -33
- package/dist/core/hooks.js +1 -1
- package/dist/core/lsp.js +2 -2
- package/dist/core/mcp-server-spec.d.ts +17 -0
- package/dist/core/mcp.js +3 -1
- package/dist/core/memory-engine/tools.js +4 -12
- package/dist/core/permission-rule-consent.d.ts +29 -36
- package/dist/core/permission-rule-consent.js +110 -60
- package/dist/core/permission-rule-model.d.ts +254 -55
- package/dist/core/permission-rule-model.js +323 -43
- package/dist/core/permission-rule-org.d.ts +9 -8
- package/dist/core/permission-rule-org.js +6 -15
- package/dist/core/permission-rule-provider.d.ts +10 -6
- package/dist/core/permission-rule-provider.js +12 -8
- package/dist/core/permission-rule-session.d.ts +7 -6
- package/dist/core/permission-rule-session.js +29 -10
- package/dist/core/permission-rule-store.d.ts +40 -18
- package/dist/core/permission-rule-store.js +68 -42
- package/dist/core/permission-rule-sync.d.ts +9 -3
- package/dist/core/permission-rule-sync.js +29 -25
- package/dist/core/permission-rule-syntax.d.ts +30 -0
- package/dist/core/permission-rule-syntax.js +44 -0
- package/dist/core/permission-rules.d.ts +85 -33
- package/dist/core/permission-rules.js +104 -88
- package/dist/core/persisted-rule-arms.d.ts +56 -0
- package/dist/core/persisted-rule-arms.js +48 -0
- package/dist/core/present-plan-tool.js +3 -4
- package/dist/core/runner/active-skill-scope.js +7 -4
- package/dist/core/runner/compaction-call-options.d.ts +1 -1
- package/dist/core/runner/contracts.d.ts +46 -3
- package/dist/core/runner/permission-rule-lanes.d.ts +33 -26
- package/dist/core/runner/permission-rule-lanes.js +27 -21
- package/dist/core/runner/prepare-ask-lane.d.ts +1 -1
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +5 -4
- package/dist/core/runner/prepare-caps-and-workflow.js +16 -11
- package/dist/core/runner/prepare-defer-classify.d.ts +5 -26
- package/dist/core/runner/prepare-defer-classify.js +10 -10
- package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
- package/dist/core/runner/prepare-delegation-surface.js +15 -17
- package/dist/core/runner/prepare-gate-stations.d.ts +4 -1
- package/dist/core/runner/prepare-gate-stations.js +4 -2
- package/dist/core/runner/prepare-hands-readface.d.ts +3 -2
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +1 -1
- package/dist/core/runner/prepare-inherited-gate.js +1 -0
- package/dist/core/runner/prepare-lsp.d.ts +4 -3
- package/dist/core/runner/prepare-lsp.js +3 -2
- package/dist/core/runner/prepare-memory-engine-session.d.ts +4 -4
- package/dist/core/runner/prepare-memory-engine-session.js +4 -2
- package/dist/core/runner/prepare-offload-wrappers.js +7 -5
- package/dist/core/runner/prepare-park-ask.d.ts +4 -0
- package/dist/core/runner/prepare-park-ask.js +4 -1
- package/dist/core/runner/prepare-project-context.d.ts +5 -23
- package/dist/core/runner/prepare-project-context.js +7 -5
- package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
- package/dist/core/runner/prepare-protocol-tools.d.ts +5 -1
- package/dist/core/runner/prepare-protocol-tools.js +38 -21
- package/dist/core/runner/prepare-question-face.d.ts +4 -3
- package/dist/core/runner/prepare-question-face.js +5 -3
- package/dist/core/runner/prepare-run-refs.d.ts +16 -0
- package/dist/core/runner/prepare-run-refs.js +6 -0
- package/dist/core/runner/prepare-safety-scan.js +48 -2
- package/dist/core/runner/prepare-task.js +37 -19
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +5 -2
- package/dist/core/runner/prepare-tool-disclosure-mount.js +3 -1
- package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -0
- package/dist/core/runner/prepare-wiring-manifest.js +3 -8
- package/dist/core/runner/resume-admission.d.ts +53 -0
- package/dist/core/runner/resume-admission.js +83 -0
- package/dist/core/runner/resume-apply.d.ts +50 -0
- package/dist/core/runner/resume-apply.js +184 -0
- package/dist/core/runner/resume-checkpoint-screen.d.ts +18 -0
- package/dist/core/runner/resume-checkpoint-screen.js +108 -0
- package/dist/core/runner/resume-claim.d.ts +32 -0
- package/dist/core/runner/resume-claim.js +27 -0
- package/dist/core/runner/resume-internals-and-config.d.ts +33 -0
- package/dist/core/runner/resume-internals-and-config.js +50 -0
- package/dist/core/runner/resume-policy-outcome.d.ts +31 -0
- package/dist/core/runner/resume-policy-outcome.js +127 -0
- package/dist/core/runner/resume-preflight.d.ts +40 -0
- package/dist/core/runner/resume-preflight.js +122 -0
- package/dist/core/runner/resume-review-outcome.d.ts +30 -0
- package/dist/core/runner/resume-review-outcome.js +88 -0
- package/dist/core/runner/run-harness-handlers.d.ts +46 -0
- package/dist/core/runner/run-harness-handlers.js +345 -0
- package/dist/core/runner/run-turn-boundary.d.ts +69 -0
- package/dist/core/runner/run-turn-boundary.js +694 -0
- package/dist/core/runner/runtask.d.ts +20 -0
- package/dist/core/runner/runtask.js +129 -1774
- package/dist/core/runner/session-rule-policy.js +9 -4
- package/dist/core/runner/synthetic-tools.js +5 -8
- package/dist/core/runner/tool-disclosure.d.ts +4 -0
- package/dist/core/runner/tool-disclosure.js +9 -5
- package/dist/core/runner/tool-end-body.d.ts +12 -5
- package/dist/core/runner/tool-end-body.js +8 -7
- package/dist/core/runner/tool-face-overlay.d.ts +40 -0
- package/dist/core/runner/tool-face-overlay.js +125 -0
- package/dist/core/runner/tool-output-projection.d.ts +14 -1
- package/dist/core/runner/tool-output-projection.js +24 -18
- package/dist/core/runner/turn-attachments.d.ts +2 -2
- package/dist/core/sensitive-path-policy.d.ts +3 -0
- package/dist/core/sensitive-path-policy.js +6 -5
- package/dist/core/shared-memory/tools.js +3 -6
- package/dist/core/store-contracts/checkpoint-store-contract.js +23 -0
- package/dist/core/store-contracts/permission-rule-sync-contract.js +29 -10
- package/dist/core/task-event.d.ts +10 -0
- package/dist/core/task-registry.js +4 -5
- package/dist/core/task-spec.d.ts +8 -0
- package/dist/core/tool-catalog-entries.d.ts +48 -0
- package/dist/core/tool-catalog-entries.js +174 -0
- package/dist/core/tool-conformance.d.ts +22 -0
- package/dist/core/tool-conformance.js +37 -0
- package/dist/core/tool-face.d.ts +171 -0
- package/dist/core/tool-face.js +25 -0
- package/dist/core/tool-policy.d.ts +24 -0
- package/dist/core/tool-policy.js +4 -2
- package/dist/core/tool-registry.d.ts +114 -0
- package/dist/core/tool-registry.js +114 -0
- package/dist/core/tool-result-budget.d.ts +0 -3
- package/dist/core/tool-result-budget.js +4 -3
- package/dist/core/tool-result-store.d.ts +0 -18
- package/dist/core/tool-result-store.js +2 -18
- package/dist/core/tool-roster.d.ts +459 -0
- package/dist/core/tool-roster.js +507 -0
- package/dist/core/tool-spec.d.ts +54 -3
- package/dist/core/tools.js +7 -0
- package/dist/core/wiring-manifest.d.ts +13 -0
- package/dist/core/wiring-manifest.js +2 -1
- package/dist/core/write-protect.d.ts +2 -1
- package/dist/core/write-protect.js +5 -4
- package/dist/engine/compaction/utils.d.ts +6 -0
- package/dist/engine/compaction/utils.js +19 -19
- package/dist/engine/loop/types.d.ts +6 -0
- package/dist/index.d.ts +17 -6
- package/dist/index.js +17 -6
- package/dist/orchestration/goal.js +2 -1
- package/dist/orchestration/run-spec.js +3 -5
- package/dist/orchestration/run-workflow-tool.js +2 -2
- package/dist/prompt-assembly/event-registry.js +3 -3
- package/dist/prompt-assembly/tool-catalog.d.ts +2 -2
- package/dist/stores/file/checkpoint-store.js +1 -1
- package/dist/stores/file/permission-rule-store.d.ts +28 -23
- package/dist/stores/file/permission-rule-store.js +64 -16
- package/dist/tools/fs/bash-readonly-classifier.d.ts +1 -1
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +7 -11
- package/dist/tools/fs/fs-pdf.d.ts +1 -1
- package/dist/tools/fs/fs-read.js +2 -2
- package/dist/tools/fs/fs-search-tools.d.ts +0 -7
- package/dist/tools/fs/fs-search-tools.js +3 -16
- package/dist/tools/fs/fs-write.js +4 -6
- package/dist/tools/fs/notebook.d.ts +1 -1
- package/dist/tools/fs/repo-map.js +2 -2
- package/dist/tools/fs/safety.d.ts +3 -2
- package/dist/tools/fs/safety.js +6 -5
- package/dist/tools/monitor.js +2 -2
- package/dist/tools/scheduler-tools.js +5 -8
- package/dist/tools/task-list.js +5 -8
- package/dist/tools/todo.js +2 -2
- package/dist/tools/web.js +3 -6
- package/dist/tools/worktree.js +3 -4
- package/package.json +4 -1
- package/test/export-surface.snapshot.json +189 -17
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { closeSync, constants as FS, existsSync, fsyncSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { createHash, randomBytes } from "node:crypto";
|
|
4
|
+
import { isRuleBehavior, readRowBehavior } from "../../core/permission-rule-model.js";
|
|
4
5
|
import { PERMISSION_RULE_WRITER, applySyncJoin, applyTombstones, assertDeleteDeltaCarriesNoAdd, assertRedemptionNotQuarantined, assertWriteDeltaScopeDurable, foldDelta, } from "../../core/permission-rule-store.js";
|
|
5
6
|
import { canonicalize } from "../../core/canonical-json.js";
|
|
6
7
|
import { BootLock } from "./fs-atomic.js";
|
|
7
8
|
import { assertAdoptionBootGate } from "./adoption/marker.js";
|
|
8
9
|
function isRuleFile(v) {
|
|
9
10
|
const p = v;
|
|
10
|
-
return typeof p === "object" && p !== null && p.schemaVersion ===
|
|
11
|
+
return typeof p === "object" && p !== null && p.schemaVersion === 4 && Array.isArray(p.rules) && Array.isArray(p.tombstones) && typeof p.rev === "number" && (p.quarantined === undefined || Array.isArray(p.quarantined));
|
|
11
12
|
}
|
|
12
|
-
function
|
|
13
|
+
function isOlderRuleFile(v) {
|
|
13
14
|
const p = v;
|
|
14
|
-
return typeof p === "object" && p !== null && (p.schemaVersion === 1 || p.schemaVersion === 2) && Array.isArray(p.rules) && Array.isArray(p.tombstones) && typeof p.rev === "number";
|
|
15
|
+
return typeof p === "object" && p !== null && (p.schemaVersion === 1 || p.schemaVersion === 2 || p.schemaVersion === 3) && Array.isArray(p.rules) && Array.isArray(p.tombstones) && typeof p.rev === "number";
|
|
15
16
|
}
|
|
16
17
|
function isOrgStateFile(v) {
|
|
17
18
|
const p = v;
|
|
@@ -24,7 +25,7 @@ function jsonForm(v) {
|
|
|
24
25
|
return JSON.parse(JSON.stringify(v));
|
|
25
26
|
}
|
|
26
27
|
export const ORG_STATE_FILE = "org.json";
|
|
27
|
-
export const
|
|
28
|
+
export const SCHEMA_MARKER_FILE = ".rule-schema-4";
|
|
28
29
|
export class OrgRevisionRefusal extends Error {
|
|
29
30
|
}
|
|
30
31
|
const LOCAL_OWNER_FILE = "local-owner.json";
|
|
@@ -158,8 +159,8 @@ class FileDurableRulePartition {
|
|
|
158
159
|
}
|
|
159
160
|
if (!isRuleFile(parsed)) {
|
|
160
161
|
const sv = schemaVersionWord(parsed);
|
|
161
|
-
const why = sv === 1 || sv === 2
|
|
162
|
-
? `${this.file} is a schema ${String(sv)} bucket that was not migrated to schema
|
|
162
|
+
const why = sv === 1 || sv === 2 || sv === 3
|
|
163
|
+
? `${this.file} is a schema ${String(sv)} bucket that was not migrated to schema 4 (written by an older engine after this directory was migrated?); refusing the whole file and loading zero rules`
|
|
163
164
|
: `${this.file} does not carry a readable rule-file shape; refusing the whole file and loading zero rules`;
|
|
164
165
|
this.disclose(why);
|
|
165
166
|
return { unreadable: why };
|
|
@@ -170,6 +171,19 @@ class FileDurableRulePartition {
|
|
|
170
171
|
this.disclose(why);
|
|
171
172
|
return { unreadable: why };
|
|
172
173
|
}
|
|
174
|
+
const rowKinds = [
|
|
175
|
+
["rule", parsed.rules],
|
|
176
|
+
["tombstone", parsed.tombstones],
|
|
177
|
+
["quarantined", parsed.quarantined ?? []],
|
|
178
|
+
];
|
|
179
|
+
for (const [kind, rows] of rowKinds) {
|
|
180
|
+
const bad = rows.findIndex((r) => !isRuleBehavior(r?.behavior));
|
|
181
|
+
if (bad !== -1) {
|
|
182
|
+
const why = `${this.file} carries a ${kind} row (#${bad}) whose behavior word is outside deny/ask/allow — a schema 4 row must name its behavior; refusing the whole file and loading zero rules`;
|
|
183
|
+
this.disclose(why);
|
|
184
|
+
return { unreadable: why };
|
|
185
|
+
}
|
|
186
|
+
}
|
|
173
187
|
return { file: parsed };
|
|
174
188
|
}
|
|
175
189
|
disclose(message) {
|
|
@@ -218,7 +232,7 @@ class FileDurableRulePartition {
|
|
|
218
232
|
throw new Error(`refusing to write the permission-rule store while it cannot be read: ${r.unreadable}`);
|
|
219
233
|
}
|
|
220
234
|
if ("absent" in r) {
|
|
221
|
-
return { schemaVersion:
|
|
235
|
+
return { schemaVersion: 4, actor: `file-${randomBytes(6).toString("hex")}`, counter: 0, rev: 0, rules: [], tombstones: [] };
|
|
222
236
|
}
|
|
223
237
|
return {
|
|
224
238
|
schemaVersion: r.file.schemaVersion,
|
|
@@ -324,7 +338,7 @@ export class FileDurableRulePartitionProvider {
|
|
|
324
338
|
migrateLegacyBuckets() {
|
|
325
339
|
if (!existsSync(this.dir))
|
|
326
340
|
return;
|
|
327
|
-
if (existsSync(join(this.dir,
|
|
341
|
+
if (existsSync(join(this.dir, SCHEMA_MARKER_FILE)))
|
|
328
342
|
return;
|
|
329
343
|
try {
|
|
330
344
|
assertSafeDir(this.dir);
|
|
@@ -359,7 +373,7 @@ export class FileDurableRulePartitionProvider {
|
|
|
359
373
|
legacy.push({ name, file: read });
|
|
360
374
|
}
|
|
361
375
|
if (legacy.length === 0) {
|
|
362
|
-
this.
|
|
376
|
+
this.writeSchemaMarker();
|
|
363
377
|
return;
|
|
364
378
|
}
|
|
365
379
|
const orgHome = this.fileOrgPersistence();
|
|
@@ -380,12 +394,18 @@ export class FileDurableRulePartitionProvider {
|
|
|
380
394
|
}
|
|
381
395
|
}
|
|
382
396
|
}
|
|
383
|
-
const
|
|
397
|
+
const stamped = stampRowBehaviors(rest);
|
|
398
|
+
if (stamped === undefined) {
|
|
399
|
+
complete = false;
|
|
400
|
+
this.onError?.(`bucket ${name} left untouched: a row carries a behavior word outside deny/ask/allow, which no engine version wrote; the file is not migrated`);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const next = { ...stamped, schemaVersion: 4 };
|
|
384
404
|
atomicPublish(this.dir, join(this.dir, name), JSON.stringify({ ...next, checksum: checksumOf(next) }, null, 2));
|
|
385
|
-
this.onError?.(`migrated ${name} to rule-file schema
|
|
405
|
+
this.onError?.(`migrated ${name} to rule-file schema 4${org !== undefined ? ` (its org block moved to ${ORG_STATE_FILE})` : ""}`);
|
|
386
406
|
}
|
|
387
407
|
if (complete)
|
|
388
|
-
this.
|
|
408
|
+
this.writeSchemaMarker();
|
|
389
409
|
}
|
|
390
410
|
finally {
|
|
391
411
|
if (!held) {
|
|
@@ -394,12 +414,12 @@ export class FileDurableRulePartitionProvider {
|
|
|
394
414
|
}
|
|
395
415
|
}
|
|
396
416
|
}
|
|
397
|
-
|
|
417
|
+
writeSchemaMarker() {
|
|
398
418
|
try {
|
|
399
|
-
atomicPublish(this.dir, join(this.dir,
|
|
419
|
+
atomicPublish(this.dir, join(this.dir, SCHEMA_MARKER_FILE), JSON.stringify({ schemaVersion: 4 }));
|
|
400
420
|
}
|
|
401
421
|
catch (err) {
|
|
402
|
-
this.onError?.(`could not write ${
|
|
422
|
+
this.onError?.(`could not write ${SCHEMA_MARKER_FILE}: ${err.message}; the legacy scan will run again next construction`);
|
|
403
423
|
}
|
|
404
424
|
}
|
|
405
425
|
orgStatePersistence() {
|
|
@@ -449,6 +469,34 @@ export class FileDurableRulePartitionProvider {
|
|
|
449
469
|
this.lock = undefined;
|
|
450
470
|
}
|
|
451
471
|
}
|
|
472
|
+
function stampRowBehaviors(body) {
|
|
473
|
+
const rules = [];
|
|
474
|
+
for (const r of body.rules) {
|
|
475
|
+
const behavior = readRowBehavior(r.behavior);
|
|
476
|
+
if (behavior === undefined)
|
|
477
|
+
return undefined;
|
|
478
|
+
rules.push({ ...r, behavior });
|
|
479
|
+
}
|
|
480
|
+
const tombstones = [];
|
|
481
|
+
for (const t of body.tombstones) {
|
|
482
|
+
const behavior = readRowBehavior(t.behavior);
|
|
483
|
+
if (behavior === undefined)
|
|
484
|
+
return undefined;
|
|
485
|
+
tombstones.push({ ...t, behavior });
|
|
486
|
+
}
|
|
487
|
+
let quarantined;
|
|
488
|
+
if (body.quarantined !== undefined) {
|
|
489
|
+
quarantined = [];
|
|
490
|
+
for (const q of body.quarantined) {
|
|
491
|
+
const behavior = readRowBehavior(q.behavior);
|
|
492
|
+
if (behavior === undefined)
|
|
493
|
+
return undefined;
|
|
494
|
+
quarantined.push({ ...q, behavior });
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
const { rules: _r, tombstones: _t, quarantined: _q, ...rest } = body;
|
|
498
|
+
return { ...rest, rules, tombstones, ...(quarantined !== undefined ? { quarantined } : {}) };
|
|
499
|
+
}
|
|
452
500
|
function readLegacyBucket(file) {
|
|
453
501
|
let raw;
|
|
454
502
|
try {
|
|
@@ -467,7 +515,7 @@ function readLegacyBucket(file) {
|
|
|
467
515
|
catch {
|
|
468
516
|
return undefined;
|
|
469
517
|
}
|
|
470
|
-
if (!
|
|
518
|
+
if (!isOlderRuleFile(parsed))
|
|
471
519
|
return undefined;
|
|
472
520
|
const { checksum, ...body } = parsed;
|
|
473
521
|
return checksum === checksumOf(body) ? parsed : undefined;
|
|
@@ -80,7 +80,7 @@ export interface LeadingCommandNameOptions {
|
|
|
80
80
|
* name instead of refusing it.
|
|
81
81
|
*
|
|
82
82
|
* OPT-IN, and used by ONE family of callers: the persisted permission-RULE lane
|
|
83
|
-
* ({@link import("../../core/permission-rule-model.js").
|
|
83
|
+
* ({@link import("../../core/permission-rule-model.js").parseRuleText} and its matcher), where
|
|
84
84
|
* both sides of the comparison are literal text a person read on an approval card. The refusal this
|
|
85
85
|
* option lifts exists for the argv[0]-NAME filters (the read-only allowlist, the coarse
|
|
86
86
|
* command-name policy, the skill specifier): those compare a bare token against a name set, and a
|
|
@@ -62,7 +62,7 @@ export declare function bashReversibilityProbe(allow?: readonly string[], bounda
|
|
|
62
62
|
* the read-only allowlist face (`core.bash_readonly@1`). Consumed by prepare-task's
|
|
63
63
|
* fullShellReachable roster assertion (its first load-bearing consumer).
|
|
64
64
|
*/
|
|
65
|
-
export
|
|
65
|
+
export { FULL_SHELL_CONTRACT_ID } from "../../core/tool-catalog-entries.js";
|
|
66
66
|
/** The CC-verbatim exit-1 interpretation for `command`, or undefined when exit 1 means a real error.
|
|
67
67
|
* Conservative parse: last `;`/`&&`/`||`/newline statement → last `|` pipeline segment → leading
|
|
68
68
|
* command name (env-assignments skipped, path prefix stripped); `git grep`/`git diff` special-cased
|
package/dist/tools/fs/fs-bash.js
CHANGED
|
@@ -2,7 +2,7 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { defineTool, errorResult } from "../../core/tools.js";
|
|
3
3
|
import { defaultTaskRegistry } from "../../core/task-registry.js";
|
|
4
4
|
import { accountDroppedBytes, spoolDropNote } from "../../core/task-registry-shared.js";
|
|
5
|
-
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME,
|
|
5
|
+
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, ENV_DIRECT_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "../../core/task-tool-shape.js";
|
|
6
6
|
import { hasBackgroundShell } from "../../core/background-shell.js";
|
|
7
7
|
import { delimitUntrusted } from "../../core/untrusted-text.js";
|
|
8
8
|
import { MCP_IMAGE_MAX_BASE64 } from "../../core/mcp.js";
|
|
@@ -12,6 +12,7 @@ import { ghRateLimitHint } from "./gh-rate-limit.js";
|
|
|
12
12
|
import { resolveBashTimeoutCaps, bashTimeoutCapsSec, bashTimeoutArgRefusal, bashTimeoutParamDescription, envErrorDetail, bashMaxOutputChars, clipShellOutput, writeShellOverflowFile, createShellOverflowSpoolFence, shellRecoveryHint, CWD_SENTINEL, BASH_READONLY_CONFINEMENT_NOTE, } from "./fs-shared.js";
|
|
13
13
|
import { PROBE_CAUSE_PATH_MAX, inlineUntrusted } from "../../core/untrusted-text.js";
|
|
14
14
|
import { BASH_CLASSIFY_DEFAULT_ALLOW, BASH_READONLY_DEFAULT_ALLOW, coarseReadonlyCheck, classifyBoundedReadonlyPollLoop, classifyCompoundReadonlyDetailed, classifyOutOfRootReadGate, classifySimpleCommandReadBoundary, NOT_AUTO_ALLOWED, } from "./bash-readonly-classifier.js";
|
|
15
|
+
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
15
16
|
const RECURSIVE_CAUSE_MAX_PATHS = 3;
|
|
16
17
|
const RECURSIVE_READ_CAUSE_CODE = "shell.recursive_read_unbounded";
|
|
17
18
|
function operandFamily(paths) {
|
|
@@ -58,7 +59,7 @@ export function bashReversibilityProbe(allow, boundary) {
|
|
|
58
59
|
: { reversible: false, ...outOfRootGate() };
|
|
59
60
|
};
|
|
60
61
|
}
|
|
61
|
-
export
|
|
62
|
+
export { FULL_SHELL_CONTRACT_ID } from "../../core/tool-catalog-entries.js";
|
|
62
63
|
const EXIT1_INTERPRETATION = {
|
|
63
64
|
grep: "No matches found",
|
|
64
65
|
rg: "No matches found",
|
|
@@ -537,7 +538,7 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
|
|
|
537
538
|
const bgEnvIsolatedOwned = hasDestroy(env) && isIsolated(env);
|
|
538
539
|
return defineTool({
|
|
539
540
|
name: "Bash",
|
|
540
|
-
|
|
541
|
+
...toolFace("bash"),
|
|
541
542
|
description: bashDescription(coAuthor, timeoutCaps, bgNotifies, bgRetained, bgSessionScoped, bgEnvIsolatedOwned),
|
|
542
543
|
parameters: Type.Object({
|
|
543
544
|
command: Type.String({ description: "The command to execute" }),
|
|
@@ -559,8 +560,6 @@ export function createBashTool(env, rootCanonical, coAuthor = false, cwdRef = {
|
|
|
559
560
|
description: "Set to true to run this command in the background.",
|
|
560
561
|
})),
|
|
561
562
|
}),
|
|
562
|
-
effect: "write",
|
|
563
|
-
contentOrigin: "execution",
|
|
564
563
|
isConcurrencySafe: (args) => {
|
|
565
564
|
const cmd = args?.command;
|
|
566
565
|
if (typeof cmd !== "string")
|
|
@@ -991,7 +990,7 @@ export function createBashReadonlyTool(env, rootCanonical, allow, opts) {
|
|
|
991
990
|
const canonicalBoundary = createCanonicalReadBoundary(env, readRoots);
|
|
992
991
|
return defineTool({
|
|
993
992
|
name: "Bash",
|
|
994
|
-
|
|
993
|
+
...toolFace("bash-readonly"),
|
|
995
994
|
description: `Run a SINGLE read-only inspection command (e.g. ${sample}, …) and return its output. Shell ` +
|
|
996
995
|
"operators (pipes, redirects, ;, &&, command substitution, subshells) are rejected and only " +
|
|
997
996
|
"allowlisted commands run. Reads are confined to the workspace root(s): a path outside them is " +
|
|
@@ -1002,7 +1001,6 @@ export function createBashReadonlyTool(env, rootCanonical, allow, opts) {
|
|
|
1002
1001
|
command: Type.String({ description: "A single allowlisted read-only command (no shell operators)." }),
|
|
1003
1002
|
timeout: Type.Optional(Type.Number({ description: bashTimeoutParamDescription(timeoutCaps, true) })),
|
|
1004
1003
|
}),
|
|
1005
|
-
effect: "read",
|
|
1006
1004
|
execute: async (args, ctx) => {
|
|
1007
1005
|
const { command, timeout } = args;
|
|
1008
1006
|
const timeoutRefusal = bashTimeoutArgRefusal(timeout, timeoutCaps);
|
|
@@ -1035,10 +1033,9 @@ export function createBashReadonlyTool(env, rootCanonical, allow, opts) {
|
|
|
1035
1033
|
export function createEnvTaskOutputTool(env) {
|
|
1036
1034
|
return defineTool({
|
|
1037
1035
|
name: TASK_OUTPUT_TOOL_NAME,
|
|
1038
|
-
|
|
1036
|
+
...toolFace("task-output-hands"),
|
|
1039
1037
|
description: composeTaskOutputDescription(ENV_DIRECT_TASK_TOOL_CAPS),
|
|
1040
1038
|
parameters: composeTaskOutputParams(ENV_DIRECT_TASK_TOOL_CAPS),
|
|
1041
|
-
effect: "read",
|
|
1042
1039
|
execute: async (args) => {
|
|
1043
1040
|
const { task_id, filter } = args;
|
|
1044
1041
|
const id = resolveTaskIdArg(task_id);
|
|
@@ -1073,10 +1070,9 @@ export function createEnvTaskOutputTool(env) {
|
|
|
1073
1070
|
export function createEnvTaskStopTool(env, registry = defaultTaskRegistry) {
|
|
1074
1071
|
return defineTool({
|
|
1075
1072
|
name: TASK_STOP_TOOL_NAME,
|
|
1076
|
-
|
|
1073
|
+
...toolFace("task-stop-hands"),
|
|
1077
1074
|
description: composeTaskStopDescription(ENV_DIRECT_TASK_TOOL_CAPS),
|
|
1078
1075
|
parameters: TASK_STOP_PARAMS,
|
|
1079
|
-
effect: "write",
|
|
1080
1076
|
execute: async (args) => {
|
|
1081
1077
|
const { task_id, shell_id } = args;
|
|
1082
1078
|
const id = resolveTaskIdArg(task_id, shell_id);
|
|
@@ -29,7 +29,7 @@ type ReadPdfReturn = string | {
|
|
|
29
29
|
* downsampler seam then applies the same pipeline as the Read image branch). poppler absent → HONEST
|
|
30
30
|
* degradation with the working alternative, never a fake success.
|
|
31
31
|
* Wire discipline: the base64 payload rides `details.file` only while ≤48K chars (same OMIT rule as the
|
|
32
|
-
* image variant — a bigger card would be dropped whole by the
|
|
32
|
+
* image variant — a bigger card would be dropped whole by the card projector's 4×cap and blind the shell).
|
|
33
33
|
* structured-card audit item B (2026-08-05): that budget management assumed `"document"` was on `CC_DETAIL_TYPES`, which
|
|
34
34
|
* it was not — every `document` card, sized or not, was dropped at the gate, making the 48K rule dead
|
|
35
35
|
* code and the pages/fallback-level disclosure invisible to a host. The word is registered now.
|
package/dist/tools/fs/fs-read.js
CHANGED
|
@@ -9,6 +9,7 @@ import { MAX_READ_BYTES, SLICED_READ_MAX_BYTES, MAX_IMAGE_READ_BYTES, MAX_IMAGE_
|
|
|
9
9
|
import { readPdfFile, pdfResultToToolReturn } from "./fs-pdf.js";
|
|
10
10
|
import { openSystemReminder } from "../../core/reminder-mint.js";
|
|
11
11
|
import { discloseReminderShaped } from "../../core/reminder-disclosure.js";
|
|
12
|
+
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
12
13
|
export function createReadFileTool(env, state, rootCanonical, cwdRef, additionalRoots, imageDownsampler, pdfCapabilities, bgOutputReadExemption, readCyberReminder, readDeny, readFace, reminderMark, reminderDisclosureCounts) {
|
|
13
14
|
const cyberReminder = readCyberReminder === false ? "" : readCyberReminderText(reminderMark);
|
|
14
15
|
const reminderDisclosureWindows = new Map();
|
|
@@ -23,7 +24,7 @@ export function createReadFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
23
24
|
: "- `file_path` may be relative (resolved against the tracked working directory) or absolute (within the configured roots).\n";
|
|
24
25
|
return defineTool({
|
|
25
26
|
name: "Read",
|
|
26
|
-
|
|
27
|
+
...toolFace("read"),
|
|
27
28
|
description: "Reads a file from the local filesystem.\n" +
|
|
28
29
|
"\n" +
|
|
29
30
|
pathBoundLine +
|
|
@@ -55,7 +56,6 @@ export function createReadFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
55
56
|
limit: Type.Optional(Type.Integer({ minimum: 1, description: "The number of lines to read. Only provide if the file is too large to read at once. (Default: the whole file, bounded by the output-token cap.)" })),
|
|
56
57
|
pages: Type.Optional(Type.String({ description: `Page range for PDF files (e.g., "1-5", "3", "10-20"). Only applicable to PDF files. Maximum ${PDF_MAX_PAGES_PER_READ} pages per request.` })),
|
|
57
58
|
}),
|
|
58
|
-
effect: "read",
|
|
59
59
|
execute: async (args, ctx) => {
|
|
60
60
|
const { offset, limit, pages } = args;
|
|
61
61
|
const path = fileArgPath(args);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { AgentTool, ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
|
-
import type { ToolEffect } from "../../core/types.js";
|
|
3
2
|
import type { ReadDenyMatcher } from "./read-deny.js";
|
|
4
3
|
import type { ReadFace } from "./read-face.js";
|
|
5
4
|
/**
|
|
@@ -13,9 +12,3 @@ import type { ReadFace } from "./read-face.js";
|
|
|
13
12
|
export declare function grepDetailFields(text: string, mode: "files_with_matches" | "content" | "count", offset?: number, structuredRows?: readonly import("./search.js").GrepRow[]): Record<string, unknown>;
|
|
14
13
|
export declare function createGrepTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace): AgentTool;
|
|
15
14
|
export declare function createGlobTool(env: ExecutionEnv, rootCanonical: string, additionalRoots?: readonly string[], readDeny?: ReadDenyMatcher, readFace?: ReadFace): AgentTool;
|
|
16
|
-
/** Static side-effect class of every hand tool, by name (design/44 §3). Used by prepare-task to (a) feed
|
|
17
|
-
* wake/resume reconciliation and (b) drive the verifier read-only boundary. Every mutating hand tool is
|
|
18
|
-
* `write` (RB-264 ⑥W1 folded `Write` back in — see below); `bash` is `write` (a command can do anything);
|
|
19
|
-
* `bash_readonly` is `read` (so it survives the verifier boundary — but still goes through the policy
|
|
20
|
-
* gate, council #7: effect:read is a redo-safety class, never a policy circumvention). */
|
|
21
|
-
export declare const HAND_TOOL_EFFECTS: Readonly<Record<string, ToolEffect>>;
|
|
@@ -2,6 +2,7 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { defineTool, errorResult } from "../../core/tools.js";
|
|
3
3
|
import { resolveKey, violationText, violationDetails } from "./safety.js";
|
|
4
4
|
import { runGrepDetailed, runGlobDetailed, splitAbsoluteGlobPattern, invalidGlobTokens } from "./search.js";
|
|
5
|
+
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
5
6
|
export function grepDetailFields(text, mode, offset, structuredRows) {
|
|
6
7
|
const structuredIdentityComplete = structuredRows !== undefined && structuredRows.every((r) => r.path !== undefined || r.text === "--");
|
|
7
8
|
const structuredPaths = structuredIdentityComplete && structuredRows !== undefined ? [...new Set(structuredRows.filter((r) => r.path !== undefined).map((r) => r.path))] : undefined;
|
|
@@ -74,7 +75,7 @@ export function grepDetailFields(text, mode, offset, structuredRows) {
|
|
|
74
75
|
export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, readFace) {
|
|
75
76
|
return defineTool({
|
|
76
77
|
name: "Grep",
|
|
77
|
-
|
|
78
|
+
...toolFace("grep"),
|
|
78
79
|
description: "Content search built on ripgrep. Prefer this over `grep`/`rg` via Bash — results integrate with the permission UI and file links.\n" +
|
|
79
80
|
"\n" +
|
|
80
81
|
'- Full regex syntax (e.g. "log.*Error", "function\\s+\\w+"). Ripgrep, not grep — escape literal braces (`interface\\{\\}`).\n' +
|
|
@@ -107,7 +108,6 @@ export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, re
|
|
|
107
108
|
})),
|
|
108
109
|
offset: Type.Optional(Type.Number({ description: "Skip the first N output lines (pagination partner of head_limit)." })),
|
|
109
110
|
}),
|
|
110
|
-
effect: "read",
|
|
111
111
|
execute: async (args, ctx) => {
|
|
112
112
|
const retired = args;
|
|
113
113
|
for (const [name, repair] of [
|
|
@@ -175,7 +175,7 @@ export function createGrepTool(env, rootCanonical, additionalRoots, readDeny, re
|
|
|
175
175
|
export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, readFace) {
|
|
176
176
|
return defineTool({
|
|
177
177
|
name: "Glob",
|
|
178
|
-
|
|
178
|
+
...toolFace("glob"),
|
|
179
179
|
description: 'Fast file pattern matching. Supports glob patterns like "**/*.js" or "src/**/*.ts". Returns matching file paths sorted by modification time.\n' +
|
|
180
180
|
"\n" +
|
|
181
181
|
"- On environments that do not report modification times, results fall back to alphabetical order\n" +
|
|
@@ -194,7 +194,6 @@ export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, re
|
|
|
194
194
|
})),
|
|
195
195
|
max_results: Type.Optional(Type.Number({ description: "Cap results (default 500)." })),
|
|
196
196
|
}),
|
|
197
|
-
effect: "read",
|
|
198
197
|
execute: async (args, ctx) => {
|
|
199
198
|
let { pattern, path } = args;
|
|
200
199
|
const { max_results } = args;
|
|
@@ -222,15 +221,3 @@ export function createGlobTool(env, rootCanonical, additionalRoots, readDeny, re
|
|
|
222
221
|
},
|
|
223
222
|
});
|
|
224
223
|
}
|
|
225
|
-
export const HAND_TOOL_EFFECTS = {
|
|
226
|
-
Read: "read",
|
|
227
|
-
Edit: "write",
|
|
228
|
-
Write: "write",
|
|
229
|
-
NotebookEdit: "write",
|
|
230
|
-
Grep: "read",
|
|
231
|
-
Glob: "read",
|
|
232
|
-
RepoMap: "read",
|
|
233
|
-
Bash: "write",
|
|
234
|
-
TaskOutput: "read",
|
|
235
|
-
TaskStop: "write",
|
|
236
|
-
};
|
|
@@ -4,6 +4,7 @@ import { defineTool, errorResult } from "../../core/tools.js";
|
|
|
4
4
|
import { sha256, resolveKey, violationText, violationDetails, requireRead, checkStale, checkEditMatch, checkNoChange, fileArgPath, resolveQuoteMatch, adaptNewStringQuotes, resolveEscapeMatch, adaptNewStringEscapes, escapeMatchWasAttempted, ESCAPE_MATCH_MISS_NOTE, deletionOldString, countOccurrences, READ_REFUSED_ESCAPE_HINT, } from "./safety.js";
|
|
5
5
|
import { decodeTextBytes, encodeTextForFile, normalizeEditText, normalizeFileText } from "./encoding.js";
|
|
6
6
|
import { MAX_EDIT_BYTES, decodeEditBytes, tooLargeToEditMessage, truncatedUtf16BodyMessage, persistedTextOf, notReadRefusalText, enoentMessage, FILE_STATE_TRAILER, FILE_PATH_PARAMS, ipynbRedirect, countLines, applyRecordedEdit, } from "./fs-shared.js";
|
|
7
|
+
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
7
8
|
async function envFinalWrite(env, key, content, signal, opts) {
|
|
8
9
|
const fireEdited = () => {
|
|
9
10
|
if (opts?.edited?.hook === undefined)
|
|
@@ -88,7 +89,7 @@ async function gateToolWrite(hook, tool, path, key, content) {
|
|
|
88
89
|
export function createEditFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
89
90
|
return defineTool({
|
|
90
91
|
name: "Edit",
|
|
91
|
-
|
|
92
|
+
...toolFace("edit"),
|
|
92
93
|
description: "Performs exact string replacement in a file.\n" +
|
|
93
94
|
"\n" +
|
|
94
95
|
"- You must Read the file in this conversation before editing, or the call will fail.\n" +
|
|
@@ -101,7 +102,6 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
101
102
|
new_string: Type.Optional(Type.String({ description: "The text to replace it with (must be different from old_string)" })),
|
|
102
103
|
replace_all: Type.Optional(Type.Boolean({ description: "Replace all occurrences of old_string (default false)" })),
|
|
103
104
|
}),
|
|
104
|
-
effect: "write",
|
|
105
105
|
validateInput: async (args, ctx) => {
|
|
106
106
|
const a = args;
|
|
107
107
|
if (Array.isArray(a.edits) && a.edits.length > 0)
|
|
@@ -327,7 +327,7 @@ export function createEditFileTool(env, state, rootCanonical, cwdRef, additional
|
|
|
327
327
|
export function createWriteFileTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
328
328
|
return defineTool({
|
|
329
329
|
name: "Write",
|
|
330
|
-
|
|
330
|
+
...toolFace("write"),
|
|
331
331
|
description: "Writes a file to the local filesystem, overwriting if one exists.\n" +
|
|
332
332
|
"\n" +
|
|
333
333
|
"When to use: creating a new file, or fully replacing one you've already Read. Overwriting an existing " +
|
|
@@ -337,7 +337,6 @@ export function createWriteFileTool(env, state, rootCanonical, cwdRef, additiona
|
|
|
337
337
|
...FILE_PATH_PARAMS,
|
|
338
338
|
content: Type.String({ description: "The content to write to the file" }),
|
|
339
339
|
}),
|
|
340
|
-
effect: "write",
|
|
341
340
|
validateInput: async (args, ctx) => {
|
|
342
341
|
const path = fileArgPath(args);
|
|
343
342
|
if (path === undefined)
|
|
@@ -427,7 +426,7 @@ const NOTEBOOK_CELL_ID_REQUIRED_MESSAGE = `Error (NotebookEdit): cell_id is requ
|
|
|
427
426
|
export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additionalRoots, beforeWrite, trackEdit, onEdited) {
|
|
428
427
|
return defineTool({
|
|
429
428
|
name: "NotebookEdit",
|
|
430
|
-
|
|
429
|
+
...toolFace("notebook-edit"),
|
|
431
430
|
description: "Replaces, inserts, or deletes a single cell in a Jupyter notebook (.ipynb file).\n" +
|
|
432
431
|
"\n" +
|
|
433
432
|
"Usage:\n" +
|
|
@@ -449,7 +448,6 @@ export function createNotebookEditTool(env, state, rootCanonical, cwdRef, additi
|
|
|
449
448
|
description: "The type of edit to make (replace, insert, delete). Defaults to replace.",
|
|
450
449
|
})),
|
|
451
450
|
}),
|
|
452
|
-
effect: "write",
|
|
453
451
|
validateInput: async (args, ctx) => {
|
|
454
452
|
const a = args;
|
|
455
453
|
const mode = a.edit_mode ?? "replace";
|
|
@@ -92,5 +92,5 @@ export interface NotebookRender {
|
|
|
92
92
|
export declare function renderNotebookCells(cells: readonly NotebookCell[], imageBase64Budget: number): NotebookRender;
|
|
93
93
|
/** Wire-card projection of a cell: base64 replaced by a length marker so the `details` card never
|
|
94
94
|
* carries raw image bytes (they ride the model-facing content blocks; an oversized card would be
|
|
95
|
-
* omitted whole by the
|
|
95
|
+
* omitted whole by the card projector's 4×cap and blind the shell). */
|
|
96
96
|
export declare function stripNotebookImageData(cell: NotebookCell): NotebookCell;
|
|
@@ -2,6 +2,7 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { defineTool, errorResult } from "../../core/tools.js";
|
|
3
3
|
import { resolveKey, violationText, violationDetails } from "./safety.js";
|
|
4
4
|
import { buildIgnore, denyWithheldNote, walk, walkIsPartial } from "./search.js";
|
|
5
|
+
import { toolFace } from "../../core/tool-catalog-entries.js";
|
|
5
6
|
const DEFAULT_MAX_CHARS = 16_000;
|
|
6
7
|
const DEFAULT_MAX_FILES = 400;
|
|
7
8
|
const FILE_SCAN_MAX_BYTES = 256 * 1024;
|
|
@@ -82,7 +83,7 @@ function rankEntries(a, b) {
|
|
|
82
83
|
export function createRepoMapTool(env, rootCanonical, additionalRoots, readDeny, readFace) {
|
|
83
84
|
return defineTool({
|
|
84
85
|
name: "RepoMap",
|
|
85
|
-
|
|
86
|
+
...toolFace("repo-map"),
|
|
86
87
|
description: "Get a compact map of the repository: each source file with its top-level symbols (functions, " +
|
|
87
88
|
"classes, types). Use this FIRST to orient in an unfamiliar codebase before grepping — it shows " +
|
|
88
89
|
"what exists and where, so you can jump to the relevant files. Token-budgeted and honest about " +
|
|
@@ -92,7 +93,6 @@ export function createRepoMapTool(env, rootCanonical, additionalRoots, readDeny,
|
|
|
92
93
|
max_chars: Type.Optional(Type.Number({ description: `Token budget for the rendered map in chars (default ${DEFAULT_MAX_CHARS}; a very small value yields only a truncation note).` })),
|
|
93
94
|
max_files: Type.Optional(Type.Number({ description: `Max files to scan (default ${DEFAULT_MAX_FILES}).` })),
|
|
94
95
|
}),
|
|
95
|
-
effect: "read",
|
|
96
96
|
execute: async (args, ctx) => {
|
|
97
97
|
const a = args;
|
|
98
98
|
const signal = ctx.signal;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
|
+
import { protectivePathTargetOf, type ToolCallLike } from "../../core/tool-registry.js";
|
|
2
3
|
/**
|
|
3
4
|
* batch-B (CC 2.1.187 parity): Read/Edit/Write take the file path as `file_path`. Read the target the same way
|
|
4
5
|
* EVERYWHERE the path is consumed from a tool-call's args — not just the tool body but ALSO every gate that
|
|
@@ -28,7 +29,7 @@ export declare function fileArgPath(args: unknown): string | undefined;
|
|
|
28
29
|
* this module stays dependency-free of the alias table — "NotebookEdit" is the canonical CC name and
|
|
29
30
|
* old→new normalization is identity for it).
|
|
30
31
|
*/
|
|
31
|
-
export declare function writeTargetPath(
|
|
32
|
+
export declare function writeTargetPath(call: ToolCallLike, pt?: ReturnType<typeof protectivePathTargetOf>): string | undefined;
|
|
32
33
|
/**
|
|
33
34
|
* The "hand" file tools (design/44) — safety invariants ported from CC's `FileEditTool` (design/43 Rule
|
|
34
35
|
* 7): without these a weak model blind-edits / overwrites files (P0 safety, not polish). This module is
|
|
@@ -320,7 +321,7 @@ export declare function violationText(toolName: string, v: FsViolation): string;
|
|
|
320
321
|
* every renderer of a {@link resolveKey} violation — `undefined` keeps the pre-existing detail-less
|
|
321
322
|
* shape for all other violation codes, so nothing else changes byte-wise.
|
|
322
323
|
*
|
|
323
|
-
* RB-397-c (回溯复查批): `type` is the WIRE discriminator —
|
|
324
|
+
* RB-397-c (回溯复查批): `type` is the WIRE discriminator — the leg's card projector (`createStructuredProjector`, design/388 L4)
|
|
324
325
|
* (tool-output-projection.ts) keys on `details.type`, so without it the card never reached
|
|
325
326
|
* `tool_end.structured` and the "count from fields" promise above held for in-process readers only.
|
|
326
327
|
* `code` stays as-is for the existing RB-371 consumers — the new key is strictly additive.
|
package/dist/tools/fs/safety.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
|
|
3
|
+
import { pathTargetValue, protectivePathTargetOf } from "../../core/tool-registry.js";
|
|
3
4
|
export function fileArgPath(args) {
|
|
4
5
|
const a = args;
|
|
5
6
|
const fp = a?.file_path;
|
|
@@ -8,12 +9,12 @@ export function fileArgPath(args) {
|
|
|
8
9
|
const p = a?.path;
|
|
9
10
|
return typeof p === "string" && p.length > 0 ? p : undefined;
|
|
10
11
|
}
|
|
11
|
-
export function writeTargetPath(
|
|
12
|
-
|
|
12
|
+
export function writeTargetPath(call, pt = protectivePathTargetOf(call)) {
|
|
13
|
+
if (pt !== undefined)
|
|
14
|
+
return pathTargetValue(call.args, pt);
|
|
15
|
+
const np = call.args?.notebook_path;
|
|
13
16
|
const notebook = typeof np === "string" && np.length > 0 ? np : undefined;
|
|
14
|
-
|
|
15
|
-
return notebook ?? fileArgPath(args);
|
|
16
|
-
return fileArgPath(args) ?? notebook;
|
|
17
|
+
return fileArgPath(call.args) ?? notebook;
|
|
17
18
|
}
|
|
18
19
|
export function sha256(content) {
|
|
19
20
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
package/dist/tools/monitor.js
CHANGED
|
@@ -2,6 +2,7 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { defineTool, errorResult } from "../core/tools.js";
|
|
3
3
|
import { hasBackgroundShell } from "../core/background-shell.js";
|
|
4
4
|
import { MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, } from "../core/task-registry.js";
|
|
5
|
+
import { toolFace } from "../core/tool-catalog-entries.js";
|
|
5
6
|
const MONITOR_DESCRIPTION = "Run a shell command in the background and watch its stdout as a stream of events.\n" +
|
|
6
7
|
"\n" +
|
|
7
8
|
"- Each line the command writes to stdout becomes a notification event delivered to you between turns; " +
|
|
@@ -42,7 +43,7 @@ function envBackgroundWallSec(caps, requestedSec) {
|
|
|
42
43
|
export function createMonitorTool(env, opts) {
|
|
43
44
|
return defineTool({
|
|
44
45
|
name: "Monitor",
|
|
45
|
-
|
|
46
|
+
...toolFace("monitor"),
|
|
46
47
|
description: MONITOR_DESCRIPTION,
|
|
47
48
|
parameters: Type.Object({
|
|
48
49
|
command: Type.String({
|
|
@@ -58,7 +59,6 @@ export function createMonitorTool(env, opts) {
|
|
|
58
59
|
description: "Set to true for a session-resident monitor with no watch timeout of its own (default false). It keeps watching until the execution environment's background time budget expires, until you stop it with TaskStop, or until the session ends.",
|
|
59
60
|
})),
|
|
60
61
|
}),
|
|
61
|
-
effect: "write",
|
|
62
62
|
execute: async (args, ctx) => {
|
|
63
63
|
const { command, description, timeout_ms, persistent } = args;
|
|
64
64
|
if (!hasBackgroundShell(env)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { defineTool, errorResult } from "../core/tools.js";
|
|
3
3
|
import { hasScheduler, isValidCronExpr, } from "../core/scheduler.js";
|
|
4
|
+
import { toolFace } from "../core/tool-catalog-entries.js";
|
|
4
5
|
export const SCHEDULE_WAKEUP_TOOL_NAME = "ScheduleWakeup";
|
|
5
6
|
export const AUTONOMOUS_LOOP_SENTINEL = "<<autonomous-loop>>";
|
|
6
7
|
export const AUTONOMOUS_LOOP_DYNAMIC_SENTINEL = "<<autonomous-loop-dynamic>>";
|
|
@@ -199,7 +200,7 @@ Every user who asks for "9am" gets \`0 9\`, and every user who asks for "hourly"
|
|
|
199
200
|
Only use minute 0 or 30 when the user names that exact time and clearly means it ("at 9:00 sharp", "at half past", coordinating with a meeting). When in doubt, nudge a few minutes early or late — the user will not notice, and the fleet will.`;
|
|
200
201
|
const cronCreate = defineTool({
|
|
201
202
|
name: "CronCreate",
|
|
202
|
-
|
|
203
|
+
...toolFace("cron-create"),
|
|
203
204
|
description: cronCreateDescription,
|
|
204
205
|
parameters: Type.Object({
|
|
205
206
|
prompt: Type.String({ description: "Self-contained instructions for the future task (it runs unattended)." }),
|
|
@@ -222,7 +223,6 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
|
|
|
222
223
|
})),
|
|
223
224
|
label: Type.Optional(Type.String({ description: "Short label (also a dedup key with the schedule)." })),
|
|
224
225
|
}),
|
|
225
|
-
effect: "write",
|
|
226
226
|
execute: async (args) => serializedCronCreateOp(async () => {
|
|
227
227
|
const a = args;
|
|
228
228
|
if (a.schedule !== undefined && a.cron !== undefined) {
|
|
@@ -305,10 +305,9 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
|
|
|
305
305
|
};
|
|
306
306
|
const cronCancel = defineTool({
|
|
307
307
|
name: "CronDelete",
|
|
308
|
-
|
|
308
|
+
...toolFace("cron-delete"),
|
|
309
309
|
description: "Cancel a cron job previously scheduled with CronCreate. Idempotent; ids also appear in CronList.",
|
|
310
310
|
parameters: Type.Object({ id: Type.String({ description: "Job ID returned by CronCreate." }) }),
|
|
311
|
-
effect: "idempotent",
|
|
312
311
|
execute: async (args) => {
|
|
313
312
|
const { id } = args;
|
|
314
313
|
const taskId = id;
|
|
@@ -329,10 +328,9 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
|
|
|
329
328
|
});
|
|
330
329
|
const cronList = defineTool({
|
|
331
330
|
name: "CronList",
|
|
332
|
-
|
|
331
|
+
...toolFace("cron-list"),
|
|
333
332
|
description: "List all cron jobs scheduled via CronCreate (id, when, label).",
|
|
334
333
|
parameters: Type.Object({}),
|
|
335
|
-
effect: "read",
|
|
336
334
|
execute: async () => {
|
|
337
335
|
const rows = [];
|
|
338
336
|
const seen = new Set();
|
|
@@ -409,7 +407,7 @@ MOUNT NOTE: this host has no resident wakeup leg, so ${SCHEDULE_WAKEUP_TOOL_NAME
|
|
|
409
407
|
MOUNT NOTE: this host does not vouch for session-scoped scheduling, so a wakeup scheduled here is PERSISTENT — contrary to the paragraph above, it can outlive this session instead of ending with it. End the loop explicitly with \`stop: true\` when the work is done; do not rely on the session ending to cancel it.`;
|
|
410
408
|
const scheduleWakeup = defineTool({
|
|
411
409
|
name: SCHEDULE_WAKEUP_TOOL_NAME,
|
|
412
|
-
|
|
410
|
+
...toolFace("schedule-wakeup"),
|
|
413
411
|
description: `${SCHEDULE_WAKEUP_PROMPT}${wakeupMountNote}`,
|
|
414
412
|
parameters: Type.Object({
|
|
415
413
|
delaySeconds: Type.Optional(Type.Number({
|
|
@@ -428,7 +426,6 @@ MOUNT NOTE: this host does not vouch for session-scoped scheduling, so a wakeup
|
|
|
428
426
|
description: "true = nothing changed (you checked and there is nothing to report). false = something happened worth keeping (edited a file, posted a message, advanced state, surfaced a finding). Consecutive noop:true ticks are collapsed in the user's terminal view and tracked as a streak. Required unless `stop` is true.",
|
|
429
427
|
})),
|
|
430
428
|
}),
|
|
431
|
-
effect: "write",
|
|
432
429
|
execute: async (args) => serializedWakeupOp(async () => {
|
|
433
430
|
const a = args;
|
|
434
431
|
if (a.stop === true) {
|