@sema-agent/core 7.7.0 → 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.
Files changed (135) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agents/agent-transcript-tool.js +2 -2
  3. package/dist/agents/list-agents-tool.js +2 -3
  4. package/dist/agents/observer.js +2 -2
  5. package/dist/agents/send-message-tool.js +2 -1
  6. package/dist/agents/subagent-steps.js +5 -4
  7. package/dist/agents/subagent.js +2 -3
  8. package/dist/core/ask-question.js +2 -2
  9. package/dist/core/checkpoint-store.d.ts +38 -4
  10. package/dist/core/context-edit.d.ts +0 -22
  11. package/dist/core/context-edit.js +2 -11
  12. package/dist/core/engine-notice.d.ts +6 -0
  13. package/dist/core/fs-write-gate-policy.d.ts +7 -1
  14. package/dist/core/fs-write-gate-policy.js +7 -7
  15. package/dist/core/gate-fold.js +2 -2
  16. package/dist/core/gate-lanes.js +4 -3
  17. package/dist/core/governance-codes.d.ts +1 -1
  18. package/dist/core/governance-codes.js +6 -0
  19. package/dist/core/hooks.d.ts +8 -2
  20. package/dist/core/hooks.js +1 -1
  21. package/dist/core/lsp.js +2 -2
  22. package/dist/core/mcp-server-spec.d.ts +17 -0
  23. package/dist/core/mcp.js +3 -1
  24. package/dist/core/memory-engine/tools.js +4 -12
  25. package/dist/core/permission-rules.d.ts +32 -2
  26. package/dist/core/permission-rules.js +43 -37
  27. package/dist/core/present-plan-tool.js +3 -4
  28. package/dist/core/runner/active-skill-scope.js +7 -4
  29. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  30. package/dist/core/runner/contracts.d.ts +27 -3
  31. package/dist/core/runner/prepare-ask-lane.d.ts +1 -1
  32. package/dist/core/runner/prepare-caps-and-workflow.d.ts +5 -4
  33. package/dist/core/runner/prepare-caps-and-workflow.js +16 -11
  34. package/dist/core/runner/prepare-defer-classify.d.ts +5 -26
  35. package/dist/core/runner/prepare-defer-classify.js +10 -10
  36. package/dist/core/runner/prepare-delegation-surface.d.ts +5 -4
  37. package/dist/core/runner/prepare-delegation-surface.js +15 -17
  38. package/dist/core/runner/prepare-gate-stations.d.ts +4 -1
  39. package/dist/core/runner/prepare-gate-stations.js +3 -1
  40. package/dist/core/runner/prepare-hands-readface.d.ts +3 -2
  41. package/dist/core/runner/prepare-hands-readface.js +3 -2
  42. package/dist/core/runner/prepare-inherited-gate.d.ts +1 -1
  43. package/dist/core/runner/prepare-inherited-gate.js +1 -0
  44. package/dist/core/runner/prepare-lsp.d.ts +4 -3
  45. package/dist/core/runner/prepare-lsp.js +3 -2
  46. package/dist/core/runner/prepare-memory-engine-session.d.ts +4 -4
  47. package/dist/core/runner/prepare-memory-engine-session.js +4 -2
  48. package/dist/core/runner/prepare-offload-wrappers.js +7 -5
  49. package/dist/core/runner/prepare-park-ask.d.ts +4 -0
  50. package/dist/core/runner/prepare-park-ask.js +4 -1
  51. package/dist/core/runner/prepare-project-context.d.ts +5 -23
  52. package/dist/core/runner/prepare-project-context.js +7 -5
  53. package/dist/core/runner/prepare-prompt-assembly.d.ts +1 -1
  54. package/dist/core/runner/prepare-protocol-tools.d.ts +5 -1
  55. package/dist/core/runner/prepare-protocol-tools.js +38 -21
  56. package/dist/core/runner/prepare-question-face.d.ts +4 -3
  57. package/dist/core/runner/prepare-question-face.js +5 -3
  58. package/dist/core/runner/prepare-run-refs.d.ts +16 -0
  59. package/dist/core/runner/prepare-run-refs.js +6 -0
  60. package/dist/core/runner/prepare-safety-scan.js +42 -2
  61. package/dist/core/runner/prepare-task.js +37 -19
  62. package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +5 -2
  63. package/dist/core/runner/prepare-tool-disclosure-mount.js +3 -1
  64. package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -0
  65. package/dist/core/runner/prepare-wiring-manifest.js +3 -8
  66. package/dist/core/runner/run-harness-handlers.d.ts +46 -0
  67. package/dist/core/runner/run-harness-handlers.js +345 -0
  68. package/dist/core/runner/run-turn-boundary.d.ts +69 -0
  69. package/dist/core/runner/run-turn-boundary.js +694 -0
  70. package/dist/core/runner/runtask.js +47 -1043
  71. package/dist/core/runner/session-rule-policy.js +9 -4
  72. package/dist/core/runner/synthetic-tools.js +5 -8
  73. package/dist/core/runner/tool-disclosure.d.ts +4 -0
  74. package/dist/core/runner/tool-disclosure.js +9 -5
  75. package/dist/core/runner/tool-end-body.js +3 -2
  76. package/dist/core/runner/tool-face-overlay.d.ts +40 -0
  77. package/dist/core/runner/tool-face-overlay.js +125 -0
  78. package/dist/core/runner/tool-output-projection.d.ts +10 -0
  79. package/dist/core/runner/tool-output-projection.js +33 -10
  80. package/dist/core/sensitive-path-policy.d.ts +3 -0
  81. package/dist/core/sensitive-path-policy.js +6 -5
  82. package/dist/core/shared-memory/tools.js +3 -6
  83. package/dist/core/store-contracts/checkpoint-store-contract.js +23 -0
  84. package/dist/core/task-event.d.ts +10 -0
  85. package/dist/core/task-registry.js +4 -5
  86. package/dist/core/task-spec.d.ts +8 -0
  87. package/dist/core/tool-catalog-entries.d.ts +48 -0
  88. package/dist/core/tool-catalog-entries.js +174 -0
  89. package/dist/core/tool-conformance.d.ts +22 -0
  90. package/dist/core/tool-conformance.js +37 -0
  91. package/dist/core/tool-face.d.ts +171 -0
  92. package/dist/core/tool-face.js +25 -0
  93. package/dist/core/tool-policy.d.ts +24 -0
  94. package/dist/core/tool-policy.js +4 -2
  95. package/dist/core/tool-registry.d.ts +114 -0
  96. package/dist/core/tool-registry.js +114 -0
  97. package/dist/core/tool-result-budget.d.ts +0 -3
  98. package/dist/core/tool-result-budget.js +4 -3
  99. package/dist/core/tool-result-store.d.ts +0 -18
  100. package/dist/core/tool-result-store.js +2 -18
  101. package/dist/core/tool-roster.d.ts +450 -0
  102. package/dist/core/tool-roster.js +497 -0
  103. package/dist/core/tool-spec.d.ts +54 -3
  104. package/dist/core/tools.js +7 -0
  105. package/dist/core/wiring-manifest.d.ts +13 -0
  106. package/dist/core/wiring-manifest.js +2 -1
  107. package/dist/core/write-protect.d.ts +2 -1
  108. package/dist/core/write-protect.js +5 -4
  109. package/dist/engine/compaction/utils.d.ts +6 -0
  110. package/dist/engine/compaction/utils.js +19 -19
  111. package/dist/engine/loop/types.d.ts +6 -0
  112. package/dist/index.d.ts +15 -4
  113. package/dist/index.js +15 -4
  114. package/dist/orchestration/goal.js +2 -1
  115. package/dist/orchestration/run-spec.js +3 -5
  116. package/dist/orchestration/run-workflow-tool.js +2 -2
  117. package/dist/prompt-assembly/event-registry.js +3 -3
  118. package/dist/prompt-assembly/tool-catalog.d.ts +2 -2
  119. package/dist/tools/fs/fs-bash.d.ts +1 -1
  120. package/dist/tools/fs/fs-bash.js +7 -11
  121. package/dist/tools/fs/fs-read.js +2 -2
  122. package/dist/tools/fs/fs-search-tools.d.ts +0 -7
  123. package/dist/tools/fs/fs-search-tools.js +3 -16
  124. package/dist/tools/fs/fs-write.js +4 -6
  125. package/dist/tools/fs/repo-map.js +2 -2
  126. package/dist/tools/fs/safety.d.ts +2 -1
  127. package/dist/tools/fs/safety.js +6 -5
  128. package/dist/tools/monitor.js +2 -2
  129. package/dist/tools/scheduler-tools.js +5 -8
  130. package/dist/tools/task-list.js +5 -8
  131. package/dist/tools/todo.js +2 -2
  132. package/dist/tools/web.js +3 -6
  133. package/dist/tools/worktree.js +3 -4
  134. package/package.json +4 -1
  135. package/test/export-surface.snapshot.json +147 -7
@@ -1,5 +1,11 @@
1
1
  import type { Message } from "../llm/index.js";
2
2
  import type { AgentMessage } from "../loop/types.js";
3
+ /**
4
+ * design/388 A-18 — the FROZEN history table: tool names a persisted pre-rename transcript may still carry
5
+ * (the upstream trio and the pre-CC-name core trio), mapped to the path access the live definition declares.
6
+ * Never grows: a new name declares its access on the catalog instead. Registered with the name-table gate.
7
+ */
8
+ export declare const DURABLE_HISTORY_NAMES: ReadonlyMap<string, "read" | "create" | "edit">;
3
9
  /** File paths touched by a session branch or compaction range. */
4
10
  export interface FileOperations {
5
11
  /** Files read but not necessarily modified. */
@@ -2,6 +2,15 @@ import { normalizeLlmMessageContent } from "../harness/messages.js";
2
2
  import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
3
3
  import { cutEnginePrefix, cutEngineSegments, flattenableUserText } from "../../core/untrusted-text.js";
4
4
  import { fileArgPath } from "../../tools/fs/safety.js";
5
+ import { fileOpAccessOf } from "../../core/tool-registry.js";
6
+ export const DURABLE_HISTORY_NAMES = new Map([
7
+ ["read", "read"],
8
+ ["write", "create"],
9
+ ["edit", "edit"],
10
+ ["read_file", "read"],
11
+ ["write_file", "create"],
12
+ ["edit_file", "edit"],
13
+ ]);
5
14
  export function createFileOps() {
6
15
  return {
7
16
  read: new Set(),
@@ -35,25 +44,16 @@ export function extractFileOpsFromMessage(message, fileOps) {
35
44
  if (!path) {
36
45
  continue;
37
46
  }
38
- switch (block.name) {
39
- case "read":
40
- case "Read":
41
- case "read_file":
42
- fileOps.read.add(path);
43
- break;
44
- case "write":
45
- case "Write":
46
- case "write_file":
47
- fileOps.written.add(path);
48
- touchModified(fileOps, path);
49
- break;
50
- case "edit":
51
- case "Edit":
52
- case "edit_file":
53
- case "MultiEdit":
54
- fileOps.edited.add(path);
55
- touchModified(fileOps, path);
56
- break;
47
+ const access = fileOpAccessOf(block.name) ?? DURABLE_HISTORY_NAMES.get(block.name);
48
+ if (access === "read")
49
+ fileOps.read.add(path);
50
+ else if (access === "create") {
51
+ fileOps.written.add(path);
52
+ touchModified(fileOps, path);
53
+ }
54
+ else if (access === "edit") {
55
+ fileOps.edited.add(path);
56
+ touchModified(fileOps, path);
57
57
  }
58
58
  }
59
59
  }
@@ -750,6 +750,12 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = unk
750
750
  * server's own channel for keeping one tool's full schema inlined. Absent on non-MCP tools.
751
751
  */
752
752
  mcpAlwaysLoad?: boolean;
753
+ /**
754
+ * design/388 — the server's RAW tool name this mounted MCP tool was accepted under (byte-exact, the `tools/call`
755
+ * spelling). The `toolFaces` overlay binds to it: two raw names can fold onto one minted wire name, and an overlay
756
+ * keyed by the folded-away spelling must stay inert rather than land on the other tool. Absent on non-MCP tools.
757
+ */
758
+ mcpRemoteName?: string;
753
759
  /**
754
760
  * design/120 P1.5 (CC `isConcurrencySafe(parsedInput)` parity): INPUT-level concurrency refinement
755
761
  * for a tool whose safety depends on its arguments — CC's only real consumer is
package/dist/index.d.ts CHANGED
@@ -24,9 +24,9 @@ export { createTodoWriteTool } from "./tools/todo.js";
24
24
  export { createTaskListTools, createMemoryTaskListStore, assertJsonMetadata, type TaskListItem, type TaskListStore } from "./tools/task-list.js";
25
25
  export { assembleCodeTools, type CodeToolsConfig, CODE_ROLE } from "./scenarios/full-body.js";
26
26
  export { TOOL_MODEL_GATE_CLASSES, isModelGatedForClass, type ToolModelGateRule } from "./core/tool-model-gate.js";
27
- export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, type AggregateBudgetOptions, } from "./core/tool-result-budget.js";
27
+ export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, type AggregateBudgetOptions, } from "./core/tool-result-budget.js";
28
28
  export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES, type MediaStripInfo } from "./core/media-byte-cap.js";
29
- export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, type OnQuestionOutcome, type QuestionUnavailable, type OnQuestion, type AskQuestion, type AskQuestionOption, type AskQuestionRequest, type QuestionAnswer, type QuestionAnswerItem, type AskQuestionCardDetails, type AskUserQuestionToolOptions, type AskAnswerContinuationSource, type SyntheticContinuationReason, } from "./core/ask-question.js";
29
+ export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, type OnQuestionOutcome, type QuestionUnavailable, type OnQuestion, type AskQuestion, type AskQuestionOption, type AskQuestionRequest, type QuestionAnswer, type QuestionAnswerItem, type AskQuestionCardDetails, type AskUserQuestionToolOptions, type AskAnswerContinuationSource, type SyntheticContinuationReason, ASK_USER_QUESTION_TOOL_NAME, } from "./core/ask-question.js";
30
30
  export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, LSP_FAILURE_BRAND, brandLspFailure, lspFailureOf, SharedAbortScope, settleOnAbort, type LspNoneReason, type LspOperation, type LspServerManager, type LspSession, type LspResult, type LspLocation, type LspSymbolInfo, type LspRequestParams, type LspToolOptions, type LspTransport, type LspReadText, } from "./core/lsp.js";
31
31
  export { buildRequest, parseResult, callHierarchyMethod, pathToUri } from "./core/lsp-protocol.js";
32
32
  export { TransportLspSession, type SessionWarmup } from "./core/lsp-session.js";
@@ -91,7 +91,7 @@ export { sanitizeUntrustedText, delimitUntrusted, inlineUntrusted } from "./core
91
91
  export { mintReminderMark, isValidReminderMark, openSystemReminder, mintSystemReminder, reminderMarkDeclaration } from "./core/reminder-mint.js";
92
92
  export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
93
93
  export type { InvariantKind, FunctionContract, Invariant, InvariantViolation, CheckResult, } from "./core/property-harness.js";
94
- export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
94
+ export { bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
95
95
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, type BashReadonlyRootBoundary, type CompoundReadonlyVerdict, } from "./tools/fs/index.js";
96
96
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
97
97
  export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, type ReadDenyEntry, type ReadDenyMatcher, type NormalizedReadDenyEntry, type ReadDenyBuiltinTier, type ReadDenyBuiltinRow, type ReadDenyBuiltinConfig, } from "./tools/fs/index.js";
@@ -124,6 +124,17 @@ export { createFsWriteGatePolicy, type FsWriteGatePolicyOptions } from "./core/f
124
124
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
125
125
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
126
126
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, type TaskNotificationPayload, type TaskNotificationStatus, type ExternalNotificationInput, type SystemInjection, type SystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, type SemaProvenance, } from "./core/task-notification.js";
127
+ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, type ToolFamily, type ToolMountTag, type ToolApprovalCard, type ToolPathAccess, type ToolPathTarget, type ToolRuleFace, type ToolRenderHints, type ToolFaceDeclaration, type ToolCatalogEntry, } from "./core/tool-face.js";
128
+ export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, type ToolCallLike, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
129
+ export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, type RosterSource, type ToolIdentity, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
130
+ export { type ToolCallFace } from "./core/tool-policy.js";
131
+ export { TOOL_CONFORMANCE_VECTORS, describeToolConformanceVectors, type ToolConformanceVector } from "./core/tool-conformance.js";
132
+ export { applyMcpToolFaces, toolFaceProblem, APPROVAL_CARD_REQUIRED_KEYS, type McpToolFace } from "./core/runner/tool-face-overlay.js";
133
+ export { createStructuredProjector } from "./core/runner/tool-output-projection.js";
134
+ export { parkedToolIdentity } from "./core/tool-roster.js";
135
+ export { type ToolCallIdentity } from "./core/fs-write-gate-policy.js";
136
+ export { type RuleFaceView, catalogRuleFaceView } from "./core/permission-rules.js";
137
+ export { DURABLE_HISTORY_NAMES } from "./engine/compaction/utils.js";
127
138
  export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, type SubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, type AutoModeArmReason, type WiringManifest, type WiringManifestMcpEntry, type WiringFacts, type WiringLegKind, type AskSeamForm, type AskEffective, type QuestionChannelState, type SeamProvenance, type ParkLaneReason, type ManifestDurability, type StaticWiringDeps, type StaticWiringSpec, } from "./core/wiring-manifest.js";
128
139
  export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, type ParkSelfCheckResult, type ParkProbeFinding, type ParkProbeFindingCode, } from "./core/park-selfcheck.js";
129
140
  export { type StoreDurability } from "./core/checkpoint-store.js";
@@ -280,7 +291,7 @@ export { defineAgent } from "./agents/agent-definition.js";
280
291
  export { builtinAgentDefinitions, BUILTIN_READONLY_DENY_TOOLS, EXPLORE_WHEN_TO_USE, EXPLORE_WHEN_TO_USE_LEAN, PLAN_WHEN_TO_USE, EXPLORE_SYSTEM_PROMPT, PLAN_SYSTEM_PROMPT, } from "./agents/builtin-agents.js";
281
292
  export { runTeamDiscussion, type TeamDiscussionOptions, type TeamMember, type TeamResult, type TeamTurn, type TeamEvent, } from "./agents/team.js";
282
293
  export { createTaskServer, type TaskServerOptions, type TaskRequestBody, } from "./server/http.js";
283
- export { clearStaleToolResults, COMPACTABLE_TOOLS, editBudget, type ContextEditOptions, } from "./core/context-edit.js";
294
+ export { clearStaleToolResults, editBudget, type ContextEditOptions, } from "./core/context-edit.js";
284
295
  export { createOpenAIBrain, type OpenAIBrainConfig } from "./brain/openai.js";
285
296
  export { createAnthropicBrain, type AnthropicBrainConfig } from "./brain/anthropic.js";
286
297
  export { createOpenResponsesBrain, type OpenResponsesBrainConfig } from "./brain/open-responses.js";
package/dist/index.js CHANGED
@@ -12,9 +12,9 @@ export { createTodoWriteTool } from "./tools/todo.js";
12
12
  export { createTaskListTools, createMemoryTaskListStore, assertJsonMetadata } from "./tools/task-list.js";
13
13
  export { assembleCodeTools, CODE_ROLE } from "./scenarios/full-body.js";
14
14
  export { TOOL_MODEL_GATE_CLASSES, isModelGatedForClass } from "./core/tool-model-gate.js";
15
- export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, DEFAULT_BUDGET_EXEMPT_TOOLS, } from "./core/tool-result-budget.js";
15
+ export { capAggregateToolResults, AGGREGATE_TOOL_RESULT_BUDGET_CHARS, } from "./core/tool-result-budget.js";
16
16
  export { capAggregateMediaBytes, AGGREGATE_MEDIA_BUDGET_BYTES } from "./core/media-byte-cap.js";
17
- export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, } from "./core/ask-question.js";
17
+ export { createAskUserQuestionTool, createDurableQuestionPolicy, QUESTION_AWAITS_RESUME, isQuestionUnavailable, ASK_USER_QUESTION_TOOL_NAME, } from "./core/ask-question.js";
18
18
  export { createLspTool, gitCheckIgnoreFilter, LSP_OPERATIONS, LSP_FAILURE_BRAND, brandLspFailure, lspFailureOf, SharedAbortScope, settleOnAbort, } from "./core/lsp.js";
19
19
  export { buildRequest, parseResult, callHierarchyMethod, pathToUri } from "./core/lsp-protocol.js";
20
20
  export { TransportLspSession } from "./core/lsp-session.js";
@@ -70,7 +70,7 @@ export { runExecGate } from "./core/exec-gate.js";
70
70
  export { sanitizeUntrustedText, delimitUntrusted, inlineUntrusted } from "./core/untrusted-text.js";
71
71
  export { mintReminderMark, isValidReminderMark, openSystemReminder, mintSystemReminder, reminderMarkDeclaration } from "./core/reminder-mint.js";
72
72
  export { deriveInvariants, checkInvariants } from "./core/property-harness.js";
73
- export { HAND_TOOL_EFFECTS, bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
73
+ export { bashReversibilityProbe, BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName, classifyCompoundReadonly, MAX_EDIT_BYTES } from "./tools/fs/index.js";
74
74
  export { classifyCompoundReadonlyDetailed, formatOutOfRootReadApprovalOption, } from "./tools/fs/index.js";
75
75
  export { resolveBashTimeoutCaps } from "./tools/fs/index.js";
76
76
  export { READ_FACE_DEFAULT_DENY_ENTRIES, READ_FACE_BUILTIN_DENY_TABLE, READ_DENY_BUILTIN_TIERS, READ_DENY_DEFAULT_TIERS, resolveReadDenyBuiltins, compileReadDeny, } from "./tools/fs/index.js";
@@ -100,6 +100,17 @@ export { createFsWriteGatePolicy } from "./core/fs-write-gate-policy.js";
100
100
  export { RETIRED_TOOL_NAMES } from "./core/tool-name-aliases.js";
101
101
  export { DEFAULT_SUBAGENT_TOOL_NAME } from "./agents/subagent.js";
102
102
  export { renderTaskNotificationXml, taskNotificationDedupKey, isDelegatedAgentTerminal, isTerminalTaskNotification, SystemInjectionQueue, SYSTEM_INJECTION_PRIORITIES, isSystemInjectionPriority, AGENT_MESSAGE_TAG, renderAgentMessageFrame, } from "./core/task-notification.js";
103
+ export { TOOL_FAMILIES, TOOL_MOUNT_TAGS, TOOL_APPROVAL_CARDS, TOOL_PATH_ACCESSES, TOOL_WIRE_NAME_MAX_CHARS, TOOL_CONTRACT_MAX_CHARS, TOOL_KEY_MAX_CHARS, TOOL_KEYS_MAX, RENDER_HINT_MAX_CHARS, RENDER_HINT_ACTIVITY_MAX_CHARS, RENDER_HINT_MAX_LIST, } from "./core/tool-face.js";
104
+ export { describeToolCatalog, catalogEntriesNamed, catalogToolNames, catalogRuleFaceOf, pathTargetOf, pathTargetValue, protectivePathTargetOf, declaredPathTargetOf, isProtectedWrite, isDeclaredWrite, fileOpAccessOf, pathConfinableWriteToolNames, skillScopeWriteToolNames, pathTargetingToolNames, compactableToolNames, budgetExemptToolNames, handBandEffects, engineCardTypes, predictMountedToolNames, HANDS_BAND_TAGS, MOUNT_TAG_IN_HANDS_BAND, } from "./core/tool-registry.js";
105
+ export { ToolRosterEntry, ToolRoster, ToolRosterDelta, ToolRosterRenderHints, ROSTER_SOURCES, toolIdentityOf, mintCapabilityId, diffToolRoster, judgeParkedToolIdentity, ruleFacesFromRoster, toolCallFaceOf, inputKeysOf, ruleFaceOfInstance, ToolRosterDeltaSeat, } from "./core/tool-roster.js";
106
+ export {} from "./core/tool-policy.js";
107
+ export { TOOL_CONFORMANCE_VECTORS, describeToolConformanceVectors } from "./core/tool-conformance.js";
108
+ export { applyMcpToolFaces, toolFaceProblem, APPROVAL_CARD_REQUIRED_KEYS } from "./core/runner/tool-face-overlay.js";
109
+ export { createStructuredProjector } from "./core/runner/tool-output-projection.js";
110
+ export { parkedToolIdentity } from "./core/tool-roster.js";
111
+ export {} from "./core/fs-write-gate-policy.js";
112
+ export { catalogRuleFaceView } from "./core/permission-rules.js";
113
+ export { DURABLE_HISTORY_NAMES } from "./engine/compaction/utils.js";
103
114
  export { describeStaticWiring, deriveWiringManifest, deriveAskEffective, resolveDeclaredDurability, resolveSubagentTranscriptTier, resolveAskSeamForm, resolveQuestionSeam, countElicitOptIns, AUTO_MODE_ARM_REASONS, } from "./core/wiring-manifest.js";
104
115
  export { probeParkRoundTrip, durableParkGapOf, durableParkGapFor, PARK_SELFCHECK_SCOPE_PREFIX, } from "./core/park-selfcheck.js";
105
116
  export {} from "./core/checkpoint-store.js";
@@ -236,7 +247,7 @@ export { defineAgent } from "./agents/agent-definition.js";
236
247
  export { builtinAgentDefinitions, BUILTIN_READONLY_DENY_TOOLS, EXPLORE_WHEN_TO_USE, EXPLORE_WHEN_TO_USE_LEAN, PLAN_WHEN_TO_USE, EXPLORE_SYSTEM_PROMPT, PLAN_SYSTEM_PROMPT, } from "./agents/builtin-agents.js";
237
248
  export { runTeamDiscussion, } from "./agents/team.js";
238
249
  export { createTaskServer, } from "./server/http.js";
239
- export { clearStaleToolResults, COMPACTABLE_TOOLS, editBudget, } from "./core/context-edit.js";
250
+ export { clearStaleToolResults, editBudget, } from "./core/context-edit.js";
240
251
  export { createOpenAIBrain } from "./brain/openai.js";
241
252
  export { createAnthropicBrain } from "./brain/anthropic.js";
242
253
  export { createOpenResponsesBrain } from "./brain/open-responses.js";
@@ -3,16 +3,17 @@ import { Type } from "typebox";
3
3
  import { delimitUntrusted } from "../core/untrusted-text.js";
4
4
  import { OUTPUT_TOOL_NAME } from "../core/runner/synthetic-tools.js";
5
5
  import { isDurablePause, pauseFamily } from "../agents/suspend-guard.js";
6
+ import { toolFace } from "../core/tool-catalog-entries.js";
6
7
  export const DECLARE_DONE_TOOL_NAME = "DeclareDone";
7
8
  function createDeclareDoneTool(ref) {
8
9
  return {
9
10
  name: DECLARE_DONE_TOOL_NAME,
11
+ ...toolFace("declare-done"),
10
12
  description: "Call this ONLY when you believe the goal is FULLY achieved — verified against evidence, not just " +
11
13
  "attempted. Provide a concise summary of what was achieved and how you verified it. Calling this " +
12
14
  "declares completion; a verification check then runs. If it passes, the goal stops and surfaces for " +
13
15
  "human review. If it does NOT pass, you will be asked to keep working with the verification feedback.",
14
16
  parameters: Type.Object({ summary: Type.String() }),
15
- effect: "read",
16
17
  execute: (args) => {
17
18
  ref.declared = true;
18
19
  ref.summary = String(args.summary ?? "").trim();
@@ -7,7 +7,7 @@ import { isDurablePause } from "../agents/suspend-guard.js";
7
7
  import { terminalProjection } from "../core/runner/terminal-projection.js";
8
8
  import { isAbsolutePathForm, writeTargetPath } from "../tools/fs/safety.js";
9
9
  import { captureBaseline, resolveFrozenPaths, restoreFrozenPaths, runOracle, snapshotFrozenPaths, specError, } from "../core/spec-contract.js";
10
- const WRITE_TOOLS = new Set(["Write", "Edit", "NotebookEdit"]);
10
+ import { isProtectedWrite } from "../core/tool-registry.js";
11
11
  const CASE_INSENSITIVE_FS = process.platform === "darwin" || process.platform === "win32";
12
12
  const pathKey = (p) => {
13
13
  let n = p.replace(/\\/g, "/");
@@ -19,11 +19,9 @@ function frozenDenyPolicy(rootDir, frozenResolved) {
19
19
  const frozen = new Set(frozenResolved.map(pathKey));
20
20
  return {
21
21
  check(req) {
22
- const toolName = req.toolName;
23
- if (!WRITE_TOOLS.has(toolName))
22
+ if (!isProtectedWrite(req))
24
23
  return { action: "allow" };
25
- const args = req.args;
26
- const raw = writeTargetPath(toolName, args);
24
+ const raw = writeTargetPath(req);
27
25
  if (typeof raw !== "string" || raw.length === 0)
28
26
  return { action: "allow" };
29
27
  const absForm = isAbsolutePathForm(raw);
@@ -10,6 +10,7 @@ import { parseWorkflowMeta, splitWorkflowMeta, workflowScriptReadsClockOrRandom
10
10
  import { mergeWorkflowArgs, normalizeStringArg } from "./workflow-script-store.js";
11
11
  import { builtinWorkflowListings, canonicalWorkflowName, resolveBuiltinWorkflow, workflowNameProbeOrder } from "./builtin-workflows.js";
12
12
  import { workflowSizeGuidelineSection } from "./workflow-size-guideline.js";
13
+ import { toolFace } from "../core/tool-catalog-entries.js";
13
14
  export const RUN_WORKFLOW_TOOL_NAME = "Workflow";
14
15
  function stripCodeFences(s) {
15
16
  const t = s.trim();
@@ -244,7 +245,7 @@ export async function createRunWorkflowTool(d) {
244
245
  const structuredError = (message) => errorResult(JSON.stringify({ error: message }));
245
246
  return defineTool({
246
247
  name: RUN_WORKFLOW_TOOL_NAME,
247
- contract: { contractId: "core.workflow@1", implementationRevision: "1" },
248
+ ...toolFace("workflow"),
248
249
  approvalPreview: (raw) => {
249
250
  const a = (raw ?? {});
250
251
  if (typeof a.name === "string" && a.name)
@@ -374,7 +375,6 @@ export async function createRunWorkflowTool(d) {
374
375
  "Requires a journal-backed deployment; omit for a fresh run.",
375
376
  })),
376
377
  }),
377
- effect: "write",
378
378
  execute: async (rawArgs, ctx) => {
379
379
  const rawScript = rawArgs.script;
380
380
  const rawName = rawArgs.name;
@@ -6,7 +6,7 @@ export const EVENT_PROMPT_REGISTRY = new Map([
6
6
  { kind: "plan_mode", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#PLAN_MODE_FULL_BODY" },
7
7
  { kind: "date_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "turn-attachments.ts#renderDateChange" },
8
8
  { kind: "instructions_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", maxBytes: 512, defaultPolicy: "always", rendererRef: "turn-attachments.ts#collectInstructionsChange" },
9
- { kind: "workflow_size_guideline_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "runtask.ts#workflowSizeGuidelineChangeNotice" },
9
+ { kind: "workflow_size_guideline_change", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "run-turn-boundary.ts#workflowSizeGuidelineChangeNotice" },
10
10
  { kind: "budget_usd", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderBudgetUsd" },
11
11
  { kind: "total_tokens_reminder", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "on", rendererRef: "turn-attachments.ts#renderTotalTokensReminder" },
12
12
  { kind: "background_tasks", carrier: "message.user-prefix", trust: "operator", dedupe: "replace-by-key", defaultPolicy: "on", rendererRef: "turn-attachments.ts#renderBackgroundTasks" },
@@ -16,8 +16,8 @@ export const EVENT_PROMPT_REGISTRY = new Map([
16
16
  { kind: "mcp_instructions", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpInstructionsDelta" },
17
17
  { kind: "mcp_dropped_tools", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "off", rendererRef: "turn-attachments.ts#renderMcpDroppedTools" },
18
18
  { kind: "git_status", carrier: "message.user-prefix", trust: "external", dedupe: "replace-by-key", defaultPolicy: "always", rendererRef: "git-status-frame.ts#renderGitStatusFrameBody" },
19
- { kind: "limit_approach_converge", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#limitApproachFrames" },
20
- { kind: "limit_approach_deliver", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#limitApproachFrames" },
19
+ { kind: "limit_approach_converge", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "run-turn-boundary.ts#limitApproachFrames" },
20
+ { kind: "limit_approach_deliver", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "run-turn-boundary.ts#limitApproachFrames" },
21
21
  { kind: "final_verification", carrier: "message.user-prefix", trust: "operator", dedupe: "once-per-run", defaultPolicy: "always", rendererRef: "runtask.ts#final-verification" },
22
22
  ].map((e) => [e.kind, e]));
23
23
  export function eventDefaultOn(kind) {
@@ -61,7 +61,7 @@ export declare function attachToolContract(tool: AgentTool, input: AttachContrac
61
61
  export declare function getToolContract(tool: AgentTool, fallbackOrigin?: ToolOrigin): ToolContractDescriptor;
62
62
  export declare function hasToolContract(tool: AgentTool): boolean;
63
63
  /** Manifest projection for the final mounted tool set (manifest v2 `tools`, protocol §5.4). */
64
- export declare function projectToolManifest(tools: AgentTool[], originOf?: (tool: AgentTool) => ToolOrigin): ToolManifestRow[];
64
+ export declare function projectToolManifest(tools: readonly AgentTool[], originOf?: (tool: AgentTool) => ToolOrigin): ToolManifestRow[];
65
65
  /**
66
66
  * RB-15: wire-identity collision audit over the FINAL mounted tool set.
67
67
  * Rules (plan §6.2 tool row):
@@ -71,7 +71,7 @@ export declare function projectToolManifest(tools: AgentTool[], originOf?: (tool
71
71
  * a built-in; the harness Map keeps the later mount) — NOT fatal, but returned as an explicit
72
72
  * exemption record so the mount site can surface it (observable, never silent).
73
73
  */
74
- export declare function auditToolCollisions(tools: AgentTool[]): {
74
+ export declare function auditToolCollisions(tools: readonly AgentTool[]): {
75
75
  yields: Array<{
76
76
  name: string;
77
77
  }>;
@@ -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 declare const FULL_SHELL_CONTRACT_ID = "core.bash@1";
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
@@ -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, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, 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";
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 const FULL_SHELL_CONTRACT_ID = "core.bash@1";
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
- contract: { contractId: FULL_SHELL_CONTRACT_ID, implementationRevision: "1" },
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
- contract: { contractId: "core.bash_readonly@1", implementationRevision: "1" },
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
- contract: TASK_OUTPUT_CONTRACT,
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
- contract: TASK_STOP_CONTRACT,
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);
@@ -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
- contract: { contractId: "core.read@1", implementationRevision: "1" },
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
- contract: { contractId: "core.grep@1", implementationRevision: "1" },
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
- contract: { contractId: "core.glob@1", implementationRevision: "1" },
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
- contract: { contractId: "core.edit@1", implementationRevision: "1" },
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
- contract: { contractId: "core.write@1", implementationRevision: "1" },
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
- contract: { contractId: "core.notebook_edit@1", implementationRevision: "1" },
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";
@@ -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
- contract: { contractId: "core.repo_map@1", implementationRevision: "1" },
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(canonicalName: string, args: unknown): string | undefined;
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