@sema-agent/core 7.23.4 → 7.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +133 -0
- package/dist/agents/list-agents-tool.d.ts +8 -1
- package/dist/agents/list-agents-tool.js +26 -3
- package/dist/agents/observer.js +5 -8
- package/dist/agents/subagent.js +2 -2
- package/dist/agents/team.js +9 -0
- package/dist/brain/anthropic.js +3 -2
- package/dist/brain/circuit-breaker.js +3 -3
- package/dist/brain/degrading.js +11 -14
- package/dist/brain/errors.d.ts +35 -18
- package/dist/brain/errors.js +49 -31
- package/dist/brain/failover.js +2 -3
- package/dist/brain/open-responses.js +6 -5
- package/dist/brain/openai.js +6 -5
- package/dist/brain/route-adjudicator.d.ts +50 -15
- package/dist/brain/route-adjudicator.js +112 -20
- package/dist/brain/route-conformance.js +9 -0
- package/dist/brain/stream-engine.js +18 -10
- package/dist/core/arg-summary.js +17 -6
- package/dist/core/auto-compaction.d.ts +42 -1
- package/dist/core/auto-compaction.js +21 -1
- package/dist/core/auto-mode.js +2 -8
- package/dist/core/checkpoint-store.d.ts +4 -6
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/exec-gate.js +3 -5
- package/dist/core/file-history-store.js +11 -10
- package/dist/core/hooks.d.ts +7 -0
- package/dist/core/mcp.js +7 -5
- package/dist/core/memory-engine/distiller.d.ts +7 -4
- package/dist/core/memory-engine/distiller.js +11 -3
- package/dist/core/memory.js +1 -0
- package/dist/core/permission-rule-model.js +8 -5
- package/dist/core/runner/assemble-result.js +12 -19
- package/dist/core/runner/contracts.d.ts +1 -0
- package/dist/core/runner/prepare-delegation-surface.d.ts +3 -0
- package/dist/core/runner/prepare-delegation-surface.js +1 -0
- package/dist/core/runner/prepare-file-history.js +19 -0
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -1
- package/dist/core/runner/prepare-hands-readface.js +4 -0
- package/dist/core/runner/prepare-task.js +2 -2
- package/dist/core/runner/run-leg.js +2 -13
- package/dist/core/runner/run-recovery-lanes.js +4 -20
- package/dist/core/runner/run-settle-and-teardown.js +2 -16
- package/dist/core/runner/run-turn-boundary.js +4 -20
- package/dist/core/runner/runtask.js +3 -5
- package/dist/core/runner/stream-settle-backstop.js +5 -5
- package/dist/core/runner/tool-end-body.js +5 -4
- package/dist/core/runner/tool-output-projection.js +3 -2
- package/dist/core/runner/turn-attachments.d.ts +0 -20
- package/dist/core/runner/turn-attachments.js +41 -36
- package/dist/core/session-store.js +2 -4
- package/dist/core/session.d.ts +4 -1
- package/dist/core/session.js +2 -1
- package/dist/core/task-event.d.ts +19 -9
- package/dist/core/task-registry-shared.js +1 -0
- package/dist/core/thrown-value.d.ts +52 -11
- package/dist/core/thrown-value.js +35 -1
- package/dist/core/tool-catalog-entries.js +1 -1
- package/dist/core/tool-errors.d.ts +6 -0
- package/dist/core/tool-errors.js +6 -14
- package/dist/core/tool-model-gate.js +3 -15
- package/dist/core/tools.js +15 -16
- package/dist/core/untrusted-text.d.ts +6 -0
- package/dist/core/untrusted-text.js +1 -0
- package/dist/core/workflow-run-store-contract.js +13 -0
- package/dist/core/workflow-run-store.d.ts +15 -0
- package/dist/core/workflow-run-store.js +1 -0
- package/dist/engine/llm/types.d.ts +4 -4
- package/dist/engine/loop/agent-loop.js +10 -24
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/orchestration/workflow.js +44 -23
- package/dist/stores/file/file-history-store.js +12 -11
- package/dist/tools/fs/bash-program-position.d.ts +23 -0
- package/dist/tools/fs/bash-program-position.js +11 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +30 -7
- package/dist/tools/fs/bash-readonly-classifier.js +31 -10
- package/dist/tools/fs/fs-bash.d.ts +30 -1
- package/dist/tools/fs/fs-bash.js +123 -54
- package/dist/tools/fs/read-deny.d.ts +14 -0
- package/dist/tools/fs/read-deny.js +5 -0
- package/dist/tools/fs/safety.d.ts +1 -1
- package/dist/tools/fs/safety.js +2 -1
- package/dist/tools/monitor.d.ts +5 -0
- package/dist/tools/monitor.js +7 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +7 -3
- package/wire-changes/core@7.23.5.json +133 -0
- package/wire-changes/core@7.24.0.json +249 -0
|
@@ -29,7 +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
|
+
import { asThrownError, thrownMarksOf, thrownText } from "../thrown-value.js";
|
|
33
33
|
export async function runLeg(input) {
|
|
34
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;
|
|
35
35
|
let final;
|
|
@@ -693,18 +693,7 @@ async function resolvePendingCall(runner, prepared, resume, emit, emitCommitted,
|
|
|
693
693
|
}
|
|
694
694
|
catch (err) {
|
|
695
695
|
const execError = `Error: ${thrownText(err)}`;
|
|
696
|
-
const marks = (
|
|
697
|
-
if (err === null || typeof err !== "object")
|
|
698
|
-
return undefined;
|
|
699
|
-
const src = err;
|
|
700
|
-
let d;
|
|
701
|
-
if (src.details !== null && typeof src.details === "object" && !Array.isArray(src.details)) {
|
|
702
|
-
d = { ...src.details };
|
|
703
|
-
}
|
|
704
|
-
if (typeof src.errorKind === "string")
|
|
705
|
-
d = { ...(d ?? {}), errorKind: src.errorKind };
|
|
706
|
-
return d;
|
|
707
|
-
})();
|
|
696
|
+
const marks = thrownMarksOf(err);
|
|
708
697
|
await settleEnd(true, { content: execError, ...(marks !== undefined ? { details: marks } : {}) });
|
|
709
698
|
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, execError, true));
|
|
710
699
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
|
|
2
2
|
import { planRejectionClears } from "../context-edit.js";
|
|
3
3
|
import { landStaleResultClears } from "./prepare-context-lane.js";
|
|
4
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact } from "../auto-compaction.js";
|
|
4
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, maybeCompact, mintCompactedEvent } from "../auto-compaction.js";
|
|
5
5
|
import { emitTrace } from "../trace.js";
|
|
6
6
|
import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
|
|
7
7
|
import { rebaseCadenceWindows } from "./turn-attachments.js";
|
|
@@ -48,26 +48,10 @@ export function runRecoveryLanes(input) {
|
|
|
48
48
|
if (comp.compacted) {
|
|
49
49
|
compactionBreaker.failures = 0;
|
|
50
50
|
recordCompactionReuse(runner, prepared, comp);
|
|
51
|
-
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
52
|
-
|
|
53
|
-
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
51
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON && comp.postTriggerTokens !== undefined) {
|
|
52
|
+
rs.counters.compactionFloor = Math.ceil(comp.postTriggerTokens * COMPACTION_REGROWTH_FACTOR);
|
|
54
53
|
}
|
|
55
|
-
queue.push(
|
|
56
|
-
type: "compacted",
|
|
57
|
-
trigger: "forced",
|
|
58
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
59
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
60
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
61
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
62
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
63
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
64
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
65
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
66
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
67
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
68
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
69
|
-
...ident(),
|
|
70
|
-
});
|
|
54
|
+
queue.push(mintCompactedEvent(comp, "forced", ident()));
|
|
71
55
|
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
72
56
|
const pd = comp.phaseDurations;
|
|
73
57
|
const pdDur = comp.durationMs;
|
|
@@ -20,27 +20,13 @@ 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 { mintCompactedEvent } from "../auto-compaction.js";
|
|
23
24
|
import { asThrownError } from "../thrown-value.js";
|
|
24
25
|
export async function runSettleAndTeardown(input) {
|
|
25
26
|
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;
|
|
26
27
|
try {
|
|
27
28
|
if (comp?.compacted) {
|
|
28
|
-
queue.push(
|
|
29
|
-
type: "compacted",
|
|
30
|
-
trigger: "auto",
|
|
31
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
32
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
33
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
34
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
35
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
36
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
37
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
38
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
39
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
40
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
41
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
42
|
-
...ident(),
|
|
43
|
-
});
|
|
29
|
+
queue.push(mintCompactedEvent(comp, "auto", ident()));
|
|
44
30
|
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
45
31
|
const pd = comp.phaseDurations;
|
|
46
32
|
const pdDur = comp.durationMs;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { mintSystemReminder } from "../reminder-mint.js";
|
|
2
2
|
import { DEFAULT_COMPACTION_SETTINGS } from "../../internal/harness.js";
|
|
3
3
|
import { eventDefaultOn } from "../../prompt-assembly/event-registry.js";
|
|
4
|
-
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
4
|
+
import { DEFAULT_COMPACTION_INSTRUCTIONS, isCompactionManualCancel, maybeCompact, mintCompactedEvent, nextTrimForceBackoff, recordCompactionAndCheckRapidRefill } from "../auto-compaction.js";
|
|
5
5
|
import { emitTrace } from "../trace.js";
|
|
6
6
|
import { redactThenCut } from "../../agents/subagent-steps.js";
|
|
7
7
|
import { ATTACHMENT_BYTE_CAP, CHANGED_FILES_MAX, TOTAL_TOKENS_REMINDER_DEFAULT_MODE, attachmentEnvelopeTags, clipToBytes, collectDateChange, collectDueAttachments, collectInstructionsChange, commitAgentListing, commitInstructionsChange, commitSkillsListing, isIntactSource, rebaseCadenceWindows, renderAgentListingFrame, renderMcpDroppedTools, renderMcpInstructionsDelta, selectMcpDroppedBatch, renderSkillsListingFrame, renderToolsDelta } from "./turn-attachments.js";
|
|
@@ -643,22 +643,7 @@ export function createTurnBoundary(input) {
|
|
|
643
643
|
}
|
|
644
644
|
recordCompactionReuse(runner, prepared, comp);
|
|
645
645
|
if (comp.compacted) {
|
|
646
|
-
queue.push(
|
|
647
|
-
type: "compacted",
|
|
648
|
-
trigger: passTrigger,
|
|
649
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
650
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
651
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
652
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
653
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
654
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
655
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
656
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
657
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
658
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
659
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
660
|
-
...ident(),
|
|
661
|
-
});
|
|
646
|
+
queue.push(mintCompactedEvent(comp, passTrigger, ident()));
|
|
662
647
|
if (!forceManual && recordCompactionAndCheckRapidRefill(rapidRefill, stats.turns)) {
|
|
663
648
|
compactionBreaker.failures = MAX_CONSECUTIVE_COMPACTION_FAILURES;
|
|
664
649
|
queue.push({ type: "compaction_outcome", outcome: "disabled", trigger: passTrigger, reason: "rapid_refill: compaction disabled for the rest of this task", ...ident() });
|
|
@@ -674,9 +659,8 @@ export function createTurnBoundary(input) {
|
|
|
674
659
|
rs.attach.attachState.postCompactPending = true;
|
|
675
660
|
rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
|
|
676
661
|
}
|
|
677
|
-
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
678
|
-
|
|
679
|
-
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
662
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON && comp.postTriggerTokens !== undefined) {
|
|
663
|
+
rs.counters.compactionFloor = Math.ceil(comp.postTriggerTokens * COMPACTION_REGROWTH_FACTOR);
|
|
680
664
|
}
|
|
681
665
|
}
|
|
682
666
|
}
|
|
@@ -6,7 +6,7 @@ import { createRapidRefillState } from "../auto-compaction.js";
|
|
|
6
6
|
import { boundInputHashOf } from "../canonical-json.js";
|
|
7
7
|
import { emitTrace } from "../trace.js";
|
|
8
8
|
import { emitTaskOutcome } from "../task-outcome.js";
|
|
9
|
-
import { authCarrierFingerprint,
|
|
9
|
+
import { authCarrierFingerprint, displayUrlOf, routeKeyOf } from "../../brain/route-adjudicator.js";
|
|
10
10
|
import { runWithReasoningWireFacts } from "../../brain/status-sink.js";
|
|
11
11
|
import { expandTiers } from "../roles.js";
|
|
12
12
|
import { screenSwappableDeps } from "../swappable-deps.js";
|
|
@@ -208,10 +208,8 @@ export class Runner {
|
|
|
208
208
|
const prior = this.deps.models?.[name];
|
|
209
209
|
if (!prior)
|
|
210
210
|
continue;
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (from !== to && authCarrierFingerprint(prior.headers) === authCarrierFingerprint(nextModel.headers)) {
|
|
214
|
-
movedEntries.push({ modelId: name, from, to });
|
|
211
|
+
if (routeKeyOf(prior.baseUrl) !== routeKeyOf(nextModel.baseUrl) && authCarrierFingerprint(prior.headers) === authCarrierFingerprint(nextModel.headers)) {
|
|
212
|
+
movedEntries.push({ modelId: name, from: displayUrlOf(prior.baseUrl), to: displayUrlOf(nextModel.baseUrl) });
|
|
215
213
|
}
|
|
216
214
|
}
|
|
217
215
|
}
|
|
@@ -6,7 +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
|
+
import { asThrownError, readMember, thrownText } from "../thrown-value.js";
|
|
10
10
|
export function streamSettleBackstop(input) {
|
|
11
11
|
const { spec, resume, queue, taskIdRef, entryTracer, runStartedAt, live, runner, sessions, locallyClaimedTokens, parentConstraintRegistry, suspendedEnvReaps } = input;
|
|
12
12
|
const onRunRejected = async (err) => {
|
|
@@ -57,11 +57,11 @@ export function streamSettleBackstop(input) {
|
|
|
57
57
|
}
|
|
58
58
|
const remoteEnvFailure = (() => {
|
|
59
59
|
let cur = err;
|
|
60
|
-
for (let depth = 0; cur && typeof cur === "object" && depth < 8; depth++) {
|
|
61
|
-
const note = cur
|
|
60
|
+
for (let depth = 0; cur !== null && cur !== undefined && typeof cur === "object" && depth < 8; depth++) {
|
|
61
|
+
const note = readMember(cur, "remoteEnvFailure");
|
|
62
62
|
if (note !== undefined)
|
|
63
63
|
return [note];
|
|
64
|
-
cur = cur
|
|
64
|
+
cur = readMember(cur, "cause");
|
|
65
65
|
}
|
|
66
66
|
return undefined;
|
|
67
67
|
})();
|
|
@@ -79,7 +79,7 @@ export function streamSettleBackstop(input) {
|
|
|
79
79
|
return observed !== undefined && observed.length > 0 ? { editedFiles: observed } : {};
|
|
80
80
|
})(),
|
|
81
81
|
...(() => {
|
|
82
|
-
const hinted = err
|
|
82
|
+
const hinted = readMember(err, "retryAfterMs");
|
|
83
83
|
return code === "memory.admission_required" && typeof hinted === "number" && Number.isFinite(hinted) && hinted > 0
|
|
84
84
|
? { retryAfterMs: hinted }
|
|
85
85
|
: {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {} from "../../internal/harness.js";
|
|
2
|
+
import { readMember } from "../thrown-value.js";
|
|
2
3
|
import { MCP_DELIVERY_VERDICTS } from "../mcp-failure.js";
|
|
3
4
|
import { TOOL_SEARCH_NAME } from "./tool-disclosure.js";
|
|
4
5
|
import { toolOutputFrom } from "./tool-output-projection.js";
|
|
@@ -35,11 +36,11 @@ export function batchResponseDigest(result) {
|
|
|
35
36
|
export function toolEndBodyFrom(result, isError, structured, gate, gatedCallIdOfRun) {
|
|
36
37
|
const o = toolOutputFrom(result);
|
|
37
38
|
const st = structured(result);
|
|
38
|
-
const det = isError ? result
|
|
39
|
-
const codeRaw = det
|
|
40
|
-
const kindRaw = det
|
|
39
|
+
const det = isError ? readMember(result, "details") : undefined;
|
|
40
|
+
const codeRaw = readMember(det, "code");
|
|
41
|
+
const kindRaw = readMember(det, "errorKind");
|
|
41
42
|
const code = typeof codeRaw === "string" ? codeRaw : typeof kindRaw === "string" ? kindRaw : undefined;
|
|
42
|
-
const deliveredRaw = det
|
|
43
|
+
const deliveredRaw = readMember(det, "delivered");
|
|
43
44
|
const delivered = typeof deliveredRaw === "string" && MCP_DELIVERY_VERDICTS.includes(deliveredRaw) ? deliveredRaw : undefined;
|
|
44
45
|
const gatedCallId = code === "gate.parked" ? gatedCallIdOfRun : undefined;
|
|
45
46
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { engineCardTypes } from "../tool-registry.js";
|
|
2
|
+
import { readMember } from "../thrown-value.js";
|
|
2
3
|
const MAX_TOOL_END_OUTPUT_CHARS = 16384;
|
|
3
4
|
const safeJsonLength = (v) => {
|
|
4
5
|
try {
|
|
@@ -62,10 +63,10 @@ export function createStructuredProjector(opts = {}) {
|
|
|
62
63
|
const allowed = new Set([...CC_DETAIL_TYPES, ...(opts.declaredCards ?? [])]);
|
|
63
64
|
const announced = new Set();
|
|
64
65
|
return (result) => {
|
|
65
|
-
const details = result
|
|
66
|
+
const details = readMember(result, "details");
|
|
66
67
|
if (details === null || typeof details !== "object")
|
|
67
68
|
return undefined;
|
|
68
|
-
const t = details
|
|
69
|
+
const t = readMember(details, "type");
|
|
69
70
|
if (typeof t !== "string")
|
|
70
71
|
return undefined;
|
|
71
72
|
if (!allowed.has(t)) {
|
|
@@ -346,26 +346,6 @@ export declare const INSTRUCTIONS_CHANGE_BYTE_CAP = 512;
|
|
|
346
346
|
* `steering_injected` echo are all keyed on that wordlist.
|
|
347
347
|
*/
|
|
348
348
|
export declare function createAttachmentState(): AttachmentState;
|
|
349
|
-
/**
|
|
350
|
-
* H4 — thin-projection reducer over a tool's RAW `result.details` (NOT the wire card projector
|
|
351
|
-
* projection and its 4×16K caps: a huge todo list must shrink into a bounded snapshot here, never
|
|
352
|
-
* disarm the reminder by being dropped wholesale). Only the first-party discriminants `todo` /
|
|
353
|
-
* `task` / `task-list` are recognized — every other card type (subagent `agent`/`fork`/`task-output`,
|
|
354
|
-
* bash, fs, mcp, …) is structurally ignored, which is also the child-isolation contract: a subagent's
|
|
355
|
-
* own todo state never reaches the parent's onToolEnd, and its delegation-card types don't parse here.
|
|
356
|
-
*
|
|
357
|
-
* task #51 F3 (窗口复位/投影摄入拆分): this reducer is PROJECTION INGEST ONLY — it
|
|
358
|
-
* never touches the write windows. CC's window anchor is the tool_use block's PRESENCE in the
|
|
359
|
-
* transcript (qFm/BFm match the block by name, :480217-480219/:480253-480254, with no execution-
|
|
360
|
-
* outcome check), so the run loop stamps the windows on INITIATION via {@link stampWriteAnchor}
|
|
361
|
-
* (inside {@link advanceCadenceClock}'s message walk); ingest stays success-only — a failed write
|
|
362
|
-
* has no trustworthy `details`, exactly like CC's reminder body rendering from the authoritative
|
|
363
|
-
* store rather than from tool results.
|
|
364
|
-
*
|
|
365
|
-
* Idempotent (same event twice ⇒ same state) and commutative across DISTINCT task ids (F10); two
|
|
366
|
-
* whole-list writes (TodoWrite / TaskList) racing in one batch are last-write-wins by nature — the
|
|
367
|
-
* later list IS the truer snapshot, documented rather than fought.
|
|
368
|
-
*/
|
|
369
349
|
export declare function reduceToolEnd(state: AttachmentState, details: unknown): void;
|
|
370
350
|
/** task #51 F2/F3 — which reminder window a tool_use block resets. Resolved by the run loop (alias-aware:
|
|
371
351
|
* ToolSpec.aliases over the mounted roster; retired-name normalization is gone); the producers
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
|
|
2
|
+
import { readMember } from "../thrown-value.js";
|
|
2
3
|
import { delimitUntrustedWithClip, sanitizeUntrustedText, SHELLED_BODY_ENVELOPE_TAGS } from "../untrusted-text.js";
|
|
3
4
|
import { protocolOf } from "../protocol-table.js";
|
|
4
5
|
import { renderSkillLinesWithin, renderSkillsBlock } from "./synthetic-tools.js";
|
|
@@ -24,6 +25,7 @@ export const CHANGED_FILES_MAX = 20;
|
|
|
24
25
|
export const CHANGED_FILES_MTIME_EPS_MS = 2000;
|
|
25
26
|
export const ATTACHMENT_BYTE_CAP = 8 * 1024;
|
|
26
27
|
const PROJECTION_ITEMS_MAX = 50;
|
|
28
|
+
const PROJECTION_SCAN_MAX = 10_000;
|
|
27
29
|
const PROJECTION_CONTENT_MAX = 80;
|
|
28
30
|
const LISTING_FRAME_FLOOR_BYTES = 768;
|
|
29
31
|
const DATE_CHANGE_MAX_BYTES = 160;
|
|
@@ -72,52 +74,55 @@ export function createAttachmentState() {
|
|
|
72
74
|
postCompactPending: false,
|
|
73
75
|
};
|
|
74
76
|
}
|
|
77
|
+
function readListLength(value) {
|
|
78
|
+
const n = value !== null && typeof value === "object" ? readMember(value, "length") : undefined;
|
|
79
|
+
return typeof n === "number" && Number.isInteger(n) && n >= 0 ? Math.min(n, PROJECTION_SCAN_MAX) : undefined;
|
|
80
|
+
}
|
|
81
|
+
function readTaskEntry(raw) {
|
|
82
|
+
const id = readMember(raw, "id");
|
|
83
|
+
if (typeof id !== "string")
|
|
84
|
+
return undefined;
|
|
85
|
+
const subject = readMember(raw, "subject");
|
|
86
|
+
const status = readMember(raw, "status");
|
|
87
|
+
return { id, item: { content: typeof subject === "string" ? subject.slice(0, PROJECTION_CONTENT_MAX) : "", status: typeof status === "string" ? status : "pending" } };
|
|
88
|
+
}
|
|
75
89
|
export function reduceToolEnd(state, details) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (!Array.isArray(newTodos))
|
|
90
|
+
const type = readMember(details, "type");
|
|
91
|
+
if (type === "todo") {
|
|
92
|
+
const newTodos = readMember(details, "newTodos");
|
|
93
|
+
const count = readListLength(newTodos);
|
|
94
|
+
if (count === undefined)
|
|
82
95
|
return;
|
|
83
|
-
const shaped =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
96
|
+
const shaped = [];
|
|
97
|
+
for (let i = 0; i < count; i++) {
|
|
98
|
+
const item = readMember(newTodos, String(i));
|
|
99
|
+
const content = readMember(item, "content");
|
|
100
|
+
const status = readMember(item, "status");
|
|
101
|
+
if (typeof content === "string" && typeof status === "string")
|
|
102
|
+
shaped.push({ content: content.slice(0, PROJECTION_CONTENT_MAX), status });
|
|
103
|
+
}
|
|
89
104
|
const allDone = shaped.length > 0 && shaped.every((t) => t.status === "completed");
|
|
90
105
|
state.todo = allDone ? { items: [] } : { items: shaped.slice(0, PROJECTION_ITEMS_MAX) };
|
|
91
106
|
}
|
|
92
|
-
else if (
|
|
107
|
+
else if (type === "task") {
|
|
93
108
|
const map = state.taskItems ?? (state.taskItems = new Map());
|
|
94
|
-
const deleted =
|
|
109
|
+
const deleted = readMember(details, "deleted");
|
|
95
110
|
if (typeof deleted === "string")
|
|
96
111
|
map.delete(deleted);
|
|
97
|
-
const
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else if (d.type === "task-list") {
|
|
109
|
-
const tasks = d.tasks;
|
|
110
|
-
if (!Array.isArray(tasks))
|
|
112
|
+
const entry = readTaskEntry(readMember(details, "task"));
|
|
113
|
+
if (entry !== undefined)
|
|
114
|
+
map.set(entry.id, entry.item);
|
|
115
|
+
}
|
|
116
|
+
else if (type === "task-list") {
|
|
117
|
+
const tasks = readMember(details, "tasks");
|
|
118
|
+
const count = readListLength(tasks);
|
|
119
|
+
if (count === undefined)
|
|
111
120
|
return;
|
|
112
121
|
const map = new Map();
|
|
113
|
-
for (
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
116
|
-
map.set(
|
|
117
|
-
content: typeof t.subject === "string" ? t.subject.slice(0, PROJECTION_CONTENT_MAX) : "",
|
|
118
|
-
status: typeof t.status === "string" ? t.status : "pending",
|
|
119
|
-
});
|
|
120
|
-
}
|
|
122
|
+
for (let i = 0; i < count; i++) {
|
|
123
|
+
const entry = readTaskEntry(readMember(tasks, String(i)));
|
|
124
|
+
if (entry !== undefined)
|
|
125
|
+
map.set(entry.id, entry.item);
|
|
121
126
|
}
|
|
122
127
|
state.taskItems = map;
|
|
123
128
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { InMemorySessionRepo, SessionError, uuidv7, } from "../internal/harness.js";
|
|
2
2
|
import { isSessionConflict } from "./session.js";
|
|
3
|
+
import { readMember } from "./thrown-value.js";
|
|
3
4
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
4
5
|
import { SESSION_DEFAULT_TTL_DAYS } from "../config/defaults.js";
|
|
5
6
|
export { SESSION_DEFAULT_TTL_DAYS };
|
|
@@ -285,8 +286,5 @@ async function settledForkOptions(source) {
|
|
|
285
286
|
return {};
|
|
286
287
|
}
|
|
287
288
|
function isNotFound(err) {
|
|
288
|
-
return (
|
|
289
|
-
err !== null &&
|
|
290
|
-
"code" in err &&
|
|
291
|
-
err.code === "not_found");
|
|
289
|
+
return readMember(err, "code") === "not_found";
|
|
292
290
|
}
|
package/dist/core/session.d.ts
CHANGED
|
@@ -8,7 +8,10 @@ export type { SessionWriteOptions } from "../internal/harness.js";
|
|
|
8
8
|
import type { Session } from "../internal/harness.js";
|
|
9
9
|
import type { SessionTreeEntry } from "../internal/harness.js";
|
|
10
10
|
export type { SessionPlacementRecord } from "../internal/harness.js";
|
|
11
|
-
/** True when `err` is a session optimistic-lock conflict (a concurrent writer won the branch leaf).
|
|
11
|
+
/** True when `err` is a session optimistic-lock conflict (a concurrent writer won the branch leaf). Both
|
|
12
|
+
* halves read what a HOST `SessionStorage` threw, so both are guarded — unreadable is not a conflict,
|
|
13
|
+
* never a raise inside the classify-and-rethrow guard. The class test is KEPT: widening the verdict to
|
|
14
|
+
* any value carrying the word would arm the CAS retry loop on a foreign error (KL-942b). */
|
|
12
15
|
export declare function isSessionConflict(err: unknown): boolean;
|
|
13
16
|
/**
|
|
14
17
|
* Subagent transcript persistence — the ACQUIRE-form placement declaration a trusted spawner passes
|
package/dist/core/session.js
CHANGED
|
@@ -3,8 +3,9 @@ export { InMemorySessionRepo, InMemorySessionStorage, uuidv7, validateEntriesFor
|
|
|
3
3
|
export { BaseSessionStorage, leafIdAfterEntry, } from "../internal/harness.js";
|
|
4
4
|
export { SessionError } from "../internal/harness.js";
|
|
5
5
|
import { SessionError } from "../internal/harness.js";
|
|
6
|
+
import { readMember, thrownIs } from "./thrown-value.js";
|
|
6
7
|
export function isSessionConflict(err) {
|
|
7
|
-
return err
|
|
8
|
+
return thrownIs(err, SessionError) && readMember(err, "code") === "conflict";
|
|
8
9
|
}
|
|
9
10
|
export function hasSessionFork(store) {
|
|
10
11
|
return typeof store.fork === "function";
|
|
@@ -592,16 +592,23 @@ export type TaskEvent = ({
|
|
|
592
592
|
type: "compacted";
|
|
593
593
|
trigger: "auto" | "manual" | "forced";
|
|
594
594
|
tokensBefore: number;
|
|
595
|
-
/**
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
*
|
|
599
|
-
*
|
|
600
|
-
|
|
595
|
+
/** How much context this compaction FREED, as the engine measured it: one structural sum over
|
|
596
|
+
* the pre-messages minus the same sum over the post-messages, inside the pass. **The only
|
|
597
|
+
* subtractable number on this event**, and what a context meter should render. Clamped ≥ 0 at
|
|
598
|
+
* the mint, so `0` means "freed nothing", never "could not tell". Absent ⇒ the post
|
|
599
|
+
* measurement failed and no delta exists — never a stand-in. */
|
|
600
|
+
freedTokens?: number;
|
|
601
|
+
/** DIAGNOSTIC, NOT subtractable. Post-compaction size in the TRIGGER-GATE coordinate
|
|
602
|
+
* (structural estimate + the fixed prompt overhead). {@link triggerTokensBefore} is not its
|
|
603
|
+
* matching pre-value: the gate reads a usage-ANCHORED figure whenever the provider's last
|
|
604
|
+
* usage is fresh, while this is always structural + overhead, so the difference mixes
|
|
605
|
+
* coordinates and goes NEGATIVE on ordinary auto passes (3037 − 4591 = −1554, measured on a
|
|
606
|
+
* 4k window). Use {@link freedTokens}. Absent when the post measurement failed. */
|
|
601
607
|
tokensAfter?: number;
|
|
602
|
-
/**
|
|
603
|
-
*
|
|
604
|
-
*
|
|
608
|
+
/** DIAGNOSTIC, NOT subtractable. The quantity the gate compared against its threshold —
|
|
609
|
+
* usage-anchored when the anchor is fresh, structural + overhead when it is not. It answers
|
|
610
|
+
* WHAT tripped the trigger, a different question from how much was freed; left un-clamped and
|
|
611
|
+
* un-paired rather than made to look subtractable. Absent on the legacy emitter. */
|
|
605
612
|
triggerTokensBefore?: number;
|
|
606
613
|
/** TB telemetry B4: wall time of the whole compaction pass (summary call included) — what the
|
|
607
614
|
* boundary pause actually cost. Absent on legacy emitters. */
|
|
@@ -823,6 +830,9 @@ export type TaskEvent = ({
|
|
|
823
830
|
* Live/ephemeral — NOT persisted (a resumable trace re-derives usage from the result), like `status`.
|
|
824
831
|
* Maps to CC `SDKTaskProgress` (camelCase here → snake_case `task_id`/`total_tokens`/`tool_uses`/`duration_ms`
|
|
825
832
|
* at the SDK/service projection, the same convention as `toolCallId`→`tool_use_id`).
|
|
833
|
+
* `taskId` is the child RUN's id (`spec.taskId ?? sessionId`), NOT the registry handle: the
|
|
834
|
+
* background lanes launch with no explicit `spec.taskId`, so it equals the child's session id and
|
|
835
|
+
* hence its `transcriptId`. Both domains are tabled in `docs/INTEGRATION-CORE.md` §1.6a.
|
|
826
836
|
*/
|
|
827
837
|
type: "task_progress";
|
|
828
838
|
taskId: string;
|
|
@@ -227,6 +227,7 @@ export function formatWorkflowRun(run) {
|
|
|
227
227
|
: {}),
|
|
228
228
|
...(run.journalSkips !== undefined && run.journalSkips > 0 ? { journal_skipped: run.journalSkips } : {}),
|
|
229
229
|
...(run.status === "failed" && run.error ? { error: delimitUntrusted("workflow error", boundedRedactedSummary(run.error, 500)) } : {}),
|
|
230
|
+
...(run.errorCode !== undefined ? { errorCode: run.errorCode } : {}),
|
|
230
231
|
...(run.status === "completed" && run.result !== undefined
|
|
231
232
|
? { result: delimitUntrusted("workflow result", run.resultFull ?? run.result) }
|
|
232
233
|
: {}),
|
|
@@ -1,18 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Is this an Error, including one minted in ANOTHER REALM?
|
|
3
|
+
*
|
|
4
|
+
* `instanceof` compares against THIS realm's `Error.prototype`, so a genuine `Error` minted outside it — by
|
|
5
|
+
* a script the engine ran through a deployment-supplied runner, or a duplicated module instance — fails and would be
|
|
6
|
+
* described as an anonymous object, losing the message that is the entire point of reporting it. The brand
|
|
7
|
+
* check is the realm-independent one: an object with the `[[ErrorData]]` internal slot answers `[object Error]`
|
|
8
|
+
* no matter where it was made, and a proxy forwards it. A plain object can forge the tag through
|
|
9
|
+
* `Symbol.toStringTag`, which costs nothing — the only thing this decides is whether we look for a `message`,
|
|
10
|
+
* and a forger that supplies a string message has told us what it wanted rendered anyway.
|
|
11
|
+
*
|
|
12
|
+
* Guarded, because both operations are attacker-reachable: `instanceof` walks a prototype chain a revoked
|
|
13
|
+
* proxy traps, and the tag is a getter that can throw.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isErrorLike(value: unknown): boolean;
|
|
16
|
+
/** `value instanceof ctor`, TOTAL — the class half of {@link readMember}'s rule. The prototype-chain walk
|
|
17
|
+
* runs two proxy traps and a revoked proxy throws on both, while every caller tests a value someone else
|
|
18
|
+
* threw inside the handler that decides how a failure settles. Unrecognizable is not that class, and
|
|
19
|
+
* {@link isErrorLike} is this same rule for `Error`. */
|
|
20
|
+
export declare function thrownIs<T>(value: unknown, ctor: abstract new (...args: never[]) => T): value is T;
|
|
21
|
+
/** The NAME a thrown value carries (`Error.prototype.name` and its subclasses' overrides), read
|
|
22
|
+
* realm-independently and guarded — `undefined` when the value is not error-like or its `name` is not a
|
|
23
|
+
* string. Callers that compare against a name CONSTANT need exactly this: the comparison is on the WORD
|
|
24
|
+
* precisely because the value crossed a seam, so binding the test to this realm's `Error.prototype`
|
|
25
|
+
* defeats it. Whether an EMPTY name folds to `undefined` is policy and stays at the call site. */
|
|
26
|
+
export declare function thrownName(err: unknown): string | undefined;
|
|
1
27
|
/**
|
|
2
28
|
* Turning a value that was THROWN at us into text — the ONE rule, for the whole engine.
|
|
3
29
|
*
|
|
4
|
-
* The idiom
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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.
|
|
30
|
+
* The idiom it replaces — `err instanceof Error ? err.message : String(err)` — is PARTIAL on its non-Error
|
|
31
|
+
* arm: `String` calls the value's own `toString` / `Symbol.toPrimitive`, so it throws on a value that has
|
|
32
|
+
* neither and on one whose hook throws. The thrower is not ours (a deployment hook, a plugin callback, a
|
|
33
|
+
* model-supplied tool body may reject with anything), so the rendering step was the one step that could
|
|
34
|
+
* destroy the report it exists to produce — and inside a promise rejection handler it took the process down.
|
|
12
35
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* dominant shapes keep their EXACT previous bytes and only the shapes that used to be useless or fatal move:
|
|
36
|
+
* TOTALITY is therefore the contract, met the way the structural describer meets it: the value is never
|
|
37
|
+
* asked to say what it is. Three arms, ordered so the dominant shapes keep their EXACT previous bytes:
|
|
16
38
|
* - a thrown STRING is its own message (a bare `throw "…"`), returned verbatim — the describer would quote
|
|
17
39
|
* it, which is right when rejecting a config value and wrong when relaying a message someone threw;
|
|
18
40
|
* - an ERROR hands over `message` verbatim — no cap, no one-lining, no quoting. This is the arm nearly all
|
|
@@ -75,5 +97,24 @@ export declare function asThrownError(err: unknown): Error;
|
|
|
75
97
|
* is `undefined` for BOTH "absent" and "unreadable" — deliberately, because a caller that must tell those
|
|
76
98
|
* apart is asking a question an untrusted value is not obliged to answer; what it does instead is fall back
|
|
77
99
|
* to describing the value, which {@link thrownText} already does totally.
|
|
100
|
+
*
|
|
101
|
+
* The read walks the PROTOTYPE CHAIN deliberately — `name` lives on `Error.prototype`. `"__proto__"` is
|
|
102
|
+
* the one key where that differs from an own read, and its only caller is {@link readOwnMembers}, which
|
|
103
|
+
* passes it only when it IS an own key (a data property shadows the accessor; both readings agree).
|
|
78
104
|
*/
|
|
79
105
|
export declare function readMember(value: unknown, key: string): unknown;
|
|
106
|
+
/** The PLURAL of {@link readMember}: read a value's own enumerable members, once each, into an inert
|
|
107
|
+
* snapshot — for sites that want a COPY of a whole bag an untrusted value handed over (a thrown
|
|
108
|
+
* error's `details` mark, on its way onto a tool result). The idiom it replaces is `{ ...value }`,
|
|
109
|
+
* a second way of running the value's code: the spread asks for its own keys and evaluates every
|
|
110
|
+
* getter behind them, so one hostile member took the copy down with it, and a revoked proxy traps the
|
|
111
|
+
* enumeration itself (even `Array.isArray` on one throws). Non-objects, arrays (spreading one yields
|
|
112
|
+
* index keys) and anything whose keys cannot be enumerated answer `undefined` — "there is no bag
|
|
113
|
+
* here"; a member that refuses to be read comes back `undefined` in a PRESENT key. */
|
|
114
|
+
export declare function readOwnMembers(value: unknown): Record<string, unknown> | undefined;
|
|
115
|
+
/** The MACHINE-READABLE marks a thrown value carries (`details`, with a string `errorKind` folded in) —
|
|
116
|
+
* the one extractor for both legs that lift them onto a tool result. The live leg and the resume leg
|
|
117
|
+
* each had their own copy, and the resume copy still spread the bag and called `Array.isArray` on it,
|
|
118
|
+
* so a hostile `details` failed the whole leg where the live one had stopped losing anything but the
|
|
119
|
+
* mark. `undefined` = nothing to carry; a caller that must always present a bag folds that itself. */
|
|
120
|
+
export declare function thrownMarksOf(err: unknown): Record<string, unknown> | undefined;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describeRejectedValue } from "./tool-search-setting.js";
|
|
2
|
-
function isErrorLike(value) {
|
|
2
|
+
export function isErrorLike(value) {
|
|
3
3
|
try {
|
|
4
4
|
if (value instanceof Error)
|
|
5
5
|
return true;
|
|
@@ -9,6 +9,18 @@ function isErrorLike(value) {
|
|
|
9
9
|
return false;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
export function thrownIs(value, ctor) {
|
|
13
|
+
try {
|
|
14
|
+
return value instanceof ctor;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function thrownName(err) {
|
|
21
|
+
const name = isErrorLike(err) ? readMember(err, "name") : undefined;
|
|
22
|
+
return typeof name === "string" ? name : undefined;
|
|
23
|
+
}
|
|
12
24
|
export function thrownText(err) {
|
|
13
25
|
try {
|
|
14
26
|
if (typeof err === "string")
|
|
@@ -54,3 +66,25 @@ export function readMember(value, key) {
|
|
|
54
66
|
return undefined;
|
|
55
67
|
}
|
|
56
68
|
}
|
|
69
|
+
export function readOwnMembers(value) {
|
|
70
|
+
let keys;
|
|
71
|
+
try {
|
|
72
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
73
|
+
return undefined;
|
|
74
|
+
keys = Object.keys(value);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
const out = {};
|
|
80
|
+
for (const key of keys)
|
|
81
|
+
Object.defineProperty(out, key, { value: readMember(value, key), enumerable: true, writable: true, configurable: true });
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
export function thrownMarksOf(err) {
|
|
85
|
+
const details = readOwnMembers(readMember(err, "details"));
|
|
86
|
+
const errorKind = readMember(err, "errorKind");
|
|
87
|
+
if (typeof errorKind !== "string")
|
|
88
|
+
return details;
|
|
89
|
+
return { ...(details ?? {}), errorKind };
|
|
90
|
+
}
|