@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.
- package/CHANGELOG.md +64 -0
- package/dist/agents/agent-transcript-tool.js +2 -1
- package/dist/agents/cascade.js +2 -1
- package/dist/agents/list-agents-tool.js +2 -1
- package/dist/agents/observer.js +3 -2
- package/dist/agents/peer-session-drain.js +2 -1
- package/dist/agents/retain-ledger.js +2 -1
- package/dist/agents/send-message-tool.js +11 -10
- package/dist/agents/subagent.js +18 -17
- package/dist/agents/teacher.js +5 -4
- package/dist/brain/circuit-breaker.js +2 -1
- package/dist/brain/degrading.js +2 -1
- package/dist/brain/errors.js +24 -15
- package/dist/brain/failover.js +2 -1
- package/dist/brain/stream-engine.js +2 -1
- package/dist/core/a2a.js +4 -3
- package/dist/core/artifact-host.js +2 -1
- package/dist/core/ask-question.js +3 -2
- package/dist/core/auto-promote.js +3 -2
- package/dist/core/gate-bounded-await.d.ts +1 -1
- package/dist/core/gate-bounded-await.js +3 -2
- package/dist/core/gate-crash-trace.d.ts +7 -0
- package/dist/core/gate-crash-trace.js +3 -0
- package/dist/core/gate-fold.js +6 -2
- package/dist/core/gate-lanes.js +6 -3
- package/dist/core/git-worktree-env.js +6 -5
- package/dist/core/hooks.js +6 -5
- package/dist/core/lsp-session.js +3 -2
- package/dist/core/mcp.js +4 -3
- package/dist/core/memory-admission.js +2 -1
- package/dist/core/memory-engine/consolidation-driver.js +5 -4
- package/dist/core/memory-engine/consolidation.js +2 -1
- package/dist/core/memory-engine/delegation-settlement.js +4 -3
- package/dist/core/memory-engine/distiller.js +8 -4
- package/dist/core/memory-engine/engine.js +63 -55
- package/dist/core/memory-engine/file-backend.js +11 -10
- package/dist/core/memory-engine/layout.js +5 -4
- package/dist/core/memory-engine/session-incarnation.js +2 -1
- package/dist/core/memory.js +2 -1
- package/dist/core/oracle-isolation.js +3 -2
- package/dist/core/permission-rule-store.js +2 -1
- package/dist/core/runner/announce-once-ledger.js +2 -1
- package/dist/core/runner/compaction-seams.js +3 -2
- package/dist/core/runner/gate-exit.d.ts +5 -5
- package/dist/core/runner/gate-exit.js +0 -3
- package/dist/core/runner/git-leg-delivery.js +4 -3
- package/dist/core/runner/git-status-frame.js +2 -1
- package/dist/core/runner/mcp-redial.js +2 -1
- package/dist/core/runner/memory-consolidation-driver.js +2 -1
- package/dist/core/runner/permission-rule-lanes.js +3 -2
- package/dist/core/runner/prepare-artifact.js +2 -1
- package/dist/core/runner/prepare-ask-lane.js +2 -1
- package/dist/core/runner/prepare-caps-and-workflow.js +4 -3
- package/dist/core/runner/prepare-inherited-gate.js +2 -1
- package/dist/core/runner/prepare-memory-engine-session.js +2 -1
- package/dist/core/runner/prepare-memory.js +5 -4
- package/dist/core/runner/prepare-policy-chain.js +3 -2
- package/dist/core/runner/prepare-project-context.js +3 -2
- package/dist/core/runner/prepare-protocol-tools.js +3 -2
- package/dist/core/runner/prepare-suspend-saga.js +2 -1
- package/dist/core/runner/prepare-task.js +6 -5
- package/dist/core/runner/prepare-turn-wiring.js +2 -1
- package/dist/core/runner/resume-apply.js +5 -4
- package/dist/core/runner/resume-preflight.js +3 -2
- package/dist/core/runner/run-git-lane.js +4 -3
- package/dist/core/runner/run-leg.js +4 -3
- package/dist/core/runner/run-recovery-lanes.js +3 -2
- package/dist/core/runner/run-settle-and-teardown.js +2 -1
- package/dist/core/runner/run-stop-and-final-verify.js +2 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.js +2 -1
- package/dist/core/runner/run-terminal-adoption.js +6 -5
- package/dist/core/runner/run-turn-boundary.js +5 -4
- package/dist/core/runner/runtask.js +2 -1
- package/dist/core/runner/stream-settle-backstop.js +5 -4
- package/dist/core/runner/stream-steer-verb.js +2 -1
- package/dist/core/runner/strict-output-schema.js +2 -1
- package/dist/core/runner/teardown-bounded.js +2 -1
- package/dist/core/safe-notify.js +2 -1
- package/dist/core/skills-directory.js +2 -1
- package/dist/core/strategy-store.js +2 -1
- package/dist/core/task-registry-agent.js +2 -1
- package/dist/core/task-registry-monitor.js +2 -1
- package/dist/core/task-registry-workflow.js +2 -1
- package/dist/core/task-registry.js +3 -2
- package/dist/core/thrown-value.d.ts +79 -0
- package/dist/core/thrown-value.js +56 -0
- package/dist/core/tool-errors.d.ts +0 -6
- package/dist/core/tool-errors.js +36 -14
- package/dist/core/tool-model-gate.js +2 -1
- package/dist/core/tool-policy.js +6 -13
- package/dist/core/tool-result-store.js +2 -1
- package/dist/core/tool-spec.d.ts +12 -0
- package/dist/core/write-protect.js +4 -4
- package/dist/engine/compaction/compaction.js +3 -4
- package/dist/engine/harness/agent-harness.js +2 -1
- package/dist/engine/harness/types.js +8 -7
- package/dist/engine/loop/agent-loop.js +7 -6
- package/dist/engine/lsp/node-lsp-manager.js +5 -4
- package/dist/engine/lsp/stdio-lsp-transport.js +12 -5
- package/dist/orchestration/dev-vm-script-runner.js +2 -1
- package/dist/orchestration/run-workflow-tool.js +7 -6
- package/dist/orchestration/workflow-governance.js +2 -1
- package/dist/orchestration/workflow-sandbox-conformance.js +3 -2
- package/dist/orchestration/workflow-script-store.js +2 -1
- package/dist/orchestration/workflow.js +15 -14
- package/dist/server/http.js +3 -2
- package/dist/stores/file/adoption/adopt.js +3 -2
- package/dist/stores/file/fs-atomic.js +2 -1
- package/dist/stores/file/permission-rule-adopt.js +2 -1
- package/dist/stores/file/permission-rule-store.js +2 -1
- package/dist/stores/file/session-store.js +4 -3
- package/dist/stores/file/strategy-store.js +7 -6
- package/dist/tools/artifact/artifact-tool.js +3 -2
- package/dist/tools/artifact/conformance.js +4 -3
- package/dist/tools/fs/bash-lexer.d.ts +12 -5
- package/dist/tools/fs/bash-lexer.js +5 -2
- package/dist/tools/fs/bash-program-position.d.ts +18 -0
- package/dist/tools/fs/bash-program-position.js +13 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +23 -7
- package/dist/tools/fs/bash-readonly-classifier.js +74 -41
- package/dist/tools/fs/fs-bash.d.ts +28 -0
- package/dist/tools/fs/fs-bash.js +31 -13
- package/dist/tools/fs/fs-write.js +3 -2
- package/dist/tools/fs/safety.js +2 -1
- package/dist/tools/fs/search.js +3 -2
- package/dist/tools/monitor.js +3 -2
- package/dist/tools/sql.js +2 -1
- package/dist/tools/web.js +5 -15
- package/dist/tools/worktree.js +12 -9
- package/package.json +2 -1
- package/wire-changes/core@7.23.1.json +205 -0
- package/wire-changes/core@7.23.2.json +42 -0
|
@@ -4,6 +4,7 @@ import { hasDestroy } from "../remote-env.js";
|
|
|
4
4
|
import { inlineUntrusted } from "../untrusted-text.js";
|
|
5
5
|
import { describeSuppliedValue } from "./decide-continuation.js";
|
|
6
6
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
7
|
+
import { asThrownError } from "../thrown-value.js";
|
|
7
8
|
const RESUME_PREFLIGHT_DEFAULT_TIMEOUT_MS = 10_000;
|
|
8
9
|
export async function resumePreflight(input) {
|
|
9
10
|
const { token, cp, resumeSignal, recheckGovernanceWindow, store, runner, locallyClaimedTokens, parentConstraintRegistry, suspendedEnvReaps } = input;
|
|
@@ -40,7 +41,7 @@ export async function resumePreflight(input) {
|
|
|
40
41
|
abortEnds: true,
|
|
41
42
|
onBadTimeout: (badErr) => {
|
|
42
43
|
try {
|
|
43
|
-
runner.deps.onError?.(
|
|
44
|
+
runner.deps.onError?.(asThrownError(badErr), { phase: "hook", sessionId: cp.sessionId });
|
|
44
45
|
}
|
|
45
46
|
catch {
|
|
46
47
|
}
|
|
@@ -57,7 +58,7 @@ export async function resumePreflight(input) {
|
|
|
57
58
|
catch (preflightErr) {
|
|
58
59
|
if (preflightErr instanceof CheckpointError)
|
|
59
60
|
throw preflightErr;
|
|
60
|
-
const err =
|
|
61
|
+
const err = asThrownError(preflightErr);
|
|
61
62
|
try {
|
|
62
63
|
runner.deps.onError?.(err, { phase: "hook", sessionId: cp.sessionId });
|
|
63
64
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { GIT_STATUS_ECHO_PREVIEW } from "./git-status-frame.js";
|
|
2
2
|
import { wrapGitFrame } from "./git-leg-delivery.js";
|
|
3
|
+
import { thrownText } from "../thrown-value.js";
|
|
3
4
|
export function runGitLane(input) {
|
|
4
5
|
const { prepared, queue, rs, ident, onMessageUpdate, onMessageEnd, onToolStart, onToolEnd, onTurnEnd, onTurnBoundary, runner } = input;
|
|
5
6
|
prepared.gitStatusRef.reassert = async () => {
|
|
@@ -34,7 +35,7 @@ export function runGitLane(input) {
|
|
|
34
35
|
}
|
|
35
36
|
catch (mirrorErr) {
|
|
36
37
|
try {
|
|
37
|
-
runner.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; next leg re-announces): ${
|
|
38
|
+
runner.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; next leg re-announces): ${thrownText(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
38
39
|
}
|
|
39
40
|
catch {
|
|
40
41
|
}
|
|
@@ -43,7 +44,7 @@ export function runGitLane(input) {
|
|
|
43
44
|
catch (err) {
|
|
44
45
|
ref.announced = { kind: use.kind, hash: use.hash, pending: true };
|
|
45
46
|
try {
|
|
46
|
-
runner.deps.onError?.(new Error(`git status frame re-assert append failed (pending; retried at the next boundary): ${
|
|
47
|
+
runner.deps.onError?.(new Error(`git status frame re-assert append failed (pending; retried at the next boundary): ${thrownText(err)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
47
48
|
}
|
|
48
49
|
catch {
|
|
49
50
|
}
|
|
@@ -64,7 +65,7 @@ export function runGitLane(input) {
|
|
|
64
65
|
catch (mirrorErr) {
|
|
65
66
|
prepared.gitStatusRef.mirrorOwed = owed;
|
|
66
67
|
try {
|
|
67
|
-
runner.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; retried at the next serialization point): ${
|
|
68
|
+
runner.deps.onError?.(new Error(`git announcement mirror write failed (frame delivered; retried at the next serialization point): ${thrownText(mirrorErr)}`), { phase: "degraded", sessionId: prepared.sessionId });
|
|
68
69
|
}
|
|
69
70
|
catch {
|
|
70
71
|
}
|
|
@@ -29,6 +29,7 @@ import { toolEndBodyFrom, toolResultMsg } from "./tool-end-body.js";
|
|
|
29
29
|
import { announceListingTruncation } from "../skill-listing-budget.js";
|
|
30
30
|
import { attachmentEnvelopeTags, commitAgentListing, commitSkillsListing, reduceToolEnd, renderAgentListingFrame, renderOrphanedBackgroundTasks, renderSkillsListingFrame, stampWriteAnchor } from "./turn-attachments.js";
|
|
31
31
|
import { flushOwedAnnouncedSnapshot } from "./announce-once-ledger.js";
|
|
32
|
+
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
32
33
|
export async function runLeg(input) {
|
|
33
34
|
const { spec, queue, prepared, resume, rs, isDelegatedChild, entryActor, effectiveTimeoutMs, timeout, pushContent, ident, emitCommitted, writeFamilyOf, startedToolCallIds, announceWorkspaceMove, withBrainSinks, flushGitMirror, loopLatch, notificationLane, unsubscribeTaskNotifications, unsubGitRetry, unsubBoundary, unsub, runner, next } = input;
|
|
34
35
|
let final;
|
|
@@ -164,7 +165,7 @@ export async function runLeg(input) {
|
|
|
164
165
|
: { ...(seat.value?.additionalContext !== undefined && seat.value.additionalContext !== "" ? { context: seat.value.additionalContext } : {}) };
|
|
165
166
|
}
|
|
166
167
|
catch (hookErr) {
|
|
167
|
-
const err =
|
|
168
|
+
const err = asThrownError(hookErr);
|
|
168
169
|
try {
|
|
169
170
|
runner.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
|
|
170
171
|
}
|
|
@@ -301,7 +302,7 @@ export async function runLeg(input) {
|
|
|
301
302
|
}
|
|
302
303
|
}
|
|
303
304
|
catch (hookErr) {
|
|
304
|
-
const err =
|
|
305
|
+
const err = asThrownError(hookErr);
|
|
305
306
|
try {
|
|
306
307
|
runner.deps.onError?.(err, { phase: "hook", sessionId: prepared.sessionId });
|
|
307
308
|
}
|
|
@@ -691,7 +692,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
691
692
|
res = await tool.execute(pendingAction.toolCallId, args, prepared.abortController.signal);
|
|
692
693
|
}
|
|
693
694
|
catch (err) {
|
|
694
|
-
const execError = `Error: ${
|
|
695
|
+
const execError = `Error: ${thrownText(err)}`;
|
|
695
696
|
const marks = (() => {
|
|
696
697
|
if (err === null || typeof err !== "object")
|
|
697
698
|
return undefined;
|
|
@@ -9,6 +9,7 @@ import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFi
|
|
|
9
9
|
import { gitRestateOption } from "./git-leg-delivery.js";
|
|
10
10
|
import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
11
11
|
import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
|
|
12
|
+
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
12
13
|
export function runRecoveryLanes(input) {
|
|
13
14
|
const { spec, prepared, queue, rs, ident, compactionBrain, compactionBreaker, windowSafetyOptions, runner } = input;
|
|
14
15
|
const runForcedCompactionPass = async (lane, turnSignal) => {
|
|
@@ -108,8 +109,8 @@ export function runRecoveryLanes(input) {
|
|
|
108
109
|
return false;
|
|
109
110
|
}
|
|
110
111
|
compactionBreaker.failures += 1;
|
|
111
|
-
runner.deps.onError?.(
|
|
112
|
-
const msg =
|
|
112
|
+
runner.deps.onError?.(asThrownError(err), { phase: "compaction", sessionId: prepared.sessionId });
|
|
113
|
+
const msg = thrownText(err);
|
|
113
114
|
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
|
|
114
115
|
return false;
|
|
115
116
|
}
|
|
@@ -20,6 +20,7 @@ import { generatePromptSuggestions } from "./prompt-suggestions.js";
|
|
|
20
20
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
21
21
|
import { autoRunConsolidationFromRecommendation, autoRunOnRecommendationEffective, scopeHasUnconsumedRecommendation } from "./memory-consolidation-driver.js";
|
|
22
22
|
import { amendTerminal, terminalProjection } from "./terminal-projection.js";
|
|
23
|
+
import { asThrownError } from "../thrown-value.js";
|
|
23
24
|
export async function runSettleAndTeardown(input) {
|
|
24
25
|
const { spec, queue, prepared, resume, internals, rs, stats, ident, parentToolCallId, subagentName, loopLatch, timeout, ownCommittedTailRef, emitDelegationLifecycle, taskIdRef, manualCompactRef, drainManualCompact, final, threw, abortedLive, userInterruptedLive, answerSettled, strandedHumanAnswers, reasoningResolution, comp, owedUserInput, setResult, onSuggestions, onSuspend, runner, sessions } = input;
|
|
25
26
|
try {
|
|
@@ -204,7 +205,7 @@ export async function runSettleAndTeardown(input) {
|
|
|
204
205
|
}
|
|
205
206
|
catch (err) {
|
|
206
207
|
try {
|
|
207
|
-
runner.deps.onError?.(
|
|
208
|
+
runner.deps.onError?.(asThrownError(err), { phase: "hook", sessionId: prepared.sessionId });
|
|
208
209
|
}
|
|
209
210
|
catch {
|
|
210
211
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { openSystemReminder } from "../reminder-mint.js";
|
|
2
2
|
import { formatHookFeedback, hookSeatExpiredError, runHookSeat } from "../hooks.js";
|
|
3
|
+
import { asThrownError } from "../thrown-value.js";
|
|
3
4
|
const STOP_HOOK_BLOCK_CAP = 8;
|
|
4
5
|
export function runStopAndFinalVerify(input) {
|
|
5
6
|
const { spec, prepared, queue, rs, stats, ident, walltimeMonotonicDeadline, runner } = input;
|
|
@@ -120,7 +121,7 @@ export function runStopAndFinalVerify(input) {
|
|
|
120
121
|
result = stopSeat.value;
|
|
121
122
|
}
|
|
122
123
|
catch (err) {
|
|
123
|
-
runner.deps.onError?.(
|
|
124
|
+
runner.deps.onError?.(asThrownError(err), { phase: "hook", sessionId: prepared.sessionId });
|
|
124
125
|
return [];
|
|
125
126
|
}
|
|
126
127
|
const messages = [];
|
|
@@ -6,6 +6,7 @@ import { emitTrace } from "../trace.js";
|
|
|
6
6
|
import { resolveMaxTurns } from "./clock-and-limits.js";
|
|
7
7
|
import { resolveOutputRetries } from "./synthetic-tools.js";
|
|
8
8
|
import { cacheFamilyOf } from "./usage-accounting.js";
|
|
9
|
+
import { asThrownError } from "../thrown-value.js";
|
|
9
10
|
export function runTelemetryAndBudgetSeats(input) {
|
|
10
11
|
const { spec, prepared, resume, stats, taskIdRef, entryTracer, modelCatalog, runSourceTaskId, rs, runner, sessions } = input;
|
|
11
12
|
prepared.liveSpendRef.get = () => ({ costMicroUsd: stats.costMicroUsd, tokens: stats.tokens, turns: stats.turns, walltimeMs: Math.round(performance.now() - rs.telemetry.taskStartMonotonic) });
|
|
@@ -66,7 +67,7 @@ export function runTelemetryAndBudgetSeats(input) {
|
|
|
66
67
|
rs.telemetry.taskStartMonotonic = performance.now();
|
|
67
68
|
const discloseNoteTaskRunFailure = (err) => {
|
|
68
69
|
try {
|
|
69
|
-
runner.deps.onError?.(
|
|
70
|
+
runner.deps.onError?.(asThrownError(err), { phase: "config", sessionId: prepared.sessionId });
|
|
70
71
|
}
|
|
71
72
|
catch {
|
|
72
73
|
}
|
|
@@ -10,6 +10,7 @@ import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions }
|
|
|
10
10
|
import { gitRestateOption } from "./git-leg-delivery.js";
|
|
11
11
|
import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
12
12
|
import { reconciledToolEndBody } from "./tool-end-body.js";
|
|
13
|
+
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
13
14
|
export async function runTerminalAdoption(input) {
|
|
14
15
|
const { spec, queue, prepared, rs, stats, ident, emitCommitted, startedToolCallIds, toolLabels, timeout, compactionBrain, compactionBreaker, windowSafetyOptions, undrainedUserAtEnd, userInputLostAtEnd, final, abortedLive, userInterruptedLive, runner, sessions, next } = input;
|
|
15
16
|
let { threw } = input;
|
|
@@ -48,7 +49,7 @@ export async function runTerminalAdoption(input) {
|
|
|
48
49
|
reconcileComplete = true;
|
|
49
50
|
}
|
|
50
51
|
catch (reconcileErr) {
|
|
51
|
-
runner.deps.onError?.(
|
|
52
|
+
runner.deps.onError?.(asThrownError(reconcileErr), {
|
|
52
53
|
phase: "interrupt-reconcile",
|
|
53
54
|
sessionId: prepared.sessionId,
|
|
54
55
|
});
|
|
@@ -57,7 +58,7 @@ export async function runTerminalAdoption(input) {
|
|
|
57
58
|
await prepared.harness.flushQueuedSessionWrites();
|
|
58
59
|
}
|
|
59
60
|
catch (flushErr) {
|
|
60
|
-
runner.deps.onError?.(
|
|
61
|
+
runner.deps.onError?.(asThrownError(flushErr), {
|
|
61
62
|
phase: "interrupt-reconcile",
|
|
62
63
|
sessionId: prepared.sessionId,
|
|
63
64
|
});
|
|
@@ -69,7 +70,7 @@ export async function runTerminalAdoption(input) {
|
|
|
69
70
|
emitCommitted(entryId, "user");
|
|
70
71
|
}
|
|
71
72
|
catch (markerErr) {
|
|
72
|
-
runner.deps.onError?.(
|
|
73
|
+
runner.deps.onError?.(asThrownError(markerErr), {
|
|
73
74
|
phase: "interrupt-reconcile",
|
|
74
75
|
sessionId: prepared.sessionId,
|
|
75
76
|
});
|
|
@@ -96,7 +97,7 @@ export async function runTerminalAdoption(input) {
|
|
|
96
97
|
carriedContinuation += 1;
|
|
97
98
|
}
|
|
98
99
|
catch (parkErr) {
|
|
99
|
-
runner.deps.onError?.(
|
|
100
|
+
runner.deps.onError?.(asThrownError(parkErr), {
|
|
100
101
|
phase: "config",
|
|
101
102
|
sessionId: prepared.sessionId,
|
|
102
103
|
});
|
|
@@ -161,7 +162,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
|
|
|
161
162
|
comp = finishComp;
|
|
162
163
|
}
|
|
163
164
|
catch (err) {
|
|
164
|
-
const failMsg =
|
|
165
|
+
const failMsg = thrownText(err);
|
|
165
166
|
const failReason = failMsg.length > 512 ? `${failMsg.slice(0, 512)}…` : failMsg;
|
|
166
167
|
emitTrace(opts?.telemetry?.tracer ?? spec.tracer ?? runner.deps.tracer, () => ({
|
|
167
168
|
kind: "compaction.failed",
|
|
@@ -17,6 +17,7 @@ import { gitRestateOption } from "./git-leg-delivery.js";
|
|
|
17
17
|
import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
|
|
18
18
|
import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
|
|
19
19
|
import { contextUsageSections } from "../../prompt-assembly/section-usage.js";
|
|
20
|
+
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
20
21
|
const BATCH_CONTEXT_MIN_KEEP_BYTES = 160;
|
|
21
22
|
const BATCH_TRUNCATION_MARKER = "\n…[truncated]";
|
|
22
23
|
export function createTurnBoundary(input) {
|
|
@@ -49,7 +50,7 @@ export function createTurnBoundary(input) {
|
|
|
49
50
|
if (answer === GOVERNANCE_READ_STALLED) {
|
|
50
51
|
void windowRead.catch((lateErr) => {
|
|
51
52
|
try {
|
|
52
|
-
runnerHooks.onError?.(
|
|
53
|
+
runnerHooks.onError?.(asThrownError(lateErr), { phase: "config", sessionId: prepared.sessionId });
|
|
53
54
|
}
|
|
54
55
|
catch {
|
|
55
56
|
}
|
|
@@ -71,7 +72,7 @@ export function createTurnBoundary(input) {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
catch (govErr) {
|
|
74
|
-
rs.limits.platformTerminal =
|
|
75
|
+
rs.limits.platformTerminal = asThrownError(govErr);
|
|
75
76
|
void prepared.harness.abort();
|
|
76
77
|
prepared.abortController.abort();
|
|
77
78
|
return undefined;
|
|
@@ -479,7 +480,7 @@ export function createTurnBoundary(input) {
|
|
|
479
480
|
}
|
|
480
481
|
catch (err) {
|
|
481
482
|
try {
|
|
482
|
-
runnerHooks.onError?.(
|
|
483
|
+
runnerHooks.onError?.(asThrownError(err), { phase: "hook", sessionId: prepared.sessionId });
|
|
483
484
|
}
|
|
484
485
|
catch {
|
|
485
486
|
}
|
|
@@ -689,7 +690,7 @@ export function createTurnBoundary(input) {
|
|
|
689
690
|
else if (!prepared.abortController.signal.aborted) {
|
|
690
691
|
compactionBreaker.failures += 1;
|
|
691
692
|
runnerHooks.onError?.(err, { phase: "compaction", sessionId: prepared.sessionId });
|
|
692
|
-
const msg =
|
|
693
|
+
const msg = thrownText(err);
|
|
693
694
|
const failReason = msg.length > 512 ? `${msg.slice(0, 512)}…` : msg;
|
|
694
695
|
emitTrace(rs.telemetry.tracer, () => ({
|
|
695
696
|
kind: "compaction.failed",
|
|
@@ -56,6 +56,7 @@ import { runCompactionMachinery } from "./run-compaction-machinery.js";
|
|
|
56
56
|
import { runStopAndFinalVerify } from "./run-stop-and-final-verify.js";
|
|
57
57
|
import { runRecoveryLanes } from "./run-recovery-lanes.js";
|
|
58
58
|
import { runGitLane } from "./run-git-lane.js";
|
|
59
|
+
import { thrownText } from "../thrown-value.js";
|
|
59
60
|
export class Runner {
|
|
60
61
|
deps;
|
|
61
62
|
sessions;
|
|
@@ -83,7 +84,7 @@ export class Runner {
|
|
|
83
84
|
version: 1,
|
|
84
85
|
sessionId: context.sessionId,
|
|
85
86
|
phase: context.phase,
|
|
86
|
-
reason: (
|
|
87
|
+
reason: (thrownText(sinkErr)).slice(0, 256),
|
|
87
88
|
ts: Date.now(),
|
|
88
89
|
}));
|
|
89
90
|
}
|
|
@@ -6,6 +6,7 @@ import { createSafeNotifier } from "../safe-notify.js";
|
|
|
6
6
|
import { settleTeardownLeg } from "./teardown-bounded.js";
|
|
7
7
|
import { emitTrace } from "../trace.js";
|
|
8
8
|
import { deliverDelegationLifecycle } from "../types.js";
|
|
9
|
+
import { asThrownError, thrownText } from "../thrown-value.js";
|
|
9
10
|
export function streamSettleBackstop(input) {
|
|
10
11
|
const { spec, resume, queue, taskIdRef, entryTracer, runStartedAt, live, runner, sessions, locallyClaimedTokens, parentConstraintRegistry, suspendedEnvReaps } = input;
|
|
11
12
|
const onRunRejected = async (err) => {
|
|
@@ -21,8 +22,8 @@ export function streamSettleBackstop(input) {
|
|
|
21
22
|
}
|
|
22
23
|
catch (reopenErr) {
|
|
23
24
|
parentConstraintRegistry.delete(resume.cp.token);
|
|
24
|
-
const original =
|
|
25
|
-
const reopenMsg =
|
|
25
|
+
const original = asThrownError(err);
|
|
26
|
+
const reopenMsg = thrownText(reopenErr);
|
|
26
27
|
const definitive = errorCodeOf(reopenErr) === "checkpoint.reopen_failed";
|
|
27
28
|
reopenStateUnknown = !definitive;
|
|
28
29
|
err = Object.assign(new Error(definitive
|
|
@@ -47,7 +48,7 @@ export function streamSettleBackstop(input) {
|
|
|
47
48
|
}
|
|
48
49
|
if (live.resultValue !== undefined) {
|
|
49
50
|
try {
|
|
50
|
-
runner.deps.onError?.(
|
|
51
|
+
runner.deps.onError?.(asThrownError(err), { phase: "config", sessionId: live.resultValue.sessionId });
|
|
51
52
|
}
|
|
52
53
|
catch {
|
|
53
54
|
}
|
|
@@ -68,7 +69,7 @@ export function streamSettleBackstop(input) {
|
|
|
68
69
|
taskId: taskIdRef.current ?? "unknown",
|
|
69
70
|
sessionId: taskIdRef.sessionId ?? "unknown",
|
|
70
71
|
...(taskIdRef.runId !== undefined ? { runId: taskIdRef.runId } : {}),
|
|
71
|
-
terminal: { kind: "failed", ...(code !== undefined ? { code } : {}), message:
|
|
72
|
+
terminal: { kind: "failed", ...(code !== undefined ? { code } : {}), message: thrownText(err) },
|
|
72
73
|
result: "",
|
|
73
74
|
...(remoteEnvFailure !== undefined ? { remoteEnvFailures: remoteEnvFailure } : {}),
|
|
74
75
|
...(taskIdRef.effectiveMemoryScopes !== undefined ? { effectiveMemoryScopes: taskIdRef.effectiveMemoryScopes } : {}),
|
|
@@ -7,6 +7,7 @@ import { isSystemInjectionPriority, SYSTEM_INJECTION_PRIORITIES } from "../task-
|
|
|
7
7
|
import { deliverEngineNotice } from "../types.js";
|
|
8
8
|
import { inlineUntrusted, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
9
9
|
import { nextHumanInputSeq, sameAcceptedSteerInput } from "./steer-admission.js";
|
|
10
|
+
import { asThrownError } from "../thrown-value.js";
|
|
10
11
|
export function streamSteerVerb(input) {
|
|
11
12
|
const { spec, internals, queue, acceptedSteerInputs, live, ready, orTimeout, readyTimeoutMs: READY_TIMEOUT_MS, steeringError, runner } = input;
|
|
12
13
|
let steerChain = Promise.resolve();
|
|
@@ -111,7 +112,7 @@ export function streamSteerVerb(input) {
|
|
|
111
112
|
catch (hookErr) {
|
|
112
113
|
if (hookErr instanceof Error && hookErr.code === "steering.blocked_by_hook")
|
|
113
114
|
throw hookErr;
|
|
114
|
-
const err =
|
|
115
|
+
const err = asThrownError(hookErr);
|
|
115
116
|
try {
|
|
116
117
|
runner.deps.onError?.(err, { phase: "hook", sessionId: h.sessionId });
|
|
117
118
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { thrownText } from "../thrown-value.js";
|
|
1
2
|
const STRICT_KEYWORDS = new Set([
|
|
2
3
|
"$schema",
|
|
3
4
|
"type",
|
|
@@ -298,6 +299,6 @@ function compileUncached(schema) {
|
|
|
298
299
|
return { modelSchema: schema, strict: false, fallbackReason: derived.reason };
|
|
299
300
|
}
|
|
300
301
|
catch (err) {
|
|
301
|
-
return { modelSchema: schema, strict: false, fallbackReason: `derivation_error: ${
|
|
302
|
+
return { modelSchema: schema, strict: false, fallbackReason: `derivation_error: ${thrownText(err)}` };
|
|
302
303
|
}
|
|
303
304
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { asThrownError } from "../thrown-value.js";
|
|
1
2
|
export const TEARDOWN_LEG_MAX_MS = 15_000;
|
|
2
3
|
let teardownLegBudgetMs = TEARDOWN_LEG_MAX_MS;
|
|
3
4
|
export function setTeardownLegBudgetForTests(ms) {
|
|
@@ -24,7 +25,7 @@ export async function settleTeardownLeg(leg, label, report) {
|
|
|
24
25
|
}
|
|
25
26
|
catch (e) {
|
|
26
27
|
try {
|
|
27
|
-
report?.(
|
|
28
|
+
report?.(asThrownError(e));
|
|
28
29
|
}
|
|
29
30
|
catch {
|
|
30
31
|
}
|
package/dist/core/safe-notify.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { thrownText } from "./thrown-value.js";
|
|
1
2
|
function containedError(thrown) {
|
|
2
3
|
try {
|
|
3
4
|
if (thrown instanceof Error && typeof thrown.message === "string")
|
|
@@ -13,7 +14,7 @@ export function safeErrorMessage(error) {
|
|
|
13
14
|
const m = error?.message;
|
|
14
15
|
if (typeof m === "string")
|
|
15
16
|
return m;
|
|
16
|
-
return
|
|
17
|
+
return thrownText(error);
|
|
17
18
|
}
|
|
18
19
|
catch {
|
|
19
20
|
return "a failure whose message could not be read";
|
|
@@ -2,6 +2,7 @@ import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
|
2
2
|
import { isAbsolute, join, relative } from "node:path";
|
|
3
3
|
import { physicalPathOf } from "./physical-path.js";
|
|
4
4
|
import { isSkillSpecifierEnforced, parseSkillToolEntry } from "./skill-tool-specifier.js";
|
|
5
|
+
import { thrownText } from "./thrown-value.js";
|
|
5
6
|
const SKILL_FILE = "SKILL.md";
|
|
6
7
|
const RESOURCE_DIRS = ["assets", "references", "scripts"];
|
|
7
8
|
const NAME_MAX_CHARS = 64;
|
|
@@ -282,7 +283,7 @@ function isDecodableText(bytes) {
|
|
|
282
283
|
return Buffer.byteLength(decoded, "utf8") === bytes.byteLength;
|
|
283
284
|
}
|
|
284
285
|
function errText(err) {
|
|
285
|
-
return
|
|
286
|
+
return thrownText(err);
|
|
286
287
|
}
|
|
287
288
|
const ALL_TOOLS_WILDCARD = "*";
|
|
288
289
|
function splitToolNames(declared) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { termSet } from "./memory-vector.js";
|
|
2
|
+
import { thrownText } from "./thrown-value.js";
|
|
2
3
|
function invalidKnob(message, code) {
|
|
3
4
|
const e = new Error(message);
|
|
4
5
|
e.code = code;
|
|
@@ -244,7 +245,7 @@ export async function seedStrategies(store, scope, entries) {
|
|
|
244
245
|
await store.save(s);
|
|
245
246
|
}
|
|
246
247
|
catch (err) {
|
|
247
|
-
return { written, failedAt: { id: s.id, error:
|
|
248
|
+
return { written, failedAt: { id: s.id, error: thrownText(err) } };
|
|
248
249
|
}
|
|
249
250
|
written++;
|
|
250
251
|
}
|
|
@@ -7,6 +7,7 @@ import { delimitUntrusted } from "./untrusted-text.js";
|
|
|
7
7
|
import { boundedRedactedSummary } from "./untrusted-egress.js";
|
|
8
8
|
import { mintCompletionId, commitCompletionIdIfEmpty, clipTaskOutput, assertOwnership, sleepPollStep, alreadyTerminalStopNote, canAccess, normalizeAgentName, closestName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE, BG_AGENT_REAP_STOP_ERROR, } from "./task-registry-shared.js";
|
|
9
9
|
import { buildToolResultRef, OFFLOAD_TOOL_NAME, toolResultProvenanceOf } from "./tool-result-store.js";
|
|
10
|
+
import { thrownLabel } from "./thrown-value.js";
|
|
10
11
|
export function activeDelegationHandlesLane(core, scope, rootSessionId) {
|
|
11
12
|
const out = [];
|
|
12
13
|
for (const h of core.handles.values()) {
|
|
@@ -140,7 +141,7 @@ export function durableAgentWriteLane(handle, patch, clear) {
|
|
|
140
141
|
poison("handle collision (foreign durable row left untouched)");
|
|
141
142
|
return;
|
|
142
143
|
}
|
|
143
|
-
shutdownDebug(`durable-agents write failed for ${lane.record.handle} (attempt ${attempt + 1}/3)`, { error:
|
|
144
|
+
shutdownDebug(`durable-agents write failed for ${lane.record.handle} (attempt ${attempt + 1}/3)`, { error: thrownLabel(e) });
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
147
|
lane.flushFailed = true;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
2
2
|
import { assertOwnership, defaultMonitorTimers, MONITOR_MAX_TIMEOUT_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_BATCH_WINDOW_MS, MONITOR_MAX_BATCHES_PER_MINUTE, MONITOR_STORM_BURST, MONITOR_STORM_KILL_AFTER_MS, MONITOR_LINE_BUF_CAP, MONITOR_SPILL_CAP_CHARS, TASK_OUTPUT_MAX_CHARS, mintCompletionId, clipMonitorEvent, clipMonitorLine, terminalTaskSummary, accountDroppedBytes, renderSpoolBody, rollSpoolText, statusFromBackground, droppedGapNote, firstDropNote, alreadyTerminalStopNote, clipTaskOutput, sleepPollStep, } from "./task-registry-shared.js";
|
|
3
3
|
import { buildToolResultRef, OFFLOAD_TOOL_NAME, toolResultProvenanceOf } from "./tool-result-store.js";
|
|
4
|
+
import { thrownText } from "./thrown-value.js";
|
|
4
5
|
export function registerMonitorLane(core, input) {
|
|
5
6
|
assertOwnership(input, "registerMonitor");
|
|
6
7
|
const id = core.mintTaskId("monitor");
|
|
@@ -289,7 +290,7 @@ export function startMonitorWatcherLane(core, id) {
|
|
|
289
290
|
if (++pollFailures <= MAX_RETRYABLE_POLL_FAILURES)
|
|
290
291
|
return;
|
|
291
292
|
handle.status = "failed";
|
|
292
|
-
terminal("failed", `monitor watcher crashed (${
|
|
293
|
+
terminal("failed", `monitor watcher crashed (${thrownText(e)}); watch ended.${pendingSuppressionNote()}`, pendingFinalLines([]));
|
|
293
294
|
}
|
|
294
295
|
finally {
|
|
295
296
|
ticking = false;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { boundedRedactedSummary } from "./untrusted-egress.js";
|
|
2
2
|
import { delimitUntrusted } from "./untrusted-text.js";
|
|
3
|
+
import { thrownText } from "./thrown-value.js";
|
|
3
4
|
import { assertOwnership, canAccessWorkflowRun, formatWorkflowRun, sleepPollStep, } from "./task-registry-shared.js";
|
|
4
5
|
export function registerWorkflowLane(core, input) {
|
|
5
6
|
assertOwnership(input, "registerWorkflow");
|
|
@@ -37,7 +38,7 @@ export function registerWorkflowLane(core, input) {
|
|
|
37
38
|
}, (err) => {
|
|
38
39
|
if (handle.status !== "cancelled")
|
|
39
40
|
handle.status = "failed";
|
|
40
|
-
handle.error = boundedRedactedSummary(
|
|
41
|
+
handle.error = boundedRedactedSummary(thrownText(err), 500);
|
|
41
42
|
const rejectedRun = err !== null && (typeof err === "object" || typeof err === "function")
|
|
42
43
|
? err.workflowRun
|
|
43
44
|
: undefined;
|
|
@@ -13,6 +13,7 @@ export { normalizeAgentName, DURABLE_AGENT_HEARTBEAT_MS, DURABLE_AGENT_HANDLE_RE
|
|
|
13
13
|
import { TASK_OUTPUT_TOOL_NAME, TASK_STOP_TOOL_NAME, 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";
|
|
14
14
|
import { durableAgentArmedLane, durableAgentRowProbeLane, beginDurableClaimLane, endDurableClaimLane, reapDurableAgentsLane, noteBackgroundAgentActivityLane, reapStaleSessionBackgroundAgentsLane, releaseDurableTranscriptAnchorLane, bindBackgroundAgentSessionLane, registerBackgroundAgentLane, recordBackgroundAgentOrgAdmissionLane, parkBackgroundAgentLane, reconcileParkedAgentsLane, claimParkedAgentLane, rollbackParkedClaimLane, consumeParkedFlipLane, finalizeParkedResumeLane, settleBackgroundAgentLane, abortBackgroundAgentsForOwnerLane, serveDurableAgentRowLane, resolveBackgroundAgentByNameLane, backgroundAgentCycleSeqLane, activeDelegationHandlesLane, markRetainedContinuationLane, reviveBackgroundAgentLane, settleRevivedAgentLane, unmarkRetainedContinuationLane, attachAgentNotifyLane, deliverToRunningAgentLane, runningBackgroundAgentLabelsLane, runningAgentFooterLane, notFoundRunningAgentsTail, pollBackgroundAgentLane, stopBackgroundAgentLane, } from "./task-registry-agent.js";
|
|
15
15
|
import { toolFace } from "./tool-catalog-entries.js";
|
|
16
|
+
import { thrownText } from "./thrown-value.js";
|
|
16
17
|
export { canAccessWorkflowRun, clipTaskOutput, MONITOR_BATCH_WINDOW_MS, MONITOR_DEFAULT_TIMEOUT_MS, MONITOR_MAX_TIMEOUT_MS, MONITOR_MAX_BATCHES_PER_MINUTE };
|
|
17
18
|
const BLOCK_DEFAULT_TIMEOUT_MS = 30_000;
|
|
18
19
|
const BLOCK_MAX_TIMEOUT_MS = 600_000;
|
|
@@ -498,7 +499,7 @@ export class TaskRegistry {
|
|
|
498
499
|
}
|
|
499
500
|
}
|
|
500
501
|
catch (err) {
|
|
501
|
-
shutdownDebug("session reap: kill threw", { kind, id: handle.id, shellId: handle.shellId, error:
|
|
502
|
+
shutdownDebug("session reap: kill threw", { kind, id: handle.id, shellId: handle.shellId, error: thrownText(err) });
|
|
502
503
|
}
|
|
503
504
|
}
|
|
504
505
|
reapSessionBackground(sessionId, scope) {
|
|
@@ -764,7 +765,7 @@ export class TaskRegistry {
|
|
|
764
765
|
...(handle.toolUseId !== undefined ? { toolUseId: handle.toolUseId } : {}),
|
|
765
766
|
...(handle.outputFile !== undefined ? { output_file: handle.outputFile } : {}),
|
|
766
767
|
status: "failed",
|
|
767
|
-
summary: `${terminalTaskSummary("bash", (handle.description ?? "background command").slice(0, 200), "failed")} — lost its background process (poll failed: ${
|
|
768
|
+
summary: `${terminalTaskSummary("bash", (handle.description ?? "background command").slice(0, 200), "failed")} — lost its background process (poll failed: ${thrownText(e)}); no further output will arrive.${bashMirrorGapNote(handle)}`,
|
|
768
769
|
...(crashedResult.length > 0 ? { result: crashedResult } : {}),
|
|
769
770
|
...(handle.completionId !== undefined ? { completionId: handle.completionId } : {}),
|
|
770
771
|
}));
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a value that was THROWN at us into text — the ONE rule, for the whole engine.
|
|
3
|
+
*
|
|
4
|
+
* The idiom this replaces is `err instanceof Error ? err.message : String(err)` (and its template-literal
|
|
5
|
+
* twin, `` `${err}` ``). Both spell a PARTIAL function on the non-Error arm: `String` calls the value's own
|
|
6
|
+
* `toString` / `Symbol.toPrimitive`, so it throws on a value that has neither (a null-prototype object) and
|
|
7
|
+
* on one whose own hook throws. The thrower is not ours — a deployment hook, a plugin callback, a model-
|
|
8
|
+
* supplied tool body may reject with any value at all — so the rendering step was the one step that could
|
|
9
|
+
* destroy the report it exists to produce. Where the render sat inside a promise rejection handler, the
|
|
10
|
+
* resulting `TypeError` was an unhandled rejection and took the whole process down: a caller's choice of
|
|
11
|
+
* rejection value decided whether the engine stayed alive.
|
|
12
|
+
*
|
|
13
|
+
* Totality is therefore the contract, and it is met the same way the engine's structural describer already
|
|
14
|
+
* meets it: the value is never asked to say what it is. Three arms, in this order, chosen so that the
|
|
15
|
+
* dominant shapes keep their EXACT previous bytes and only the shapes that used to be useless or fatal move:
|
|
16
|
+
* - a thrown STRING is its own message (a bare `throw "…"`), returned verbatim — the describer would quote
|
|
17
|
+
* it, which is right when rejecting a config value and wrong when relaying a message someone threw;
|
|
18
|
+
* - an ERROR hands over `message` verbatim — no cap, no one-lining, no quoting. This is the arm nearly all
|
|
19
|
+
* production traffic takes, and the text on it is byte-for-byte what the old idiom produced;
|
|
20
|
+
* - anything else is DESCRIBED structurally, by the one describer, which guards every read it makes.
|
|
21
|
+
*
|
|
22
|
+
* Empty is a legitimate answer: `new Error("")` returns `""`, exactly as `err.message` did, because callers
|
|
23
|
+
* fold that into their own wording (`… || "unknown"`). Two callers want something else and have their own
|
|
24
|
+
* face for it: {@link thrownLabel} when the previous text was `String(err)` (which names the error CLASS),
|
|
25
|
+
* and `describeThrown` when the text must be non-empty and contained for a model-facing frame.
|
|
26
|
+
*/
|
|
27
|
+
export declare function thrownText(err: unknown): string;
|
|
28
|
+
/**
|
|
29
|
+
* The LABELLED face of the same rule: what `String(err)` used to produce, made total.
|
|
30
|
+
*
|
|
31
|
+
* `String` on an Error does not yield its message — it yields `"TypeError: boom"`, the class AND the message
|
|
32
|
+
* (and just `"TypeError"` when the message is empty). Sites that spelled a bare `String(err)` were therefore
|
|
33
|
+
* saying something the message-only face cannot say, and at least one of them is load-bearing rather than
|
|
34
|
+
* decorative: a stuck-detector keyed its signature on this text, so collapsing two distinct error classes
|
|
35
|
+
* with equal messages into one string would have changed when a run gets abandoned. Preserving those bytes
|
|
36
|
+
* is the whole reason this face exists as well as {@link thrownText}.
|
|
37
|
+
*
|
|
38
|
+
* Fidelity is obtained by calling `String` itself rather than reassembling `name` and `message` by hand —
|
|
39
|
+
* the spelling rules for an empty name or an empty message live in `Error.prototype.toString`, and a second
|
|
40
|
+
* copy of them here would be a second rule to keep in step. It is called INSIDE the guard, so a subclass
|
|
41
|
+
* whose own `toString`/`name`/`message` misbehaves falls back to the structural description instead of
|
|
42
|
+
* escaping. Non-Errors never reach `String` at all; they go to {@link thrownText}, which is the fix.
|
|
43
|
+
*/
|
|
44
|
+
export declare function thrownLabel(err: unknown): string;
|
|
45
|
+
/**
|
|
46
|
+
* The same rule for callers that need an `Error` OBJECT rather than text — the normalizing half of
|
|
47
|
+
* `err instanceof Error ? err : new Error(String(err))`, which carries the identical defect on the identical
|
|
48
|
+
* arm and appears wherever an unknown rejection has to be handed to a seat typed `Error` (a deployment's
|
|
49
|
+
* error lane, a promise `reject`, a report callback).
|
|
50
|
+
*
|
|
51
|
+
* An `Error` of THIS realm is passed through by IDENTITY — same object, same stack, same own properties — so
|
|
52
|
+
* the arm that carries nearly all traffic is unchanged in every observable way, not merely in its message.
|
|
53
|
+
* A cross-realm Error is WRAPPED rather than passed through, which is what the old idiom did too: the seat's
|
|
54
|
+
* type says `Error`, and an object that fails `instanceof` here would break the first consumer that trusts
|
|
55
|
+
* it. Its message survives the wrap, because the new message is the {@link thrownLabel} of the original —
|
|
56
|
+
* the same text `String(err)` produced.
|
|
57
|
+
*
|
|
58
|
+
* The original value is deliberately NOT attached as `cause`: `cause` chains are walked elsewhere to
|
|
59
|
+
* attribute error codes, so attaching one here would let an arbitrary thrown object start supplying codes
|
|
60
|
+
* to readers that never saw it before. Preserving it is a worthwhile change on its own terms and belongs
|
|
61
|
+
* with those readers, not smuggled in by a rendering fix.
|
|
62
|
+
*/
|
|
63
|
+
export declare function asThrownError(err: unknown): Error;
|
|
64
|
+
/**
|
|
65
|
+
* Read ONE member of an untrusted value, once, and hand back an inert snapshot.
|
|
66
|
+
*
|
|
67
|
+
* Inspecting a thrown value is attacker-reachable in the same way coercing it is: any property can be an
|
|
68
|
+
* accessor that throws, or one that answers differently on a second read. Sites that need a member — a
|
|
69
|
+
* shell error's `exitCode`, a refusal's `code`, a transport error's `cause` — were each wrapping their own
|
|
70
|
+
* `try`, and a guard written per SITE is coarse: one hostile member took the readable ones down with it,
|
|
71
|
+
* so a report lost an exit code it had already read because a later `stdout` threw.
|
|
72
|
+
*
|
|
73
|
+
* One guarded read, per member, is the finer and the smaller rule: the caller keeps what it could read and
|
|
74
|
+
* loses only what it could not, and nothing it holds afterwards can run the value's code again. The result
|
|
75
|
+
* is `undefined` for BOTH "absent" and "unreadable" — deliberately, because a caller that must tell those
|
|
76
|
+
* apart is asking a question an untrusted value is not obliged to answer; what it does instead is fall back
|
|
77
|
+
* to describing the value, which {@link thrownText} already does totally.
|
|
78
|
+
*/
|
|
79
|
+
export declare function readMember(value: unknown, key: string): unknown;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
2
|
+
function isErrorLike(value) {
|
|
3
|
+
try {
|
|
4
|
+
if (value instanceof Error)
|
|
5
|
+
return true;
|
|
6
|
+
return Object.prototype.toString.call(value) === "[object Error]";
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export function thrownText(err) {
|
|
13
|
+
try {
|
|
14
|
+
if (typeof err === "string")
|
|
15
|
+
return err;
|
|
16
|
+
if (isErrorLike(err)) {
|
|
17
|
+
const message = err.message;
|
|
18
|
+
if (typeof message === "string")
|
|
19
|
+
return message;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
}
|
|
24
|
+
return describeRejectedValue(err);
|
|
25
|
+
}
|
|
26
|
+
export function thrownLabel(err) {
|
|
27
|
+
try {
|
|
28
|
+
if (isErrorLike(err)) {
|
|
29
|
+
const label = String(err);
|
|
30
|
+
if (label !== "")
|
|
31
|
+
return label;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
}
|
|
36
|
+
return thrownText(err);
|
|
37
|
+
}
|
|
38
|
+
export function asThrownError(err) {
|
|
39
|
+
try {
|
|
40
|
+
if (err instanceof Error)
|
|
41
|
+
return err;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
}
|
|
45
|
+
return new Error(thrownLabel(err));
|
|
46
|
+
}
|
|
47
|
+
export function readMember(value, key) {
|
|
48
|
+
try {
|
|
49
|
+
if (value === null || value === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
return value[key];
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|