@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,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
3
+ import { pathTargetValue, protectivePathTargetOf } from "../../core/tool-registry.js";
3
4
  export function fileArgPath(args) {
4
5
  const a = args;
5
6
  const fp = a?.file_path;
@@ -8,12 +9,12 @@ export function fileArgPath(args) {
8
9
  const p = a?.path;
9
10
  return typeof p === "string" && p.length > 0 ? p : undefined;
10
11
  }
11
- export function writeTargetPath(canonicalName, args) {
12
- const np = args?.notebook_path;
12
+ export function writeTargetPath(call, pt = protectivePathTargetOf(call)) {
13
+ if (pt !== undefined)
14
+ return pathTargetValue(call.args, pt);
15
+ const np = call.args?.notebook_path;
13
16
  const notebook = typeof np === "string" && np.length > 0 ? np : undefined;
14
- if (canonicalName === "NotebookEdit")
15
- return notebook ?? fileArgPath(args);
16
- return fileArgPath(args) ?? notebook;
17
+ return fileArgPath(call.args) ?? notebook;
17
18
  }
18
19
  export function sha256(content) {
19
20
  return createHash("sha256").update(content, "utf8").digest("hex");
@@ -2,6 +2,7 @@ import { Type } from "typebox";
2
2
  import { defineTool, errorResult } from "../core/tools.js";
3
3
  import { hasBackgroundShell } from "../core/background-shell.js";
4
4
  import { MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, } from "../core/task-registry.js";
5
+ import { toolFace } from "../core/tool-catalog-entries.js";
5
6
  const MONITOR_DESCRIPTION = "Run a shell command in the background and watch its stdout as a stream of events.\n" +
6
7
  "\n" +
7
8
  "- Each line the command writes to stdout becomes a notification event delivered to you between turns; " +
@@ -42,7 +43,7 @@ function envBackgroundWallSec(caps, requestedSec) {
42
43
  export function createMonitorTool(env, opts) {
43
44
  return defineTool({
44
45
  name: "Monitor",
45
- contract: { contractId: "core.monitor@1", implementationRevision: "1" },
46
+ ...toolFace("monitor"),
46
47
  description: MONITOR_DESCRIPTION,
47
48
  parameters: Type.Object({
48
49
  command: Type.String({
@@ -58,7 +59,6 @@ export function createMonitorTool(env, opts) {
58
59
  description: "Set to true for a session-resident monitor with no watch timeout of its own (default false). It keeps watching until the execution environment's background time budget expires, until you stop it with TaskStop, or until the session ends.",
59
60
  })),
60
61
  }),
61
- effect: "write",
62
62
  execute: async (args, ctx) => {
63
63
  const { command, description, timeout_ms, persistent } = args;
64
64
  if (!hasBackgroundShell(env)) {
@@ -1,6 +1,7 @@
1
1
  import { Type } from "typebox";
2
2
  import { defineTool, errorResult } from "../core/tools.js";
3
3
  import { hasScheduler, isValidCronExpr, } from "../core/scheduler.js";
4
+ import { toolFace } from "../core/tool-catalog-entries.js";
4
5
  export const SCHEDULE_WAKEUP_TOOL_NAME = "ScheduleWakeup";
5
6
  export const AUTONOMOUS_LOOP_SENTINEL = "<<autonomous-loop>>";
6
7
  export const AUTONOMOUS_LOOP_DYNAMIC_SENTINEL = "<<autonomous-loop-dynamic>>";
@@ -199,7 +200,7 @@ Every user who asks for "9am" gets \`0 9\`, and every user who asks for "hourly"
199
200
  Only use minute 0 or 30 when the user names that exact time and clearly means it ("at 9:00 sharp", "at half past", coordinating with a meeting). When in doubt, nudge a few minutes early or late — the user will not notice, and the fleet will.`;
200
201
  const cronCreate = defineTool({
201
202
  name: "CronCreate",
202
- contract: { contractId: "core.cron_create@1", implementationRevision: "1" },
203
+ ...toolFace("cron-create"),
203
204
  description: cronCreateDescription,
204
205
  parameters: Type.Object({
205
206
  prompt: Type.String({ description: "Self-contained instructions for the future task (it runs unattended)." }),
@@ -222,7 +223,6 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
222
223
  })),
223
224
  label: Type.Optional(Type.String({ description: "Short label (also a dedup key with the schedule)." })),
224
225
  }),
225
- effect: "write",
226
226
  execute: async (args) => serializedCronCreateOp(async () => {
227
227
  const a = args;
228
228
  if (a.schedule !== undefined && a.cron !== undefined) {
@@ -305,10 +305,9 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
305
305
  };
306
306
  const cronCancel = defineTool({
307
307
  name: "CronDelete",
308
- contract: { contractId: "core.cron_delete@1", implementationRevision: "1" },
308
+ ...toolFace("cron-delete"),
309
309
  description: "Cancel a cron job previously scheduled with CronCreate. Idempotent; ids also appear in CronList.",
310
310
  parameters: Type.Object({ id: Type.String({ description: "Job ID returned by CronCreate." }) }),
311
- effect: "idempotent",
312
311
  execute: async (args) => {
313
312
  const { id } = args;
314
313
  const taskId = id;
@@ -329,10 +328,9 @@ Only use minute 0 or 30 when the user names that exact time and clearly means it
329
328
  });
330
329
  const cronList = defineTool({
331
330
  name: "CronList",
332
- contract: { contractId: "core.cron_list@1", implementationRevision: "1" },
331
+ ...toolFace("cron-list"),
333
332
  description: "List all cron jobs scheduled via CronCreate (id, when, label).",
334
333
  parameters: Type.Object({}),
335
- effect: "read",
336
334
  execute: async () => {
337
335
  const rows = [];
338
336
  const seen = new Set();
@@ -409,7 +407,7 @@ MOUNT NOTE: this host has no resident wakeup leg, so ${SCHEDULE_WAKEUP_TOOL_NAME
409
407
  MOUNT NOTE: this host does not vouch for session-scoped scheduling, so a wakeup scheduled here is PERSISTENT — contrary to the paragraph above, it can outlive this session instead of ending with it. End the loop explicitly with \`stop: true\` when the work is done; do not rely on the session ending to cancel it.`;
410
408
  const scheduleWakeup = defineTool({
411
409
  name: SCHEDULE_WAKEUP_TOOL_NAME,
412
- contract: { contractId: "core.schedule_wakeup@1", implementationRevision: "1" },
410
+ ...toolFace("schedule-wakeup"),
413
411
  description: `${SCHEDULE_WAKEUP_PROMPT}${wakeupMountNote}`,
414
412
  parameters: Type.Object({
415
413
  delaySeconds: Type.Optional(Type.Number({
@@ -428,7 +426,6 @@ MOUNT NOTE: this host does not vouch for session-scoped scheduling, so a wakeup
428
426
  description: "true = nothing changed (you checked and there is nothing to report). false = something happened worth keeping (edited a file, posted a message, advanced state, surfaced a finding). Consecutive noop:true ticks are collapsed in the user's terminal view and tracked as a streak. Required unless `stop` is true.",
429
427
  })),
430
428
  }),
431
- effect: "write",
432
429
  execute: async (args) => serializedWakeupOp(async () => {
433
430
  const a = args;
434
431
  if (a.stop === true) {
@@ -1,4 +1,5 @@
1
1
  import { Type } from "typebox";
2
+ import { toolFace } from "../core/tool-catalog-entries.js";
2
3
  const STATUS = Type.Union([
3
4
  Type.Literal("pending"),
4
5
  Type.Literal("in_progress"),
@@ -96,7 +97,7 @@ export function createTaskListTools(store) {
96
97
  const tasks = store ?? createMemoryTaskListStore();
97
98
  const taskCreate = {
98
99
  name: "TaskCreate",
99
- contract: { contractId: "core.task_create@1", implementationRevision: "1" },
100
+ ...toolFace("task-create"),
100
101
  description: "Use this tool to create a structured task list for your current session. This helps you track progress, organize complex tasks, and demonstrate thoroughness.\n" +
101
102
  "It also helps the user understand the progress of the task and overall progress of their requests.\n" +
102
103
  "\n" +
@@ -144,7 +145,6 @@ export function createTaskListTools(store) {
144
145
  activeForm: Type.Optional(Type.String({ description: 'Present continuous form shown when in_progress (e.g., "Running tests")' })),
145
146
  metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown(), { description: "Arbitrary metadata to attach to the task" })),
146
147
  }),
147
- effect: "write",
148
148
  execute: (args) => serialized(tasks, async (tx) => {
149
149
  const a = args;
150
150
  if (a.metadata)
@@ -169,7 +169,7 @@ export function createTaskListTools(store) {
169
169
  };
170
170
  const taskGet = {
171
171
  name: "TaskGet",
172
- contract: { contractId: "core.task_get@1", implementationRevision: "1" },
172
+ ...toolFace("task-get"),
173
173
  description: "Use this tool to retrieve a task by its ID from the task list.\n" +
174
174
  "\n" +
175
175
  "## When to Use This Tool\n" +
@@ -189,7 +189,6 @@ export function createTaskListTools(store) {
189
189
  parameters: Type.Object({
190
190
  taskId: Type.String({ description: "The ID of the task to retrieve" }),
191
191
  }),
192
- effect: "read",
193
192
  execute: (args) => serialized(tasks, async (tx) => {
194
193
  const id = String(args.taskId);
195
194
  const t = await tx.get(id);
@@ -201,7 +200,7 @@ export function createTaskListTools(store) {
201
200
  };
202
201
  const taskUpdate = {
203
202
  name: "TaskUpdate",
204
- contract: { contractId: "core.task_update@1", implementationRevision: "1" },
203
+ ...toolFace("task-update"),
205
204
  description: "Use this tool to update a task in the task list.\n" +
206
205
  "\n" +
207
206
  "## When to Use This Tool\n" +
@@ -285,7 +284,6 @@ export function createTaskListTools(store) {
285
284
  addBlocks: Type.Optional(Type.Array(Type.String(), { description: "Task IDs that this task blocks" })),
286
285
  addBlockedBy: Type.Optional(Type.Array(Type.String(), { description: "Task IDs that block this task" })),
287
286
  }),
288
- effect: "idempotent",
289
287
  execute: (args) => serialized(tasks, async (tx) => {
290
288
  const a = args;
291
289
  const id = String(a.taskId);
@@ -395,7 +393,7 @@ export function createTaskListTools(store) {
395
393
  };
396
394
  const taskList = {
397
395
  name: "TaskList",
398
- contract: { contractId: "core.task_list@1", implementationRevision: "1" },
396
+ ...toolFace("task-list"),
399
397
  description: "Use this tool to list all tasks in the task list.\n" +
400
398
  "\n" +
401
399
  "## When to Use This Tool\n" +
@@ -420,7 +418,6 @@ export function createTaskListTools(store) {
420
418
  "\n" +
421
419
  "Use TaskGet with a specific task ID to view full details including description and metadata.",
422
420
  parameters: Type.Object({}),
423
- effect: "read",
424
421
  execute: () => serialized(tasks, async (tx) => {
425
422
  const all = await tx.list();
426
423
  if (all.length === 0) {
@@ -1,10 +1,11 @@
1
1
  import { Type } from "typebox";
2
2
  import { errorResult } from "../core/tools.js";
3
+ import { toolFace } from "../core/tool-catalog-entries.js";
3
4
  export function createTodoWriteTool() {
4
5
  let todos = [];
5
6
  return {
6
7
  name: "TodoWrite",
7
- contract: { contractId: "core.todo_write@1", implementationRevision: "1" },
8
+ ...toolFace("todo-write"),
8
9
  description: "Create and update a task list for the current session. The list is rendered to the user as your working plan.\n" +
9
10
  "\n" +
10
11
  '- Each todo has `content`, `status` ("pending" | "in_progress" | "completed"), and `activeForm` (present-tense label shown while in progress).\n' +
@@ -80,7 +81,6 @@ export function createTodoWriteTool() {
80
81
  activeForm: Type.String({ minLength: 1, description: "Present-continuous form shown while in progress (e.g. \"Running the tests\")." }),
81
82
  })),
82
83
  }),
83
- effect: "idempotent",
84
84
  execute: (args) => {
85
85
  const shape = (t) => ({ content: t.content, status: t.status, activeForm: t.activeForm });
86
86
  const oldTodos = todos.map(shape);
package/dist/tools/web.js CHANGED
@@ -7,6 +7,7 @@ import { isPrivateHost } from "../core/runner/image.js";
7
7
  import { DEFAULT_CHARS_PER_TOKEN } from "../internal/harness.js";
8
8
  import { binaryMagicFormat } from "./fs/safety.js";
9
9
  import { pdfMagicMatches } from "./fs/pdf.js";
10
+ import { toolFace } from "../core/tool-catalog-entries.js";
10
11
  const MAX_REDIRECTS = 10;
11
12
  const MAX_URL_LENGTH = 2048;
12
13
  const WEBFETCH_ACCEPT = "text/markdown, text/html, */*";
@@ -262,7 +263,7 @@ export function webFetchToolSpec(config = {}) {
262
263
  const canSummarize = config.summarize !== undefined;
263
264
  return {
264
265
  name: "WebFetch",
265
- contract: { contractId: "core.web_fetch@1", implementationRevision: "1" },
266
+ ...toolFace("web-fetch"),
266
267
  description: (canSummarize
267
268
  ? "Fetches a URL, converts the page to markdown, and answers `prompt` against it with a sub-model.\n"
268
269
  : "Fetches a URL, converts the page to markdown, and returns the markdown. This deployment has no " +
@@ -278,8 +279,6 @@ export function webFetchToolSpec(config = {}) {
278
279
  ? { prompt: Type.Optional(Type.String({ description: "The prompt to run on the fetched content (answered by the configured sub-model)." })) }
279
280
  : {}),
280
281
  }),
281
- effect: "read",
282
- contentOrigin: "external",
283
282
  execute: async (args, ctx) => {
284
283
  const { url, prompt } = args;
285
284
  const startedAt = Date.now();
@@ -844,7 +843,7 @@ export function createWebSearchTool(config) {
844
843
  const currentMonthYear = new Date().toLocaleString("en-US", { month: "long", year: "numeric" });
845
844
  return {
846
845
  name: "WebSearch",
847
- contract: { contractId: "core.web_search@1", implementationRevision: "1" },
846
+ ...toolFace("web-search"),
848
847
  description: "Search the web. Returns result blocks with titles and URLs.\n" +
849
848
  "\n" +
850
849
  `- The current month is ${currentMonthYear} — use this when searching for recent information.\n` +
@@ -857,8 +856,6 @@ export function createWebSearchTool(config) {
857
856
  allowed_domains: Type.Optional(Type.Array(Type.String(), { description: "Only include search results from these domains" })),
858
857
  blocked_domains: Type.Optional(Type.Array(Type.String(), { description: "Never include search results from these domains" })),
859
858
  }),
860
- effect: "read",
861
- contentOrigin: "external",
862
859
  execute: async (args, ctx) => {
863
860
  const { query, allowed_domains, blocked_domains } = args;
864
861
  const startedAt = Date.now();
@@ -2,6 +2,7 @@ import { resolve } from "node:path";
2
2
  import { Type } from "typebox";
3
3
  import { defineTool, errorResult } from "../core/tools.js";
4
4
  import { addWorktree, pruneWorktrees, WORKTREE_PARENT } from "../core/git-worktree-env.js";
5
+ import { toolFace } from "../core/tool-catalog-entries.js";
5
6
  const ENTER_DESCRIPTION = "Use this tool ONLY when explicitly instructed to work in a worktree — either by the user directly, or by " +
6
7
  "project instructions. This tool creates an isolated git worktree and switches the current session into it.\n" +
7
8
  "\n" +
@@ -108,7 +109,7 @@ export function createWorktreeTools(env, opts) {
108
109
  const session = opts.session ?? {};
109
110
  const enter = defineTool({
110
111
  name: "EnterWorktree",
111
- contract: { contractId: "core.enter_worktree@1", implementationRevision: "1" },
112
+ ...toolFace("enter-worktree"),
112
113
  description: ENTER_DESCRIPTION,
113
114
  parameters: Type.Object({
114
115
  name: Type.Optional(Type.String({
@@ -121,7 +122,6 @@ export function createWorktreeTools(env, opts) {
121
122
  description: '"fresh" (default) bases the worktree on the default branch tip (origin default branch, else local main/master, else the current HEAD with a note); "head" bases it on the current local HEAD (unpushed commits present).',
122
123
  })),
123
124
  }),
124
- effect: "write",
125
125
  execute: async (args) => {
126
126
  const { name, path, baseRef } = args;
127
127
  if (path !== undefined && name !== undefined) {
@@ -238,7 +238,7 @@ export function createWorktreeTools(env, opts) {
238
238
  });
239
239
  const exit = defineTool({
240
240
  name: "ExitWorktree",
241
- contract: { contractId: "core.exit_worktree@1", implementationRevision: "1" },
241
+ ...toolFace("exit-worktree"),
242
242
  description: EXIT_DESCRIPTION,
243
243
  parameters: Type.Object({
244
244
  keep: Type.Optional(Type.Boolean({
@@ -254,7 +254,6 @@ export function createWorktreeTools(env, opts) {
254
254
  description: 'Set true to remove the worktree even when it has uncommitted files or unmerged commits — they are permanently discarded. Without it a changed worktree is KEPT (the exit still completes and the reply lists the changes plus this escape), never refused.',
255
255
  })),
256
256
  }),
257
- effect: "write",
258
257
  execute: async (args) => {
259
258
  const raw = args;
260
259
  const keep = raw.keep ?? (raw.action === "keep" ? true : undefined);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "7.7.0",
3
+ "version": "7.8.0",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",
@@ -79,6 +79,9 @@
79
79
  "gate:closed-sets": "node scripts/verify-closed-sets.mjs",
80
80
  "gate:agent-returns": "node scripts/verify-agent-returns.mjs",
81
81
  "gate:contract-sentences": "node scripts/verify-contract-sentences.mjs",
82
+ "gate:tool-catalog": "node scripts/verify-tool-catalog.mjs",
83
+ "gate:tool-names": "node scripts/verify-tool-name-tables.mjs",
84
+ "gate:tool-docs": "node scripts/gen-tool-spec-fields.mjs --check",
82
85
  "gate:all": "node scripts/gate-all.mjs"
83
86
  },
84
87
  "dependencies": {