@sema-agent/core 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/agents/cascade.d.ts +1 -0
  3. package/dist/agents/cascade.js +1 -1
  4. package/dist/agents/repair-loop.d.ts +2 -0
  5. package/dist/agents/repair-loop.js +21 -5
  6. package/dist/agents/roster-store.d.ts +1 -0
  7. package/dist/agents/roster-store.js +1 -1
  8. package/dist/agents/send-message-tool.d.ts +1 -0
  9. package/dist/agents/send-message-tool.js +2 -0
  10. package/dist/agents/subagent.d.ts +3 -1
  11. package/dist/agents/subagent.js +15 -32
  12. package/dist/agents/tool-filter.js +6 -7
  13. package/dist/agents/verify.d.ts +1 -0
  14. package/dist/agents/verify.js +1 -1
  15. package/dist/core/arg-summary.d.ts +21 -1
  16. package/dist/core/arg-summary.js +61 -14
  17. package/dist/core/auto-compaction.d.ts +1 -0
  18. package/dist/core/auto-compaction.js +1 -1
  19. package/dist/core/fs-write-gate-policy.js +2 -3
  20. package/dist/core/hooks.d.ts +1 -0
  21. package/dist/core/hooks.js +1 -1
  22. package/dist/core/mcp.js +0 -6
  23. package/dist/core/permission-rules.js +2 -3
  24. package/dist/core/runner/active-skill-scope.js +1 -2
  25. package/dist/core/runner/prepare-task.js +16 -8
  26. package/dist/core/runner/runtask.js +19 -16
  27. package/dist/core/runner/session-rule-policy.js +3 -4
  28. package/dist/core/sensitive-path-policy.js +2 -3
  29. package/dist/core/session-reconcile.js +1 -2
  30. package/dist/core/skill-tool-specifier.js +2 -3
  31. package/dist/core/skills-directory.js +2 -3
  32. package/dist/core/task-registry-shared.d.ts +1 -1
  33. package/dist/core/task-registry.d.ts +5 -1
  34. package/dist/core/task-registry.js +12 -30
  35. package/dist/core/task-tool-shape.d.ts +0 -2
  36. package/dist/core/task-tool-shape.js +2 -5
  37. package/dist/core/tool-name-aliases.d.ts +1 -2
  38. package/dist/core/tool-name-aliases.js +42 -60
  39. package/dist/core/tool-policy.js +11 -12
  40. package/dist/core/trace.d.ts +7 -0
  41. package/dist/core/untrusted-egress.d.ts +4 -2
  42. package/dist/core/untrusted-egress.js +21 -9
  43. package/dist/engine/execution-env/node-execution-env.js +1 -1
  44. package/dist/engine/loop/agent-loop.js +3 -12
  45. package/dist/index.d.ts +3 -2
  46. package/dist/index.js +2 -2
  47. package/dist/orchestration/run-spec.js +2 -3
  48. package/dist/orchestration/run-workflow-tool.js +0 -1
  49. package/dist/orchestration/workflow-governance.js +2 -1
  50. package/dist/orchestration/workflow.d.ts +1 -0
  51. package/dist/orchestration/workflow.js +1 -1
  52. package/dist/prompt-assembly/packs/sema-default.js +1 -3
  53. package/dist/prompts/default.js +1 -3
  54. package/dist/prompts/simple-sections.d.ts +0 -1
  55. package/dist/prompts/simple-sections.js +0 -1
  56. package/dist/tools/fs/fs-bash.js +6 -8
  57. package/dist/tools/fs/fs-write.js +0 -1
  58. package/package.json +1 -1
@@ -1,4 +1,3 @@
1
- import { canonicalToolName } from "./tool-name-aliases.js";
2
1
  export const BASH_GENERIC_PARAMS = new Set(["command", "timeout", "description", "run_in_background"]);
3
2
  const DEFAULT_CAPS = {
4
3
  maxRules: 256,
@@ -142,7 +141,7 @@ function compile(rules, caps, primaryFieldGeneric) {
142
141
  continue;
143
142
  }
144
143
  }
145
- const toolName = canonicalToolName(parsed.toolName);
144
+ const toolName = parsed.toolName;
146
145
  if (toolName.startsWith("mcp__") && (parsed.ruleContent !== undefined || indexOfUnescaped(text, "(") !== -1)) {
147
146
  bad(text, "unsupported.mcp_paren", "MCP rules do not support patterns in parentheses (CC parity); use the toolAxes system");
148
147
  continue;
@@ -262,7 +261,7 @@ export function createPermissionRulePolicy(rules, opts) {
262
261
  const defaultAction = opts?.defaultAction ?? "allow";
263
262
  return {
264
263
  check(req) {
265
- const entry = byTool.get(canonicalToolName(req.toolName));
264
+ const entry = byTool.get(req.toolName);
266
265
  if (entry) {
267
266
  if (entry.bare.deny) {
268
267
  return { action: "deny", reason: ruleMessage("denied", entry.bare.deny.ruleText, entry.bare.deny.source) };
@@ -1,6 +1,5 @@
1
1
  import { canonicalizeTarget, fileArgPath } from "../../tools/fs/safety.js";
2
2
  import { PATH_WRITE_TOOLS, isWithin } from "./session-rule-policy.js";
3
- import { canonicalToolName } from "../tool-name-aliases.js";
4
3
  import { parseSkillToolEntry, skillSpecifierRejection } from "../skill-tool-specifier.js";
5
4
  export class ActiveSkillScope {
6
5
  frames = [];
@@ -21,7 +20,7 @@ export function createActiveSkillScopePolicy(opts) {
21
20
  const { scope, env, rootPath, toolEffects } = opts;
22
21
  return {
23
22
  async check(req, signal) {
24
- const toolName = canonicalToolName(req.toolName);
23
+ const toolName = req.toolName;
25
24
  const frames = scope.active();
26
25
  if (frames.length === 0)
27
26
  return { action: "allow" };
@@ -29,7 +29,7 @@ import { reconcileInterruptedSession } from "../session-reconcile.js";
29
29
  import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
30
30
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
31
31
  import { defineTool, isDefineToolProduct } from "../tools.js";
32
- import { canonicalToolName } from "../tool-name-aliases.js";
32
+ import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
33
33
  import { pathToUri } from "../lsp-protocol.js";
34
34
  import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, createOffloadPersist, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, createReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
35
35
  import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
@@ -2018,9 +2018,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2018
2018
  };
2019
2019
  }
2020
2020
  if (spec.agents !== undefined && spec.agents.length > 0) {
2021
- const known = new Set(tools.flatMap((t) => [canonicalToolName(t.name), ...(t.aliases ?? []).map((a) => canonicalToolName(a))]));
2021
+ const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
2022
2022
  for (const def of spec.agents) {
2023
- const unknownAllow = (def.allowTools ?? []).filter((n) => n !== "*" && !known.has(canonicalToolName(n)));
2023
+ const unknownAllow = (def.allowTools ?? []).filter((n) => n !== "*" && !known.has(n));
2024
2024
  if (unknownAllow.length > 0) {
2025
2025
  try {
2026
2026
  deps.onError?.(new Error(`TaskSpec.agents: agent "${def.name}" allows tool(s) ${unknownAllow.join(", ")} not present in this task's assembled roster — likely a typo (the entry would be item-filtered at spawn; the agent stays usable). Advisory only: the delegation pool can differ from this roster, so a tool mounted only on the delegation tool or produced by per-spawn extraTools makes this spurious.`), { phase: "config", sessionId });
@@ -2031,7 +2031,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2031
2031
  for (const n of def.denyTools ?? []) {
2032
2032
  if (n === "*")
2033
2033
  continue;
2034
- if (!known.has(canonicalToolName(n))) {
2034
+ if (!known.has(n)) {
2035
2035
  const e = new Error(`TaskSpec.agents: agent "${def.name}" declares tool "${n}" in its denied tools, but no such tool exists in this deployment — fix the agent's tools list or mount the tool.`);
2036
2036
  e.code = "config.agent.unknown_tool";
2037
2037
  throw e;
@@ -2193,15 +2193,22 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2193
2193
  {
2194
2194
  const nameGroups = toolPolicyNameSets(policy);
2195
2195
  if (nameGroups.length > 0) {
2196
- const known = new Set(tools.flatMap((t) => [canonicalToolName(t.name), ...(t.aliases ?? []).map((a) => canonicalToolName(a))]));
2196
+ const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
2197
2197
  for (const t of harnessTools)
2198
- known.add(canonicalToolName(t.name));
2198
+ known.add(t.name);
2199
2199
  const unmatched = new Set();
2200
2200
  for (const g of nameGroups) {
2201
2201
  for (const [kind, list] of [["deny", g.deny], ["ask", g.ask], ["allow", g.allow]]) {
2202
2202
  for (const n of list ?? []) {
2203
- if (!known.has(canonicalToolName(n)))
2204
- unmatched.add(`"${n}" (${kind})`);
2203
+ if (known.has(n))
2204
+ continue;
2205
+ const retired = RETIRED_TOOL_NAMES.get(n);
2206
+ if (retired !== undefined) {
2207
+ const err = new Error(`tool policy ${kind}-list entry "${n}" is a RETIRED tool name (${retired}) and matches nothing in this run's roster — legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Update the deployment's rule to the current name.`);
2208
+ err.code = "config.legacy_tool_name";
2209
+ throw err;
2210
+ }
2211
+ unmatched.add(`"${n}" (${kind})`);
2205
2212
  }
2206
2213
  }
2207
2214
  }
@@ -3160,6 +3167,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3160
3167
  let result;
3161
3168
  try {
3162
3169
  result = await runToolGate({
3170
+ onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: spec.taskId ?? sessionId, site: f.site, message: f.error.message, ts: Date.now() })),
3163
3171
  event: e,
3164
3172
  preToolUse: hooks?.preToolUse,
3165
3173
  adjudicate,
@@ -36,7 +36,6 @@ import { delimitUntrusted, inlineUntrusted, sanitizeUntrustedText } from "../unt
36
36
  import { reconcileInterruptedSession } from "../session-reconcile.js";
37
37
  import { RunnerSharedToolResultStore } from "../tool-result-store.js";
38
38
  import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
39
- import { canonicalToolName } from "../tool-name-aliases.js";
40
39
  import { toolPolicyNameSets } from "../tool-policy.js";
41
40
  import { defaultTaskRegistry } from "../task-registry.js";
42
41
  import { discloseDroppedPending, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
@@ -748,6 +747,13 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
748
747
  function makeHarnessHandlers(prepared, stats, rs, deps) {
749
748
  const { spec, queue, internals, buildFinalizeText, ident, nudgeSchedule, parentToolCallId, subagentName, pushContent, emitCommitted, startedToolCallIds, toolStartAt, turnToolSpan, writeFamilyOf, toolLabels, postToolBatchHook, batchArgs } = deps;
750
749
  let lastWorkspaceCwd = prepared.cwdRef?.current;
750
+ const announceWorkspaceMove = () => {
751
+ const cwdNow = prepared.cwdRef?.current;
752
+ if (cwdNow !== undefined && cwdNow !== lastWorkspaceCwd) {
753
+ lastWorkspaceCwd = cwdNow;
754
+ queue.push({ type: "workspace_changed", cwd: cwdNow, ...ident() });
755
+ }
756
+ };
751
757
  const onMessageUpdate = (event) => {
752
758
  const ev = event.assistantMessageEvent;
753
759
  if (rs.turn.callStartAt === undefined)
@@ -1000,7 +1006,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
1000
1006
  }));
1001
1007
  internals?.onActivity?.({ phase: "end", toolCallId: event.toolCallId, toolName: event.toolName, isError: event.isError, at: toolNow });
1002
1008
  if (prepared.lspDiagnostics && !event.isError) {
1003
- const name = canonicalToolName(event.toolName);
1009
+ const name = event.toolName;
1004
1010
  if (name === "Edit" || name === "Write" || name === "NotebookEdit") {
1005
1011
  const d = event.result?.details;
1006
1012
  const p = typeof d?.filePath === "string" ? d.filePath : typeof d?.notebookPath === "string" ? d.notebookPath : undefined;
@@ -1026,11 +1032,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
1026
1032
  })(),
1027
1033
  ...ident(),
1028
1034
  });
1029
- const cwdNow = prepared.cwdRef?.current;
1030
- if (cwdNow !== undefined && cwdNow !== lastWorkspaceCwd) {
1031
- lastWorkspaceCwd = cwdNow;
1032
- queue.push({ type: "workspace_changed", cwd: cwdNow, ...ident() });
1033
- }
1035
+ announceWorkspaceMove();
1034
1036
  if (spec.outputSchema && event.toolName === OUTPUT_TOOL_NAME) {
1035
1037
  if (event.isError) {
1036
1038
  rs.degrade.outputErrorStreak += 1;
@@ -1108,7 +1110,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
1108
1110
  }
1109
1111
  }
1110
1112
  };
1111
- return { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd };
1113
+ return { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove };
1112
1114
  }
1113
1115
  export class Runner {
1114
1116
  deps;
@@ -1914,18 +1916,18 @@ export class Runner {
1914
1916
  }
1915
1917
  }
1916
1918
  rs.attach.attachmentsInjected = 0;
1917
- const todoToolMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TodoWrite");
1918
- const taskToolsMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => canonicalToolName(t.name) === "TaskCreate");
1919
+ const todoToolMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => t.name === "TodoWrite");
1920
+ const taskToolsMounted = rs.attach.attachState !== undefined && prepared.tools.some((t) => t.name === "TaskCreate");
1919
1921
  const writeFamilyByName = new Map();
1920
1922
  if (rs.attach.attachState !== undefined) {
1921
1923
  for (const t of prepared.tools) {
1922
- const family = writeFamilyOfCanonical(canonicalToolName(t.name));
1924
+ const family = writeFamilyOfCanonical(t.name);
1923
1925
  if (family !== undefined)
1924
1926
  for (const n of [t.name, ...(t.aliases ?? [])])
1925
1927
  writeFamilyByName.set(n, family);
1926
1928
  }
1927
1929
  }
1928
- const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(canonicalToolName(name));
1930
+ const writeFamilyOf = (name) => writeFamilyByName.get(name) ?? writeFamilyOfCanonical(name);
1929
1931
  const toolStartAt = new Map();
1930
1932
  const turnToolSpan = {};
1931
1933
  const startedToolCallIds = new Set();
@@ -2091,7 +2093,7 @@ export class Runner {
2091
2093
  const postToolBatchHook = (spec.hooks ?? this.deps.hooks)?.postToolBatch;
2092
2094
  const batchArgs = postToolBatchHook ? new Map() : undefined;
2093
2095
  rs.turn.toolBatch = [];
2094
- const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd } = makeHarnessHandlers(prepared, stats, rs, {
2096
+ const { onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, announceWorkspaceMove } = makeHarnessHandlers(prepared, stats, rs, {
2095
2097
  spec, queue, internals, buildFinalizeText, ident, nudgeSchedule, parentToolCallId, subagentName,
2096
2098
  pushContent, emitCommitted, startedToolCallIds, toolStartAt, turnToolSpan, writeFamilyOf,
2097
2099
  toolLabels, postToolBatchHook, batchArgs,
@@ -2461,6 +2463,7 @@ export class Runner {
2461
2463
  });
2462
2464
  if (resume.outcome.gate === "policy_ask")
2463
2465
  resume.decisionDelivered = true;
2466
+ announceWorkspaceMove();
2464
2467
  }
2465
2468
  if (walltimeExhaustedResume) {
2466
2469
  timeout.fired = true;
@@ -3290,7 +3293,7 @@ export class Runner {
3290
3293
  return;
3291
3294
  if (resume.outcome.gate === "policy_ask" &&
3292
3295
  resume.outcome.decision === "allow" &&
3293
- !prepared.tools.some((t) => t.name === canonicalToolName(pendingAction.toolName))) {
3296
+ !prepared.tools.some((t) => t.name === pendingAction.toolName)) {
3294
3297
  const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume — refusing to continue as if it ran; the checkpoint is reopened for a retry with the tool present`);
3295
3298
  e.code = "resume.tool_unavailable";
3296
3299
  throw e;
@@ -3355,7 +3358,7 @@ export class Runner {
3355
3358
  return;
3356
3359
  }
3357
3360
  }
3358
- const tool = prepared.tools.find((t) => t.name === canonicalToolName(pendingAction.toolName));
3361
+ const tool = prepared.tools.find((t) => t.name === pendingAction.toolName);
3359
3362
  if (!tool) {
3360
3363
  const e = new Error(`the approved tool "${pendingAction.toolName}" is no longer available on resume`);
3361
3364
  e.code = "resume.tool_unavailable";
@@ -3375,7 +3378,7 @@ export class Runner {
3375
3378
  }
3376
3379
  const executedIsError = res.isError === true;
3377
3380
  emitEnd(executedIsError);
3378
- onResolvedToolSuccess?.(canonicalToolName(pendingAction.toolName), executedIsError ? undefined : res.details);
3381
+ onResolvedToolSuccess?.(pendingAction.toolName, executedIsError ? undefined : res.details);
3379
3382
  const eid = await prepared.session.appendMessage({
3380
3383
  role: "toolResult",
3381
3384
  toolCallId: pendingAction.toolCallId,
@@ -1,7 +1,6 @@
1
1
  import { canonicalizeTarget, fileArgPath } from "../../tools/fs/safety.js";
2
2
  import { isWinFormPath } from "../../tools/fs/safety.js";
3
3
  import { createCoarseCommandNamePolicy } from "../tool-policy.js";
4
- import { canonicalToolName } from "../tool-name-aliases.js";
5
4
  export const PATH_WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit"]);
6
5
  export function isWithin(root, p) {
7
6
  if (!root)
@@ -22,8 +21,8 @@ export function isWithin(root, p) {
22
21
  const deny = (reason) => ({ action: "deny", reason, decisionReason: "rule" });
23
22
  export function createSessionRulePolicy(rules, opts) {
24
23
  const { env, rootPath, toolEffects } = opts;
25
- const toolDeny = new Set((rules.toolDeny ?? []).map(canonicalToolName));
26
- const toolAllow = rules.toolAllow ? new Set(rules.toolAllow.map(canonicalToolName)) : undefined;
24
+ const toolDeny = new Set(rules.toolDeny ?? []);
25
+ const toolAllow = rules.toolAllow ? new Set(rules.toolAllow) : undefined;
27
26
  const cmdPolicy = rules.commandAllow || rules.commandDeny
28
27
  ? createCoarseCommandNamePolicy({
29
28
  ...(rules.commandAllow ? { allow: rules.commandAllow } : {}),
@@ -34,7 +33,7 @@ export function createSessionRulePolicy(rules, opts) {
34
33
  const allowDirs = rules.allowDirs && rules.allowDirs.length > 0 ? rules.allowDirs : undefined;
35
34
  return {
36
35
  async check(req, signal) {
37
- const toolName = canonicalToolName(req.toolName);
36
+ const toolName = req.toolName;
38
37
  if (toolDeny.has(toolName))
39
38
  return deny(`tool "${req.toolName}" is denied by a session rule`);
40
39
  if (toolAllow && !toolAllow.has(toolName)) {
@@ -1,5 +1,4 @@
1
1
  import { canonicalizeTarget, writeTargetPath } from "../tools/fs/safety.js";
2
- import { canonicalToolName } from "./tool-name-aliases.js";
3
2
  const DEFAULT_GUARDED_TOOLS = ["Write", "Edit", "MultiEdit", "NotebookEdit"];
4
3
  export const RECOMMENDED_SENSITIVE_PATTERNS = [
5
4
  ".env",
@@ -56,10 +55,10 @@ function matchSensitive(canonicalKey, compiled) {
56
55
  }
57
56
  export function createSensitivePathPolicy(opts) {
58
57
  const compiled = compilePatterns(opts.patterns);
59
- const guarded = new Set((opts.tools ?? DEFAULT_GUARDED_TOOLS).map(canonicalToolName));
58
+ const guarded = new Set(opts.tools ?? DEFAULT_GUARDED_TOOLS);
60
59
  return {
61
60
  async check(req, signal) {
62
- const canonical = canonicalToolName(req.toolName);
61
+ const canonical = req.toolName;
63
62
  if (compiled.length === 0 || !guarded.has(canonical))
64
63
  return { action: "allow" };
65
64
  const path = writeTargetPath(canonical, req.args);
@@ -1,4 +1,3 @@
1
- import { canonicalToolName } from "./tool-name-aliases.js";
2
1
  const INTERRUPTED_UNKNOWN = "[INTERRUPTED] The previous run ended before this tool call's result was recorded. Its outcome " +
3
2
  "is UNKNOWN — the action may have completed, partially completed, or never run. Do not assume " +
4
3
  "success or failure. Before relying on it: if it is safe to read, query the current state to " +
@@ -72,7 +71,7 @@ export async function reconcileInterruptedSession(session, toolEffects, suspende
72
71
  const orphans = findOrphanToolCalls(messages, suspendedBatch).filter((o) => o.kind !== "result");
73
72
  const recovered = [];
74
73
  for (const orphan of orphans) {
75
- const effect = toolEffects?.get(canonicalToolName(orphan.toolName)) ?? "write";
74
+ const effect = toolEffects?.get(orphan.toolName) ?? "write";
76
75
  const effectText = effect === "read" ? INTERRUPTED_SAFE : effect === "idempotent" ? INTERRUPTED_IDEMPOTENT : INTERRUPTED_UNKNOWN;
77
76
  const neverStarted = startedToolCallIds !== undefined && !startedToolCallIds.has(orphan.toolCallId);
78
77
  const text = neverStarted ? INTERRUPTED_NEVER_STARTED : effectText;
@@ -1,11 +1,10 @@
1
1
  import { parsePermissionRule, wildcardMatch } from "./permission-rules.js";
2
- import { canonicalToolName } from "./tool-name-aliases.js";
3
2
  import { COARSE_SHELL_TOOLS } from "./tool-policy.js";
4
3
  import { parseLeadingCommandName } from "../tools/fs/bash-readonly-classifier.js";
5
- const SPECIFIER_ENFORCED_TOOLS = new Set(COARSE_SHELL_TOOLS.map(canonicalToolName));
4
+ const SPECIFIER_ENFORCED_TOOLS = new Set(COARSE_SHELL_TOOLS);
6
5
  export function parseSkillToolEntry(entry) {
7
6
  const parsed = parsePermissionRule(entry);
8
- const name = canonicalToolName(parsed.toolName);
7
+ const name = parsed.toolName;
9
8
  return parsed.ruleContent === undefined ? { raw: entry, name } : { raw: entry, name, specifier: parsed.ruleContent };
10
9
  }
11
10
  export function isSkillSpecifierEnforced(canonicalName) {
@@ -1,6 +1,5 @@
1
1
  import { readFileSync, readdirSync, realpathSync, statSync } from "node:fs";
2
2
  import { isAbsolute, join, relative } from "node:path";
3
- import { canonicalToolName } from "./tool-name-aliases.js";
4
3
  import { isSkillSpecifierEnforced, parseSkillToolEntry } from "./skill-tool-specifier.js";
5
4
  const SKILL_FILE = "SKILL.md";
6
5
  const RESOURCE_DIRS = ["assets", "references", "scripts"];
@@ -340,7 +339,7 @@ function manifestFromAllowedTools(declared, disallowed, skillName, deployedTools
340
339
  return undefined;
341
340
  let allowTools = names;
342
341
  if (deployedTools !== undefined) {
343
- const mounted = new Set(deployedTools.map(canonicalToolName));
342
+ const mounted = new Set(deployedTools);
344
343
  allowTools = names.filter((n) => mounted.has(parseSkillToolEntry(n).name));
345
344
  for (const n of names) {
346
345
  if (!mounted.has(parseSkillToolEntry(n).name)) {
@@ -353,7 +352,7 @@ function manifestFromAllowedTools(declared, disallowed, skillName, deployedTools
353
352
  }
354
353
  }
355
354
  if (disallowed.length > 0) {
356
- const denied = new Set(disallowed.map(canonicalToolName));
355
+ const denied = new Set(disallowed);
357
356
  allowTools = allowTools.filter((n) => !denied.has(parseSkillToolEntry(n).name));
358
357
  }
359
358
  return { allowTools, lineageId: `skill:${skillName}` };
@@ -104,7 +104,7 @@ export interface BackgroundAgentTaskHandle extends SemaTaskHandle {
104
104
  durable?: DurableAgentLane;
105
105
  notify?: (notification: import("./task-notification.js").TaskNotificationPayload, opts?: {
106
106
  priority?: import("./task-notification.js").SystemInjectionPriority;
107
- }) => void | "queued" | "parked" | "dropped_duplicate" | Promise<"queued" | "parked" | "dropped_duplicate">;
107
+ }) => "queued" | "parked" | "dropped_duplicate" | Promise<"queued" | "parked" | "dropped_duplicate">;
108
108
  retainedContinuation?: boolean;
109
109
  reviveCycle?: number;
110
110
  cycleSeq?: number;
@@ -63,13 +63,17 @@ export interface AccessibleTaskRow {
63
63
  parentTaskId?: string;
64
64
  parentSessionId?: string;
65
65
  rootSessionId?: string;
66
+ createdAt?: number;
66
67
  }
67
68
  export declare class TaskRegistry {
68
69
  private handles;
69
70
  private sessionReapHooks;
70
71
  private bgQuiescenceWatchers;
71
- private legacyToTaskId;
72
72
  private readonly notifier;
73
+ notifierFailureCounts(): ReadonlyArray<{
74
+ site: string;
75
+ count: number;
76
+ }>;
73
77
  private readonly core;
74
78
  durableAgentArmed(id: string): boolean;
75
79
  durableAgentRowProbe(id: string): (() => Promise<boolean>) | undefined;
@@ -9,7 +9,7 @@ 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, TASK_OUTPUT_ALIASES, TASK_STOP_ALIASES, TASK_OUTPUT_CONTRACT, TASK_STOP_CONTRACT, 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";
12
+ 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, REGISTRY_TASK_TOOL_CAPS, composeTaskOutputDescription, composeTaskOutputParams, composeTaskStopDescription, } from "./task-tool-shape.js";
13
13
  import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
14
14
  export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
15
15
  const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
@@ -102,8 +102,10 @@ export class TaskRegistry {
102
102
  handles = new Map();
103
103
  sessionReapHooks = new Set();
104
104
  bgQuiescenceWatchers = new Map();
105
- legacyToTaskId = new Map();
106
105
  notifier = createSafeNotifier();
106
+ notifierFailureCounts() {
107
+ return this.notifier.failedSites().map((site) => ({ site, count: this.notifier.failuresAt(site) }));
108
+ }
107
109
  core = (() => {
108
110
  const self = this;
109
111
  return {
@@ -226,7 +228,7 @@ export class TaskRegistry {
226
228
  const prefix = TASK_PREFIX[type];
227
229
  for (let i = 0; i < 32; i++) {
228
230
  const id = `${prefix}${randomBytes(8).toString("hex")}`;
229
- if (!this.handles.has(id) && !this.legacyToTaskId.has(id))
231
+ if (!this.handles.has(id))
230
232
  return id;
231
233
  }
232
234
  throw new Error(`TaskRegistry: exhausted task id mint retries for ${type}`);
@@ -253,7 +255,6 @@ export class TaskRegistry {
253
255
  ...(input.onTerminal !== undefined ? { onTerminal: input.onTerminal } : {}),
254
256
  ...(input.outputFile !== undefined ? { outputFile: input.outputFile } : {}),
255
257
  });
256
- this.legacyToTaskId.set(String(input.shellId), id);
257
258
  if (input.onTerminal)
258
259
  this.startBashWatcher(id, input.onTerminal);
259
260
  return id;
@@ -290,6 +291,7 @@ export class TaskRegistry {
290
291
  ...(hb.parentTaskId !== undefined ? { parentTaskId: hb.parentTaskId } : {}),
291
292
  ...(hb.parentSessionId !== undefined ? { parentSessionId: hb.parentSessionId } : {}),
292
293
  ...(hb.rootSessionId !== undefined ? { rootSessionId: hb.rootSessionId } : {}),
294
+ createdAt: h.createdAt,
293
295
  };
294
296
  }
295
297
  markStopSource(id, source) {
@@ -318,17 +320,9 @@ export class TaskRegistry {
318
320
  return marked;
319
321
  }
320
322
  markStopSourceByShellId(shellId, source, env) {
321
- const sid = String(shellId);
322
- const taskId = this.legacyToTaskId.get(sid);
323
- if (taskId !== undefined) {
324
- const handle = this.handles.get(taskId);
325
- if (handle && handle.type === "background_bash" && (env === undefined || handle.env === env)) {
326
- this.markStopSource(taskId, source);
327
- return;
328
- }
329
- }
330
323
  if (env === undefined)
331
324
  return;
325
+ const sid = String(shellId);
332
326
  for (const [id, handle] of this.handles) {
333
327
  if (handle.type === "background_bash" && String(handle.shellId) === sid && handle.env === env) {
334
328
  this.markStopSource(id, source);
@@ -349,9 +343,6 @@ export class TaskRegistry {
349
343
  }
350
344
  return true;
351
345
  };
352
- const taskId = this.legacyToTaskId.get(sid);
353
- if (taskId !== undefined && clear(taskId))
354
- return;
355
346
  if (env === undefined)
356
347
  return;
357
348
  for (const [id, handle] of this.handles) {
@@ -958,11 +949,8 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
958
949
  if (now - handle.updatedAt < terminalTtlMs)
959
950
  continue;
960
951
  this.handles.delete(id);
961
- if (handle.type === "background_bash") {
962
- this.legacyToTaskId.delete(String(handle.shellId));
963
- if (handle.watcher !== undefined)
964
- clearInterval(handle.watcher);
965
- }
952
+ if (handle.type === "background_bash" && handle.watcher !== undefined)
953
+ clearInterval(handle.watcher);
966
954
  if (handle.type === "monitor" && handle.watcher !== undefined) {
967
955
  handle.timers.clearInterval(handle.watcher);
968
956
  handle.watcher = undefined;
@@ -999,7 +987,6 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
999
987
  this.handles.delete(id);
1000
988
  if (handle.status === "running" && handle.owner !== undefined)
1001
989
  evictedRunningOwners.add(handle.owner);
1002
- this.legacyToTaskId.delete(String(handle.shellId));
1003
990
  if (handle.watcher !== undefined)
1004
991
  clearInterval(handle.watcher);
1005
992
  deleted++;
@@ -1009,10 +996,7 @@ The durable record is still marked running (last update ${new Date(row.updatedAt
1009
996
  return deleted;
1010
997
  }
1011
998
  resolveInternal(id, access) {
1012
- const taskId = this.handles.has(id) ? id : this.legacyToTaskId.get(id);
1013
- if (!taskId)
1014
- return undefined;
1015
- const handle = this.handles.get(taskId);
999
+ const handle = this.handles.get(id);
1016
1000
  if (!handle || !canAccess(handle, access))
1017
1001
  return undefined;
1018
1002
  return handle;
@@ -1216,14 +1200,13 @@ export function createTaskOutputTool(opts) {
1216
1200
  const caps = { ...REGISTRY_TASK_TOOL_CAPS, notification: opts.notificationWired !== false };
1217
1201
  return defineTool({
1218
1202
  name: TASK_OUTPUT_TOOL_NAME,
1219
- aliases: [...TASK_OUTPUT_ALIASES],
1220
1203
  contract: TASK_OUTPUT_CONTRACT,
1221
1204
  description: composeTaskOutputDescription(caps),
1222
1205
  parameters: composeTaskOutputParams(caps),
1223
1206
  effect: "read",
1224
1207
  execute: async (rawArgs, ctx) => {
1225
1208
  const args = rawArgs;
1226
- const id = resolveTaskIdArg(args.task_id, args.bash_id, args.runId);
1209
+ const id = resolveTaskIdArg(args.task_id);
1227
1210
  if (!id)
1228
1211
  return errorResult(TASK_OUTPUT_MISSING_ID_MESSAGE);
1229
1212
  const requestedMs = typeof args.timeout === "number" ? args.timeout : undefined;
@@ -1266,14 +1249,13 @@ export function createTaskOutputTool(opts) {
1266
1249
  export function createTaskStopTool(opts) {
1267
1250
  return defineTool({
1268
1251
  name: TASK_STOP_TOOL_NAME,
1269
- aliases: [...TASK_STOP_ALIASES],
1270
1252
  contract: TASK_STOP_CONTRACT,
1271
1253
  description: composeTaskStopDescription(REGISTRY_TASK_TOOL_CAPS),
1272
1254
  parameters: TASK_STOP_PARAMS,
1273
1255
  effect: "write",
1274
1256
  execute: async (rawArgs, ctx) => {
1275
1257
  const args = rawArgs;
1276
- const id = resolveTaskIdArg(args.task_id, args.shell_id, args.runId);
1258
+ const id = resolveTaskIdArg(args.task_id, args.shell_id);
1277
1259
  if (!id)
1278
1260
  return errorResult(TASK_STOP_MISSING_ID_MESSAGE);
1279
1261
  const r = await opts.registry.stopTask(id, { owner: ctx.taskId ?? opts.owner, scope: ctx.principal ?? opts.scope, ...((ctx.sessionId ?? opts.sessionId) !== undefined ? { sessionId: ctx.sessionId ?? opts.sessionId } : {}) }, { workflowStore: opts.workflowStore, agentStore: opts.agentStore });
@@ -1,8 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  export declare const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
3
3
  export declare const TASK_STOP_TOOL_NAME = "TaskStop";
4
- export declare const TASK_OUTPUT_ALIASES: readonly ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
5
- export declare const TASK_STOP_ALIASES: readonly ["KillShell", "KillBash"];
6
4
  export declare const TASK_OUTPUT_CONTRACT: {
7
5
  readonly contractId: "core.task_output@1";
8
6
  readonly implementationRevision: "1";
@@ -1,8 +1,6 @@
1
1
  import { Type } from "typebox";
2
2
  export const TASK_OUTPUT_TOOL_NAME = "TaskOutput";
3
3
  export const TASK_STOP_TOOL_NAME = "TaskStop";
4
- export const TASK_OUTPUT_ALIASES = ["BashOutput", "AgentOutputTool", "BashOutputTool", "AgentOutput", "WorkflowStatus"];
5
- export const TASK_STOP_ALIASES = ["KillShell", "KillBash"];
6
4
  export const TASK_OUTPUT_CONTRACT = { contractId: "core.task_output@1", implementationRevision: "1" };
7
5
  export const TASK_STOP_CONTRACT = { contractId: "core.task_stop@1", implementationRevision: "1" };
8
6
  export const TASK_OUTPUT_MISSING_ID_MESSAGE = "Error (TaskOutput): Missing required parameter: task_id";
@@ -50,9 +48,8 @@ export function composeTaskOutputDescription(caps) {
50
48
  "poll captures any final output. ");
51
49
  }
52
50
  if (!caps.lanes)
53
- parts.push("Optional `filter` is a regex keeping only matching lines. ");
54
- parts.push("Legacy bash_id and runId arguments are accepted for compatibility.");
55
- return parts.join("");
51
+ parts.push("Optional `filter` is a regex keeping only matching lines.");
52
+ return parts.join("").trimEnd();
56
53
  }
57
54
  export function composeTaskStopDescription(caps) {
58
55
  return [
@@ -1,2 +1 @@
1
- export declare const TOMBSTONED_TOOLS: Readonly<Record<string, string>>;
2
- export declare function canonicalToolName(name: string): string;
1
+ export declare const RETIRED_TOOL_NAMES: ReadonlyMap<string, string>;