@sema-agent/core 7.23.1 → 7.23.3

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 (124) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/agents/agent-transcript-tool.js +2 -1
  3. package/dist/agents/cascade.js +2 -1
  4. package/dist/agents/list-agents-tool.js +2 -1
  5. package/dist/agents/observer.js +3 -2
  6. package/dist/agents/peer-session-drain.js +2 -1
  7. package/dist/agents/retain-ledger.js +2 -1
  8. package/dist/agents/send-message-tool.js +11 -10
  9. package/dist/agents/subagent.js +18 -17
  10. package/dist/agents/teacher.js +5 -4
  11. package/dist/brain/circuit-breaker.js +2 -1
  12. package/dist/brain/degrading.js +2 -1
  13. package/dist/brain/errors.js +24 -15
  14. package/dist/brain/failover.js +2 -1
  15. package/dist/brain/stream-engine.js +2 -1
  16. package/dist/core/a2a.js +4 -3
  17. package/dist/core/artifact-host.js +2 -1
  18. package/dist/core/ask-question.js +3 -2
  19. package/dist/core/auto-promote.js +3 -2
  20. package/dist/core/gate-bounded-await.js +2 -1
  21. package/dist/core/gate-fold.js +4 -1
  22. package/dist/core/gate-lanes.js +4 -2
  23. package/dist/core/git-worktree-env.js +6 -5
  24. package/dist/core/hooks.js +4 -4
  25. package/dist/core/lsp-session.js +3 -2
  26. package/dist/core/mcp.js +4 -3
  27. package/dist/core/memory-admission.js +2 -1
  28. package/dist/core/memory-engine/consolidation-driver.js +5 -4
  29. package/dist/core/memory-engine/consolidation.js +2 -1
  30. package/dist/core/memory-engine/delegation-settlement.js +4 -3
  31. package/dist/core/memory-engine/distiller.js +8 -4
  32. package/dist/core/memory-engine/engine.js +63 -55
  33. package/dist/core/memory-engine/file-backend.js +11 -10
  34. package/dist/core/memory-engine/layout.js +5 -4
  35. package/dist/core/memory-engine/session-incarnation.js +2 -1
  36. package/dist/core/memory.js +2 -1
  37. package/dist/core/oracle-isolation.js +3 -2
  38. package/dist/core/permission-rule-store.js +2 -1
  39. package/dist/core/runner/announce-once-ledger.js +2 -1
  40. package/dist/core/runner/compaction-seams.js +3 -2
  41. package/dist/core/runner/gate-exit.d.ts +5 -0
  42. package/dist/core/runner/git-leg-delivery.js +4 -3
  43. package/dist/core/runner/git-status-frame.js +2 -1
  44. package/dist/core/runner/mcp-redial.js +2 -1
  45. package/dist/core/runner/memory-consolidation-driver.js +2 -1
  46. package/dist/core/runner/permission-rule-lanes.js +3 -2
  47. package/dist/core/runner/prepare-artifact.js +2 -1
  48. package/dist/core/runner/prepare-ask-lane.js +2 -1
  49. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  50. package/dist/core/runner/prepare-inherited-gate.js +2 -1
  51. package/dist/core/runner/prepare-memory-engine-session.js +2 -1
  52. package/dist/core/runner/prepare-memory.js +5 -4
  53. package/dist/core/runner/prepare-policy-chain.js +3 -2
  54. package/dist/core/runner/prepare-project-context.js +3 -2
  55. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  56. package/dist/core/runner/prepare-suspend-saga.js +2 -1
  57. package/dist/core/runner/prepare-task.js +6 -5
  58. package/dist/core/runner/prepare-turn-wiring.js +2 -1
  59. package/dist/core/runner/resume-apply.js +5 -4
  60. package/dist/core/runner/resume-preflight.js +3 -2
  61. package/dist/core/runner/run-git-lane.js +4 -3
  62. package/dist/core/runner/run-leg.js +4 -3
  63. package/dist/core/runner/run-recovery-lanes.js +3 -2
  64. package/dist/core/runner/run-settle-and-teardown.js +2 -1
  65. package/dist/core/runner/run-stop-and-final-verify.js +2 -1
  66. package/dist/core/runner/run-telemetry-and-budget-seats.js +2 -1
  67. package/dist/core/runner/run-terminal-adoption.js +6 -5
  68. package/dist/core/runner/run-turn-boundary.js +5 -4
  69. package/dist/core/runner/runtask.js +2 -1
  70. package/dist/core/runner/stream-settle-backstop.js +5 -4
  71. package/dist/core/runner/stream-steer-verb.js +2 -1
  72. package/dist/core/runner/strict-output-schema.js +2 -1
  73. package/dist/core/runner/teardown-bounded.js +2 -1
  74. package/dist/core/safe-notify.js +2 -1
  75. package/dist/core/skills-directory.js +2 -1
  76. package/dist/core/strategy-store.js +2 -1
  77. package/dist/core/task-registry-agent.js +2 -1
  78. package/dist/core/task-registry-monitor.js +2 -1
  79. package/dist/core/task-registry-workflow.js +2 -1
  80. package/dist/core/task-registry.js +3 -2
  81. package/dist/core/thrown-value.d.ts +79 -0
  82. package/dist/core/thrown-value.js +56 -0
  83. package/dist/core/tool-errors.d.ts +0 -6
  84. package/dist/core/tool-errors.js +36 -14
  85. package/dist/core/tool-model-gate.js +2 -1
  86. package/dist/core/tool-policy.js +6 -13
  87. package/dist/core/tool-result-store.js +2 -1
  88. package/dist/core/tool-spec.d.ts +12 -0
  89. package/dist/core/write-protect.js +3 -2
  90. package/dist/engine/compaction/compaction.js +3 -4
  91. package/dist/engine/harness/agent-harness.js +2 -1
  92. package/dist/engine/harness/types.js +8 -7
  93. package/dist/engine/loop/agent-loop.js +7 -6
  94. package/dist/engine/lsp/node-lsp-manager.js +5 -4
  95. package/dist/engine/lsp/stdio-lsp-transport.js +12 -5
  96. package/dist/orchestration/dev-vm-script-runner.js +2 -1
  97. package/dist/orchestration/run-workflow-tool.js +7 -6
  98. package/dist/orchestration/workflow-governance.js +2 -1
  99. package/dist/orchestration/workflow-sandbox-conformance.js +3 -2
  100. package/dist/orchestration/workflow-script-store.js +2 -1
  101. package/dist/orchestration/workflow.js +15 -14
  102. package/dist/server/http.js +3 -2
  103. package/dist/stores/file/adoption/adopt.js +3 -2
  104. package/dist/stores/file/fs-atomic.js +2 -1
  105. package/dist/stores/file/permission-rule-adopt.js +2 -1
  106. package/dist/stores/file/permission-rule-store.js +2 -1
  107. package/dist/stores/file/session-store.js +4 -3
  108. package/dist/stores/file/strategy-store.js +7 -6
  109. package/dist/tools/artifact/artifact-tool.js +3 -2
  110. package/dist/tools/artifact/conformance.js +4 -3
  111. package/dist/tools/fs/bash-readonly-classifier.d.ts +20 -9
  112. package/dist/tools/fs/bash-readonly-classifier.js +7 -3
  113. package/dist/tools/fs/fs-bash.d.ts +16 -10
  114. package/dist/tools/fs/fs-bash.js +84 -35
  115. package/dist/tools/fs/fs-write.js +3 -2
  116. package/dist/tools/fs/safety.js +2 -1
  117. package/dist/tools/fs/search.js +3 -2
  118. package/dist/tools/monitor.js +3 -2
  119. package/dist/tools/sql.js +2 -1
  120. package/dist/tools/web.js +5 -15
  121. package/dist/tools/worktree.js +12 -9
  122. package/package.json +2 -1
  123. package/wire-changes/core@7.23.2.json +42 -0
  124. package/wire-changes/core@7.23.3.json +25 -0
@@ -6,6 +6,7 @@ import { classifyInputTooLong } from "./input-too-long.js";
6
6
  import { FAST_MAX_BACKOFF_MS, providerWaitHint, retryBackoffMs } from "./retry.js";
7
7
  import { bindBrainStatusEmitter, emitBrainStatus, emitBrainTelemetry } from "./status-sink.js";
8
8
  import { createConnectController, resolveStallTimeoutMs } from "./timeout.js";
9
+ import { thrownText } from "../core/thrown-value.js";
9
10
  const DEFAULT_MAX_RETRIES = 10;
10
11
  const MAX_RETRIES_ENV_CEILING = 15;
11
12
  const MAX_RETRIES_ENV = "SEMA_MAX_RETRIES";
@@ -270,7 +271,7 @@ export function runStreamingBrain(args) {
270
271
  terminalWaitDetail = aborted ? "cancelled while waiting for the first token" : "the first-token wait ended in failure";
271
272
  const errorMsg = emptyAssistant(model);
272
273
  errorMsg.stopReason = aborted ? "aborted" : "error";
273
- errorMsg.errorMessage = err instanceof Error ? err.message : String(err);
274
+ errorMsg.errorMessage = thrownText(err);
274
275
  if (!aborted && err instanceof BrainError) {
275
276
  if (err.inputTooLong === true)
276
277
  errorMsg.errorKind = "input_too_long";
package/dist/core/a2a.js CHANGED
@@ -6,6 +6,7 @@ import { describeHttpTransportFailure } from "./mcp-failure.js";
6
6
  import { findNamespacePrefixCollision, mintNamespacePrefix, mintNamespacedToolName } from "./protocol-naming.js";
7
7
  import { A2A_NAMESPACE } from "./protocol-table.js";
8
8
  import { delimitUntrusted, inlineUntrusted } from "./untrusted-text.js";
9
+ import { thrownText } from "./thrown-value.js";
9
10
  const A2A_REQUEST_TIMEOUT_MS = 30_000;
10
11
  const A2A_CARD_TIMEOUT_MS = 10_000;
11
12
  const A2A_CALL_TOTAL_TIMEOUT_MS = 10 * 60_000;
@@ -570,7 +571,7 @@ async function materializePeer(spec, principal, lifecycle, signal) {
570
571
  };
571
572
  }
572
573
  function asPeerWarning(spec, err) {
573
- const detail = err instanceof Error ? err.message : String(err);
574
+ const detail = thrownText(err);
574
575
  const warning = new Error(`a2a: peer "${spec.name}" could not be materialized — skipped (${detail})`, { cause: err });
575
576
  warning.code = "a2a.peer_unavailable";
576
577
  return warning;
@@ -603,7 +604,7 @@ export async function materializeA2aTools(specs, principal, signal) {
603
604
  }
604
605
  else {
605
606
  warnings.push(asPeerWarning(spec, outcome.reason));
606
- statuses.push({ name: spec.name, status: "failed", error: inlineUntrusted(outcome.reason instanceof Error ? outcome.reason.message : String(outcome.reason), A2A_ERROR_TEXT_MAX_CHARS) });
607
+ statuses.push({ name: spec.name, status: "failed", error: inlineUntrusted(thrownText(outcome.reason), A2A_ERROR_TEXT_MAX_CHARS) });
607
608
  }
608
609
  }
609
610
  return {
@@ -650,7 +651,7 @@ export async function materializeA2aTools(specs, principal, signal) {
650
651
  toolCount: previous.length,
651
652
  added: [],
652
653
  removed: [],
653
- error: inlineUntrusted(err instanceof Error ? err.message : String(err), A2A_ERROR_TEXT_MAX_CHARS),
654
+ error: inlineUntrusted(thrownText(err), A2A_ERROR_TEXT_MAX_CHARS),
654
655
  });
655
656
  }
656
657
  }
@@ -1,4 +1,5 @@
1
1
  import { isAbsoluteForFamily, pathFamilyOf } from "../tools/fs/safety.js";
2
+ import { thrownText } from "./thrown-value.js";
2
3
  export const ARTIFACT_ACTIONS = ["publish", "list", "read", "list_files", "read_file"];
3
4
  export const ARTIFACT_PLANES = ["hosted", "local-stub"];
4
5
  export const ARTIFACT_COMMIT_CERTAINTIES = ["throw_means_uncommitted", "uncertain"];
@@ -51,7 +52,7 @@ export function readArtifactHostCapabilities(host) {
51
52
  raw = h.capabilities.call(host);
52
53
  }
53
54
  catch (err) {
54
- return { ok: false, problem: `artifactHost.capabilities() threw: ${err instanceof Error ? err.message : String(err)}` };
55
+ return { ok: false, problem: `artifactHost.capabilities() threw: ${thrownText(err)}` };
55
56
  }
56
57
  if (raw === null || typeof raw !== "object")
57
58
  return { ok: false, problem: "artifactHost.capabilities() did not return an object" };
@@ -4,6 +4,7 @@ import { boundInputHashOf } from "./canonical-json.js";
4
4
  import { defineTool, errorResult } from "./tools.js";
5
5
  import { delimitUntrusted, inlineUntrusted } from "./untrusted-text.js";
6
6
  import { toolFace } from "./tool-catalog-entries.js";
7
+ import { thrownText } from "./thrown-value.js";
7
8
  export const ASK_USER_QUESTION_TOOL_NAME = "AskUserQuestion";
8
9
  export function classifyQuestionOutcome(outcome) {
9
10
  let unavailable;
@@ -299,7 +300,7 @@ export function createAskUserQuestionTool(onQuestion, source, opts) {
299
300
  if (opts?.posture === "interactive" && opts.interactiveFallback !== true) {
300
301
  return {
301
302
  content: `Error (AskUserQuestion): the wired human channel failed to deliver an answer ` +
302
- `(${e instanceof Error ? e.message : String(e)}). This run declared interaction posture ` +
303
+ `(${thrownText(e)}). This run declared interaction posture ` +
303
304
  `"interactive", so the engine will not silently self-answer; fix the question channel ` +
304
305
  `(or opt into the synthetic continuation explicitly via interactiveQuestionFallback).`,
305
306
  isError: true,
@@ -308,7 +309,7 @@ export function createAskUserQuestionTool(onQuestion, source, opts) {
308
309
  }
309
310
  discloseSyntheticContinuation(ctx.toolCallId, "callback_failed");
310
311
  return {
311
- content: `${NO_HUMAN} (no answer was obtained: ${e instanceof Error ? e.message : String(e)})`,
312
+ content: `${NO_HUMAN} (no answer was obtained: ${thrownText(e)})`,
312
313
  details: continuationCard(ctx.toolCallId, "synthetic_self_answer_instruction", "callback_failed"),
313
314
  };
314
315
  }
@@ -1,5 +1,6 @@
1
1
  import { extractSymbols } from "../tools/fs/repo-map.js";
2
2
  import { parseSkillToolEntry } from "./skill-tool-specifier.js";
3
+ import { thrownText } from "./thrown-value.js";
3
4
  export function decideAutoPromote(i) {
4
5
  if (i.tripwire.escalate)
5
6
  return "needs_human";
@@ -71,7 +72,7 @@ export function deriveTripwire(artifactText, declared, profileTokens) {
71
72
  catch (e) {
72
73
  return {
73
74
  escalate: true,
74
- reasons: [...reasons, `symbol-drift scan threw (fail-closed): ${String(e?.message ?? e)}`],
75
+ reasons: [...reasons, `symbol-drift scan threw (fail-closed): ${thrownText(e)}`],
75
76
  };
76
77
  }
77
78
  try {
@@ -86,7 +87,7 @@ export function deriveTripwire(artifactText, declared, profileTokens) {
86
87
  catch (e) {
87
88
  return {
88
89
  escalate: true,
89
- reasons: [...reasons, `denylist scan threw (fail-closed): ${String(e?.message ?? e)}`],
90
+ reasons: [...reasons, `denylist scan threw (fail-closed): ${thrownText(e)}`],
90
91
  };
91
92
  }
92
93
  return { escalate, reasons };
@@ -1,4 +1,5 @@
1
1
  import { traceHookCrash } from "./gate-crash-trace.js";
2
+ import { thrownText } from "./thrown-value.js";
2
3
  export function gateWaitFailureOf(err) {
3
4
  const mark = err?.gateWait;
4
5
  return mark === "timeout" || mark === "aborted" ? mark : undefined;
@@ -58,6 +59,6 @@ export async function awaitWriteProtectionJudgment(judged, ms, signal, onFailure
58
59
  }
59
60
  catch (err) {
60
61
  onFailure?.(err);
61
- return { kind: "unresolvable", cause: err instanceof Error ? err.message : String(err), unresolvedSymlink: false };
62
+ return { kind: "unresolvable", cause: thrownText(err), unresolvedSymlink: false };
62
63
  }
63
64
  }
@@ -5,6 +5,7 @@ import {} from "./runner/gate-exit.js";
5
5
  import { traceHookCrash } from "./gate-crash-trace.js";
6
6
  import { writeProtectionMandated, writeProtectionTightenDecision, writeProtectionUnresolved } from "./write-protect.js";
7
7
  import { createGateDeadline, awaitWriteProtectionJudgment, gateWaitFailureOf, withGateTimeout } from "./gate-bounded-await.js";
8
+ import { thrownText } from "./thrown-value.js";
8
9
  export async function runGateFold(pass) {
9
10
  const { input, toolName, toolCallId, callSignal, ledger, notifier, adjudicate } = pass;
10
11
  const boundedAwaitDeadlineMs = createGateDeadline(input, notifier);
@@ -76,6 +77,8 @@ export async function runGateFold(pass) {
76
77
  probeCause = normalizeProbeCause(verdict?.cause);
77
78
  if (verdict?.mandated === true)
78
79
  probeMandated = true;
80
+ if (verdict?.boundaryDeclined === true)
81
+ pass.probeBoundaryDeclined = true;
79
82
  probeReadRootCandidate = normalizeReadRootCandidate(verdict?.readRootCandidate);
80
83
  }
81
84
  }
@@ -120,7 +123,7 @@ export async function runGateFold(pass) {
120
123
  const kind = gateWaitFailureOf(err);
121
124
  if (kind === "aborted" || input.abortSignal?.aborted === true)
122
125
  return;
123
- traceHookCrash(input, new Error(`write-protection judge ${kind === "timeout" ? "timed out" : "threw"} for tool "${toolName}" (toolCallId ${toolCallId}) — this call's write target could not be judged: ${err instanceof Error ? err.message : String(err)}`), notifier);
126
+ traceHookCrash(input, new Error(`write-protection judge ${kind === "timeout" ? "timed out" : "threw"} for tool "${toolName}" (toolCallId ${toolCallId}) — this call's write target could not be judged: ${thrownText(err)}`), notifier);
124
127
  });
125
128
  if (verdict !== undefined && writeProtectionMandated(verdict))
126
129
  pass.writeProtected = true;
@@ -12,6 +12,7 @@ import { ORG_ADJUDICATION_TIMEOUT_MS, ORG_RULE_DECISION_REASON, ORG_UNAVAILABLE_
12
12
  import { exitGate } from "./runner/gate-exit.js";
13
13
  import { traceHookCrash } from "./gate-crash-trace.js";
14
14
  import { createGateDeadline, awaitWriteProtectionJudgment } from "./gate-bounded-await.js";
15
+ import { thrownText } from "./thrown-value.js";
15
16
  function isPlainOwnRecord(x) {
16
17
  try {
17
18
  if (x === null || typeof x !== "object" || Array.isArray(x))
@@ -345,7 +346,7 @@ export async function runGateLanes(pass) {
345
346
  const stamped = { ...unstamped, ruleEvidence: mintRuleEvidence(personalEvidence), ...(laneCoverage !== undefined ? { segmentCoverage: laneCoverage } : {}), ...(originFacts.ruleStore !== undefined ? { ruleStoreUnreadable: originFacts.ruleStore } : {}) };
346
347
  pass.decision = stampAskOrigin(stamped, originFacts);
347
348
  }
348
- if (allowLayerMayClear() && pass.decision.action === "ask" && persistedRuleMandate === undefined && pass.decision.matchedAskRule === undefined && pass.req.toolName === COMMAND_RULE_TOOL) {
349
+ if (allowLayerMayClear() && pass.decision.action === "ask" && persistedRuleMandate === undefined && pass.probeBoundaryDeclined !== true && pass.decision.matchedAskRule === undefined && pass.req.toolName === COMMAND_RULE_TOOL) {
349
350
  const commandParam = catalogRuleFaceOf(COMMAND_RULE_TOOL)?.primaryParams[0] ?? "command";
350
351
  const args = pass.req.args;
351
352
  const command = typeof args === "object" && args !== null && !Array.isArray(args) ? args[commandParam] : undefined;
@@ -422,6 +423,7 @@ export async function runGateLanes(pass) {
422
423
  input.egress !== true &&
423
424
  input.peerMessage !== true &&
424
425
  sandboxMayAdmitMandate(persistedRuleMandate) &&
426
+ pass.probeBoundaryDeclined !== true &&
425
427
  input.sandboxAdmission.boundaryCapable !== true) {
426
428
  const recorded = input.sandboxAdmission.askClassesOf(toolCallId);
427
429
  const admissible = recorded !== undefined && recorded.length > 0 && recorded.every((r) => r.cls === "sandbox_local");
@@ -605,7 +607,7 @@ export async function runGateLanes(pass) {
605
607
  catch (err) {
606
608
  recheck = {
607
609
  action: "deny",
608
- message: `policy re-check of the approved edit for "${toolName}" errored: ${err instanceof Error ? err.message : String(err)}`,
610
+ message: `policy re-check of the approved edit for "${toolName}" errored: ${thrownText(err)}`,
609
611
  };
610
612
  }
611
613
  }
@@ -1,4 +1,5 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { thrownLabel, thrownText } from "./thrown-value.js";
2
3
  export const WORKTREE_PARENT = ".sema-worktrees";
3
4
  function shq(s) {
4
5
  return `'${s.replace(/'/g, "'\\''")}'`;
@@ -10,10 +11,10 @@ async function removeWorktree(baseEnv, repoRoot, worktreeDir) {
10
11
  const r = await baseEnv.exec(`git worktree remove --force ${shq(worktreeDir)}`, { cwd: repoRoot });
11
12
  if (r.ok && r.value.exitCode === 0)
12
13
  return { ok: true };
13
- detail = r.ok ? r.value.stderr || r.value.stdout || `exit ${r.value.exitCode}` : String(r.error);
14
+ detail = r.ok ? r.value.stderr || r.value.stdout || `exit ${r.value.exitCode}` : thrownText(r.error);
14
15
  }
15
16
  catch (err) {
16
- detail = String(err);
17
+ detail = thrownLabel(err);
17
18
  }
18
19
  }
19
20
  return { ok: false, detail };
@@ -25,7 +26,7 @@ export async function addWorktree(baseEnv, opts) {
25
26
  cwd: opts.repoRoot,
26
27
  });
27
28
  if (!add.ok || add.value.exitCode !== 0) {
28
- const detail = add.ok ? add.value.stderr || add.value.stdout : String(add.error);
29
+ const detail = add.ok ? add.value.stderr || add.value.stdout : thrownText(add.error);
29
30
  throw new Error(`git worktree add failed: ${detail}`);
30
31
  }
31
32
  const excludeLine = shq(`/${WORKTREE_PARENT}/`);
@@ -65,10 +66,10 @@ export async function pruneWorktrees(baseEnv, repoRoot) {
65
66
  return { ok: true };
66
67
  return {
67
68
  ok: false,
68
- detail: r.ok ? (r.value.stderr || r.value.stdout || `exit ${r.value.exitCode}`).trim() : String(r.error),
69
+ detail: r.ok ? (r.value.stderr || r.value.stdout || `exit ${r.value.exitCode}`).trim() : thrownText(r.error),
69
70
  };
70
71
  }
71
72
  catch (err) {
72
- return { ok: false, detail: String(err) };
73
+ return { ok: false, detail: thrownLabel(err) };
73
74
  }
74
75
  }
@@ -1,4 +1,5 @@
1
- import { decisionText, refuseOutOfContractDecision } from "./tool-policy.js";
1
+ import { decisionText, describeThrown, refuseOutOfContractDecision } from "./tool-policy.js";
2
+ import { asThrownError } from "./thrown-value.js";
2
3
  import { createSettlementLedger, engineFailClosedExit, hookDenyExit } from "./runner/gate-exit.js";
3
4
  import { traceHookCrash } from "./gate-crash-trace.js";
4
5
  import { runGateFold } from "./gate-fold.js";
@@ -88,7 +89,7 @@ export async function runHookSeat(seat, bound, call) {
88
89
  listening = true;
89
90
  }
90
91
  }
91
- pending.then((value) => finish(() => resolve({ expired: false, value })), (err) => finish(() => reject(err instanceof Error ? err : new Error(String(err)))));
92
+ pending.then((value) => finish(() => resolve({ expired: false, value })), (err) => finish(() => reject(asThrownError(err))));
92
93
  });
93
94
  }
94
95
  export function hookSeatExpiredError(seat, timeoutMs, cause, consequence) {
@@ -170,8 +171,7 @@ export function mintHookInvocationIdentity(facts) {
170
171
  }));
171
172
  }
172
173
  function preToolUseCrashReason(subject, err) {
173
- const raw = err instanceof Error ? err.message.trim() || err.name : String(err);
174
- const cause = inlineUntrusted(raw, 200);
174
+ const cause = inlineUntrusted(describeThrown(err), 200);
175
175
  return (`a PreToolUse hook crashed while screening ${subject}; the call was NOT executed (fail-closed). ` +
176
176
  `This is a failure of the deployment's hook, NOT of the tool itself — an identical retry reaches the ` +
177
177
  `same hook and fails the same way. Hook error: ${cause || "(no message)"}`);
@@ -1,5 +1,6 @@
1
1
  import { uriToPath, buildRequest, callHierarchyMethod, parseResult, pathToUri } from "./lsp-protocol.js";
2
2
  import { lspFailureOf, SharedAbortScope, settleOnAbort } from "./lsp.js";
3
+ import { thrownText } from "./thrown-value.js";
3
4
  const WARMUP_MAX_ATTEMPTS = 3;
4
5
  export class TransportLspSession {
5
6
  transport;
@@ -81,7 +82,7 @@ export class TransportLspSession {
81
82
  }
82
83
  catch (e) {
83
84
  const f = lspFailureOf(e) ?? TransportLspSession.classifyUnbranded(signal, this.transport);
84
- this.log("lsp_op_degraded", { op, file: params.filePath, reason: f.reason, err: e instanceof Error ? e.message : String(e) });
85
+ this.log("lsp_op_degraded", { op, file: params.filePath, reason: f.reason, err: thrownText(e) });
85
86
  return { kind: "none", reason: f.reason, ...(f.detail !== undefined ? { detail: f.detail } : {}) };
86
87
  }
87
88
  const result = noHierarchyItem
@@ -152,7 +153,7 @@ export class TransportLspSession {
152
153
  text = await this.readText(filePath, scope.signal);
153
154
  }
154
155
  catch (e) {
155
- const error = e instanceof Error ? e.message : String(e);
156
+ const error = thrownText(e);
156
157
  this.log("lsp_sync_skipped", { file: filePath, error, opened: this.opened.has(filePath) });
157
158
  return { ok: false, error };
158
159
  }
package/dist/core/mcp.js CHANGED
@@ -21,6 +21,7 @@ import { withContentOrigin } from "./memory-engine/content-origin.js";
21
21
  import { snapshotMcpEntries } from "./locked-config.js";
22
22
  import { validateJsonSchemaShape } from "./runner/strict-output-schema.js";
23
23
  import { schemaKeyBoundProblem } from "./tool-roster.js";
24
+ import { thrownText } from "./thrown-value.js";
24
25
  export const MCP_PREFIX = MCP_NAMESPACE.prefix;
25
26
  const MCP_OUTPUT_TOKENS_DEFAULT = 25_000;
26
27
  const MCP_CHARS_PER_TOKEN = 4;
@@ -190,7 +191,7 @@ function namedMcpFailureText(err) {
190
191
  return condition !== undefined ? `${condition}: ${detail}` : detail;
191
192
  }
192
193
  function remoteErrorText(err, opts) {
193
- const raw = err instanceof Error ? err.message : String(err);
194
+ const raw = thrownText(err);
194
195
  return redactSecrets(opts?.collapse === true ? collapseMcpErrorPrefix(raw) : raw);
195
196
  }
196
197
  function writeEffectWarning(writeEffect) {
@@ -202,7 +203,7 @@ function rethrowHonestMcpError(err, ctx) {
202
203
  const finish = (e) => {
203
204
  if (ctx.reminder !== undefined) {
204
205
  observeReminderMarkEcho({
205
- text: e instanceof Error ? e.message : String(e),
206
+ text: thrownText(e),
206
207
  mark: ctx.reminder.mark,
207
208
  outlet: ctx.reminder.outlet,
208
209
  counts: ctx.reminder.counts,
@@ -321,7 +322,7 @@ async function persistMcpBlob(base64Data, mimeType, server) {
321
322
  return { filePath, bytes: bytes.length };
322
323
  }
323
324
  catch (err) {
324
- return { error: err instanceof Error ? err.message : String(err), bytes: bytes.length };
325
+ return { error: thrownText(err), bytes: bytes.length };
325
326
  }
326
327
  }
327
328
  async function blobToTextBlock(base64Data, mimeType, server, prefix, refetchHint = "") {
@@ -1,4 +1,5 @@
1
1
  import { parseScopeKey } from "./memory-engine/scope-contract.js";
2
+ import { thrownText } from "./thrown-value.js";
2
3
  function codedError(code, message) {
3
4
  const e = new Error(message);
4
5
  e.code = code;
@@ -122,7 +123,7 @@ export async function admitMemoryScopes(input) {
122
123
  }
123
124
  catch (err) {
124
125
  const hinted = err.retryAfterMs;
125
- const e = codedError("memory.admission_required", `memory-scope admission resolver failed (${err instanceof Error ? err.message : String(err)}) — refused fail-closed.`);
126
+ const e = codedError("memory.admission_required", `memory-scope admission resolver failed (${thrownText(err)}) — refused fail-closed.`);
126
127
  if (typeof hinted === "number" && Number.isFinite(hinted) && hinted > 0)
127
128
  e.retryAfterMs = hinted;
128
129
  throw e;
@@ -5,6 +5,7 @@ import { inlineUntrusted } from "../untrusted-text.js";
5
5
  import { deliverEngineNotice } from "../engine-notice.js";
6
6
  import { CONSOLIDATION_FORCE_THROTTLE_FLOOR_MS, screenConsolidationOptions, supersessionFuseCeiling, updateConsolidationGate, recommendationUnconsumed, } from "./consolidation.js";
7
7
  import { memoryConsolidationIncompleteNotice, memoryConsolidationWithheldNotice } from "./engine.js";
8
+ import { asThrownError, thrownText } from "../thrown-value.js";
8
9
  import { CONSOLIDATION_DRIVE_ABORT, CONSOLIDATION_SNAPSHOT_PARKED, MEMORY_DISTILLER_CONTRACT_V1, detectCleanArmVerbatimLeak, driveConsolidationToFixpoint, isAliasModelId, llmPlanDistiller, mintExposurePartitionedPlan, mintLlmConsolidationPlan, normalizedCandidateRev, } from "./distiller.js";
9
10
  export const CONSOLIDATION_DRIVER_RUNS_FILE = "distiller-runs.json";
10
11
  export const CONSOLIDATION_DRIVER_PLANS_DIR = "distiller-plans";
@@ -119,7 +120,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
119
120
  await rawSleep(ms);
120
121
  }
121
122
  catch (err) {
122
- const e = typeof err === "object" && err !== null ? err : new Error(String(err));
123
+ const e = typeof err === "object" && err !== null ? err : asThrownError(err);
123
124
  Reflect.set(e, CONSOLIDATION_DRIVE_ABORT, true);
124
125
  throw e;
125
126
  }
@@ -155,7 +156,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
155
156
  enqueueMemoryAnnouncement(controlDir, { kind: "gate", at: now(), items });
156
157
  }
157
158
  catch (err) {
158
- announceFailures.push(err instanceof Error ? err.message : String(err));
159
+ announceFailures.push(thrownText(err));
159
160
  }
160
161
  };
161
162
  const egressEndpoint = ((raw) => {
@@ -211,7 +212,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
211
212
  });
212
213
  }
213
214
  catch (err) {
214
- announceFailures.push(err instanceof Error ? err.message : String(err));
215
+ announceFailures.push(thrownText(err));
215
216
  }
216
217
  }
217
218
  const notices = [];
@@ -279,7 +280,7 @@ export async function runMemoryConsolidationDriver(engine, scope, opts) {
279
280
  announce([
280
281
  `memory consolidation driver run ${run.runId} for scope ${inlineUntrusted(scope, 80)} ATTEMPTED to send memory content to the configured consolidation model: ${egressSeat} — the seat threw before any answer was recorded, so whether any content reached the provider is NOT observable from here; up to ${candidates.length} candidate(s) were in flight, each rendered as its name, description and body. Treat this as a possible transmission.`,
281
282
  ]);
282
- throw new DriverMintFailure(`the model seat failed during the mint: ${err instanceof Error ? err.message : String(err)}`);
283
+ throw new DriverMintFailure(`the model seat failed during the mint: ${thrownText(err)}`);
283
284
  }
284
285
  const transcript = minted.ok ? minted.plan.minting.transcript : minted.calls;
285
286
  run.usage.calls += transcript.length;
@@ -2,6 +2,7 @@ import { existsSync, readdirSync, renameSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { uuidv7 } from "../../engine/session/uuid.js";
4
4
  import { atomicWriteFileSync, lockedStrictUpdate, readStrictSidecar, ControlPlaneCorruptError, ensureDirExists } from "./layout.js";
5
+ import { thrownText } from "../thrown-value.js";
5
6
  export function deriveSupersededSet(headers, opts = {}) {
6
7
  const byId = new Map();
7
8
  for (const h of headers)
@@ -296,7 +297,7 @@ export function readConsolidationPlan(controlDir, planId) {
296
297
  return { state: "ok", plan: raw };
297
298
  }
298
299
  catch (err) {
299
- return { state: "corrupt", detail: err instanceof Error ? err.message : String(err) };
300
+ return { state: "corrupt", detail: thrownText(err) };
300
301
  }
301
302
  }
302
303
  export function listConsolidationPlanIds(controlDir) {
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
2
2
  import { closeSync, existsSync, fsyncSync, linkSync, lstatSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
3
3
  import { basename, dirname, join } from "node:path";
4
4
  import { QUARANTINE_DIR, appendChallengeEvents, enqueueMemoryAnnouncement, ensureDirExists, v1MemoryPathKeyResolver, lineageContributionsOfSession, lockedStrictUpdate, markSessionPolluted, readProjectionDebts, readSessionPollution, readStrictSidecar, ControlPlaneCorruptError } from "./layout.js";
5
+ import { thrownText } from "../thrown-value.js";
5
6
  export const DELEGATION_SETTLEMENT_FILE = "delegation-settlement.json";
6
7
  function reqStr(v) {
7
8
  return typeof v === "string" && v.length > 0;
@@ -584,13 +585,13 @@ export function openInstructionHold(controlDir, input) {
584
585
  });
585
586
  }
586
587
  catch (err) {
587
- return { ok: false, reason: `hold ledger refused: ${err instanceof Error ? err.message : String(err)}` };
588
+ return { ok: false, reason: `hold ledger refused: ${thrownText(err)}` };
588
589
  }
589
590
  try {
590
591
  ensureDirExists(holdDir);
591
592
  }
592
593
  catch (err) {
593
- return refuse(`hold capture failed: ${err instanceof Error ? err.message : String(err)}`, "capture_lost");
594
+ return refuse(`hold capture failed: ${thrownText(err)}`, "capture_lost");
594
595
  }
595
596
  if (!writeCustodyVerified(join(holdDir, captureName), buf)) {
596
597
  return refuse("hold capture verification failed (the custody copy could not be written, made durable, or hashed back to the captured digest)", "capture_lost");
@@ -633,7 +634,7 @@ export function openInstructionHold(controlDir, input) {
633
634
  });
634
635
  }
635
636
  catch (err) {
636
- return refuse(`hold ledger flip refused: ${err instanceof Error ? err.message : String(err)}`, "discarded");
637
+ return refuse(`hold ledger flip refused: ${thrownText(err)}`, "discarded");
637
638
  }
638
639
  return { ok: true, holdId, ...(thirdWriterStrandedAt !== undefined ? { thirdWriterStrandedAt } : {}) };
639
640
  }
@@ -1,3 +1,4 @@
1
+ import { readMember, thrownText } from "../thrown-value.js";
1
2
  export const DISTILLER_DEFAULT_MAX_INPUTS_PER_PRODUCT = 32;
2
3
  const SIZE_CLAUSE = {
3
4
  "dl-2": "- the index should be as small as it can be made without losing either of the above.",
@@ -595,10 +596,13 @@ export function scheduleUnderFuse(products, ceiling, { maxProductsPerRun = Infin
595
596
  };
596
597
  }
597
598
  export function codedReason(err) {
598
- const e = err;
599
- const code = e?.code !== undefined ? `${String(e.code)}: ` : "";
600
- const reasons = Array.isArray(e?.reasons) && e.reasons.length > 0 ? `\n - ${e.reasons.join("\n - ")}` : "";
601
- return `${code}${String(e?.message ?? err)}${reasons}`;
599
+ const codeValue = readMember(err, "code");
600
+ const code = codeValue !== undefined ? `${thrownText(codeValue)}: ` : "";
601
+ const rs = readMember(err, "reasons");
602
+ const reasons = Array.isArray(rs) && rs.length > 0 ? `\n - ${rs.map((r) => thrownText(r)).join("\n - ")}` : "";
603
+ const m = readMember(err, "message");
604
+ const duckMessage = typeof m === "string" && m !== "" ? m : undefined;
605
+ return `${code}${duckMessage ?? thrownText(err)}${reasons}`;
602
606
  }
603
607
  async function tryReconcile(engine, scope) {
604
608
  try {