@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
|
@@ -108,7 +108,7 @@ export const TOOL_CATALOG_ENTRIES = [
|
|
|
108
108
|
{ id: "task-stop-hands", name: "TaskStop", source: "builtin", definedIn: "src/tools/fs/fs-bash.ts", mountedBy: ["hands"], cards: [], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
109
109
|
{ id: "task-output", name: "TaskOutput", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-output"], face: { family: "delegate", effect: "read", contract: TASK_OUTPUT_CONTRACT } },
|
|
110
110
|
{ id: "task-stop", name: "TaskStop", source: "builtin", definedIn: "src/core/task-registry.ts", mountedBy: ["delegation"], cards: ["task-stop"], face: { family: "delegate", effect: "write", contract: TASK_STOP_CONTRACT } },
|
|
111
|
-
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), offloadThresholdChars: 10_000 } },
|
|
111
|
+
{ id: "monitor", name: "Monitor", source: "builtin", definedIn: "src/tools/monitor.ts", mountedBy: ["backgroundTasks"], cards: ["monitor-start", "readonly_out_of_root"], face: { family: "other", effect: "write", contract: c("core.monitor@1"), offloadThresholdChars: 10_000 } },
|
|
112
112
|
{ id: "send-message", name: "SendMessage", source: "builtin", definedIn: "src/agents/send-message-tool.ts", mountedBy: ["delegation", "hostInternals", "peerLane"], cards: ["send-message"], face: { family: "protocol", effect: "write", contract: c("core.send_message@1") } },
|
|
113
113
|
{ id: "agent-transcript", name: "AgentTranscript", source: "builtin", definedIn: "src/agents/agent-transcript-tool.ts", mountedBy: ["delegation"], cards: ["agent-transcript"], face: { family: "delegate", effect: "read", contract: c("core.agent_transcript@1") } },
|
|
114
114
|
{ id: "list-agents", name: "ListAgents", source: "builtin", definedIn: "src/agents/list-agents-tool.ts", mountedBy: ["peerLane"], cards: ["list-agents"], face: { family: "protocol", effect: "read", aliases: ["ListPeers"], contract: c("core.list_agents@1") } },
|
|
@@ -33,6 +33,12 @@ 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
|
+
*/
|
|
36
42
|
export declare function formatToolError(error: unknown): string;
|
|
37
43
|
/**
|
|
38
44
|
* A typed, read-side fold of a `TaskResult.errorCode` into the failure CLASS it belongs to (design/80
|
package/dist/core/tool-errors.js
CHANGED
|
@@ -2,7 +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
|
+
import { isErrorLike, readMember, thrownName, thrownText } from "./thrown-value.js";
|
|
6
6
|
export function truncateError(s) {
|
|
7
7
|
if (s.length <= MAX_ERROR_CHARS)
|
|
8
8
|
return s;
|
|
@@ -39,18 +39,10 @@ function isShellLike(e) {
|
|
|
39
39
|
return false;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
function isErrorInstance(v) {
|
|
43
|
-
try {
|
|
44
|
-
return v instanceof Error;
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
return false;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
42
|
export function formatToolError(error) {
|
|
51
43
|
const parts = [];
|
|
52
|
-
const name =
|
|
53
|
-
if (
|
|
44
|
+
const name = thrownName(error);
|
|
45
|
+
if (name === "AbortError")
|
|
54
46
|
return "Interrupted";
|
|
55
47
|
const message = readMember(error, "message");
|
|
56
48
|
const messageText = typeof message === "string" ? message : undefined;
|
|
@@ -60,15 +52,15 @@ export function formatToolError(error) {
|
|
|
60
52
|
const stdout = readMember(error, "stdout");
|
|
61
53
|
if (typeof exitCode === "number")
|
|
62
54
|
parts.push(`Exit code ${exitCode}`);
|
|
63
|
-
if (
|
|
55
|
+
if (isErrorLike(error) && messageText !== undefined && messageText !== "")
|
|
64
56
|
parts.push(messageText);
|
|
65
57
|
if (typeof stderr === "string" && stderr.trim())
|
|
66
58
|
parts.push(stderr);
|
|
67
59
|
if (typeof stdout === "string" && stdout.trim())
|
|
68
60
|
parts.push(stdout);
|
|
69
61
|
}
|
|
70
|
-
else if (
|
|
71
|
-
parts.push((messageText ?? "") || (
|
|
62
|
+
else if (isErrorLike(error)) {
|
|
63
|
+
parts.push((messageText ?? "") || (name ?? "") || "Error");
|
|
72
64
|
}
|
|
73
65
|
else {
|
|
74
66
|
parts.push(thrownText(error));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { thrownText } from "./thrown-value.js";
|
|
1
|
+
import { readMember, thrownText } from "./thrown-value.js";
|
|
2
2
|
export function modelIdTail(id) {
|
|
3
3
|
return id.toLowerCase().split("/").pop() ?? "";
|
|
4
4
|
}
|
|
@@ -207,22 +207,10 @@ export function applyToolModelGate(input) {
|
|
|
207
207
|
merged = input.depsSeat === false ? undefined : mergeToolModelGateClasses(input.depsSeat);
|
|
208
208
|
}
|
|
209
209
|
catch (e) {
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
code = e?.code;
|
|
213
|
-
}
|
|
214
|
-
catch {
|
|
215
|
-
code = undefined;
|
|
216
|
-
}
|
|
210
|
+
const code = readMember(e, "code");
|
|
217
211
|
if (code !== undefined)
|
|
218
212
|
throw e;
|
|
219
|
-
|
|
220
|
-
try {
|
|
221
|
-
msg = thrownText(e);
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
msg = "[unreportable throw]";
|
|
225
|
-
}
|
|
213
|
+
const msg = thrownText(e);
|
|
226
214
|
throw gateConfigError(`RunnerDeps.toolModelGate could not be read (${msg}) — a config seat whose reads throw is a bad value, refused rather than passed.`);
|
|
227
215
|
}
|
|
228
216
|
const raw = input.envRaw;
|
package/dist/core/tools.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Value } from "typebox/value";
|
|
2
2
|
import { formatToolError, formatZodValidationError } from "./tool-errors.js";
|
|
3
|
+
import { readMember } from "./thrown-value.js";
|
|
3
4
|
import { attachToolContract } from "../prompt-assembly/tool-catalog.js";
|
|
4
5
|
function normalizeContent(ret) {
|
|
5
6
|
if (typeof ret === "string") {
|
|
6
7
|
return { content: [{ type: "text", text: ret }], details: undefined, terminate: false, isError: false };
|
|
7
8
|
}
|
|
8
|
-
const raw = ret
|
|
9
|
+
const raw = readMember(ret, "content");
|
|
9
10
|
if (typeof raw !== "string" && !Array.isArray(raw)) {
|
|
10
11
|
return {
|
|
11
12
|
content: [{ type: "text", text: "Tool error: content must be a string or an array of content blocks." }],
|
|
@@ -15,7 +16,7 @@ function normalizeContent(ret) {
|
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
const content = typeof raw === "string" ? [{ type: "text", text: raw }] : raw;
|
|
18
|
-
return { content, details: ret
|
|
19
|
+
return { content, details: readMember(ret, "details"), terminate: readMember(ret, "terminate") === true, isError: readMember(ret, "isError") === true };
|
|
19
20
|
}
|
|
20
21
|
function isEmptyToolContent(content) {
|
|
21
22
|
if (content.length === 0)
|
|
@@ -107,28 +108,26 @@ export function defineTool(spec, options) {
|
|
|
107
108
|
isError: true,
|
|
108
109
|
};
|
|
109
110
|
}
|
|
110
|
-
let ret;
|
|
111
111
|
try {
|
|
112
112
|
const baseCtx = { toolCallId, signal };
|
|
113
113
|
const ctx = options?.enrichCtx ? stampCallIdentity(options.enrichCtx(baseCtx), toolCallId, signal) : baseCtx;
|
|
114
|
-
ret = await spec.execute(params, ctx);
|
|
114
|
+
const ret = await spec.execute(params, ctx);
|
|
115
|
+
const { content, details, terminate, isError } = normalizeContent(ret);
|
|
116
|
+
const guarded = isEmptyToolContent(content)
|
|
117
|
+
? [{ type: "text", text: isError ? `(${spec.name} reported an error with no further output)` : `(${spec.name} completed with no output)` }]
|
|
118
|
+
: content;
|
|
119
|
+
return { content: guarded, details, terminate, ...(isError ? { isError: true } : {}) };
|
|
115
120
|
}
|
|
116
121
|
catch (err) {
|
|
117
122
|
const wrapped = new Error(formatToolError(err));
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
123
|
+
const marks = readMember(err, "details");
|
|
124
|
+
if (marks !== undefined)
|
|
125
|
+
wrapped.details = marks;
|
|
126
|
+
const errorKind = readMember(err, "errorKind");
|
|
127
|
+
if (typeof errorKind === "string")
|
|
128
|
+
wrapped.errorKind = errorKind;
|
|
125
129
|
throw wrapped;
|
|
126
130
|
}
|
|
127
|
-
const { content, details, terminate, isError } = normalizeContent(ret);
|
|
128
|
-
const guarded = isEmptyToolContent(content)
|
|
129
|
-
? [{ type: "text", text: isError ? `(${spec.name} reported an error with no further output)` : `(${spec.name} completed with no output)` }]
|
|
130
|
-
: content;
|
|
131
|
-
return { content: guarded, details, terminate, ...(isError ? { isError: true } : {}) };
|
|
132
131
|
},
|
|
133
132
|
};
|
|
134
133
|
if (spec.contract) {
|
|
@@ -405,6 +405,12 @@ export declare function inlineUntrusted(text: string, maxLen?: number): string;
|
|
|
405
405
|
* checkpoint-store closed an ESM evaluation cycle (checkpoint-store → tool-policy → tools/fs →
|
|
406
406
|
* checkpoint-store, a latent TDZ). Both already import this module; one cap, no cycle. */
|
|
407
407
|
export declare const PROBE_CAUSE_PATH_MAX = 200;
|
|
408
|
+
/** The bloat ceiling on a probe's one-sentence account (`RiskDescriptor.probeReason` /
|
|
409
|
+
* `AskRequest.probeReason`); the neutralizer is the INJECTION ceiling, this is only the LENGTH one. Homed
|
|
410
|
+
* here for the same reason as the constant above: three readers — the shell probe that MINTS the sentence
|
|
411
|
+
* (tools/fs) and must fit every load-bearing part inside this bound, the gate, the persist boundary — and
|
|
412
|
+
* a value import from tools/fs into checkpoint-store closes an ESM cycle. */
|
|
413
|
+
export declare const PROBE_REASON_MAX = 200;
|
|
408
414
|
/** The one body bound every "reviewer note" relay passes to {@link delimitUntrusted} — the decider's
|
|
409
415
|
* free text attached to a deny (sync `AskOutcome.reason` and the durable `ResumeOutcome` `reason`
|
|
410
416
|
* legs alike). A note is steering, not payload: unbounded it can flood the transcript/context the
|
|
@@ -601,6 +601,7 @@ function sanitizeLabel(label) {
|
|
|
601
601
|
return inlineUntrusted(label, LABEL_MAX);
|
|
602
602
|
}
|
|
603
603
|
export const PROBE_CAUSE_PATH_MAX = 200;
|
|
604
|
+
export const PROBE_REASON_MAX = 200;
|
|
604
605
|
export const REVIEWER_NOTE_MAX_BODY = 2048;
|
|
605
606
|
export function delimitUntrusted(label, text, maxBody) {
|
|
606
607
|
return delimitUntrustedWithClip(label, text, maxBody).text;
|
|
@@ -118,6 +118,19 @@ export async function workflowRunStoreContract(make, runAssertion = defaultSeque
|
|
|
118
118
|
byId.get("d-over").budgetOvershoot.spentTokens = 7;
|
|
119
119
|
assert.equal((await store.get("d-over")).budgetOvershoot.spentTokens, 1_200);
|
|
120
120
|
});
|
|
121
|
+
run("the failure CODE reaches the LIST row verbatim, and a run without one keeps the key ABSENT", async () => {
|
|
122
|
+
const store = make();
|
|
123
|
+
const coded = createWorkflowRun({ id: "f-coded", status: "failed", createdAt: 5_000, error: "the body gave up", errorCode: "workflow.agent_blocked" });
|
|
124
|
+
const codeless = createWorkflowRun({ id: "f-codeless", status: "failed", createdAt: 6_000, error: "the body gave up" });
|
|
125
|
+
const ok = createWorkflowRun({ id: "f-ok", status: "completed", createdAt: 7_000 });
|
|
126
|
+
for (const r of [coded, codeless, ok])
|
|
127
|
+
await store.put(r.id, r);
|
|
128
|
+
const byId = new Map((await store.listByScope("tenant-a")).map((s) => [s.id, s]));
|
|
129
|
+
assert.equal(byId.get("f-coded").errorCode, "workflow.agent_blocked");
|
|
130
|
+
assert.equal("errorCode" in byId.get("f-codeless"), false, "a failed run with no code ⇒ the key is OMITTED, not an `unknown` stand-in");
|
|
131
|
+
assert.equal("errorCode" in byId.get("f-ok"), false, "a completed run never carries one ⇒ the key is OMITTED");
|
|
132
|
+
assert.deepEqual(byId.get("f-coded"), summarizeWorkflowRun(coded));
|
|
133
|
+
});
|
|
121
134
|
run("the TOTAL-TIMEOUT disclosure reaches the LIST row: timeoutInterruption projects (de-aliased), and a run without it keeps the key ABSENT", async () => {
|
|
122
135
|
const store = make();
|
|
123
136
|
const seat = { timeoutMs: 600_000, agentsCompleted: 3, agentsFailed: 1, agentsInFlight: 2, approvalWaitedMs: 540_000 };
|
|
@@ -46,6 +46,21 @@ export interface WorkflowRunSummary {
|
|
|
46
46
|
description?: string;
|
|
47
47
|
/** Lifecycle status ({@link WorkflowRun.status}): running / completed / failed. */
|
|
48
48
|
status: WorkflowRunStatus;
|
|
49
|
+
/** The MACHINE code of the failure that ended the run ({@link WorkflowRun.errorCode}) — the same word,
|
|
50
|
+
* in the same vocabulary, that the full run carries; never re-derived and never a second taxonomy.
|
|
51
|
+
* The fourth member of the same family as {@link agentFailures} / {@link budgetOvershoot} /
|
|
52
|
+
* {@link timeoutInterruption}: those three qualify a row that LOOKS clean, and this one says WHY a
|
|
53
|
+
* `failed` row failed. Nothing else on this projection can answer that — {@link status} is `failed`
|
|
54
|
+
* for a refused spawn, a blown budget, a stalled run and a body that simply threw alike, so a list
|
|
55
|
+
* face without this seat must `get` the full run per failed row to say anything at all, which is the
|
|
56
|
+
* N+1 this projection exists to avoid.
|
|
57
|
+
*
|
|
58
|
+
* PRESENT EXACTLY WHEN THE RUN CARRIES ONE, absent otherwise — an absent key reads "no code on the
|
|
59
|
+
* record", never "the run succeeded" and never a `workflow.unknown` stand-in. A `completed` run never
|
|
60
|
+
* carries one (per {@link WorkflowRun.errorCode}), and a `failed` run whose body threw a codeless
|
|
61
|
+
* error with no failed child on the record carries none either. Free of any own attribution rule: the
|
|
62
|
+
* attribution (thrown code → first failed agent → absent) happens once, where the run is written. */
|
|
63
|
+
errorCode?: string;
|
|
49
64
|
/** Count of agent-runs that ended failed ({@link WorkflowRun.agentFailures}) — present only when > 0.
|
|
50
65
|
* Lets a list view flag a "completed, with failures" run without an N+1 `get` of the full run. */
|
|
51
66
|
agentFailures?: number;
|
|
@@ -8,6 +8,7 @@ export function summarizeWorkflowRun(run) {
|
|
|
8
8
|
...(run.name !== undefined ? { name: run.name } : {}),
|
|
9
9
|
...(run.description !== undefined ? { description: run.description } : {}),
|
|
10
10
|
status: run.status,
|
|
11
|
+
...(run.errorCode !== undefined ? { errorCode: run.errorCode } : {}),
|
|
11
12
|
...(run.agentFailures !== undefined ? { agentFailures: run.agentFailures } : {}),
|
|
12
13
|
...(run.budgetOvershoot !== undefined ? { budgetOvershoot: { ...run.budgetOvershoot } } : {}),
|
|
13
14
|
...(run.timeoutInterruption !== undefined ? { timeoutInterruption: { ...run.timeoutInterruption } } : {}),
|
|
@@ -911,14 +911,14 @@ export interface RoutePairingConfig {
|
|
|
911
911
|
export type RoutePairingPosture = "per-model" | "paired" | "unpinned" | "keyless";
|
|
912
912
|
/** Machine codes an adjudication refusal carries (minted here, forwarded verbatim by consumers). */
|
|
913
913
|
export type RouteRefusalCode = "route.credential_mismatch" | "route.credential_missing";
|
|
914
|
-
/** The structured account of a refusal —
|
|
914
|
+
/** The structured account of a refusal — the two halves and the fix, every URL a DISPLAY spelling. */
|
|
915
915
|
export interface RouteRefusalDetail {
|
|
916
916
|
modelId: string;
|
|
917
|
-
/** The entry's declared URL half (`Model.baseUrl
|
|
917
|
+
/** The entry's declared URL half (`Model.baseUrl`), via `displayUrlOf` — NOT route identity. */
|
|
918
918
|
entryBaseUrl: string;
|
|
919
|
-
/** The root the request would actually target (
|
|
919
|
+
/** The root the request would actually target, rendered (use `routeKeyOf` for identity). */
|
|
920
920
|
requestBaseUrl: string;
|
|
921
|
-
/** The
|
|
921
|
+
/** The declared root the credential is paired with (mismatch refusals), rendered. */
|
|
922
922
|
configBaseUrl?: string;
|
|
923
923
|
/** One-line configuration fix. */
|
|
924
924
|
fixHint: string;
|
|
@@ -2,7 +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
|
+
import { isErrorLike, readMember, thrownMarksOf, thrownText } from "../../core/thrown-value.js";
|
|
6
6
|
function appendTextDeltaToAssistantMessage(message, contentIndex, delta) {
|
|
7
7
|
const content = [...message.content];
|
|
8
8
|
const currentContent = content[contentIndex];
|
|
@@ -1537,33 +1537,19 @@ function safeAbortDetails(fn) {
|
|
|
1537
1537
|
}
|
|
1538
1538
|
}
|
|
1539
1539
|
function isAbortSemanticsError(error) {
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
cursor = next;
|
|
1549
|
-
}
|
|
1550
|
-
}
|
|
1551
|
-
catch {
|
|
1552
|
-
return false;
|
|
1540
|
+
let cursor = error;
|
|
1541
|
+
for (let depth = 0; depth < 8 && isErrorLike(cursor); depth++) {
|
|
1542
|
+
if (readMember(cursor, "name") === "AbortError")
|
|
1543
|
+
return true;
|
|
1544
|
+
const next = readMember(cursor, "cause");
|
|
1545
|
+
if (next === cursor)
|
|
1546
|
+
break;
|
|
1547
|
+
cursor = next;
|
|
1553
1548
|
}
|
|
1554
1549
|
return false;
|
|
1555
1550
|
}
|
|
1556
1551
|
function createErrorToolResult(message, source) {
|
|
1557
|
-
|
|
1558
|
-
if (source !== null && typeof source === "object") {
|
|
1559
|
-
const marks = source;
|
|
1560
|
-
if (marks.details !== null && typeof marks.details === "object" && !Array.isArray(marks.details)) {
|
|
1561
|
-
details = { ...marks.details };
|
|
1562
|
-
}
|
|
1563
|
-
if (typeof marks.errorKind === "string") {
|
|
1564
|
-
details.errorKind = marks.errorKind;
|
|
1565
|
-
}
|
|
1566
|
-
}
|
|
1552
|
+
const details = thrownMarksOf(source) ?? {};
|
|
1567
1553
|
return {
|
|
1568
1554
|
content: [{ type: "text", text: truncateError(message) }],
|
|
1569
1555
|
details,
|
package/dist/index.d.ts
CHANGED
|
@@ -325,7 +325,7 @@ export { createCircuitBreakerBrain, type CircuitBreakerOptions, type BreakerStat
|
|
|
325
325
|
export { createDegradingBrain, readDegradation, DEGRADED_DIAGNOSTIC_TYPE, type DegradingBrainOptions, type DegradeReason, type DegradationInfo, } from "./brain/degrading.js";
|
|
326
326
|
export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
|
|
327
327
|
export { WAITING_FIRST_TOKEN_AFTER_MS, WAITING_FIRST_TOKEN_EVERY_MS } from "./brain/stream-engine.js";
|
|
328
|
-
export { adjudicateModelRoute, resolveRouteCredential, routeRefusalText, routePairingStatus,
|
|
328
|
+
export { adjudicateModelRoute, resolveRouteCredential, routeRefusalText, routePairingStatus, routeKeyOf, displayUrlOf, resolveRequestRoot, hasAuthCarrier, type RoutePairingStatus, } from "./brain/route-adjudicator.js";
|
|
329
329
|
export type { RouteAdjudication, RouteCredential, RouteCredentialSource, RoutePairingConfig, RoutePairingPosture, RouteRefusalCode, RouteRefusalDetail, } from "./internal/llm.js";
|
|
330
330
|
export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS, type RouteAdjudicationVector } from "./brain/route-conformance.js";
|
|
331
331
|
export { type BrainTimeoutConfig } from "./brain/timeout.js";
|
package/dist/index.js
CHANGED
|
@@ -279,7 +279,7 @@ export { createCircuitBreakerBrain, CIRCUIT_OPEN_MARKER, } from "./brain/circuit
|
|
|
279
279
|
export { createDegradingBrain, readDegradation, DEGRADED_DIAGNOSTIC_TYPE, } from "./brain/degrading.js";
|
|
280
280
|
export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
|
|
281
281
|
export { WAITING_FIRST_TOKEN_AFTER_MS, WAITING_FIRST_TOKEN_EVERY_MS } from "./brain/stream-engine.js";
|
|
282
|
-
export { adjudicateModelRoute, resolveRouteCredential, routeRefusalText, routePairingStatus,
|
|
282
|
+
export { adjudicateModelRoute, resolveRouteCredential, routeRefusalText, routePairingStatus, routeKeyOf, displayUrlOf, resolveRequestRoot, hasAuthCarrier, } from "./brain/route-adjudicator.js";
|
|
283
283
|
export { ROUTE_ADJUDICATION_CONFORMANCE_CORPUS } from "./brain/route-conformance.js";
|
|
284
284
|
export {} from "./brain/timeout.js";
|
|
285
285
|
export { createAssistantMessageEventStream } from "./internal/llm.js";
|
|
@@ -23,7 +23,7 @@ import { compileOutputSchema } from "../core/runner/strict-output-schema.js";
|
|
|
23
23
|
import { WorkflowBudgetExceededError, WorkflowNestingError, WorkflowAgentBlockedError, WorkflowAgentParkedError, WorkflowAgentSchemaError, WorkflowAgentStalledError, WorkflowMaxAgentsError, WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } from "./workflow-types.js";
|
|
24
24
|
import { WorkflowScriptError } from "./workflow-meta.js";
|
|
25
25
|
import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS } from "../brain/timeout.js";
|
|
26
|
-
import { asThrownError, thrownText } from "../core/thrown-value.js";
|
|
26
|
+
import { asThrownError, isErrorLike, readMember, thrownIs, thrownName, thrownText } from "../core/thrown-value.js";
|
|
27
27
|
export * from "./workflow-types.js";
|
|
28
28
|
const MAX_TRANSCRIPT_CHARS = 4000;
|
|
29
29
|
const WORKFLOW_RESULT_MAX = 4000;
|
|
@@ -176,7 +176,7 @@ export function assertSupportedAgentIsolation(isolation) {
|
|
|
176
176
|
}
|
|
177
177
|
}
|
|
178
178
|
function rethrowIfMaxAgents(e) {
|
|
179
|
-
if (e
|
|
179
|
+
if (thrownIs(e, WorkflowMaxAgentsError))
|
|
180
180
|
throw e;
|
|
181
181
|
return null;
|
|
182
182
|
}
|
|
@@ -186,7 +186,7 @@ function foldFanOutSettled(settled, lane, errorsOut, emitRunLog) {
|
|
|
186
186
|
const results = settled.map((s) => {
|
|
187
187
|
if (s.ok)
|
|
188
188
|
return s.v;
|
|
189
|
-
if (s.e
|
|
189
|
+
if (thrownIs(s.e, WorkflowBudgetExceededError)) {
|
|
190
190
|
dropped++;
|
|
191
191
|
droppedSlots.push(s.i);
|
|
192
192
|
errorsOut?.push(workflowFanOutSlotError(s.i, s.e));
|
|
@@ -201,9 +201,19 @@ function foldFanOutSettled(settled, lane, errorsOut, emitRunLog) {
|
|
|
201
201
|
emitRunLog(`${lane}: ${dropped} ${dropped === 1 ? "slot" : "slots"} dropped — token budget exceeded (${dropped === 1 ? "slot" : "slots"} ${droppedSlots.join(", ")})`);
|
|
202
202
|
return results;
|
|
203
203
|
}
|
|
204
|
+
function thrownMessageOf(err) {
|
|
205
|
+
if (!isErrorLike(err))
|
|
206
|
+
return undefined;
|
|
207
|
+
const message = readMember(err, "message");
|
|
208
|
+
return typeof message === "string" && message !== "" ? message : undefined;
|
|
209
|
+
}
|
|
210
|
+
function thrownNameOf(err) {
|
|
211
|
+
const name = thrownName(err);
|
|
212
|
+
return name !== undefined && name !== "" ? name : undefined;
|
|
213
|
+
}
|
|
204
214
|
function workflowFanOutSlotError(index, e) {
|
|
205
|
-
const code = e
|
|
206
|
-
const kind = typeof code === "string" && code.length > 0 ? code : e
|
|
215
|
+
const code = readMember(e, "code");
|
|
216
|
+
const kind = typeof code === "string" && code.length > 0 ? code : thrownNameOf(e) ?? "Error";
|
|
207
217
|
return { index, kind, message: boundedRedactedSummary(thrownText(e), 300) };
|
|
208
218
|
}
|
|
209
219
|
export class WorkflowResultTooLargeError extends Error {
|
|
@@ -247,12 +257,20 @@ export const MAX_WORKFLOW_ITEMS = 4096;
|
|
|
247
257
|
function describeFanOutValue(v) {
|
|
248
258
|
if (v === null)
|
|
249
259
|
return "null";
|
|
250
|
-
if (
|
|
260
|
+
if (isFanOutArray(v))
|
|
251
261
|
return "array";
|
|
252
|
-
if (v
|
|
262
|
+
if (thrownIs(v, Promise))
|
|
253
263
|
return "a Promise (an already-CALLED agent(...) — wrap it: () => agent(...))";
|
|
254
264
|
return typeof v;
|
|
255
265
|
}
|
|
266
|
+
function isFanOutArray(v) {
|
|
267
|
+
try {
|
|
268
|
+
return Array.isArray(v);
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
256
274
|
function assertFanOutCallables(lane, member, list) {
|
|
257
275
|
for (let i = 0; i < list.length; i++) {
|
|
258
276
|
if (typeof list[i] !== "function") {
|
|
@@ -263,7 +281,7 @@ function assertFanOutCallables(lane, member, list) {
|
|
|
263
281
|
}
|
|
264
282
|
}
|
|
265
283
|
function assertFanOutArray(lane, what, v) {
|
|
266
|
-
if (!
|
|
284
|
+
if (!isFanOutArray(v)) {
|
|
267
285
|
throw new WorkflowScriptError(`${lane}(): ${what} must be an ARRAY (got ${describeFanOutValue(v)}). Nothing was run.`);
|
|
268
286
|
}
|
|
269
287
|
}
|
|
@@ -1740,14 +1758,14 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1740
1758
|
}
|
|
1741
1759
|
}
|
|
1742
1760
|
catch (err) {
|
|
1743
|
-
if (drive !== undefined && !finalized && !(err
|
|
1761
|
+
if (drive !== undefined && !finalized && !thrownIs(err, WorkflowAgentParkedError)) {
|
|
1744
1762
|
emitRunLog(`[park] the parked leg "${label}" could not be driven — the resume was refused before the checkpoint was consumed (${withoutToken(errText(err), drive.token)}); the park stands.`);
|
|
1745
1763
|
return await parkAgentLeg(rec, drive.paused, activityTail, false);
|
|
1746
1764
|
}
|
|
1747
1765
|
if (!finalized) {
|
|
1748
1766
|
rec.status = "failed";
|
|
1749
1767
|
rec.endedAt = now();
|
|
1750
|
-
const salvaged = err
|
|
1768
|
+
const salvaged = thrownIs(err, WorkflowAgentStalledError) ? err.lastResult : undefined;
|
|
1751
1769
|
if (salvaged?.runId === undefined)
|
|
1752
1770
|
delete rec.taskRunId;
|
|
1753
1771
|
if (salvaged !== undefined) {
|
|
@@ -1763,17 +1781,18 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1763
1781
|
rec.output = boundedRedactedSummary(salvaged.structuredOutput ?? salvaged.result, MAX_TRANSCRIPT_CHARS) || (rec.errorMessage ?? "");
|
|
1764
1782
|
rec.stats = { tokens: salvaged.stats.tokens ?? 0, turns: salvaged.stats.turns ?? 0, costMicroUsd: salvaged.stats.costMicroUsd };
|
|
1765
1783
|
}
|
|
1766
|
-
if (err
|
|
1784
|
+
if (thrownIs(err, WorkflowAgentBlockedError)) {
|
|
1767
1785
|
rec.errorCode = WORKFLOW_SPAWN_BLOCKED_ERROR_CODE;
|
|
1768
1786
|
rec.errorMessage = boundedRedactedSummary(err.message, MAX_TRANSCRIPT_CHARS);
|
|
1769
1787
|
}
|
|
1770
|
-
if (err
|
|
1788
|
+
if (thrownIs(err, WorkflowBudgetExceededError)) {
|
|
1771
1789
|
rec.errorCode = err.code;
|
|
1772
1790
|
rec.errorMessage = boundedRedactedSummary(err.message, MAX_TRANSCRIPT_CHARS);
|
|
1773
1791
|
}
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1792
|
+
const preLaunchMessage = thrownMessageOf(err);
|
|
1793
|
+
if (rec.errorMessage === undefined && preLaunchMessage !== undefined)
|
|
1794
|
+
rec.errorMessage = boundedRedactedSummary(preLaunchMessage, MAX_TRANSCRIPT_CHARS);
|
|
1795
|
+
if (thrownIs(err, WorkflowAgentStalledError) && err.attempts > 1) {
|
|
1777
1796
|
rec.attempts = err.attempts;
|
|
1778
1797
|
rec.lastAttemptReason = "stalled";
|
|
1779
1798
|
}
|
|
@@ -1785,12 +1804,12 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1785
1804
|
result: boundedRedactedSummary(thrownText(err), 500),
|
|
1786
1805
|
terminal: {
|
|
1787
1806
|
kind: "failed",
|
|
1788
|
-
...(err
|
|
1807
|
+
...(thrownIs(err, WorkflowAgentBlockedError) ? { code: WORKFLOW_SPAWN_BLOCKED_ERROR_CODE } : thrownIs(err, WorkflowBudgetExceededError) ? { code: err.code } : {}),
|
|
1789
1808
|
message: boundedRedactedSummary(thrownText(err), 500),
|
|
1790
1809
|
},
|
|
1791
1810
|
stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
|
|
1792
1811
|
}, label).catch(() => undefined);
|
|
1793
|
-
if (!((err
|
|
1812
|
+
if (!((thrownIs(err, WorkflowBudgetExceededError) || thrownIs(err, WorkflowAgentParkedError)) && rec.startedAt === undefined))
|
|
1794
1813
|
await journaled;
|
|
1795
1814
|
bceTerminal(callKey, "failed", rec.output ?? (thrownText(err)), rec.sessionId, rec.stats);
|
|
1796
1815
|
}
|
|
@@ -1955,9 +1974,10 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
1955
1974
|
releaseLeg();
|
|
1956
1975
|
return await parkAgentLeg(rec, drive.paused, [], false);
|
|
1957
1976
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1977
|
+
const spawnMessage = thrownMessageOf(err);
|
|
1978
|
+
if (!finalized && rec.errorMessage === undefined && spawnMessage !== undefined)
|
|
1979
|
+
rec.errorMessage = boundedRedactedSummary(spawnMessage, MAX_TRANSCRIPT_CHARS);
|
|
1980
|
+
if (!finalized && thrownIs(err, WorkflowAgentBlockedError)) {
|
|
1961
1981
|
rec.errorCode = WORKFLOW_SPAWN_BLOCKED_ERROR_CODE;
|
|
1962
1982
|
rec.errorMessage = boundedRedactedSummary(err.message, MAX_TRANSCRIPT_CHARS);
|
|
1963
1983
|
void journalAppend(callKey, {
|
|
@@ -2210,8 +2230,9 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
2210
2230
|
prior = await opts.store.get(opts.resumeFromRunId);
|
|
2211
2231
|
}
|
|
2212
2232
|
catch (err) {
|
|
2213
|
-
const
|
|
2214
|
-
const
|
|
2233
|
+
const errName = thrownNameOf(err);
|
|
2234
|
+
const errCode = errName !== undefined ? readMember(err, "code") : undefined;
|
|
2235
|
+
const errClass = errName !== undefined ? `${errName}${typeof errCode === "string" ? `/${errCode}` : ""}` : typeof err;
|
|
2215
2236
|
throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `startWorkflow: resume from "${fromRunId}" was REFUSED — the prior run's record could not be read from the run store (${errClass}), so its parks cannot be proven.`, { reason: "store_threw" });
|
|
2216
2237
|
}
|
|
2217
2238
|
if (prior !== null && prior.scope !== scope)
|
|
@@ -2376,7 +2397,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
|
|
|
2376
2397
|
run.status = "failed";
|
|
2377
2398
|
run.completionId ??= uuidv7();
|
|
2378
2399
|
run.endedAt = now();
|
|
2379
|
-
if (parkedLeg !== undefined && !(err
|
|
2400
|
+
if (parkedLeg !== undefined && !thrownIs(err, WorkflowAgentParkedError))
|
|
2380
2401
|
err = parkedError();
|
|
2381
2402
|
run.error = thrownText(err);
|
|
2382
2403
|
closeAbandonedAgents(run.endedAt);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash, randomBytes } from "node:crypto";
|
|
2
2
|
import { appendFileSync, closeSync, existsSync, fsyncSync, openSync, readdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import { thrownText } from "../../core/thrown-value.js";
|
|
4
5
|
import { applyFileRestoreViaEnv, boundaryPublishVerdict, worldStillMatchesMint, captureFileBackupViaEnv, modeProvenDifferent, previewFileDelta, describeTrackKey, fileHistoryExportDigest, resolveTrackKey, trackKeyOf, validateFileHistoryExport, } from "../../core/file-history-store.js";
|
|
5
6
|
import { fileHistoryBoundariesToKeep, resolveFileHistoryRetention } from "../../core/file-history-retention.js";
|
|
6
7
|
import { atomicWriteFile, ensureDir, sanitizePathComponent, sanitizeScope, writeThenLink } from "./fs-atomic.js";
|
|
@@ -456,7 +457,7 @@ export class FileFileHistoryStore {
|
|
|
456
457
|
return { ok: true, minted: true };
|
|
457
458
|
}
|
|
458
459
|
catch (err) {
|
|
459
|
-
return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} could not be persisted: ${err
|
|
460
|
+
return { ok: false, error: { code: "track_failed", message: `first-touch record for ${absPath} could not be persisted: ${thrownText(err)}` } };
|
|
460
461
|
}
|
|
461
462
|
}
|
|
462
463
|
async annulTrack(scope, absPath, root, verify) {
|
|
@@ -511,7 +512,7 @@ export class FileFileHistoryStore {
|
|
|
511
512
|
return { ok: true };
|
|
512
513
|
}
|
|
513
514
|
catch (err) {
|
|
514
|
-
return { ok: false, error: { code: "track_failed", message: `annulling the first-touch record for ${absPath} failed: ${err
|
|
515
|
+
return { ok: false, error: { code: "track_failed", message: `annulling the first-touch record for ${absPath} failed: ${thrownText(err)}` } };
|
|
515
516
|
}
|
|
516
517
|
}
|
|
517
518
|
scopeIsEmptySync(scope) {
|
|
@@ -677,7 +678,7 @@ export class FileFileHistoryStore {
|
|
|
677
678
|
}
|
|
678
679
|
catch (err) {
|
|
679
680
|
nestedSetAdd(this.spentAttempts, scope, entryId);
|
|
680
|
-
return { ok: false, error: { code: "snapshot_failed", message: `boundary for entry "${entryId}" could not be committed: ${err
|
|
681
|
+
return { ok: false, error: { code: "snapshot_failed", message: `boundary for entry "${entryId}" could not be committed: ${thrownText(err)}` } };
|
|
681
682
|
}
|
|
682
683
|
finally {
|
|
683
684
|
this.activeCaptures.get(scope)?.delete(entryId);
|
|
@@ -754,7 +755,7 @@ export class FileFileHistoryStore {
|
|
|
754
755
|
});
|
|
755
756
|
}
|
|
756
757
|
catch (err) {
|
|
757
|
-
applied = { outcome: "failed", reason: err
|
|
758
|
+
applied = { outcome: "failed", reason: thrownText(err) };
|
|
758
759
|
}
|
|
759
760
|
if (applied.outcome === "applied")
|
|
760
761
|
ledger.filesChanged.push(key);
|
|
@@ -771,7 +772,7 @@ export class FileFileHistoryStore {
|
|
|
771
772
|
}
|
|
772
773
|
catch (err) {
|
|
773
774
|
const touched = ledger.filesChanged.length > 0 || ledger.identicalSkipped.length > 0 || ledger.refused.length > 0 || ledger.failed.length > 0;
|
|
774
|
-
return { outcome: touched ? "partial" : "failed", ...ledger, error: { code: "restore_failed", message: err
|
|
775
|
+
return { outcome: touched ? "partial" : "failed", ...ledger, error: { code: "restore_failed", message: thrownText(err) } };
|
|
775
776
|
}
|
|
776
777
|
}
|
|
777
778
|
async canRestore(scope, entryId) {
|
|
@@ -889,7 +890,7 @@ export class FileFileHistoryStore {
|
|
|
889
890
|
this.writeTracked(scope, tracked);
|
|
890
891
|
}
|
|
891
892
|
catch (err) {
|
|
892
|
-
this.disclose(this.trackedPath(scope), `version-chain trim could not be committed (${err
|
|
893
|
+
this.disclose(this.trackedPath(scope), `version-chain trim could not be committed (${thrownText(err)}) — trim skipped this pass; untrimmed versions keep their blobs live`);
|
|
893
894
|
}
|
|
894
895
|
}
|
|
895
896
|
this.collectBlobs();
|
|
@@ -1091,7 +1092,7 @@ export class FileFileHistoryStore {
|
|
|
1091
1092
|
return { ok: true };
|
|
1092
1093
|
}
|
|
1093
1094
|
catch (err) {
|
|
1094
|
-
return { ok: false, error: { code: "adopt_failed", message: `adoptScope failed: ${err
|
|
1095
|
+
return { ok: false, error: { code: "adopt_failed", message: `adoptScope failed: ${thrownText(err)}` } };
|
|
1095
1096
|
}
|
|
1096
1097
|
}
|
|
1097
1098
|
async exportHistory(scope) {
|
|
@@ -1151,7 +1152,7 @@ export class FileFileHistoryStore {
|
|
|
1151
1152
|
return await this.runImport(scope, data, srcGetBlob, importJournal, identicalRepublish);
|
|
1152
1153
|
}
|
|
1153
1154
|
catch (err) {
|
|
1154
|
-
return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${err
|
|
1155
|
+
return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}` } };
|
|
1155
1156
|
}
|
|
1156
1157
|
}
|
|
1157
1158
|
async runImport(scope, data, srcGetBlob, importJournal, identicalRepublish) {
|
|
@@ -1178,7 +1179,7 @@ export class FileFileHistoryStore {
|
|
|
1178
1179
|
bytes = await srcGetBlob(v.blobHash);
|
|
1179
1180
|
}
|
|
1180
1181
|
catch (err) {
|
|
1181
|
-
return { ok: false, error: { code: "unsupported_format", message: `source blob ${v.blobHash} fetch failed: ${err
|
|
1182
|
+
return { ok: false, error: { code: "unsupported_format", message: `source blob ${v.blobHash} fetch failed: ${thrownText(err)}` } };
|
|
1182
1183
|
}
|
|
1183
1184
|
if (bytes === undefined)
|
|
1184
1185
|
return { ok: false, error: { code: "not_found", message: `source blob ${v.blobHash} is missing — the envelope's graph cannot be completed, so nothing is published` } };
|
|
@@ -1222,7 +1223,7 @@ export class FileFileHistoryStore {
|
|
|
1222
1223
|
}
|
|
1223
1224
|
catch (err) {
|
|
1224
1225
|
safeRm(staging);
|
|
1225
|
-
return { ok: false, error: { code: "adopt_failed", message: `importHistory could not publish the staged graph for "${scope}": ${err
|
|
1226
|
+
return { ok: false, error: { code: "adopt_failed", message: `importHistory could not publish the staged graph for "${scope}": ${thrownText(err)} — nothing was published (a local storage fault, not a malformed envelope)` } };
|
|
1226
1227
|
}
|
|
1227
1228
|
if (published !== "published") {
|
|
1228
1229
|
safeRm(staging);
|
|
@@ -1238,7 +1239,7 @@ export class FileFileHistoryStore {
|
|
|
1238
1239
|
return { ok: true };
|
|
1239
1240
|
}
|
|
1240
1241
|
catch (err) {
|
|
1241
|
-
return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${err
|
|
1242
|
+
return { ok: false, error: { code: "unsupported_format", message: `importHistory failed: ${thrownText(err)}` } };
|
|
1242
1243
|
}
|
|
1243
1244
|
finally {
|
|
1244
1245
|
safeRm(importJournal);
|