@sema-agent/core 7.23.0 → 7.23.2

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 (132) hide show
  1. package/CHANGELOG.md +64 -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.d.ts +1 -1
  21. package/dist/core/gate-bounded-await.js +3 -2
  22. package/dist/core/gate-crash-trace.d.ts +7 -0
  23. package/dist/core/gate-crash-trace.js +3 -0
  24. package/dist/core/gate-fold.js +6 -2
  25. package/dist/core/gate-lanes.js +6 -3
  26. package/dist/core/git-worktree-env.js +6 -5
  27. package/dist/core/hooks.js +6 -5
  28. package/dist/core/lsp-session.js +3 -2
  29. package/dist/core/mcp.js +4 -3
  30. package/dist/core/memory-admission.js +2 -1
  31. package/dist/core/memory-engine/consolidation-driver.js +5 -4
  32. package/dist/core/memory-engine/consolidation.js +2 -1
  33. package/dist/core/memory-engine/delegation-settlement.js +4 -3
  34. package/dist/core/memory-engine/distiller.js +8 -4
  35. package/dist/core/memory-engine/engine.js +63 -55
  36. package/dist/core/memory-engine/file-backend.js +11 -10
  37. package/dist/core/memory-engine/layout.js +5 -4
  38. package/dist/core/memory-engine/session-incarnation.js +2 -1
  39. package/dist/core/memory.js +2 -1
  40. package/dist/core/oracle-isolation.js +3 -2
  41. package/dist/core/permission-rule-store.js +2 -1
  42. package/dist/core/runner/announce-once-ledger.js +2 -1
  43. package/dist/core/runner/compaction-seams.js +3 -2
  44. package/dist/core/runner/gate-exit.d.ts +5 -5
  45. package/dist/core/runner/gate-exit.js +0 -3
  46. package/dist/core/runner/git-leg-delivery.js +4 -3
  47. package/dist/core/runner/git-status-frame.js +2 -1
  48. package/dist/core/runner/mcp-redial.js +2 -1
  49. package/dist/core/runner/memory-consolidation-driver.js +2 -1
  50. package/dist/core/runner/permission-rule-lanes.js +3 -2
  51. package/dist/core/runner/prepare-artifact.js +2 -1
  52. package/dist/core/runner/prepare-ask-lane.js +2 -1
  53. package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
  54. package/dist/core/runner/prepare-inherited-gate.js +2 -1
  55. package/dist/core/runner/prepare-memory-engine-session.js +2 -1
  56. package/dist/core/runner/prepare-memory.js +5 -4
  57. package/dist/core/runner/prepare-policy-chain.js +3 -2
  58. package/dist/core/runner/prepare-project-context.js +3 -2
  59. package/dist/core/runner/prepare-protocol-tools.js +3 -2
  60. package/dist/core/runner/prepare-suspend-saga.js +2 -1
  61. package/dist/core/runner/prepare-task.js +6 -5
  62. package/dist/core/runner/prepare-turn-wiring.js +2 -1
  63. package/dist/core/runner/resume-apply.js +5 -4
  64. package/dist/core/runner/resume-preflight.js +3 -2
  65. package/dist/core/runner/run-git-lane.js +4 -3
  66. package/dist/core/runner/run-leg.js +4 -3
  67. package/dist/core/runner/run-recovery-lanes.js +3 -2
  68. package/dist/core/runner/run-settle-and-teardown.js +2 -1
  69. package/dist/core/runner/run-stop-and-final-verify.js +2 -1
  70. package/dist/core/runner/run-telemetry-and-budget-seats.js +2 -1
  71. package/dist/core/runner/run-terminal-adoption.js +6 -5
  72. package/dist/core/runner/run-turn-boundary.js +5 -4
  73. package/dist/core/runner/runtask.js +2 -1
  74. package/dist/core/runner/stream-settle-backstop.js +5 -4
  75. package/dist/core/runner/stream-steer-verb.js +2 -1
  76. package/dist/core/runner/strict-output-schema.js +2 -1
  77. package/dist/core/runner/teardown-bounded.js +2 -1
  78. package/dist/core/safe-notify.js +2 -1
  79. package/dist/core/skills-directory.js +2 -1
  80. package/dist/core/strategy-store.js +2 -1
  81. package/dist/core/task-registry-agent.js +2 -1
  82. package/dist/core/task-registry-monitor.js +2 -1
  83. package/dist/core/task-registry-workflow.js +2 -1
  84. package/dist/core/task-registry.js +3 -2
  85. package/dist/core/thrown-value.d.ts +79 -0
  86. package/dist/core/thrown-value.js +56 -0
  87. package/dist/core/tool-errors.d.ts +0 -6
  88. package/dist/core/tool-errors.js +36 -14
  89. package/dist/core/tool-model-gate.js +2 -1
  90. package/dist/core/tool-policy.js +6 -13
  91. package/dist/core/tool-result-store.js +2 -1
  92. package/dist/core/tool-spec.d.ts +12 -0
  93. package/dist/core/write-protect.js +4 -4
  94. package/dist/engine/compaction/compaction.js +3 -4
  95. package/dist/engine/harness/agent-harness.js +2 -1
  96. package/dist/engine/harness/types.js +8 -7
  97. package/dist/engine/loop/agent-loop.js +7 -6
  98. package/dist/engine/lsp/node-lsp-manager.js +5 -4
  99. package/dist/engine/lsp/stdio-lsp-transport.js +12 -5
  100. package/dist/orchestration/dev-vm-script-runner.js +2 -1
  101. package/dist/orchestration/run-workflow-tool.js +7 -6
  102. package/dist/orchestration/workflow-governance.js +2 -1
  103. package/dist/orchestration/workflow-sandbox-conformance.js +3 -2
  104. package/dist/orchestration/workflow-script-store.js +2 -1
  105. package/dist/orchestration/workflow.js +15 -14
  106. package/dist/server/http.js +3 -2
  107. package/dist/stores/file/adoption/adopt.js +3 -2
  108. package/dist/stores/file/fs-atomic.js +2 -1
  109. package/dist/stores/file/permission-rule-adopt.js +2 -1
  110. package/dist/stores/file/permission-rule-store.js +2 -1
  111. package/dist/stores/file/session-store.js +4 -3
  112. package/dist/stores/file/strategy-store.js +7 -6
  113. package/dist/tools/artifact/artifact-tool.js +3 -2
  114. package/dist/tools/artifact/conformance.js +4 -3
  115. package/dist/tools/fs/bash-lexer.d.ts +12 -5
  116. package/dist/tools/fs/bash-lexer.js +5 -2
  117. package/dist/tools/fs/bash-program-position.d.ts +18 -0
  118. package/dist/tools/fs/bash-program-position.js +13 -1
  119. package/dist/tools/fs/bash-readonly-classifier.d.ts +23 -7
  120. package/dist/tools/fs/bash-readonly-classifier.js +74 -41
  121. package/dist/tools/fs/fs-bash.d.ts +28 -0
  122. package/dist/tools/fs/fs-bash.js +31 -13
  123. package/dist/tools/fs/fs-write.js +3 -2
  124. package/dist/tools/fs/safety.js +2 -1
  125. package/dist/tools/fs/search.js +3 -2
  126. package/dist/tools/monitor.js +3 -2
  127. package/dist/tools/sql.js +2 -1
  128. package/dist/tools/web.js +5 -15
  129. package/dist/tools/worktree.js +12 -9
  130. package/package.json +2 -1
  131. package/wire-changes/core@7.23.1.json +205 -0
  132. package/wire-changes/core@7.23.2.json +42 -0
@@ -33,12 +33,6 @@ export declare function truncateError(s: string): string;
33
33
  * budget of one character. */
34
34
  export declare const OUTPUT_BUDGET_MIN_CHARS = 512;
35
35
  export declare function clipWithFilePointer(s: string, limit: number, fullOutputPath?: string): string;
36
- /**
37
- * Format a thrown tool error into model-facing text (design/64 §11.1/§11.2): a shell/exec error becomes
38
- * `Exit code N` + stderr + stdout; a plain Error becomes its message (+ any captured streams); anything
39
- * else is stringified. The result is truncated to {@link MAX_ERROR_CHARS}. An AbortError is reported as an
40
- * interruption.
41
- */
42
36
  export declare function formatToolError(error: unknown): string;
43
37
  /**
44
38
  * A typed, read-side fold of a `TaskResult.errorCode` into the failure CLASS it belongs to (design/80
@@ -2,6 +2,7 @@ import { Value } from "typebox/value";
2
2
  const MAX_ERROR_CHARS = 10_000;
3
3
  const HALF = MAX_ERROR_CHARS / 2;
4
4
  import { isHighSurrogate, isLowSurrogate, sliceHeadSafe, sliceTailSafe } from "./surrogate-safe-slice.js";
5
+ import { readMember, thrownText } from "./thrown-value.js";
5
6
  export function truncateError(s) {
6
7
  if (s.length <= MAX_ERROR_CHARS)
7
8
  return s;
@@ -31,28 +32,49 @@ export function clipWithFilePointer(s, limit, fullOutputPath) {
31
32
  return `${s.slice(0, headEnd)}${markerFor(kept)}${s.slice(tailStart)}`;
32
33
  }
33
34
  function isShellLike(e) {
34
- return typeof e === "object" && e !== null && ("exitCode" in e || "stderr" in e || "stdout" in e);
35
+ try {
36
+ return typeof e === "object" && e !== null && ("exitCode" in e || "stderr" in e || "stdout" in e);
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ }
42
+ function isErrorInstance(v) {
43
+ try {
44
+ return v instanceof Error;
45
+ }
46
+ catch {
47
+ return false;
48
+ }
35
49
  }
36
50
  export function formatToolError(error) {
37
- if (error instanceof Error && error.name === "AbortError")
38
- return "Interrupted";
39
51
  const parts = [];
52
+ const name = readMember(error, "name");
53
+ if (isErrorInstance(error) && name === "AbortError")
54
+ return "Interrupted";
55
+ const message = readMember(error, "message");
56
+ const messageText = typeof message === "string" ? message : undefined;
40
57
  if (isShellLike(error)) {
41
- if (typeof error.exitCode === "number")
42
- parts.push(`Exit code ${error.exitCode}`);
43
- if (error instanceof Error && error.message)
44
- parts.push(error.message);
45
- if (error.stderr && error.stderr.trim())
46
- parts.push(error.stderr);
47
- if (error.stdout && error.stdout.trim())
48
- parts.push(error.stdout);
58
+ const exitCode = readMember(error, "exitCode");
59
+ const stderr = readMember(error, "stderr");
60
+ const stdout = readMember(error, "stdout");
61
+ if (typeof exitCode === "number")
62
+ parts.push(`Exit code ${exitCode}`);
63
+ if (isErrorInstance(error) && messageText !== undefined && messageText !== "")
64
+ parts.push(messageText);
65
+ if (typeof stderr === "string" && stderr.trim())
66
+ parts.push(stderr);
67
+ if (typeof stdout === "string" && stdout.trim())
68
+ parts.push(stdout);
49
69
  }
50
- else if (error instanceof Error) {
51
- parts.push(error.message || error.name || "Error");
70
+ else if (isErrorInstance(error)) {
71
+ parts.push((messageText ?? "") || (typeof name === "string" ? name : "") || "Error");
52
72
  }
53
73
  else {
54
- parts.push(String(error));
74
+ parts.push(thrownText(error));
55
75
  }
76
+ if (parts.length === 0)
77
+ parts.push(thrownText(error));
56
78
  return truncateError(parts.join("\n").trim() || "Error");
57
79
  }
58
80
  const BRAIN_FLAT_CODES = new Set([
@@ -1,3 +1,4 @@
1
+ import { thrownText } from "./thrown-value.js";
1
2
  export function modelIdTail(id) {
2
3
  return id.toLowerCase().split("/").pop() ?? "";
3
4
  }
@@ -217,7 +218,7 @@ export function applyToolModelGate(input) {
217
218
  throw e;
218
219
  let msg;
219
220
  try {
220
- msg = e instanceof Error ? e.message : String(e);
221
+ msg = thrownText(e);
221
222
  }
222
223
  catch {
223
224
  msg = "[unreportable throw]";
@@ -8,6 +8,8 @@ import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/f
8
8
  import { protectivePathTargetOf } from "./tool-registry.js";
9
9
  import { boundInputHashOf } from "./canonical-json.js";
10
10
  import { delimitUntrusted, inlineUntrusted, REVIEWER_NOTE_MAX_BODY } from "./untrusted-text.js";
11
+ import { describeRejectedValue } from "./tool-search-setting.js";
12
+ import { thrownText } from "./thrown-value.js";
11
13
  import { isNamespacedCoveringRuleName, namespacedRuleNameCovers, parsePermissionRule } from "./permission-rules.js";
12
14
  import { protocolOf } from "./protocol-table.js";
13
15
  import { isAbsoluteForFamily, isAbsolutePathForm, joinForFamily, normalizeAbsPathLexically, pathFamilyOf, writeTargetPath } from "../tools/fs/safety.js";
@@ -944,19 +946,10 @@ export function tryCloneArgs(v) {
944
946
  }
945
947
  }
946
948
  export function describeThrown(err) {
947
- try {
948
- if (typeof err === "string")
949
- return containThrownText(err);
950
- if (err instanceof Error) {
951
- const message = err.message;
952
- if (typeof message === "string" && message !== "")
953
- return containThrownText(message);
954
- }
955
- return `a non-Error value was thrown (${err === null ? "null" : typeof err})`;
956
- }
957
- catch {
958
- return "the value could not be described";
959
- }
949
+ const contained = containThrownText(thrownText(err));
950
+ if (contained !== "")
951
+ return contained;
952
+ return containThrownText(describeRejectedValue(err));
960
953
  }
961
954
  const THROWN_TEXT_MAX = 300;
962
955
  function containThrownText(text) {
@@ -5,6 +5,7 @@ import { defineTool, errorResult } from "./tools.js";
5
5
  import { deliverEngineNotice } from "./types.js";
6
6
  import { TOOL_SEARCH_NAME } from "./runner/tool-disclosure.js";
7
7
  import { toolFace } from "./tool-catalog-entries.js";
8
+ import { thrownText } from "./thrown-value.js";
8
9
  export const TOOL_RESULT_REF_CONFLICT_CODE = "tool_result.ref_conflict";
9
10
  export class ToolResultRefConflictError extends Error {
10
11
  ref;
@@ -176,7 +177,7 @@ export function createOffloadPersist(store, sessionId, onNotice) {
176
177
  return (toolCallId, fullText) => {
177
178
  const ref = buildToolResultRef(sessionId, toolCallId, toolResultContentSegment(fullText));
178
179
  const reportFailedWrite = (err) => {
179
- const cause = err instanceof Error ? err.message : String(err);
180
+ const cause = thrownText(err);
180
181
  const message = `tool-result offload: persisting ref "${ref}" failed (${cause}) — this write attempt stored nothing; ` +
181
182
  `unless an earlier attempt already stored this ref, the inline preview is all that survived. ` +
182
183
  `A later re-clear of the same result retries the write.`;
@@ -69,6 +69,18 @@ export interface ReversibilityVerdict {
69
69
  * arm with no named directory) is confirm-or-refuse, and must not be worded as though a grant answers it.
70
70
  */
71
71
  mandated?: boolean;
72
+ /**
73
+ * The WEAKER rung of the same ladder as {@link mandated}: a read boundary the deployment declared was
74
+ * consulted and DECLINED TO JUDGE where this command reads — it named no path, so it neither declared an
75
+ * objection nor vouched anything. The resulting ask is ORDINARY on purpose: every ANSWERER may settle it (a
76
+ * person, the auto-mode classifier, a person's stored allow rule). What it excludes is the other kind of
77
+ * clearing — an engine table that answers nothing and merely recognises a command SHAPE
78
+ * (`readOnlyShellVerdict`, whose tables read program names and never paths). A mandate would take the
79
+ * answerers out too, a trade this class was deliberately not given. TIGHTENING-ONLY like {@link mandated}:
80
+ * one more question surviving one more configuration, never an ask becoming an allow, read on the demotion
81
+ * arm alone. Read PRESENCE, never absence.
82
+ */
83
+ boundaryDeclined?: boolean;
72
84
  /**
73
85
  * The directory-scoped GRANT that would clear THIS ask, when the probe can name one: PRESENT ⇒ the only
74
86
  * thing between this call and a plain `reversible: true` verdict is the session's declared read
@@ -3,6 +3,7 @@ import { protectivePathTargetOf } from "./tool-registry.js";
3
3
  import { pathTargetBaseOf } from "./effective-path-target.js";
4
4
  import { canonicalDataRootKeys, dataRootSpellings, relativeUnderRoot } from "./sensitive-path-policy.js";
5
5
  import { symlinkChainHops } from "./path-identity.js";
6
+ import { thrownText } from "./thrown-value.js";
6
7
  const freezeTable = (rows) => Object.freeze(rows.map((r) => Object.freeze(r)));
7
8
  export const WRITE_PROTECTED_DEFAULT_TABLE = freezeTable([
8
9
  { name: ".gitconfig", kind: "basename" },
@@ -66,8 +67,7 @@ function stripInvisibleFormat(s) {
66
67
  }
67
68
  function stripWin32Component(raw, normalization) {
68
69
  const base = normalization.invisibleFirst ? stripInvisibleFormat(raw) : raw;
69
- const colon = normalization.stream ? base.indexOf(":") : -1;
70
- return (colon < 0 ? base : base.slice(0, colon)).replace(/[. ]+$/, "");
70
+ return (normalization.stream ? base.replace(/:.*$/, "") : base).replace(/[. ]+$/, "");
71
71
  }
72
72
  function foldWriteProtectName(name) {
73
73
  return name.split("/").map(foldWriteProtectSegment).join("/");
@@ -277,7 +277,7 @@ export function compileWriteProtection(entries, opts) {
277
277
  noteAbsence(canon.message, canon.unresolvedSymlink === true);
278
278
  }
279
279
  catch (err) {
280
- noteAbsence(err instanceof Error ? err.message : String(err), false);
280
+ noteAbsence(thrownText(err), false);
281
281
  }
282
282
  try {
283
283
  if (canonKey !== undefined) {
@@ -309,7 +309,7 @@ export function compileWriteProtection(entries, opts) {
309
309
  }
310
310
  }
311
311
  catch (err) {
312
- noteAbsence(err instanceof Error ? err.message : String(err), false);
312
+ noteAbsence(thrownText(err), false);
313
313
  }
314
314
  return { kind: "clear" };
315
315
  },
@@ -5,6 +5,7 @@ import { CompactionError, err, isSyntheticApiErrorMessage, ok, } from "../harnes
5
5
  import { budgetInvokedSkillsRetention, computeFileLists, createFileOps, extractFileOpsFromMessage, extractInvokedSkills, extractPersistedOutputRefs, formatFileOperations, formatPersistedOutputRefs, PERSISTED_OUTPUT_REFS_MAX_ENTRIES, readElidedMessages, readPersistedOutputRefs, readRetainedInvokedSkills, readUnsummarizedMessages, replaceInvokedSkillBodiesForSummary, serializeConversation, stripFileOperationsFooter, } from "./utils.js";
6
6
  import { ENGINE_AUTHORITY_ENVELOPE_TAGS, sanitizeUntrustedText } from "../../core/untrusted-text.js";
7
7
  import { sliceHeadSafe } from "../../core/surrogate-safe-slice.js";
8
+ import { thrownText } from "../../core/thrown-value.js";
8
9
  function safeJsonStringify(value) {
9
10
  try {
10
11
  return JSON.stringify(value) ?? "undefined";
@@ -803,9 +804,7 @@ async function summarizeWithPtlRetry(req) {
803
804
  return ok(unwrapped);
804
805
  }
805
806
  const failureMessage = didThrow
806
- ? thrown instanceof Error
807
- ? thrown.message
808
- : String(thrown)
807
+ ? thrownText(thrown)
809
808
  : result && !result.ok
810
809
  ? result.error.message
811
810
  : "";
@@ -913,7 +912,7 @@ async function forkSummarize(fork, model, baseMaxTokens, apiKey, headers, custom
913
912
  result = await summarizeWithLengthRecovery("Summarization", model, context, baseMaxTokens, apiKey, headers, signal, thinkingLevel, streamFn, runtime, charsPerToken);
914
913
  }
915
914
  catch (e) {
916
- const msg = e instanceof Error ? e.message : String(e);
915
+ const msg = thrownText(e);
917
916
  if (parsePromptTooLong(msg).isPtl)
918
917
  return { kind: "fallback", detail: msg };
919
918
  throw e;
@@ -4,6 +4,7 @@ import { resolveAgentCoreStreamFn } from "../loop/runtime-deps.js";
4
4
  import { normalizeEngineSegments } from "../../core/untrusted-text.js";
5
5
  import { AUTH_CARRIER_NAMES } from "../../brain/request-params.js";
6
6
  import { convertToLlm } from "./messages.js";
7
+ import { thrownText } from "../../core/thrown-value.js";
7
8
  import { AgentHarnessError, CompactionError, SessionError, SYNTHETIC_MESSAGE_MODEL, toError, } from "./types.js";
8
9
  const API_FAILURE_RECORD_MAX_CHARS = 2000;
9
10
  function createUserMessage(text, images, provenance) {
@@ -57,7 +58,7 @@ function createFailureMessage(model, error, aborted) {
57
58
  provider: model.provider,
58
59
  model: model.id,
59
60
  stopReason: aborted ? "aborted" : "error",
60
- errorMessage: error instanceof Error ? error.message : String(error),
61
+ errorMessage: thrownText(error),
61
62
  timestamp: Date.now(),
62
63
  usage: {
63
64
  input: 0,
@@ -1,3 +1,4 @@
1
+ import { asThrownError } from "../../core/thrown-value.js";
1
2
  export function ok(value) {
2
3
  return { ok: true, value };
3
4
  }
@@ -5,18 +6,18 @@ export function err(error) {
5
6
  return { ok: false, error };
6
7
  }
7
8
  export function toError(error) {
8
- if (error instanceof Error) {
9
- return error;
10
- }
11
- if (typeof error === "string") {
12
- return new Error(error);
13
- }
14
9
  try {
10
+ if (error instanceof Error) {
11
+ return error;
12
+ }
13
+ if (typeof error === "string") {
14
+ return new Error(error);
15
+ }
15
16
  return new Error(JSON.stringify(error));
16
17
  }
17
18
  catch {
18
- return new Error(String(error));
19
19
  }
20
+ return asThrownError(error);
20
21
  }
21
22
  export class FileError extends Error {
22
23
  code;
@@ -2,6 +2,7 @@ import { findToolByName, validateToolArguments } from "../llm/index.js";
2
2
  import { truncateError } from "../../core/tool-errors.js";
3
3
  import { INTERRUPTED_BY_USER_FOR_TOOL_USE_MARKER, INTERRUPTED_BY_USER_MARKER, } from "../../core/session-reconcile.js";
4
4
  import { resolveAgentCoreStreamFn } from "./runtime-deps.js";
5
+ import { thrownText } from "../../core/thrown-value.js";
5
6
  function appendTextDeltaToAssistantMessage(message, contentIndex, delta) {
6
7
  const content = [...message.content];
7
8
  const currentContent = content[contentIndex];
@@ -1107,7 +1108,7 @@ class StreamToolExecutor {
1107
1108
  this.retainCancellation(error);
1108
1109
  entry.immediate = {
1109
1110
  kind: "immediate",
1110
- result: createErrorToolResult(error instanceof Error ? error.message : String(error), error),
1111
+ result: createErrorToolResult(thrownText(error), error),
1111
1112
  isError: true,
1112
1113
  };
1113
1114
  }
@@ -1359,7 +1360,7 @@ async function prepareToolCall(currentContext, assistantMessage, toolCall, confi
1359
1360
  catch (error) {
1360
1361
  return {
1361
1362
  kind: "immediate",
1362
- result: createErrorToolResult(error instanceof Error ? error.message : String(error), error),
1363
+ result: createErrorToolResult(thrownText(error), error),
1363
1364
  isError: true,
1364
1365
  };
1365
1366
  }
@@ -1400,7 +1401,7 @@ async function executePreparedToolCall(prepared, signal, emit, abortResultDetail
1400
1401
  }
1401
1402
  catch (error) {
1402
1403
  outcome = {
1403
- result: createErrorToolResult(error instanceof Error ? error.message : String(error), error),
1404
+ result: createErrorToolResult(thrownText(error), error),
1404
1405
  isError: true,
1405
1406
  };
1406
1407
  }
@@ -1410,7 +1411,7 @@ async function executePreparedToolCall(prepared, signal, emit, abortResultDetail
1410
1411
  return outcome;
1411
1412
  const reason = deliveryFailure.reason;
1412
1413
  const details = annotateDeliveryFailure(outcome.result, {
1413
- message: reason instanceof Error ? reason.message : String(reason),
1414
+ message: thrownText(reason),
1414
1415
  });
1415
1416
  return details === UNANNOTATED ? outcome : { result: { ...outcome.result, details }, isError: outcome.isError };
1416
1417
  })();
@@ -1482,7 +1483,7 @@ async function executePreparedWithDisclosure(currentContext, assistantMessage, p
1482
1483
  if (isAbortSemanticsError(error))
1483
1484
  throw error;
1484
1485
  executed = {
1485
- result: createErrorToolResult(`[tool execution harness failed: ${error instanceof Error ? error.message : String(error)}]`, error),
1486
+ result: createErrorToolResult(`[tool execution harness failed: ${thrownText(error)}]`, error),
1486
1487
  isError: true,
1487
1488
  };
1488
1489
  }
@@ -1512,7 +1513,7 @@ async function finalizeExecutedToolCall(currentContext, assistantMessage, prepar
1512
1513
  }
1513
1514
  }
1514
1515
  catch (error) {
1515
- const note = truncateError(`[post-tool processing failed (the tool already executed): ${error instanceof Error ? error.message : String(error)}]`);
1516
+ const note = truncateError(`[post-tool processing failed (the tool already executed): ${thrownText(error)}]`);
1516
1517
  result = { ...result, content: [...result.content, { type: "text", text: note }] };
1517
1518
  }
1518
1519
  if (deliveryMark !== undefined && readDeliveryFailureMark(result.details) === undefined) {
@@ -8,6 +8,7 @@ import { LspDiagnosticsRegistry } from "../../core/lsp-diagnostics.js";
8
8
  import { TransportLspSession } from "../../core/lsp-session.js";
9
9
  import { StdioLspTransport } from "./stdio-lsp-transport.js";
10
10
  import { MAX_LSP_FILE_BYTES, SharedAbortScope, settleOnAbort } from "../../core/lsp.js";
11
+ import { asThrownError, thrownText } from "../../core/thrown-value.js";
11
12
  export const DEFAULT_LSP_SERVERS = {
12
13
  typescript: "typescript-language-server --stdio",
13
14
  javascript: "typescript-language-server --stdio",
@@ -231,7 +232,7 @@ export class NodeLspManager {
231
232
  child = await this.spawnFn(command, args, root, signal);
232
233
  }
233
234
  catch (e) {
234
- this.log("lsp_spawn_error", { language, root, command, err: e instanceof Error ? e.message : String(e) });
235
+ this.log("lsp_spawn_error", { language, root, command, err: thrownText(e) });
235
236
  return undefined;
236
237
  }
237
238
  if (!child) {
@@ -262,7 +263,7 @@ export class NodeLspManager {
262
263
  }
263
264
  else
264
265
  this.initFailures.set(key, attempt);
265
- this.log("lsp_init_degraded", { language, root, err: e instanceof Error ? e.message : String(e), attempt, latched });
266
+ this.log("lsp_init_degraded", { language, root, err: thrownText(e), attempt, latched });
266
267
  return undefined;
267
268
  }
268
269
  this.initFailures.delete(key);
@@ -333,7 +334,7 @@ export const defaultLspSpawn = (command, args, cwd, signal) => new Promise((reso
333
334
  child = spawn(command, args, lspSpawnOptions(cwd));
334
335
  }
335
336
  catch (e) {
336
- return reject(e instanceof Error ? e : new Error(String(e)));
337
+ return reject(asThrownError(e));
337
338
  }
338
339
  child.on("error", () => { });
339
340
  const onSpawn = () => {
@@ -345,7 +346,7 @@ export const defaultLspSpawn = (command, args, cwd, signal) => new Promise((reso
345
346
  if (err?.code === "ENOENT")
346
347
  resolve(undefined);
347
348
  else
348
- reject(err instanceof Error ? err : new Error(String(err)));
349
+ reject(asThrownError(err));
349
350
  };
350
351
  const onAbort = () => {
351
352
  cleanup();
@@ -1,6 +1,7 @@
1
1
  import { encodeFrame, createFrameDecoder } from "./frame-decoder.js";
2
2
  import { killProcessTree } from "../execution-env/kill-tree.js";
3
3
  import { brandLspFailure } from "../../core/lsp.js";
4
+ import { asThrownError } from "../../core/thrown-value.js";
4
5
  export class StdioLspTransport {
5
6
  child;
6
7
  requestTimeoutMs;
@@ -132,7 +133,7 @@ export class StdioLspTransport {
132
133
  this.send({ jsonrpc: "2.0", id, method, params });
133
134
  }
134
135
  catch (e) {
135
- this.failAll(brandLspFailure(e instanceof Error ? e : new Error(String(e)), "server_terminated"));
136
+ this.failAll(brandLspFailure(asThrownError(e), "server_terminated"));
136
137
  }
137
138
  });
138
139
  }
@@ -166,8 +167,14 @@ function jsonRpcReason(code) {
166
167
  return "server_error";
167
168
  }
168
169
  function toError(e) {
169
- if (e instanceof Error)
170
- return e;
171
- const m = e?.message;
172
- return new Error(typeof m === "string" && m ? m : String(e));
170
+ try {
171
+ if (e instanceof Error)
172
+ return e;
173
+ const m = e?.message;
174
+ if (typeof m === "string" && m)
175
+ return new Error(m);
176
+ }
177
+ catch {
178
+ }
179
+ return asThrownError(e);
173
180
  }
@@ -1,5 +1,6 @@
1
1
  import vm from "node:vm";
2
2
  import { splitWorkflowMeta, WorkflowScriptError } from "./workflow-meta.js";
3
+ import { thrownText } from "../core/thrown-value.js";
3
4
  export const WORKFLOW_DATE_ERR = "Date.now() / new Date() are unavailable in workflow scripts (breaks resume). Stamp results after the workflow returns, or pass timestamps via args.";
4
5
  export const WORKFLOW_RANDOM_ERR = "Math.random() is unavailable in workflow scripts (breaks resume). For N independent samples, include the index in the agent label or prompt.";
5
6
  const DETERMINISM_SHIM = `(() => {
@@ -45,7 +46,7 @@ export const devWorkflowScriptRunner = {
45
46
  }
46
47
  }
47
48
  catch (err) {
48
- throw new WorkflowScriptError(`workflow script failed to compile: ${err instanceof Error ? err.message : String(err)}`);
49
+ throw new WorkflowScriptError(`workflow script failed to compile: ${thrownText(err)}`);
49
50
  }
50
51
  const noHostApi = (name) => () => {
51
52
  throw new WorkflowScriptError(`${name} is not available in workflow scripts — there are no Node/host APIs here. Use agent()/log()/` +
@@ -20,6 +20,7 @@ import { mergeWorkflowArgs, normalizeStringArg } from "./workflow-script-store.j
20
20
  import { builtinWorkflowListings, canonicalWorkflowName, resolveBuiltinWorkflow, workflowNameProbeOrder } from "./builtin-workflows.js";
21
21
  import { workflowSizeGuidelineSection } from "./workflow-size-guideline.js";
22
22
  import { toolFace } from "../core/tool-catalog-entries.js";
23
+ import { thrownText } from "../core/thrown-value.js";
23
24
  export const RUN_WORKFLOW_TOOL_NAME = "Workflow";
24
25
  function parentFaceSeat(ctxSeat, dep) {
25
26
  if (ctxSeat !== "roots")
@@ -289,7 +290,7 @@ export async function createRunWorkflowTool(d) {
289
290
  };
290
291
  }
291
292
  catch (e) {
292
- return { form: "script", parseError: String(e instanceof Error ? e.message : e).slice(0, 300), scriptChars: a.script.length };
293
+ return { form: "script", parseError: thrownText(e).slice(0, 300), scriptChars: a.script.length };
293
294
  }
294
295
  },
295
296
  description: "Author and run your own deterministic JS WORKFLOW that spawns and coordinates sub-agents. Pass `script` " +
@@ -425,7 +426,7 @@ export async function createRunWorkflowTool(d) {
425
426
  script = await d.scriptStore.load(rawScriptPath, taskScope ?? "default");
426
427
  }
427
428
  catch (err) {
428
- return structuredError(`failed to read workflow script at scriptPath: ${redactSecrets(err instanceof Error ? err.message : String(err)).slice(0, 300)}`);
429
+ return structuredError(`failed to read workflow script at scriptPath: ${redactSecrets(thrownText(err)).slice(0, 300)}`);
429
430
  }
430
431
  }
431
432
  else if (typeof rawScript === "string" && rawScript.length > 0) {
@@ -444,7 +445,7 @@ export async function createRunWorkflowTool(d) {
444
445
  resolved = await d.scriptStore.resolveName(probeName);
445
446
  }
446
447
  catch (err) {
447
- return structuredError(`failed to resolve workflow name: ${redactSecrets(err instanceof Error ? err.message : String(err)).slice(0, 300)}`);
448
+ return structuredError(`failed to resolve workflow name: ${redactSecrets(thrownText(err)).slice(0, 300)}`);
448
449
  }
449
450
  const resolvedShape = resolved;
450
451
  if (resolvedShape !== undefined && (resolvedShape === null || typeof resolvedShape !== "object")) {
@@ -478,7 +479,7 @@ export async function createRunWorkflowTool(d) {
478
479
  meta = parseWorkflowMeta(script);
479
480
  }
480
481
  catch (err) {
481
- return structuredError(err instanceof Error ? err.message : String(err));
482
+ return structuredError(thrownText(err));
482
483
  }
483
484
  try {
484
485
  const { body } = splitWorkflowMeta(script);
@@ -489,7 +490,7 @@ export async function createRunWorkflowTool(d) {
489
490
  }
490
491
  }
491
492
  catch (err) {
492
- return structuredError(`workflow script failed to compile: ${err instanceof Error ? err.message : String(err)}`);
493
+ return structuredError(`workflow script failed to compile: ${thrownText(err)}`);
493
494
  }
494
495
  const hostOnAsk = ctx.onAsk ?? d.parentOnAsk;
495
496
  const hostDurableApproval = ctx.durableApprovalForChildren ?? d.parentDurableApproval;
@@ -583,7 +584,7 @@ export async function createRunWorkflowTool(d) {
583
584
  }, { workflowDepth: d.workflowDepth });
584
585
  }
585
586
  catch (err) {
586
- return structuredError(err instanceof Error ? err.message : String(err));
587
+ return structuredError(thrownText(err));
587
588
  }
588
589
  const runId = handle.runId;
589
590
  let persistedScriptPath;
@@ -5,6 +5,7 @@ import { compileReadDeny } from "../tools/fs/read-deny.js";
5
5
  import { WorkflowScriptError } from "./workflow-meta.js";
6
6
  import { uuidv7 } from "../engine/session/uuid.js";
7
7
  import { retiredCheckpointStoreNull } from "../core/checkpoint-store.js";
8
+ import { thrownText } from "../core/thrown-value.js";
8
9
  export class WorkflowModelNotAllowedError extends Error {
9
10
  modelName;
10
11
  code = "workflow.model_not_allowed";
@@ -302,7 +303,7 @@ function pickWhitelist(scriptSpec) {
302
303
  compileReadDeny(entries, "agent(spec).readDenyPatterns");
303
304
  }
304
305
  catch (err) {
305
- throw new WorkflowScriptError(err instanceof Error ? err.message : String(err));
306
+ throw new WorkflowScriptError(thrownText(err));
306
307
  }
307
308
  safe.readDenyPatterns = entries;
308
309
  }
@@ -1,4 +1,5 @@
1
1
  import assert from "node:assert/strict";
2
+ import { thrownText } from "../core/thrown-value.js";
2
3
  const META = "export const meta = { name: 'conformance', description: 'sandbox escape probes' }\n";
3
4
  const WITNESS_FN = "__probeWitness";
4
5
  const WITNESS_PRELUDE = `function ${WITNESS_FN}(v) {\n` +
@@ -114,7 +115,7 @@ async function runLivenessCanary(runner) {
114
115
  result = out.result;
115
116
  }
116
117
  catch (err) {
117
- return { ok: false, detail: `run() REJECTED with: ${err instanceof Error ? err.message : String(err)}` };
118
+ return { ok: false, detail: `run() REJECTED with: ${thrownText(err)}` };
118
119
  }
119
120
  const expected = { marker: CANARY_MARKER, nonce, sum: 42, joined: `${CANARY_MARKER}:${nonce}` };
120
121
  try {
@@ -134,7 +135,7 @@ async function runProbeBody(runner, scriptSource, primitives, family, signal) {
134
135
  const live = await runLivenessCanary(runner);
135
136
  assert.ok(live.ok, `${family}: the probe threw AND the runner then failed the liveness canary — this runner is BROKEN, ` +
136
137
  `not blocking, so nothing about its membrane was proven. probe: ${scriptSource.slice(META.length)} | ` +
137
- `probe threw: ${err instanceof Error ? err.message : String(err)} | canary: ${live.detail}`);
138
+ `probe threw: ${thrownText(err)} | canary: ${live.detail}`);
138
139
  return { executed: false };
139
140
  }
140
141
  }
@@ -2,6 +2,7 @@ import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
2
2
  import { join, resolve, sep } from "node:path";
3
3
  import { physicalPathOfExisting } from "../core/physical-path.js";
4
4
  import { sanitizeScope } from "../stores/file/fs-atomic.js";
5
+ import { thrownText } from "../core/thrown-value.js";
5
6
  function isPlainObject(v) {
6
7
  return typeof v === "object" && v !== null && !Array.isArray(v);
7
8
  }
@@ -85,7 +86,7 @@ export function createFileWorkflowScriptStore(dir) {
85
86
  defaultArgs = JSON.parse(readFileSync(argsPath, "utf-8"));
86
87
  }
87
88
  catch (err) {
88
- throw new Error(`workflow script store: invalid defaultArgs JSON in ${stem}.args.json: ${err instanceof Error ? err.message : String(err)}`);
89
+ throw new Error(`workflow script store: invalid defaultArgs JSON in ${stem}.args.json: ${thrownText(err)}`);
89
90
  }
90
91
  return { script, defaultArgs };
91
92
  },