@sema-agent/core 7.6.3 → 7.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 +70 -10
- 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/peer-admission.d.ts +1 -1
- package/dist/agents/send-message-tool.js +2 -1
- package/dist/agents/subagent-steps.js +5 -4
- package/dist/agents/subagent.js +2 -3
- package/dist/brain/anthropic.js +8 -2
- package/dist/brain/open-responses.js +5 -3
- package/dist/brain/openai.js +31 -8
- package/dist/brain/reasoning.d.ts +32 -0
- package/dist/brain/reasoning.js +18 -0
- package/dist/core/ask-question.js +2 -2
- package/dist/core/auto-mode-defaults.d.ts +16 -0
- package/dist/core/auto-mode-defaults.js +1 -0
- package/dist/core/auto-mode.d.ts +19 -0
- package/dist/core/auto-mode.js +74 -56
- package/dist/core/checkpoint-execution-record.d.ts +110 -0
- package/dist/core/checkpoint-execution-record.js +49 -0
- package/dist/core/checkpoint-store.d.ts +114 -2
- package/dist/core/checkpoint-store.js +35 -2
- package/dist/core/context-edit.d.ts +0 -22
- package/dist/core/context-edit.js +2 -11
- package/dist/core/engine-notice.d.ts +17 -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 +4 -3
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +8 -0
- package/dist/core/hooks.d.ts +8 -2
- 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-rules.d.ts +32 -2
- package/dist/core/permission-rules.js +43 -37
- package/dist/core/present-plan-tool.js +3 -4
- package/dist/core/runner/active-skill-scope.js +7 -4
- package/dist/core/runner/clock-and-limits.d.ts +117 -0
- package/dist/core/runner/clock-and-limits.js +118 -0
- package/dist/core/runner/compaction-call-options.d.ts +1 -1
- package/dist/core/runner/contracts.d.ts +37 -3
- package/dist/core/runner/decide-continuation.d.ts +98 -0
- package/dist/core/runner/decide-continuation.js +133 -0
- package/dist/core/runner/execution-record.d.ts +26 -0
- package/dist/core/runner/execution-record.js +19 -0
- package/dist/core/runner/git-leg-delivery.d.ts +28 -0
- package/dist/core/runner/git-leg-delivery.js +94 -0
- package/dist/core/runner/initial-run-state.d.ts +14 -0
- package/dist/core/runner/initial-run-state.js +11 -0
- 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 +33 -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 +3 -1
- 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-safety-scan.js +42 -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/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 +0 -68
- package/dist/core/runner/runtask.js +66 -1486
- package/dist/core/runner/session-rule-policy.js +9 -4
- package/dist/core/runner/steer-admission.d.ts +17 -0
- package/dist/core/runner/steer-admission.js +17 -0
- 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 +71 -0
- package/dist/core/runner/tool-end-body.js +75 -0
- 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 +10 -0
- package/dist/core/runner/tool-output-projection.js +33 -10
- 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.d.ts +4 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +108 -0
- 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 +450 -0
- package/dist/core/tool-roster.js +497 -0
- package/dist/core/tool-spec.d.ts +54 -3
- package/dist/core/tools.js +7 -0
- package/dist/core/trace.d.ts +24 -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 +20 -8
- package/dist/index.js +19 -7
- 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.d.ts +7 -0
- package/dist/stores/file/checkpoint-store.js +20 -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-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/repo-map.js +2 -2
- package/dist/tools/fs/safety.d.ts +2 -1
- 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 +183 -7
|
@@ -2,6 +2,7 @@ import { Type } from "typebox";
|
|
|
2
2
|
import { errorResult } from "../tools.js";
|
|
3
3
|
import { CONNECTING_MESSAGE, GENERIC_FAILURE_MESSAGE, NO_STORES_MESSAGE, SHARED_STORE_FRAME, UNAVAILABLE_MESSAGE, datePrefixOf, echoUntrusted, foldReaderError, listPrefixArgument, normalizeEntries, normalizeMemoryPath, normalizeReadValue, normalizeSnapshot, pageDocuments, relativePathRefusal, sanitizeSharedContent, validateDocumentPath, validatePathPrefix, } from "./normalize.js";
|
|
4
4
|
import { SHARED_MEMORY_LIST_PAGE_SIZE, SHARED_MEMORY_LIST_TOOL_NAME, SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_READ_TOOL_NAME, } from "./types.js";
|
|
5
|
+
import { toolFace } from "../tool-catalog-entries.js";
|
|
5
6
|
const LIST_HINT = "List shared memory stores and their documents — check here before telling the user you do not have something.";
|
|
6
7
|
const LIST_DESCRIPTION = [
|
|
7
8
|
LIST_HINT,
|
|
@@ -125,11 +126,9 @@ export function createSharedMemoryTools(opts) {
|
|
|
125
126
|
};
|
|
126
127
|
const listTool = {
|
|
127
128
|
name: SHARED_MEMORY_LIST_TOOL_NAME,
|
|
129
|
+
...toolFace("memory-list"),
|
|
128
130
|
description: LIST_DESCRIPTION,
|
|
129
|
-
effect: "read",
|
|
130
131
|
defer: true,
|
|
131
|
-
offload: false,
|
|
132
|
-
contract: { contractId: "core.memory_list@1", implementationRevision: "1" },
|
|
133
132
|
parameters: Type.Object({
|
|
134
133
|
store: Type.Optional(Type.String({
|
|
135
134
|
description: "Id of the connected memory store to list. Omit to list the memory stores connected to this session (id, description, and writable or read-only).",
|
|
@@ -258,11 +257,9 @@ export function createSharedMemoryTools(opts) {
|
|
|
258
257
|
};
|
|
259
258
|
const readTool = {
|
|
260
259
|
name: SHARED_MEMORY_READ_TOOL_NAME,
|
|
260
|
+
...toolFace("memory-read"),
|
|
261
261
|
description: READ_DESCRIPTION,
|
|
262
|
-
effect: "read",
|
|
263
262
|
defer: true,
|
|
264
|
-
offload: false,
|
|
265
|
-
contract: { contractId: "core.memory_read@1", implementationRevision: "1" },
|
|
266
263
|
parameters: Type.Object({
|
|
267
264
|
store: Type.String({
|
|
268
265
|
description: `Id of the connected memory store to read from (call ${SHARED_MEMORY_LIST_TOOL_NAME} with no arguments to see the connected stores).`,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type Checkpoint, type CheckpointStore, type ResumeOutcome } from "../checkpoint-store.js";
|
|
2
|
+
import type { GateOutcome } from "../gate-outcome.js";
|
|
2
3
|
import { type CheckpointFields } from "../pause-registry.js";
|
|
3
4
|
import { type ContractAssertionRunner } from "./contract-harness.js";
|
|
4
5
|
/**
|
|
@@ -23,6 +24,9 @@ export declare function createCheckpointFixture(over?: Partial<CheckpointFields>
|
|
|
23
24
|
export declare const ALLOW: Extract<ResumeOutcome, {
|
|
24
25
|
gate: "policy_ask";
|
|
25
26
|
}>;
|
|
27
|
+
export declare const EXECUTED_ALLOW: GateOutcome;
|
|
28
|
+
export declare const EXECUTED_ALLOW_REORDERED: GateOutcome;
|
|
29
|
+
export declare const VETOED_BY_POLICY: GateOutcome;
|
|
26
30
|
/**
|
|
27
31
|
* The full cross-backend contract for {@link CheckpointStore} — the SAME assertions for every
|
|
28
32
|
* backend. Entries are a 1:1 port of the vitest originals (assertion semantics unchanged).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { strict as assert } from "node:assert";
|
|
2
|
+
import { executionVerdict } from "../checkpoint-execution-record.js";
|
|
2
3
|
import { MAX_PENDING_STEER_ENTRIES, mintCheckpointToken, readPendingSteerQueue, } from "../checkpoint-store.js";
|
|
3
4
|
import { checkpointFrom } from "../pause-registry.js";
|
|
4
5
|
import { beginContract } from "./contract-harness.js";
|
|
@@ -36,6 +37,10 @@ export const ALLOW = {
|
|
|
36
37
|
boundInputHash: "h0",
|
|
37
38
|
hostDecision: { decidedBy: "person" },
|
|
38
39
|
};
|
|
40
|
+
const SETTLED = { kind: "human_allowed", who: { party: "person" }, when: 1_700_000_000_500 };
|
|
41
|
+
export const EXECUTED_ALLOW = Object.freeze({ disposition: Object.freeze({ kind: "allowed" }), settlement: Object.freeze({ ...SETTLED, who: Object.freeze({ ...SETTLED.who }) }), origin: "policy" });
|
|
42
|
+
export const EXECUTED_ALLOW_REORDERED = Object.freeze({ origin: "policy", settlement: Object.freeze({ when: 1_700_000_000_500, who: Object.freeze({ party: "person" }), kind: "human_allowed" }), disposition: Object.freeze({ kind: "allowed" }) });
|
|
43
|
+
export const VETOED_BY_POLICY = Object.freeze({ disposition: Object.freeze({ kind: "denied", deniedBy: "policy" }), settlement: Object.freeze({ ...SETTLED, who: Object.freeze({ ...SETTLED.who }) }), origin: "policy" });
|
|
39
44
|
export async function checkpointStoreContract(make, runAssertion) {
|
|
40
45
|
const { run, settle } = beginContract(runAssertion);
|
|
41
46
|
run("kit prerequisites: reopen/setPendingSteer/listByScope are implemented (REQUIRED by this kit)", async () => {
|
|
@@ -43,6 +48,86 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
43
48
|
const missing = ["reopen", "setPendingSteer", "listByScope"].filter((m) => typeof probe[m] !== "function");
|
|
44
49
|
assert.equal(missing.length, 0, `backend does not implement ${missing.join(", ")} — required for the CheckpointStore contract kit`);
|
|
45
50
|
});
|
|
51
|
+
run("execution declaration: the backend declares execution.outcome === true (a REQUIRED member of the contract)", async () => {
|
|
52
|
+
const probe = make();
|
|
53
|
+
assert.equal(probe.execution?.outcome, true, "backend does not declare `execution = { outcome: true }` — the run refuses the store at its first read");
|
|
54
|
+
assert.equal(typeof probe.recordExecutionOutcome, "function", "backend does not implement recordExecutionOutcome — required by the CheckpointStore contract");
|
|
55
|
+
});
|
|
56
|
+
run("recordExecutionOutcome on a resolved row → `recorded`; the record and its stamp ride the row, the decision winner is untouched, executionVerdict reads executed", async () => {
|
|
57
|
+
const store = make();
|
|
58
|
+
const cp = createCheckpointFixture();
|
|
59
|
+
await store.put(cp.token, cp);
|
|
60
|
+
assert.equal(executionVerdict((await store.get(cp.token))).kind, "unknown");
|
|
61
|
+
await store.resolve(cp.token, cp.scope, ALLOW);
|
|
62
|
+
const before = (await store.get(cp.token));
|
|
63
|
+
assert.equal(executionVerdict(before).kind, "unknown", "a resolved row with no record reads UNKNOWN — never allowed by default");
|
|
64
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, VETOED_BY_POLICY), "recorded");
|
|
65
|
+
const after = (await store.get(cp.token));
|
|
66
|
+
assert.deepEqual(after.executionOutcome, VETOED_BY_POLICY);
|
|
67
|
+
assert.equal(typeof after.executionAtMs, "number");
|
|
68
|
+
assert.equal(Number.isFinite(after.executionAtMs), true);
|
|
69
|
+
assert.deepEqual(executionVerdict(after), { kind: "executed", gate: VETOED_BY_POLICY });
|
|
70
|
+
assert.deepEqual(after.resolvedOutcome, before.resolvedOutcome);
|
|
71
|
+
assert.equal(after.status, "resolved");
|
|
72
|
+
assert.equal(after.rev, before.rev);
|
|
73
|
+
});
|
|
74
|
+
run("recordExecutionOutcome is idempotent on the SAME record (structural, key-order-blind) → `already_recorded`; a DIFFERENT record throws checkpoint.execution_outcome_conflict and the row is byte-identical", async () => {
|
|
75
|
+
const store = make();
|
|
76
|
+
const cp = createCheckpointFixture();
|
|
77
|
+
await store.put(cp.token, cp);
|
|
78
|
+
await store.resolve(cp.token, cp.scope, ALLOW);
|
|
79
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "recorded");
|
|
80
|
+
const filed = (await store.get(cp.token));
|
|
81
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "already_recorded");
|
|
82
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW_REORDERED), "already_recorded", "equality is over the record's JSON — key order and freezing are not part of the value");
|
|
83
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, structuredClone(EXECUTED_ALLOW)), "already_recorded", "an unfrozen structural twin is the same record");
|
|
84
|
+
await assert.rejects(store.recordExecutionOutcome(cp.token, cp.scope, VETOED_BY_POLICY), (e) => e.code === "checkpoint.execution_outcome_conflict");
|
|
85
|
+
const still = (await store.get(cp.token));
|
|
86
|
+
assert.deepEqual(still.executionOutcome, filed.executionOutcome);
|
|
87
|
+
assert.equal(still.executionAtMs, filed.executionAtMs, "a conflict writes nothing — not even a fresh stamp");
|
|
88
|
+
});
|
|
89
|
+
run("recordExecutionOutcome answers `not_resolved` for a pending and for an expired row, `absent` for a missing row and for a wrong-scope row; none of them write", async () => {
|
|
90
|
+
const store = make();
|
|
91
|
+
const pending = createCheckpointFixture();
|
|
92
|
+
await store.put(pending.token, pending);
|
|
93
|
+
assert.equal(await store.recordExecutionOutcome(pending.token, pending.scope, EXECUTED_ALLOW), "not_resolved");
|
|
94
|
+
assert.equal((await store.get(pending.token)).executionOutcome, undefined);
|
|
95
|
+
assert.equal(await store.recordExecutionOutcome(pending.token, "tenant-b", EXECUTED_ALLOW), "absent", "a wrong-scope row reads exactly like a missing one (the isolation rule)");
|
|
96
|
+
assert.equal(await store.recordExecutionOutcome(mintCheckpointToken(), pending.scope, EXECUTED_ALLOW), "absent");
|
|
97
|
+
const expired = createCheckpointFixture();
|
|
98
|
+
await store.put(expired.token, expired);
|
|
99
|
+
assert.equal(await store.expire(expired.token, expired.scope), true);
|
|
100
|
+
assert.equal(await store.recordExecutionOutcome(expired.token, expired.scope, EXECUTED_ALLOW), "not_resolved");
|
|
101
|
+
assert.equal((await store.get(expired.token)).executionOutcome, undefined);
|
|
102
|
+
});
|
|
103
|
+
run("reopen is refused on a row that carries an execution record (a settled action is not re-openable); the same row reopens before the record lands", async () => {
|
|
104
|
+
const store = make();
|
|
105
|
+
const cp = createCheckpointFixture();
|
|
106
|
+
await store.put(cp.token, cp);
|
|
107
|
+
await store.resolve(cp.token, cp.scope, ALLOW);
|
|
108
|
+
assert.equal(await store.reopen(cp.token, cp.scope, "env_failed"), true);
|
|
109
|
+
assert.equal(await store.resolve(cp.token, cp.scope, ALLOW, { rev: 2 }), true);
|
|
110
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "recorded");
|
|
111
|
+
assert.equal(await store.reopen(cp.token, cp.scope, "env_failed"), false);
|
|
112
|
+
assert.equal(await store.reopen(cp.token, cp.scope, "tool_unavailable"), false, "no reason reopens a recorded row");
|
|
113
|
+
const got = (await store.get(cp.token));
|
|
114
|
+
assert.equal(got.status, "resolved");
|
|
115
|
+
assert.equal(got.rev, 3, "a refused reopen bumps nothing");
|
|
116
|
+
assert.equal(got.reopenReason, undefined);
|
|
117
|
+
assert.deepEqual(got.executionOutcome, EXECUTED_ALLOW);
|
|
118
|
+
});
|
|
119
|
+
run("a row filed WITH an execution record (a restored resolved row) keeps it authoritative: get() returns it, reopen is refused, a different record conflicts, the same one is already_recorded", async () => {
|
|
120
|
+
const store = make();
|
|
121
|
+
const cp = createCheckpointFixture({ status: "resolved", rev: 1, executionOutcome: EXECUTED_ALLOW, executionAtMs: 1_700_000_001_000 });
|
|
122
|
+
await store.put(cp.token, cp);
|
|
123
|
+
const back = (await store.get(cp.token));
|
|
124
|
+
assert.deepEqual(back.executionOutcome, EXECUTED_ALLOW);
|
|
125
|
+
assert.equal(back.executionAtMs, 1_700_000_001_000);
|
|
126
|
+
assert.equal(await store.reopen(cp.token, cp.scope, "env_failed"), false, "a restored recorded row is as settled as a live one");
|
|
127
|
+
await assert.rejects(store.recordExecutionOutcome(cp.token, cp.scope, VETOED_BY_POLICY), (e) => e.code === "checkpoint.execution_outcome_conflict");
|
|
128
|
+
assert.equal(await store.recordExecutionOutcome(cp.token, cp.scope, EXECUTED_ALLOW), "already_recorded");
|
|
129
|
+
assert.equal((await store.get(cp.token)).status, "resolved");
|
|
130
|
+
});
|
|
46
131
|
run("put create-once → already_exists", async () => {
|
|
47
132
|
const store = make();
|
|
48
133
|
const cp = createCheckpointFixture();
|
|
@@ -216,6 +301,29 @@ export async function checkpointStoreContract(make, runAssertion) {
|
|
|
216
301
|
const stored = (await store.get(bitless.token)).pendingAction;
|
|
217
302
|
assert.equal("hasBidiControls" in stored, false, "projection backfill must not write back to the row");
|
|
218
303
|
});
|
|
304
|
+
run("design/388 B18 × B6 identity + face group: a row's seven optional members (capabilityId/contractId/shapeDigest/implementationRevision/rosterDigest + family/pathTarget) survive the round-trip verbatim; an absent group stays absent", async () => {
|
|
305
|
+
const store = make();
|
|
306
|
+
const carried = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-carried" });
|
|
307
|
+
const group = {
|
|
308
|
+
capabilityId: "caller::Write@acme.write@2",
|
|
309
|
+
contractId: "acme.write@2",
|
|
310
|
+
shapeDigest: "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
|
311
|
+
implementationRevision: "r7",
|
|
312
|
+
rosterDigest: "sha256:fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210",
|
|
313
|
+
family: "file-write",
|
|
314
|
+
pathTarget: { param: "destination", aliases: ["path"], access: "create", skillScopeEligible: true },
|
|
315
|
+
};
|
|
316
|
+
Object.assign(carried.pendingAction, group);
|
|
317
|
+
const bare = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "id-bare" });
|
|
318
|
+
for (const cp of [carried, bare])
|
|
319
|
+
await store.put(cp.token, cp);
|
|
320
|
+
const back = (await store.get(carried.token)).pendingAction;
|
|
321
|
+
for (const [k, v] of Object.entries(group))
|
|
322
|
+
assert.deepEqual(back[k], v, `the row's ${k} must survive the round-trip verbatim (a whitelisting serializer that drops it makes every resume-edit recheck judge by the catalog's slot for the NAME)`);
|
|
323
|
+
const bareBack = (await store.get(bare.token)).pendingAction;
|
|
324
|
+
for (const k of Object.keys(group))
|
|
325
|
+
assert.equal(k in bareBack, false, `an absent ${k} must stay OMITTED (a pre-388 row reads name-only, never a partial face)`);
|
|
326
|
+
});
|
|
219
327
|
run("#457 previewWithheld three-form matrix: a row's spelling survives + projects; absent stays absent; an out-of-contract spelling reads as absent", async () => {
|
|
220
328
|
const store = make();
|
|
221
329
|
const oversize = createCheckpointFixture({ token: mintCheckpointToken(), sessionId: "pw-oversize" });
|
|
@@ -885,6 +885,16 @@ export type TaskEvent = ({
|
|
|
885
885
|
*/
|
|
886
886
|
type: "wiring_manifest";
|
|
887
887
|
manifest: import("./wiring-manifest.js").WiringManifest;
|
|
888
|
+
} & TaskEventIdentity) | ({
|
|
889
|
+
/**
|
|
890
|
+
* design/388 §2.5 — a RUN-TIME change to this leg's mounted tool set (today: the RefreshMcpTools
|
|
891
|
+
* seam re-inserting a server's tools in place). Carries the WHOLE post-change roster (positions
|
|
892
|
+
* come from it; a consumer replaces its copy) plus a summary usable only when the consumer holds
|
|
893
|
+
* `fromDigest` — otherwise it adopts the snapshot and records a skew (never rejects). The prepared
|
|
894
|
+
* roster itself rides `wiring_manifest.manifest.tools`; this frame never precedes that one.
|
|
895
|
+
*/
|
|
896
|
+
type: "tool_roster_delta";
|
|
897
|
+
delta: import("./tool-roster.js").ToolRosterDelta;
|
|
888
898
|
} & TaskEventIdentity) | {
|
|
889
899
|
type: "done";
|
|
890
900
|
result: TaskResult;
|
|
@@ -9,8 +9,9 @@ import { registerMonitorLane, pollMonitorLane, stopMonitorLane } from "./task-re
|
|
|
9
9
|
import { registerWorkflowLane, pollWorkflowLane, stopWorkflowLane } from "./task-registry-workflow.js";
|
|
10
10
|
import { mintCompletionId, canAccessWorkflowRun, formatWorkflowRun, clipTaskOutput, assertOwnership, sleepPollStep, statusFromBackground, rollSpoolText, accountDroppedBytes, renderSpoolBody, spoolDropNote, droppedGapNote, alreadyTerminalStopNote, terminalTaskSummary, TASK_OUTPUT_MAX_CHARS, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE, canAccess, DURABLE_AGENT_HANDLE_RE, } from "./task-registry-shared.js";
|
|
11
11
|
export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR } from "./task-registry-shared.js";
|
|
12
|
-
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME,
|
|
12
|
+
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, TASK_OUTPUT_MISSING_ID_MESSAGE, TASK_STOP_MISSING_ID_MESSAGE, TASK_STOP_PARAMS, resolveTaskIdArg, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
|
|
13
13
|
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, recordBackgroundAgentOrgAdmissionLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, backgroundAgentCycleSeqLane, activeDelegationHandlesLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
14
|
+
import { toolFace } from "./tool-catalog-entries.js";
|
|
14
15
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
15
16
|
const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
|
|
16
17
|
const BLOCK_MAX_TIMEOUT_MS = 600_000;
|
|
@@ -1234,10 +1235,9 @@ export function createTaskOutputTool(opts) {
|
|
|
1234
1235
|
const caps = { ...REGISTRY_TASK_TOOL_CAPS, notification: opts.notificationWired !== false };
|
|
1235
1236
|
return defineTool({
|
|
1236
1237
|
name: TASK_OUTPUT_TOOL_NAME,
|
|
1237
|
-
|
|
1238
|
+
...toolFace("task-output"),
|
|
1238
1239
|
description: composeTaskOutputDescription(caps),
|
|
1239
1240
|
parameters: composeTaskOutputParams(caps),
|
|
1240
|
-
effect: "read",
|
|
1241
1241
|
execute: async (rawArgs, ctx) => {
|
|
1242
1242
|
const args = rawArgs;
|
|
1243
1243
|
const id = resolveTaskIdArg(args.task_id);
|
|
@@ -1283,10 +1283,9 @@ export function createTaskOutputTool(opts) {
|
|
|
1283
1283
|
export function createTaskStopTool(opts) {
|
|
1284
1284
|
return defineTool({
|
|
1285
1285
|
name: TASK_STOP_TOOL_NAME,
|
|
1286
|
-
|
|
1286
|
+
...toolFace("task-stop"),
|
|
1287
1287
|
description: composeTaskStopDescription(REGISTRY_TASK_TOOL_CAPS),
|
|
1288
1288
|
parameters: TASK_STOP_PARAMS,
|
|
1289
|
-
effect: "write",
|
|
1290
1289
|
execute: async (rawArgs, ctx) => {
|
|
1291
1290
|
const args = rawArgs;
|
|
1292
1291
|
const id = resolveTaskIdArg(args.task_id, args.shell_id);
|
package/dist/core/task-spec.d.ts
CHANGED
|
@@ -387,6 +387,14 @@ export interface TaskSpec {
|
|
|
387
387
|
* owns that risk, same trust level as assembling `spec.tools` itself.
|
|
388
388
|
*/
|
|
389
389
|
excludeTools?: string[];
|
|
390
|
+
/**
|
|
391
|
+
* design/388 §2.2 ⑥ — unmount the WHOLE tool face: the leg mounts nothing (no hands, no
|
|
392
|
+
* scaffolds, no MCP or skill tools; the effect roster reads `entries: []` and the model request
|
|
393
|
+
* carries `tools: []`). The utility-task shape a shell used to express with a hand-kept exclusion
|
|
394
|
+
* list. Not spelled `excludeTools: ["*"]` on purpose — `"*"` is a legal caller tool name (only `__`
|
|
395
|
+
* is reserved). `excludeTools` beside it is redundant, not an error.
|
|
396
|
+
*/
|
|
397
|
+
excludeAllTools?: true;
|
|
390
398
|
/**
|
|
391
399
|
* Per-request DEFERRED disclosure for ALREADY-MOUNTED tools (built-ins
|
|
392
400
|
* included — `ToolSpec.defer` only covers caller specs): wire names listed here ship as
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The STATIC TOOL CATALOG (design/388 §2.1 "静态目录"): one row per built-in / synthetic tool DEFINITION
|
|
3
|
+
* core owns — its wire name, the conditions core (or a host scenario factory) mounts it under, the
|
|
4
|
+
* `details.type` cards it mints, and its declared FACE (family / axes / contract / path target / rule
|
|
5
|
+
* vocabulary / render hints / policy bits).
|
|
6
|
+
*
|
|
7
|
+
* SINGLE SOURCE, TWO READERS. Each definition module spreads its row's `face` into the `ToolSpec` it
|
|
8
|
+
* hands `defineTool` (`...TOOL_FACES.read`), so the face a tool declares at mount time IS the face
|
|
9
|
+
* listed here — there is no second declaration to drift. The effect roster (tool-roster.ts) reads every
|
|
10
|
+
* axis off the MOUNTED INSTANCE; the static catalog (`describeToolCatalog()`) reads this table without
|
|
11
|
+
* an env or a deps object. The conformance gate (test/tool-catalog-conformance.test.ts) proves the two
|
|
12
|
+
* agree for every definition it can instantiate; the catalog completeness gate
|
|
13
|
+
* (scripts/verify-tool-catalog.mjs) proves every `defineTool` site in src/ that mints a core-owned name
|
|
14
|
+
* has a row here that declares a `family` — a missing declaration is a RED gate naming the file, never a
|
|
15
|
+
* silent `"other"`.
|
|
16
|
+
*
|
|
17
|
+
* LEAF MODULE by design: it imports vocabulary types and one leaf constant module only. Every definition
|
|
18
|
+
* module and every policy module that derives a name set from the catalog imports THIS file, so a
|
|
19
|
+
* dependency from here back into a tool module would be a load-order cycle with a `const` table in it.
|
|
20
|
+
*
|
|
21
|
+
* Keyed by DEFINITION identity (`id`), not by name — the full and the read-only shell both answer to
|
|
22
|
+
* `Bash`; a catalog predicate counts a name as a member when ANY row under that name matches.
|
|
23
|
+
*/
|
|
24
|
+
import type { ToolCatalogEntry, ToolFaceDeclaration } from "./tool-face.js";
|
|
25
|
+
/** The full shell's contract id — declared here (the leaf) and re-exported by fs-bash.ts. */
|
|
26
|
+
export declare const FULL_SHELL_CONTRACT_ID = "core.bash@1";
|
|
27
|
+
export declare const READONLY_SHELL_CONTRACT_ID = "core.bash_readonly@1";
|
|
28
|
+
/** Wire-name constants for definitions that had none (design/388 A-21: literals become references). */
|
|
29
|
+
export declare const TASK_CREATE_TOOL_NAME = "TaskCreate";
|
|
30
|
+
export declare const TASK_GET_TOOL_NAME = "TaskGet";
|
|
31
|
+
export declare const TASK_UPDATE_TOOL_NAME = "TaskUpdate";
|
|
32
|
+
export declare const TASK_LIST_TOOL_NAME = "TaskList";
|
|
33
|
+
export declare const TODO_WRITE_TOOL_NAME = "TodoWrite";
|
|
34
|
+
export declare const REFRESH_MCP_TOOLS_TOOL_NAME = "RefreshMcpTools";
|
|
35
|
+
/**
|
|
36
|
+
* Every row. Order is documentation order (hands → delegation → scaffolds → memory → scheduler → host
|
|
37
|
+
* scenarios); nothing reads the order.
|
|
38
|
+
*/
|
|
39
|
+
export declare const TOOL_CATALOG_ENTRIES: readonly ToolCatalogEntry[];
|
|
40
|
+
/**
|
|
41
|
+
* `details.type` cards the ENGINE mints outside any tool definition — the protocol channels' result
|
|
42
|
+
* cards and the fs boundary refusals (minted by the shared safety module on behalf of every fs tool).
|
|
43
|
+
*/
|
|
44
|
+
export declare const ENGINE_CARDS: Readonly<Record<string, string>>;
|
|
45
|
+
/** The declared face of one definition, by definition id — what a definition module spreads into its spec. */
|
|
46
|
+
export declare function toolFace(id: string): ToolFaceDeclaration;
|
|
47
|
+
/** Definition-id → face, for the modules that spread one (`...TOOL_FACES.read`). Frozen: a face is data. */
|
|
48
|
+
export declare const TOOL_FACES: Readonly<Record<string, ToolFaceDeclaration>>;
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT } from "./task-tool-shape.js";
|
|
2
|
+
export const FULL_SHELL_CONTRACT_ID = "core.bash@1";
|
|
3
|
+
export const READONLY_SHELL_CONTRACT_ID = "core.bash_readonly@1";
|
|
4
|
+
export const TASK_CREATE_TOOL_NAME = "TaskCreate";
|
|
5
|
+
export const TASK_GET_TOOL_NAME = "TaskGet";
|
|
6
|
+
export const TASK_UPDATE_TOOL_NAME = "TaskUpdate";
|
|
7
|
+
export const TASK_LIST_TOOL_NAME = "TaskList";
|
|
8
|
+
export const TODO_WRITE_TOOL_NAME = "TodoWrite";
|
|
9
|
+
export const REFRESH_MCP_TOOLS_TOOL_NAME = "RefreshMcpTools";
|
|
10
|
+
const SHELL_RULE_FACE = { primaryParams: ["command"], params: ["command", "timeout", "description", "run_in_background"] };
|
|
11
|
+
const c = (contractId, implementationRevision = "1") => ({ contractId, implementationRevision });
|
|
12
|
+
export const TOOL_CATALOG_ENTRIES = [
|
|
13
|
+
{
|
|
14
|
+
id: "read", name: "Read", source: "builtin", definedIn: "src/tools/fs/fs-read.ts", mountedBy: ["hands"],
|
|
15
|
+
cards: ["text", "notebook", "file_unchanged", "image", "document"],
|
|
16
|
+
face: {
|
|
17
|
+
family: "file-read", effect: "read", contract: c("core.read@1"),
|
|
18
|
+
pathTarget: { param: "file_path", aliases: ["path"], access: "read" },
|
|
19
|
+
ruleFace: { params: ["file_path", "path", "offset", "limit", "pages"] },
|
|
20
|
+
renderHints: { summaryParams: ["file_path"] },
|
|
21
|
+
offload: false, compactable: true, budgetExempt: true,
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "edit", name: "Edit", source: "builtin", definedIn: "src/tools/fs/fs-write.ts", mountedBy: ["hands"],
|
|
26
|
+
cards: ["edit"],
|
|
27
|
+
face: {
|
|
28
|
+
family: "file-write", effect: "write", contract: c("core.edit@1"),
|
|
29
|
+
pathTarget: { param: "file_path", aliases: ["path"], access: "edit", skillScopeEligible: true },
|
|
30
|
+
ruleFace: { params: ["file_path", "path", "old_string", "new_string", "replace_all"] },
|
|
31
|
+
renderHints: { summaryParams: ["file_path"], approvalCard: "file-edit" },
|
|
32
|
+
compactable: true,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: "write", name: "Write", source: "builtin", definedIn: "src/tools/fs/fs-write.ts", mountedBy: ["hands"],
|
|
37
|
+
cards: ["create", "update"],
|
|
38
|
+
face: {
|
|
39
|
+
family: "file-write", effect: "write", contract: c("core.write@1"),
|
|
40
|
+
pathTarget: { param: "file_path", aliases: ["path"], access: "create", skillScopeEligible: true },
|
|
41
|
+
ruleFace: { params: ["file_path", "path", "content"] },
|
|
42
|
+
renderHints: { summaryParams: ["file_path"], approvalCard: "file-write" },
|
|
43
|
+
offload: false, compactable: true,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "notebook-edit", name: "NotebookEdit", source: "builtin", definedIn: "src/tools/fs/fs-write.ts", mountedBy: ["hands"],
|
|
48
|
+
cards: ["notebook-edit"],
|
|
49
|
+
face: {
|
|
50
|
+
family: "file-write", effect: "write", contract: c("core.notebook_edit@1"),
|
|
51
|
+
pathTarget: { param: "notebook_path", aliases: ["path"], access: "edit" },
|
|
52
|
+
ruleFace: { params: ["notebook_path", "path", "cell_id", "cell_type", "edit_mode", "new_source"] },
|
|
53
|
+
renderHints: { userFacingName: "Edit Notebook", summaryParams: ["notebook_path"], approvalCard: "notebook-edit" },
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "bash", name: "Bash", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"],
|
|
58
|
+
cards: ["bash", "bash_invalid_timeout"],
|
|
59
|
+
face: {
|
|
60
|
+
family: "shell", effect: "write", contentOrigin: "execution", contract: c(FULL_SHELL_CONTRACT_ID),
|
|
61
|
+
ruleFace: SHELL_RULE_FACE,
|
|
62
|
+
renderHints: { summaryParams: ["command"], approvalCard: "shell" },
|
|
63
|
+
offloadThresholdChars: 30_000, compactable: true,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "bash-readonly", name: "Bash", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"],
|
|
68
|
+
cards: ["bash", "readonly_out_of_root", "bash_invalid_timeout"],
|
|
69
|
+
face: {
|
|
70
|
+
family: "shell", effect: "read", contract: c(READONLY_SHELL_CONTRACT_ID),
|
|
71
|
+
ruleFace: SHELL_RULE_FACE,
|
|
72
|
+
renderHints: { summaryParams: ["command"], approvalCard: "shell" },
|
|
73
|
+
offloadThresholdChars: 30_000, compactable: true,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "grep", name: "Grep", source: "builtin", definedIn: "src/tools/fs/fs-search-tools.ts", mountedBy: ["hands"],
|
|
78
|
+
cards: ["grep"],
|
|
79
|
+
face: {
|
|
80
|
+
family: "search", effect: "read", contract: c("core.grep@1"),
|
|
81
|
+
pathTarget: { param: "path", access: "read" },
|
|
82
|
+
ruleFace: { params: ["pattern", "path", "glob", "type", "output_mode", "head_limit", "offset", "multiline", "context", "ignore_case", "-i", "-n", "-o", "-A", "-B", "-C"] },
|
|
83
|
+
renderHints: { userFacingName: "Search", summaryParams: ["pattern", "path"] },
|
|
84
|
+
offloadThresholdChars: 20_000, compactable: true,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "glob", name: "Glob", source: "builtin", definedIn: "src/tools/fs/fs-search-tools.ts", mountedBy: ["hands"],
|
|
89
|
+
cards: ["glob"],
|
|
90
|
+
face: {
|
|
91
|
+
family: "search", effect: "read", contract: c("core.glob@1"),
|
|
92
|
+
pathTarget: { param: "path", access: "read" },
|
|
93
|
+
ruleFace: { params: ["pattern", "path", "max_results"] },
|
|
94
|
+
renderHints: { userFacingName: "Search", summaryParams: ["pattern", "path"] },
|
|
95
|
+
compactable: true,
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{ id: "repo-map", name: "RepoMap", source: "builtin", definedIn: "src/tools/fs/repo-map.ts", mountedBy: ["hands"], cards: ["repo-map"], face: { family: "search", effect: "read", contract: c("core.repo_map@1") } },
|
|
99
|
+
{ id: "lsp", name: "LSP", source: "builtin", definedIn: "src/core/lsp.ts", mountedBy: ["optional"], cards: [], face: { family: "search", effect: "read", contract: c("core.lsp@1") } },
|
|
100
|
+
{ id: "enter-worktree", name: "EnterWorktree", source: "builtin", definedIn: "src/tools/worktree.ts", mountedBy: ["hands"], cards: ["worktree"], face: { family: "other", effect: "write", contract: c("core.enter_worktree@1") } },
|
|
101
|
+
{ id: "exit-worktree", name: "ExitWorktree", source: "builtin", definedIn: "src/tools/worktree.ts", mountedBy: ["hands"], cards: ["worktree"], face: { family: "other", effect: "write", contract: c("core.exit_worktree@1") } },
|
|
102
|
+
{ id: "task-output-hands", name: "TaskOutput", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
|
|
103
|
+
{ id: "task-stop-hands", name: "TaskStop", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
104
|
+
{ id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
|
|
105
|
+
{ id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
106
|
+
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), offloadThresholdChars: 10_000 } },
|
|
107
|
+
{ id: "send-message", name: "SendMessage", source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
|
|
108
|
+
{ id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", effect: "read", contract: c("core.agent_transcript@1") } },
|
|
109
|
+
{ id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["peerLane"], cards: ["list-agents"], face: { family: "protocol", effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
|
|
110
|
+
{ id: "workflow", name: "Workflow", source: "builtin", definedIn: "src/orchestration/run-workflow-tool.ts", mountedBy: ["workflow"], cards: ["workflow-run"], face: { family: "delegate", effect: "write", contract: c("core.workflow@1") } },
|
|
111
|
+
{ id: "report-blocked", name: "ReportBlocked", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.report_blocked@1") } },
|
|
112
|
+
{ id: "report-findings", name: "ReportFindings", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["always"], cards: ["report-findings"], face: { family: "scaffold", effect: "read", contract: c("core.report_findings@1") } },
|
|
113
|
+
{ id: "structured-output", name: "StructuredOutput", source: "synthetic", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.structured_output@1") } },
|
|
114
|
+
{ id: "skill", name: "Skill", source: "builtin", definedIn: "src/core/runner/synthetic-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.skill@1") } },
|
|
115
|
+
{ id: "read-tool-result", name: "ReadToolResult", source: "builtin", definedIn: "src/core/tool-result-store.ts", mountedBy: ["optional"], cards: [], face: { family: "scaffold", effect: "read", contract: c("core.read_tool_result@1"), budgetExempt: true } },
|
|
116
|
+
{ id: "tool-search", name: "ToolSearch", source: "builtin", definedIn: "src/core/runner/tool-disclosure.ts", mountedBy: ["optional"], cards: ["tool-search"], face: { family: "scaffold", effect: "read", contract: c("core.tool_search@1") } },
|
|
117
|
+
{ id: "ask-user-question", name: "AskUserQuestion", source: "builtin", definedIn: "src/core/ask-question.ts", mountedBy: ["optional"], cards: ["ask-question"], face: { family: "other", effect: "read", contract: c("core.ask_user_question@1"), renderHints: { approvalCard: "question" } } },
|
|
118
|
+
{ id: "exit-plan-mode", name: "ExitPlanMode", source: "builtin", definedIn: "src/core/present-plan-tool.ts", mountedBy: ["optional"], cards: ["exit-plan-mode"], face: { family: "plan", effect: "read", contract: c("core.exit_plan_mode@1"), renderHints: { userFacingName: "", approvalCard: "plan" } } },
|
|
119
|
+
{ id: "enter-plan-mode", name: "EnterPlanMode", source: "builtin", definedIn: "src/core/present-plan-tool.ts", mountedBy: ["optional"], cards: ["enter-plan-mode"], face: { family: "plan", effect: "read", contract: c("core.enter_plan_mode@1") } },
|
|
120
|
+
{ id: "refresh-mcp-tools", name: REFRESH_MCP_TOOLS_TOOL_NAME, source: "builtin", definedIn: "src/core/runner/prepare-protocol-tools.ts", mountedBy: ["optional"], cards: [], face: { family: "protocol", effect: "read" } },
|
|
121
|
+
{ id: "memory-search", name: "memory_search", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_search@1", "6"), offload: false } },
|
|
122
|
+
{ id: "memory-get", name: "memory_get", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_get@1", "6"), offload: false } },
|
|
123
|
+
{ id: "memory-index", name: "memory_index", source: "builtin", definedIn: "src/core/memory-engine/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contentOrigin: "local", contract: c("core.memory_index@1"), offload: false } },
|
|
124
|
+
{ id: "memory-list", name: "memory_list", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contract: c("core.memory_list@1"), offload: false } },
|
|
125
|
+
{ id: "memory-read", name: "memory_read", source: "builtin", definedIn: "src/core/shared-memory/tools.ts", mountedBy: ["memory"], cards: [], face: { family: "memory", effect: "read", contract: c("core.memory_read@1"), offload: false } },
|
|
126
|
+
{ id: "cron-create", name: "CronCreate", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-create"], face: { family: "other", effect: "write", contract: c("core.cron_create@1") } },
|
|
127
|
+
{ id: "cron-delete", name: "CronDelete", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-delete"], face: { family: "other", effect: "idempotent", contract: c("core.cron_delete@1") } },
|
|
128
|
+
{ id: "cron-list", name: "CronList", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["cron-list"], face: { family: "other", effect: "read", contract: c("core.cron_list@1") } },
|
|
129
|
+
{ id: "schedule-wakeup", name: "ScheduleWakeup", source: "builtin", definedIn: "src/tools/scheduler-tools.ts", mountedBy: ["scheduler"], cards: ["schedule-wakeup"], face: { family: "other", effect: "write", contract: c("core.schedule_wakeup@1") } },
|
|
130
|
+
{
|
|
131
|
+
id: "web-fetch", name: "WebFetch", source: "builtin", definedIn: "src/tools/web.ts", mountedBy: ["scenario"], cards: ["web-fetch"],
|
|
132
|
+
face: { family: "web", effect: "read", contentOrigin: "external", contract: c("core.web_fetch@1"), ruleFace: { primaryParams: ["url"] }, renderHints: { summaryParams: ["url"], approvalCard: "fetch" }, compactable: true },
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
id: "web-search", name: "WebSearch", source: "builtin", definedIn: "src/tools/web.ts", mountedBy: ["scenario"], cards: ["web-search"],
|
|
136
|
+
face: { family: "web", effect: "read", contentOrigin: "external", contract: c("core.web_search@1"), ruleFace: { primaryParams: ["query"] }, renderHints: { summaryParams: ["query"] }, compactable: true },
|
|
137
|
+
},
|
|
138
|
+
{ id: "todo-write", name: TODO_WRITE_TOOL_NAME, source: "builtin", definedIn: "src/tools/todo.ts", mountedBy: ["scenario"], cards: ["todo"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.todo_write@1") } },
|
|
139
|
+
{ id: "task-create", name: TASK_CREATE_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "write", contract: c("core.task_create@1") } },
|
|
140
|
+
{ id: "task-get", name: TASK_GET_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_get@1") } },
|
|
141
|
+
{ id: "task-update", name: TASK_UPDATE_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task"], modelGate: "task-scaffold", face: { family: "plan", effect: "idempotent", contract: c("core.task_update@1") } },
|
|
142
|
+
{ id: "task-list", name: TASK_LIST_TOOL_NAME, source: "builtin", definedIn: "src/tools/task-list.ts", mountedBy: ["scenario"], cards: ["task-list"], modelGate: "task-scaffold", face: { family: "plan", effect: "read", contract: c("core.task_list@1") } },
|
|
143
|
+
{ id: "agent", name: "Agent", source: "builtin", definedIn: "src/agents/subagent.ts", mountedBy: ["scenario"], cards: ["agent", "fork"], face: { family: "delegate", contentOrigin: "local", contract: c("core.agent@1") } },
|
|
144
|
+
{ id: "observer-report", name: "ObserverReport", source: "builtin", definedIn: "src/agents/observer.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "write", contract: c("core.observer_report@1") } },
|
|
145
|
+
{ id: "declare-done", name: "DeclareDone", source: "builtin", definedIn: "src/orchestration/goal.ts", mountedBy: ["scenario"], cards: [], face: { family: "scaffold", effect: "read" } },
|
|
146
|
+
];
|
|
147
|
+
export const ENGINE_CARDS = Object.freeze({
|
|
148
|
+
mcp: "src/core/mcp.ts",
|
|
149
|
+
a2a: "src/core/a2a.ts",
|
|
150
|
+
path_not_in_root: "src/tools/fs/safety.ts",
|
|
151
|
+
read_path_denied: "src/tools/fs/safety.ts",
|
|
152
|
+
});
|
|
153
|
+
const byId = new Map();
|
|
154
|
+
for (const e of TOOL_CATALOG_ENTRIES) {
|
|
155
|
+
if (byId.has(e.id))
|
|
156
|
+
throw new Error(`tool catalog: duplicate definition id ${JSON.stringify(e.id)}`);
|
|
157
|
+
byId.set(e.id, e);
|
|
158
|
+
}
|
|
159
|
+
export function toolFace(id) {
|
|
160
|
+
const e = byId.get(id);
|
|
161
|
+
if (e === undefined)
|
|
162
|
+
throw new Error(`tool catalog: no definition ${JSON.stringify(id)}`);
|
|
163
|
+
return structuredClone(e.face);
|
|
164
|
+
}
|
|
165
|
+
function deepFreeze(value) {
|
|
166
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
167
|
+
Object.freeze(value);
|
|
168
|
+
for (const k of Object.keys(value))
|
|
169
|
+
deepFreeze(value[k]);
|
|
170
|
+
}
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
deepFreeze(TOOL_CATALOG_ENTRIES);
|
|
174
|
+
export const TOOL_FACES = Object.freeze(Object.fromEntries(TOOL_CATALOG_ENTRIES.map((e) => [e.id, e.face])));
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ToolMountTag } from "./tool-face.js";
|
|
2
|
+
export interface ToolConformanceVector {
|
|
3
|
+
id: string;
|
|
4
|
+
/** The mount conditions the vector activates. */
|
|
5
|
+
active: readonly ToolMountTag[];
|
|
6
|
+
/** Predicted names the vector cannot mount, each with the seat it lacks. */
|
|
7
|
+
absent: ReadonlyArray<{
|
|
8
|
+
name: string;
|
|
9
|
+
why: string;
|
|
10
|
+
}>;
|
|
11
|
+
/** Mounted names no tag spells, each with the seat that mounts them. */
|
|
12
|
+
extra: ReadonlyArray<{
|
|
13
|
+
name: string;
|
|
14
|
+
why: string;
|
|
15
|
+
}>;
|
|
16
|
+
/** The vector's expected core-mounted name set: predict(active) − absent + extra. */
|
|
17
|
+
expected: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
/** The matrix. Vector ids are stable (a host's own conformance test keys on them). */
|
|
20
|
+
export declare const TOOL_CONFORMANCE_VECTORS: readonly ToolConformanceVector[];
|
|
21
|
+
/** The matrix as a host reads it (a frozen copy). */
|
|
22
|
+
export declare function describeToolConformanceVectors(): readonly ToolConformanceVector[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { predictMountedToolNames } from "./tool-registry.js";
|
|
2
|
+
const vector = (id, active, absent = [], extra = []) => {
|
|
3
|
+
const predicted = new Set(predictMountedToolNames(new Set(active)));
|
|
4
|
+
for (const a of absent)
|
|
5
|
+
predicted.delete(a.name);
|
|
6
|
+
for (const e of extra)
|
|
7
|
+
predicted.add(e.name);
|
|
8
|
+
return { id, active, absent, extra, expected: [...predicted].sort() };
|
|
9
|
+
};
|
|
10
|
+
const OFFLOAD_READER = { name: "ReadToolResult", why: "the offload store is wired by default (the deployment threshold), an `optional` seat every vector carries" };
|
|
11
|
+
export const TOOL_CONFORMANCE_VECTORS = [
|
|
12
|
+
vector("bare", ["always"], [], [OFFLOAD_READER]),
|
|
13
|
+
vector("hands-full", ["always", "hands", "backgroundTasks", "delegation"], [
|
|
14
|
+
{ name: "SendMessage", why: "needs the runner self seat (a direct prepare has none)" },
|
|
15
|
+
{ name: "AgentTranscript", why: "needs the runner self seat (a direct prepare has none)" },
|
|
16
|
+
], [OFFLOAD_READER]),
|
|
17
|
+
vector("hands-readonly", ["always", "hands"], [
|
|
18
|
+
{ name: "Edit", why: "the read-only band mounts the read half only" },
|
|
19
|
+
{ name: "Write", why: "the read-only band mounts the read half only" },
|
|
20
|
+
{ name: "NotebookEdit", why: "the read-only band mounts the read half only" },
|
|
21
|
+
{ name: "TaskOutput", why: "the background-shell pair mounts on the full shell only" },
|
|
22
|
+
{ name: "TaskStop", why: "the background-shell pair mounts on the full shell only" },
|
|
23
|
+
{ name: "EnterWorktree", why: "the worktree pair needs the tracked cwd the full shell owns" },
|
|
24
|
+
{ name: "ExitWorktree", why: "the worktree pair needs the tracked cwd the full shell owns" },
|
|
25
|
+
], [OFFLOAD_READER]),
|
|
26
|
+
vector("plan-mode", ["always", "optional"], [
|
|
27
|
+
{ name: "LSP", why: "needs a wired LSP manager" },
|
|
28
|
+
{ name: "Skill", why: "needs a skills listing" },
|
|
29
|
+
{ name: "StructuredOutput", why: "needs an output schema" },
|
|
30
|
+
{ name: "ToolSearch", why: "needs a deferred tool" },
|
|
31
|
+
{ name: "RefreshMcpTools", why: "needs a declared MCP server" },
|
|
32
|
+
{ name: "EnterPlanMode", why: "needs a checkpoint store beside plan mode" },
|
|
33
|
+
]),
|
|
34
|
+
];
|
|
35
|
+
export function describeToolConformanceVectors() {
|
|
36
|
+
return TOOL_CONFORMANCE_VECTORS;
|
|
37
|
+
}
|