@akagilnc/pi-workflow-roles 0.1.3932 → 0.1.3975
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/CLAUDE.md +1 -1
- package/dist/acp-host/production-host.js +2389 -6018
- package/dist/analyst-gate-cycles-read.js +15 -19
- package/dist/audit-escalation.js +16 -46
- package/dist/auditor-dossier-tool.js +7 -62
- package/dist/collector-identity.js +0 -157
- package/dist/collector-ledger.js +2 -42
- package/dist/compliance-transport.js +40 -14
- package/dist/diarist-contracts.js +2 -40
- package/dist/doctor-contracts.js +39 -108
- package/dist/doctor-evidence.js +1 -5
- package/dist/engine-detour-tool.js +1 -1
- package/dist/gatekeeper-role.js +49 -38
- package/dist/headless-host/production-host.js +4814 -8450
- package/dist/host-contracts.js +0 -5
- package/dist/merger-contracts.js +4 -6
- package/dist/navigator-attendance.js +30 -52
- package/dist/navigator-invocation-identity.js +0 -1
- package/dist/navigator-public-session.js +2 -1
- package/dist/notary-contracts.js +4 -10
- package/dist/package-contracts/auditor-output.js +5 -23
- package/dist/package-contracts/fixer-output.js +25 -49
- package/dist/package-contracts/gatekeeper-output.js +3 -13
- package/dist/package-contracts/navigator-output.js +4 -7
- package/dist/package-contracts/reviewer-output.js +1 -95
- package/dist/package-contracts/terminating-tools.js +33 -144
- package/dist/package-contracts/worker-output.js +1 -3
- package/dist/public-cli/auto-resume.js +30 -68
- package/dist/public-cli/case-dossier-delivery.js +5 -17
- package/dist/public-cli/diarist-run.js +9 -4
- package/dist/public-cli/inspector-run.js +2 -8
- package/dist/public-cli/instruction-seat-run.js +3 -7
- package/dist/public-cli/invocation.js +5 -2
- package/dist/public-cli/main.js +6068 -6964
- package/dist/public-cli/notary-run.js +1 -8
- package/dist/public-cli/post-admission.js +314 -113
- package/dist/public-cli/run-lifecycle.js +14 -38
- package/dist/public-cli/settlement.js +209 -390
- package/dist/public-cli/terminal.js +51 -45
- package/dist/public-role-summons.js +1 -12
- package/dist/reviewer-dispatch.js +12 -258
- package/dist/submission-correctable-error.js +0 -12
- package/dist/submission-errors.js +6 -1
- package/dist/submission-ledger.js +125 -168
- package/dist/ticket-provenance-contracts.js +2 -49
- package/dist/ticket-provenance.js +82 -31
- package/extensions/role-runtime.ts +1 -6
- package/package.json +1 -1
- package/resources/836-deleted-machine-instruction-inventory.md +18 -0
- package/src/acp-host/role-turn-host.ts +8 -3
- package/src/analyst-gate-cycles-read.ts +17 -29
- package/src/audit-escalation.ts +20 -50
- package/src/auditor-dossier-tool.ts +5 -103
- package/src/canonical-skill-binding.ts +3 -2
- package/src/collector-identity.ts +0 -179
- package/src/collector-ledger.ts +3 -46
- package/src/collector-role.ts +32 -61
- package/src/collector-tool-schemas.ts +20 -14
- package/src/compliance-transport.ts +55 -17
- package/src/countersign-role.ts +17 -8
- package/src/diarist-contracts.ts +3 -36
- package/src/diarist-role.ts +13 -7
- package/src/diarist.ts +4 -16
- package/src/doctor-contracts.ts +73 -94
- package/src/doctor-evidence.ts +1 -1
- package/src/doctor-role.ts +3 -3
- package/src/engine-detour-tool.ts +4 -1
- package/src/external-host-turn-loop.ts +9 -3
- package/src/gatekeeper-pass-envelope.ts +7 -12
- package/src/gatekeeper-role.ts +56 -50
- package/src/gleaner-left-role.ts +4 -2
- package/src/headless-host/role-turn-host.ts +4 -5
- package/src/host-contracts.ts +3 -8
- package/src/judge-role.ts +19 -13
- package/src/merger-contracts.ts +10 -6
- package/src/merger-role.ts +1 -5
- package/src/navigator-attendance.ts +37 -74
- package/src/navigator-invocation-identity.ts +0 -6
- package/src/navigator-public-session.ts +2 -1
- package/src/notary-contracts.ts +7 -10
- package/src/package-contracts/auditor-output.ts +4 -21
- package/src/package-contracts/fixer-output.ts +34 -52
- package/src/package-contracts/gatekeeper-output.ts +5 -22
- package/src/package-contracts/navigator-output.ts +7 -7
- package/src/package-contracts/reviewer-output.ts +2 -150
- package/src/package-contracts/terminating-tools.ts +32 -147
- package/src/package-contracts/worker-output.ts +0 -1
- package/src/pi/adapter.ts +3 -8
- package/src/public-cli/auto-resume.ts +41 -94
- package/src/public-cli/case-dossier-delivery.ts +5 -15
- package/src/public-cli/countersign-run.ts +14 -6
- package/src/public-cli/diarist-run.ts +11 -9
- package/src/public-cli/inspector-run.ts +2 -18
- package/src/public-cli/instruction-seat-run.ts +3 -17
- package/src/public-cli/invocation.ts +5 -2
- package/src/public-cli/merger-run.ts +1 -1
- package/src/public-cli/notary-run.ts +1 -18
- package/src/public-cli/post-admission.ts +385 -115
- package/src/public-cli/run-lifecycle.ts +13 -44
- package/src/public-cli/settlement.ts +314 -562
- package/src/public-cli/terminal.ts +66 -72
- package/src/public-role-summons.ts +5 -30
- package/src/reviewer-dispatch.ts +29 -331
- package/src/reviewer-role.ts +16 -83
- package/src/role-envelope.ts +16 -24
- package/src/role-runtime-dependencies.ts +1 -6
- package/src/role-runtime.ts +65 -201
- package/src/submission-correctable-error.ts +0 -14
- package/src/submission-errors.ts +6 -1
- package/src/submission-ledger.ts +187 -187
- package/src/ticket-provenance-contracts.ts +4 -60
- package/src/ticket-provenance.ts +98 -32
- package/src/worker-role.ts +35 -36
- package/dist/activation-ledger-session.js +0 -105
- package/dist/activation-ledger.js +0 -139
- package/dist/activation-trace.js +0 -33
- package/dist/atomic-write.js +0 -23
- package/dist/auditor-role.js +0 -12
- package/dist/canonical-skill-binding.js +0 -61
- package/dist/collector-handbook.js +0 -142
- package/dist/collector-receipt.js +0 -107
- package/dist/collector-role.js +0 -566
- package/dist/collector-tool-schemas.js +0 -107
- package/dist/countersign-role.js +0 -28
- package/dist/diarist-role.js +0 -44
- package/dist/diarist.js +0 -66
- package/dist/doctor-role.js +0 -75
- package/dist/fixer-bash-seatbelt.js +0 -13
- package/dist/gatekeeper-pass-envelope.js +0 -77
- package/dist/gleaner-left-role.js +0 -26
- package/dist/inspector-role.js +0 -29
- package/dist/judge-role.js +0 -104
- package/dist/merger-role.js +0 -55
- package/dist/navigator-role.js +0 -12
- package/dist/notary-role.js +0 -134
- package/dist/oauth-keepalive.js +0 -158
- package/dist/pi/adapter.js +0 -223
- package/dist/reviewer-agent.js +0 -122
- package/dist/reviewer-child-executor.js +0 -194
- package/dist/reviewer-construction.js +0 -103
- package/dist/reviewer-execution-ledger.js +0 -166
- package/dist/reviewer-failure-diagnostic.js +0 -17
- package/dist/reviewer-role.js +0 -159
- package/dist/reviewer-scope-prompt.js +0 -21
- package/dist/reviewer-settlement.js +0 -66
- package/dist/reviewer-workspace.js +0 -132
- package/dist/role-runtime.js +0 -1604
- package/dist/stderr-jsonl.js +0 -26
- package/dist/tool-execution-observation.js +0 -141
- package/dist/worker-role.js +0 -345
- package/dist/worker-submission-gates.js +0 -305
- package/src/collector-receipt.ts +0 -140
- package/src/diagnostic-tail.ts +0 -18
- package/src/reviewer-agent.ts +0 -135
- package/src/reviewer-child-executor.ts +0 -252
- package/src/reviewer-construction.ts +0 -195
- package/src/reviewer-execution-ledger.ts +0 -212
- package/src/reviewer-failure-diagnostic.ts +0 -18
- package/src/reviewer-scope-prompt.ts +0 -24
- package/src/reviewer-settlement.ts +0 -74
- package/src/reviewer-workspace.ts +0 -105
- package/src/shape-unreadable-failure.ts +0 -49
package/src/ticket-provenance.ts
CHANGED
|
@@ -56,25 +56,51 @@ export function ticketProvenanceEntryIdentity(
|
|
|
56
56
|
|
|
57
57
|
export type AppendTicketProvenanceInput = {
|
|
58
58
|
readonly ticketNumber: number;
|
|
59
|
-
readonly entry: TicketProvenanceEntry;
|
|
60
59
|
readonly cwd: string;
|
|
60
|
+
/** Original diarist row (or `{ original, unprojected: true }` envelope). */
|
|
61
|
+
readonly payload: unknown;
|
|
61
62
|
/** Explicit package home (tests / admitted run); never process.env.HOME (#604). */
|
|
62
63
|
readonly home?: string;
|
|
63
64
|
readonly host?: string;
|
|
64
65
|
readonly source?: string;
|
|
65
66
|
};
|
|
66
67
|
|
|
67
|
-
|
|
68
|
+
function identityFromPayload(ticketNumber: number, payload: unknown): string {
|
|
69
|
+
if (
|
|
70
|
+
typeof payload === "object"
|
|
71
|
+
&& payload !== null
|
|
72
|
+
&& !Array.isArray(payload)
|
|
73
|
+
) {
|
|
74
|
+
const record = payload as Record<string, unknown>;
|
|
75
|
+
if (typeof record.sourceKind === "string" && typeof record.transcript === "string") {
|
|
76
|
+
const sourceRef = typeof record.sourceRef === "object" && record.sourceRef !== null && !Array.isArray(record.sourceRef)
|
|
77
|
+
? record.sourceRef as TicketProvenanceIdentityInput["sourceRef"]
|
|
78
|
+
: {};
|
|
79
|
+
return ticketProvenanceEntryIdentity({
|
|
80
|
+
ticketNumber,
|
|
81
|
+
sourceKind: record.sourceKind,
|
|
82
|
+
sourceRef,
|
|
83
|
+
transcript: record.transcript,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return createHash("sha256")
|
|
88
|
+
.update(`${String(ticketNumber)}\u0000${JSON.stringify(payload)}`, "utf8")
|
|
89
|
+
.digest("hex");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Append one original row; returns existing pointer on identity hit. */
|
|
68
93
|
export function appendTicketProvenanceEntry(
|
|
69
94
|
input: AppendTicketProvenanceInput,
|
|
70
95
|
): RecordPointer {
|
|
71
96
|
const subject = ticketProvenanceSubject(input.ticketNumber);
|
|
72
|
-
const identity =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
97
|
+
const identity = identityFromPayload(input.ticketNumber, input.payload);
|
|
98
|
+
const record = typeof input.payload === "object" && input.payload !== null && !Array.isArray(input.payload)
|
|
99
|
+
? input.payload as Record<string, unknown>
|
|
100
|
+
: undefined;
|
|
101
|
+
const sourceRef = record !== undefined && typeof record.sourceRef === "object" && record.sourceRef !== null && !Array.isArray(record.sourceRef)
|
|
102
|
+
? record.sourceRef as { sessionFile?: unknown; entryId?: unknown }
|
|
103
|
+
: undefined;
|
|
78
104
|
return sitianReport({
|
|
79
105
|
level: "event",
|
|
80
106
|
kind: TICKET_PROVENANCE_KIND,
|
|
@@ -84,13 +110,14 @@ export function appendTicketProvenanceEntry(
|
|
|
84
110
|
...(input.home === undefined ? {} : { home: input.home }),
|
|
85
111
|
host: input.host ?? "diarist",
|
|
86
112
|
source: input.source ?? "diarist",
|
|
87
|
-
payload: input.
|
|
113
|
+
payload: input.payload,
|
|
88
114
|
raw:
|
|
89
|
-
|
|
90
|
-
|
|
115
|
+
sourceRef !== undefined
|
|
116
|
+
&& typeof sourceRef.sessionFile === "string"
|
|
117
|
+
&& (typeof sourceRef.entryId === "string" || typeof sourceRef.entryId === "number")
|
|
91
118
|
? {
|
|
92
|
-
sessionFile:
|
|
93
|
-
entryId:
|
|
119
|
+
sessionFile: sourceRef.sessionFile,
|
|
120
|
+
entryId: sourceRef.entryId,
|
|
94
121
|
}
|
|
95
122
|
: undefined,
|
|
96
123
|
});
|
|
@@ -125,6 +152,11 @@ export function resolveTicketProvenanceVolume(
|
|
|
125
152
|
export type ReadTicketProvenanceResult = {
|
|
126
153
|
/** Readable diary body entries only. */
|
|
127
154
|
readonly entries: readonly TicketProvenanceEntry[];
|
|
155
|
+
/**
|
|
156
|
+
* Original payloads that did not project — kept beside typed entries (#836 / ADR 0075).
|
|
157
|
+
* Never dropped from the volume.
|
|
158
|
+
*/
|
|
159
|
+
readonly unprojected: readonly unknown[];
|
|
128
160
|
/** Typed diagnostics on the same partition (collector/issue-source failures). */
|
|
129
161
|
readonly diagnostics: readonly TicketProvenanceDiagnostic[];
|
|
130
162
|
readonly records: readonly SitianRecord[];
|
|
@@ -142,6 +174,7 @@ export async function readTicketProvenance(
|
|
|
142
174
|
const { recordFile } = resolveTicketProvenanceVolume(ticketNumber, cwd, home);
|
|
143
175
|
const { records } = await readSitianRecords(recordFile);
|
|
144
176
|
const entries: TicketProvenanceEntry[] = [];
|
|
177
|
+
const unprojected: unknown[] = [];
|
|
145
178
|
const diagnostics: TicketProvenanceDiagnostic[] = [];
|
|
146
179
|
let skipped = 0;
|
|
147
180
|
for (const record of records) {
|
|
@@ -157,12 +190,12 @@ export async function readTicketProvenance(
|
|
|
157
190
|
}
|
|
158
191
|
const entry = projectTicketProvenanceEntry(record.payload);
|
|
159
192
|
if (entry === undefined) {
|
|
160
|
-
|
|
193
|
+
unprojected.push(record.payload);
|
|
161
194
|
continue;
|
|
162
195
|
}
|
|
163
196
|
entries.push(entry);
|
|
164
197
|
}
|
|
165
|
-
return { entries, diagnostics, records, recordFile, skipped };
|
|
198
|
+
return { entries, unprojected, diagnostics, records, recordFile, skipped };
|
|
166
199
|
}
|
|
167
200
|
|
|
168
201
|
/**
|
|
@@ -187,45 +220,76 @@ function markdownFenceFor(text: string): string {
|
|
|
187
220
|
export function renderTicketProvenanceMarkdown(input: {
|
|
188
221
|
readonly ticketNumber: number;
|
|
189
222
|
readonly entries: readonly TicketProvenanceEntry[];
|
|
223
|
+
readonly unprojected?: readonly unknown[];
|
|
190
224
|
}): string {
|
|
225
|
+
const unprojected = input.unprojected ?? [];
|
|
191
226
|
const lines: string[] = [
|
|
192
227
|
`# 起居录 · #${input.ticketNumber}`,
|
|
193
228
|
"",
|
|
194
|
-
`条目数:${input.entries.length}`,
|
|
229
|
+
`条目数:${input.entries.length + unprojected.length}`,
|
|
195
230
|
"",
|
|
196
231
|
];
|
|
197
232
|
let index = 0;
|
|
198
233
|
for (const entry of input.entries) {
|
|
199
234
|
index += 1;
|
|
200
|
-
|
|
235
|
+
const sourceKind = typeof entry.sourceKind === "string" ? entry.sourceKind : "未投影";
|
|
236
|
+
const timestamp = typeof entry.timestamp === "string" ? entry.timestamp : "";
|
|
237
|
+
lines.push(`## ${index}. ${sourceKind} · ${timestamp}`);
|
|
201
238
|
lines.push("");
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
239
|
+
const basis = entry.basis;
|
|
240
|
+
const method =
|
|
241
|
+
typeof basis === "object" && basis !== null && !Array.isArray(basis) && typeof basis.method === "string"
|
|
242
|
+
? basis.method
|
|
243
|
+
: "未投影";
|
|
244
|
+
lines.push(`- basis.method: \`${method}\``);
|
|
245
|
+
if (
|
|
246
|
+
typeof basis === "object" &&
|
|
247
|
+
basis !== null &&
|
|
248
|
+
!Array.isArray(basis) &&
|
|
249
|
+
Array.isArray(basis.anchors) &&
|
|
250
|
+
basis.anchors.length > 0
|
|
251
|
+
) {
|
|
252
|
+
lines.push(`- anchors: ${basis.anchors.map((a) => `\`${String(a)}\``).join(", ")}`);
|
|
205
253
|
}
|
|
206
|
-
if (
|
|
207
|
-
lines.push(`- note: ${
|
|
254
|
+
if (typeof basis === "object" && basis !== null && !Array.isArray(basis) && basis.note !== undefined) {
|
|
255
|
+
lines.push(`- note: ${String(basis.note)}`);
|
|
208
256
|
}
|
|
257
|
+
const sourceRef =
|
|
258
|
+
typeof entry.sourceRef === "object" && entry.sourceRef !== null && !Array.isArray(entry.sourceRef)
|
|
259
|
+
? entry.sourceRef as { sessionFile?: unknown; entryId?: unknown; path?: unknown; url?: unknown }
|
|
260
|
+
: {};
|
|
209
261
|
const refParts: string[] = [];
|
|
210
|
-
if (
|
|
211
|
-
refParts.push(`sessionFile=${
|
|
262
|
+
if (sourceRef.sessionFile !== undefined) {
|
|
263
|
+
refParts.push(`sessionFile=${String(sourceRef.sessionFile)}`);
|
|
212
264
|
}
|
|
213
|
-
if (
|
|
214
|
-
refParts.push(`entryId=${String(
|
|
265
|
+
if (sourceRef.entryId !== undefined) {
|
|
266
|
+
refParts.push(`entryId=${String(sourceRef.entryId)}`);
|
|
215
267
|
}
|
|
216
|
-
if (
|
|
217
|
-
refParts.push(`path=${
|
|
268
|
+
if (sourceRef.path !== undefined) {
|
|
269
|
+
refParts.push(`path=${String(sourceRef.path)}`);
|
|
218
270
|
}
|
|
219
|
-
if (
|
|
220
|
-
refParts.push(`url=${
|
|
271
|
+
if (sourceRef.url !== undefined) {
|
|
272
|
+
refParts.push(`url=${String(sourceRef.url)}`);
|
|
221
273
|
}
|
|
222
274
|
if (refParts.length > 0) {
|
|
223
275
|
lines.push(`- sourceRef: ${refParts.join(" · ")}`);
|
|
224
276
|
}
|
|
225
277
|
lines.push("");
|
|
226
|
-
const
|
|
278
|
+
const transcript = typeof entry.transcript === "string" ? entry.transcript : JSON.stringify(entry);
|
|
279
|
+
const fence = markdownFenceFor(transcript);
|
|
280
|
+
lines.push(fence);
|
|
281
|
+
lines.push(transcript);
|
|
282
|
+
lines.push(fence);
|
|
283
|
+
lines.push("");
|
|
284
|
+
}
|
|
285
|
+
for (const payload of unprojected) {
|
|
286
|
+
index += 1;
|
|
287
|
+
lines.push(`## ${index}. 未投影`);
|
|
288
|
+
lines.push("");
|
|
289
|
+
const text = typeof payload === "string" ? payload : JSON.stringify(payload);
|
|
290
|
+
const fence = markdownFenceFor(text);
|
|
227
291
|
lines.push(fence);
|
|
228
|
-
lines.push(
|
|
292
|
+
lines.push(text);
|
|
229
293
|
lines.push(fence);
|
|
230
294
|
lines.push("");
|
|
231
295
|
}
|
|
@@ -257,11 +321,13 @@ export function writeTicketProvenanceHumanView(input: {
|
|
|
257
321
|
readonly cwd: string;
|
|
258
322
|
readonly home?: string;
|
|
259
323
|
readonly entries: readonly TicketProvenanceEntry[];
|
|
324
|
+
readonly unprojected?: readonly unknown[];
|
|
260
325
|
}): string {
|
|
261
326
|
const volume = ensureTicketProvenanceVolume(input.ticketNumber, input.cwd, input.home);
|
|
262
327
|
const md = renderTicketProvenanceMarkdown({
|
|
263
328
|
ticketNumber: input.ticketNumber,
|
|
264
329
|
entries: input.entries,
|
|
330
|
+
...(input.unprojected === undefined ? {} : { unprojected: input.unprojected }),
|
|
265
331
|
});
|
|
266
332
|
writeFileSync(volume.humanViewFile, md, "utf8");
|
|
267
333
|
return volume.humanViewFile;
|
package/src/worker-role.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { RoleHost, HostContext, HostToolResult, HostGatekeeperActions } from "./host-contracts.ts";
|
|
2
|
-
import { stringEnum } from "./host-contracts.ts";
|
|
3
2
|
import { Type, type Static } from "typebox";
|
|
4
3
|
import { openToolObjectFromUnion } from "./open-tool-schema.ts";
|
|
5
4
|
import { withInfrastructureFailureDeclaration } from "./package-contracts/terminating-infrastructure.ts";
|
|
@@ -20,7 +19,7 @@ import {
|
|
|
20
19
|
type WorkerOutput,
|
|
21
20
|
type WorkerRoleLabel,
|
|
22
21
|
} from "./package-contracts/worker-output.ts";
|
|
23
|
-
import { fixerOutputSchema, validateFixerOutput,
|
|
22
|
+
import { fixerOutputSchema, validateFixerOutput, type FixerPhase } from "./package-contracts/fixer-output.ts";
|
|
24
23
|
import {
|
|
25
24
|
FixerPacketValidationError,
|
|
26
25
|
parseFixerPrerequisites,
|
|
@@ -45,25 +44,28 @@ export {
|
|
|
45
44
|
};
|
|
46
45
|
export type { WorkerOutput };
|
|
47
46
|
|
|
47
|
+
// #836 r16 class 1: report/remainingScope are LLM/human-read narrative content —
|
|
48
|
+
// no code branches on their length. `reason` alone keeps minLength: the worker
|
|
49
|
+
// gate reads `reason.trim().length > 0` to pick typed-reminder-bounce vs accept
|
|
50
|
+
// (src/worker-submission-gates.ts:159-163,297-302).
|
|
51
|
+
// #836 (ADR 0003 Amendment): status kept open like countersignStatus
|
|
52
|
+
// (src/countersign-role.ts) — one shared description across every variant
|
|
53
|
+
// so openToolObjectFromUnion's identical-declaration collapse drops none of it.
|
|
54
|
+
const CODER_STATUS_DESCRIPTION =
|
|
55
|
+
"planned | completed | refused | unfinished — 形状指引,非 schema 闸;completed 回执含 TDD、同模式、引入回归、行为事实四项证据;unfinished 缺前置或违宪约束致本局未完成时可用,缺待决 owner 决定或答复属缺前置。" as const;
|
|
48
56
|
const coderOutputVariants = Type.Union([
|
|
49
57
|
Type.Object({
|
|
50
|
-
status:
|
|
51
|
-
report: Type.String({
|
|
58
|
+
status: Type.Unknown({ description: CODER_STATUS_DESCRIPTION }),
|
|
59
|
+
report: Type.String({ description: "如实结果报告" }),
|
|
52
60
|
}, { additionalProperties: false }),
|
|
53
61
|
Type.Object({
|
|
54
|
-
status:
|
|
55
|
-
|
|
56
|
-
"completed | refused — 形状指引,非 schema 闸;completed 回执含 TDD、同模式、引入回归、行为事实四项证据",
|
|
57
|
-
}),
|
|
58
|
-
report: Type.String({ minLength: 1, description: "如实结果报告" }),
|
|
62
|
+
status: Type.Unknown({ description: CODER_STATUS_DESCRIPTION }),
|
|
63
|
+
report: Type.String({ description: "如实结果报告" }),
|
|
59
64
|
}, { additionalProperties: false }),
|
|
60
65
|
Type.Object({
|
|
61
|
-
status:
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}),
|
|
65
|
-
report: Type.String({ minLength: 1, description: "如实结果报告" }),
|
|
66
|
-
remainingScope: Type.String({ minLength: 1, description: "本局后剩余工作" }),
|
|
66
|
+
status: Type.Unknown({ description: CODER_STATUS_DESCRIPTION }),
|
|
67
|
+
report: Type.String({ description: "如实结果报告" }),
|
|
68
|
+
remainingScope: Type.String({ description: "本局后剩余工作" }),
|
|
67
69
|
reason: Type.Optional(Type.String({
|
|
68
70
|
minLength: 1,
|
|
69
71
|
description:
|
|
@@ -151,6 +153,17 @@ function isRecord(value: unknown): value is Record<string, unknown> {
|
|
|
151
153
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
152
154
|
}
|
|
153
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Read the accepted receipt's own `status` word as submitted (#836: `status`
|
|
158
|
+
* stays an open Type.Unknown provider field, so FixerOutput's Static type no
|
|
159
|
+
* longer narrows it to a literal union — code still just reads whatever
|
|
160
|
+
* string the role wrote; a non-string status reads as "" and simply misses
|
|
161
|
+
* every known-status gate below, same as any other unrecognized status).
|
|
162
|
+
*/
|
|
163
|
+
function workerStatusOf(output: WorkerOutput): string {
|
|
164
|
+
return typeof output.status === "string" ? output.status : "";
|
|
165
|
+
}
|
|
166
|
+
|
|
154
167
|
function deepFreeze<T>(value: T): T {
|
|
155
168
|
if (typeof value !== "object" || value === null || Object.isFrozen(value)) return value;
|
|
156
169
|
for (const child of Object.values(value)) deepFreeze(child);
|
|
@@ -261,16 +274,16 @@ export function createFixerRoleRuntime(
|
|
|
261
274
|
if (packet === undefined || phase === undefined) {
|
|
262
275
|
throw new Error("修内司修理包与阶段未装载");
|
|
263
276
|
}
|
|
264
|
-
const output = deepFreeze(
|
|
277
|
+
const output = deepFreeze(validateFixerOutput(parameters, phase));
|
|
265
278
|
assertAcceptableThroughHost(
|
|
266
279
|
submissionGate,
|
|
267
|
-
output
|
|
280
|
+
workerStatusOf(output),
|
|
268
281
|
output,
|
|
269
282
|
hostActions,
|
|
270
283
|
ctx,
|
|
271
284
|
toolCallId,
|
|
272
285
|
);
|
|
273
|
-
if (WORKER_DONE_STATUSES.has(output
|
|
286
|
+
if (WORKER_DONE_STATUSES.has(workerStatusOf(output))) {
|
|
274
287
|
await pi.requireGatekeeperPass!({
|
|
275
288
|
context: ctx,
|
|
276
289
|
subject: { kind: "worker_completion" },
|
|
@@ -331,7 +344,6 @@ export function createCoderRoleRuntime(
|
|
|
331
344
|
let tddInvocationInjected = false;
|
|
332
345
|
let originalRequest: string | undefined;
|
|
333
346
|
let expansionPending = false;
|
|
334
|
-
let expansionCaptured = false;
|
|
335
347
|
let lifecycleRegistered = false;
|
|
336
348
|
const submissionGate = createWorkerSubmissionGate();
|
|
337
349
|
|
|
@@ -352,7 +364,6 @@ export function createCoderRoleRuntime(
|
|
|
352
364
|
tddInvocationInjected = false;
|
|
353
365
|
originalRequest = undefined;
|
|
354
366
|
expansionPending = false;
|
|
355
|
-
expansionCaptured = false;
|
|
356
367
|
soul = (await dependencies.loadSoul()).trim();
|
|
357
368
|
if (soul.length === 0) throw new Error("Coder soul is empty");
|
|
358
369
|
const selectedPhase = pi.getFlag("ak-coder-phase");
|
|
@@ -400,23 +411,17 @@ export function createCoderRoleRuntime(
|
|
|
400
411
|
throw new Error("将作监任务与阶段未装载");
|
|
401
412
|
}
|
|
402
413
|
const output = validateWorkerOutput(parameters, phase, "Coder");
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
!expansionCaptured
|
|
406
|
-
) {
|
|
407
|
-
const rejection = new CoderSkillExpansionEvidenceMissingError();
|
|
408
|
-
hostActions.bindSubmissionNonPass(toolCallId, rejection.result);
|
|
409
|
-
throw rejection;
|
|
410
|
-
}
|
|
414
|
+
// #836: skill-expansion evidence rejection deleted (陛下「2.4/5 删」).
|
|
415
|
+
// Skill still ships with the package (ADR 0052); code no longer refuses on it.
|
|
411
416
|
assertAcceptableThroughHost(
|
|
412
417
|
submissionGate,
|
|
413
|
-
output
|
|
418
|
+
workerStatusOf(output),
|
|
414
419
|
output,
|
|
415
420
|
hostActions,
|
|
416
421
|
ctx,
|
|
417
422
|
toolCallId,
|
|
418
423
|
);
|
|
419
|
-
if (WORKER_DONE_STATUSES.has(output
|
|
424
|
+
if (WORKER_DONE_STATUSES.has(workerStatusOf(output))) {
|
|
420
425
|
await pi.requireGatekeeperPass!({
|
|
421
426
|
context: ctx,
|
|
422
427
|
subject: { kind: "worker_completion" },
|
|
@@ -459,12 +464,6 @@ export function createCoderRoleRuntime(
|
|
|
459
464
|
}
|
|
460
465
|
if (expansionPending) {
|
|
461
466
|
expansionPending = false;
|
|
462
|
-
if (originalRequest !== undefined) {
|
|
463
|
-
expansionCaptured = binding.captureExpansion(
|
|
464
|
-
pi.capabilities?.skillExpansion(event.prompt),
|
|
465
|
-
originalRequest,
|
|
466
|
-
) !== undefined;
|
|
467
|
-
}
|
|
468
467
|
}
|
|
469
468
|
}
|
|
470
469
|
return {
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { lstatSync, realpathSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
-
import { isAbsolute, resolve } from "node:path";
|
|
3
|
-
import { errnoCode, errorText, } from "./activation-ledger-topology.js";
|
|
4
|
-
/**
|
|
5
|
-
* Typed missing durable session principal. Callers discriminate with instanceof/code;
|
|
6
|
-
* never by parsing message prose. Original filesystem causes are retained.
|
|
7
|
-
*/
|
|
8
|
-
export class ActivationSessionFileMissingError extends Error {
|
|
9
|
-
code = "AK_ACTIVATION_SESSION_FILE_MISSING";
|
|
10
|
-
path;
|
|
11
|
-
constructor(path, options) {
|
|
12
|
-
super(`Workflow role activation durable session file does not exist: ${path}`, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
13
|
-
this.name = "ActivationSessionFileMissingError";
|
|
14
|
-
this.path = path;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function materializeDeferredSessionFile(sessionManager, resolvedFile) {
|
|
18
|
-
const header = sessionManager.getHeader?.();
|
|
19
|
-
if (header === null || header === undefined || header.type !== "session") {
|
|
20
|
-
throw new ActivationSessionFileMissingError(resolvedFile);
|
|
21
|
-
}
|
|
22
|
-
// Activation does not own record placement (ADR 0065 / #221). Parent directory is the
|
|
23
|
-
// SessionManager principal's own dir — write the header in place; do not pull ledgerHome
|
|
24
|
-
// containment or ensureRealDirectoryTree back onto this admission path.
|
|
25
|
-
try {
|
|
26
|
-
writeFileSync(resolvedFile, `${JSON.stringify(header)}\n`, { flag: "wx" });
|
|
27
|
-
}
|
|
28
|
-
catch (error) {
|
|
29
|
-
if (errnoCode(error) !== "EEXIST") {
|
|
30
|
-
throw new Error(`Workflow role activation failed to materialize durable session file (${resolvedFile}): ${errorText(error)}`, { cause: error });
|
|
31
|
-
}
|
|
32
|
-
// Lost a create race — validate the winner below.
|
|
33
|
-
}
|
|
34
|
-
// Rebind full SessionManager so subsequent Pi appends use O_APPEND (flushed=true)
|
|
35
|
-
// instead of exclusive wx create against the file we just wrote.
|
|
36
|
-
if (typeof sessionManager.setSessionFile === "function") {
|
|
37
|
-
sessionManager.setSessionFile(resolvedFile);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Admit a durable Pi session file principal (ADR 0048/0065).
|
|
42
|
-
* Requires an existing regular file at admission (or a live SessionManager header that
|
|
43
|
-
* can be materialized onto the same path). Rejects relative paths, directories,
|
|
44
|
-
* non-files, and nonexistent paths that cannot be materialized. Original filesystem
|
|
45
|
-
* causes are retained.
|
|
46
|
-
*
|
|
47
|
-
* Record-placement (session must live under the ledger book) is NOT enforced here —
|
|
48
|
-
* that check lives on the archivist record entry (createRecordSession). Activation only
|
|
49
|
-
* binds the durable principal identity for the role run.
|
|
50
|
-
*
|
|
51
|
-
* Upstream Pi defers exclusive create until the first assistant message. When the path
|
|
52
|
-
* is the live SessionManager principal and only the header is in memory, admission
|
|
53
|
-
* materializes that header onto the same path before the fact is written so the role
|
|
54
|
-
* fact never points at a session that may be created later.
|
|
55
|
-
*/
|
|
56
|
-
export function durableSessionPointer(sessionManager) {
|
|
57
|
-
const file = sessionManager.getSessionFile?.();
|
|
58
|
-
if (typeof file !== "string" || file.length === 0) {
|
|
59
|
-
throw new Error("Workflow role activation requires a durable Pi session file principal (getSessionFile); directory-only or --no-session invocations are rejected");
|
|
60
|
-
}
|
|
61
|
-
if (!isAbsolute(file)) {
|
|
62
|
-
throw new Error(`Workflow role activation requires an absolute durable session file path; got relative path: ${file}`);
|
|
63
|
-
}
|
|
64
|
-
const resolvedFile = resolve(file);
|
|
65
|
-
try {
|
|
66
|
-
lstatSync(resolvedFile);
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
if (errnoCode(error) !== "ENOENT") {
|
|
70
|
-
throw new Error(`Workflow role activation failed to stat durable session file (${resolvedFile}): ${errorText(error)}`, { cause: error });
|
|
71
|
-
}
|
|
72
|
-
try {
|
|
73
|
-
materializeDeferredSessionFile(sessionManager, resolvedFile);
|
|
74
|
-
}
|
|
75
|
-
catch (materializeError) {
|
|
76
|
-
// Missing principal: rethrow typed missing-file identity with the original ENOENT cause.
|
|
77
|
-
// Other materialize failures keep their own typed/native identity.
|
|
78
|
-
if (materializeError instanceof ActivationSessionFileMissingError) {
|
|
79
|
-
throw new ActivationSessionFileMissingError(resolvedFile, { cause: error });
|
|
80
|
-
}
|
|
81
|
-
throw materializeError;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
let realFile;
|
|
85
|
-
try {
|
|
86
|
-
realFile = realpathSync(resolvedFile);
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
throw new ActivationSessionFileMissingError(resolvedFile, { cause: error });
|
|
90
|
-
}
|
|
91
|
-
let info;
|
|
92
|
-
try {
|
|
93
|
-
info = statSync(realFile);
|
|
94
|
-
}
|
|
95
|
-
catch (error) {
|
|
96
|
-
throw new Error(`Workflow role activation failed to stat durable session file (${realFile}): ${errorText(error)}`, { cause: error });
|
|
97
|
-
}
|
|
98
|
-
if (info.isDirectory()) {
|
|
99
|
-
throw new Error(`Workflow role activation durable session principal must be a file, not a directory: ${realFile}`);
|
|
100
|
-
}
|
|
101
|
-
if (!info.isFile()) {
|
|
102
|
-
throw new Error(`Workflow role activation durable session principal is not a regular file: ${realFile}`);
|
|
103
|
-
}
|
|
104
|
-
return { kind: "session-file", path: realFile };
|
|
105
|
-
}
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { constants, closeSync, fstatSync, openSync, writeSync, } from "node:fs";
|
|
2
|
-
import { dirname, isAbsolute, resolve } from "node:path";
|
|
3
|
-
import { ActivationLedgerError, activationWaitingLedgerPath, assertLedgerFileInsideHome, ensureRealDirectoryTree, errorText, } from "./activation-ledger-topology.js";
|
|
4
|
-
/** Private open flags: O_NOFOLLOW closes the lstat→open TOCTOU window. */
|
|
5
|
-
const ACTIVATION_LEDGER_APPEND_OPEN_FLAGS = constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | constants.O_NOFOLLOW;
|
|
6
|
-
export { ActivationGitRepositoryRequiredError, resolveBookKeyFromGit, } from "./activation-ledger-git.js";
|
|
7
|
-
export { ActivationSessionFileMissingError, durableSessionPointer, } from "./activation-ledger-session.js";
|
|
8
|
-
export { ActivationLedgerError, activationBookDirectory, activationWaitingLedgerPath, resolveActivationLedgerHome, } from "./activation-ledger-topology.js";
|
|
9
|
-
export const ACCEPTED_ACTIVATION_EVENT = "accepted-activation";
|
|
10
|
-
/**
|
|
11
|
-
* Package-owned index-only top-level keys for accepted-activation facts (ADR 0049).
|
|
12
|
-
* Sole machine key contract: projection and tests consume this descriptor — not prose markers.
|
|
13
|
-
*/
|
|
14
|
-
export const ACCEPTED_ACTIVATION_FACT_KEYS = Object.freeze([
|
|
15
|
-
"event",
|
|
16
|
-
"role",
|
|
17
|
-
"observedAt",
|
|
18
|
-
"bookKey",
|
|
19
|
-
"session",
|
|
20
|
-
"correlation",
|
|
21
|
-
]);
|
|
22
|
-
const _acceptedActivationFactKeysMatch = true;
|
|
23
|
-
void _acceptedActivationFactKeysMatch;
|
|
24
|
-
/**
|
|
25
|
-
* Host correlation channel (not a CLI flag): a non-blank AK_CORRELATION_ID carries
|
|
26
|
-
* the caller id verbatim; missing/blank/whitespace-only yields the typed absent identity.
|
|
27
|
-
*/
|
|
28
|
-
export function correlationIdentityFromEnv(env = process.env) {
|
|
29
|
-
const raw = env.AK_CORRELATION_ID;
|
|
30
|
-
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
31
|
-
return { kind: "caller", id: raw };
|
|
32
|
-
}
|
|
33
|
-
return { kind: "absent" };
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Descriptor-driven top-level pick: only ACCEPTED_ACTIVATION_FACT_KEYS leave this boundary.
|
|
37
|
-
* Nested session/correlation are rebuilt closed before the pick (ADR 0049 zero-content by construction).
|
|
38
|
-
*/
|
|
39
|
-
function projectAcceptedActivationFact(input) {
|
|
40
|
-
const closed = {
|
|
41
|
-
event: ACCEPTED_ACTIVATION_EVENT,
|
|
42
|
-
role: input.role,
|
|
43
|
-
observedAt: input.observedAt,
|
|
44
|
-
bookKey: input.bookKey,
|
|
45
|
-
session: { kind: "session-file", path: input.session.path },
|
|
46
|
-
correlation: input.correlation.kind === "caller"
|
|
47
|
-
? { kind: "caller", id: input.correlation.id }
|
|
48
|
-
: { kind: "absent" },
|
|
49
|
-
};
|
|
50
|
-
// Descriptor is the sole top-level emission contract (typed pick; no content keys).
|
|
51
|
-
return Object.fromEntries(ACCEPTED_ACTIVATION_FACT_KEYS.map((key) => [key, closed[key]]));
|
|
52
|
-
}
|
|
53
|
-
/** Construct the closed fact from trusted typed inputs only (descriptor projection — no content keys). */
|
|
54
|
-
export function buildAcceptedActivationFact(input) {
|
|
55
|
-
return projectAcceptedActivationFact(input);
|
|
56
|
-
}
|
|
57
|
-
/** Serialize only the closed index fields (descriptor projection — no content keys). */
|
|
58
|
-
export function serializeAcceptedActivationFact(fact) {
|
|
59
|
-
return `${JSON.stringify(projectAcceptedActivationFact(fact))}\n`;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Bare O_APPEND write of one complete line under an absolute ledger home.
|
|
63
|
-
* Private helper: production-bound to writeSync; owns open/write/close honesty only.
|
|
64
|
-
* Close failure cannot mask open/write; simultaneous close is nested evidence.
|
|
65
|
-
*/
|
|
66
|
-
function appendActivationLedgerLine(ledgerPath, line, options) {
|
|
67
|
-
if (!isAbsolute(options.ledgerHome)) {
|
|
68
|
-
throw new ActivationLedgerError(`activation ledger home must be absolute: ${options.ledgerHome}`);
|
|
69
|
-
}
|
|
70
|
-
const resolvedLedger = resolve(ledgerPath);
|
|
71
|
-
const resolvedHome = resolve(options.ledgerHome);
|
|
72
|
-
const parent = dirname(resolvedLedger);
|
|
73
|
-
ensureRealDirectoryTree(resolvedHome, parent);
|
|
74
|
-
assertLedgerFileInsideHome(resolvedLedger, resolvedHome);
|
|
75
|
-
// Fail-closed guard (same shape as PR #418's publication seam): on platforms
|
|
76
|
-
// without O_NOFOLLOW (e.g. Windows, nodejs/node#41590) the JS bitwise-or in
|
|
77
|
-
// ACTIVATION_LEDGER_APPEND_OPEN_FLAGS would silently drop the flag and the
|
|
78
|
-
// lstat→open TOCTOU anti-symlink protection would vanish while the comment
|
|
79
|
-
// above still claims it. Refuse loudly via the existing typed ledger failure
|
|
80
|
-
// channel instead of appending unprotected; never silently degrade.
|
|
81
|
-
if (typeof constants.O_NOFOLLOW !== "number") {
|
|
82
|
-
throw new ActivationLedgerError("activation ledger append requires O_NOFOLLOW open-flag support (anti-symlink TOCTOU protection must not be silently dropped); refusing to append");
|
|
83
|
-
}
|
|
84
|
-
const bytes = Buffer.isBuffer(line) ? line : Buffer.from(line);
|
|
85
|
-
let ledgerFd;
|
|
86
|
-
let primaryFailure;
|
|
87
|
-
try {
|
|
88
|
-
try {
|
|
89
|
-
ledgerFd = openSync(resolvedLedger, ACTIVATION_LEDGER_APPEND_OPEN_FLAGS, 0o644);
|
|
90
|
-
}
|
|
91
|
-
catch (error) {
|
|
92
|
-
// Native ELOOP/filesystem cause retained; no errno dispatch.
|
|
93
|
-
throw new ActivationLedgerError(`activation ledger failed to open ledger file (${resolvedLedger}): ${errorText(error)}`, { cause: error });
|
|
94
|
-
}
|
|
95
|
-
let opened;
|
|
96
|
-
try {
|
|
97
|
-
opened = fstatSync(ledgerFd);
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
throw new ActivationLedgerError(`activation ledger failed to fstat ledger file (${resolvedLedger}): ${errorText(error)}`, { cause: error });
|
|
101
|
-
}
|
|
102
|
-
if (!opened.isFile()) {
|
|
103
|
-
throw new ActivationLedgerError(`activation ledger is not a regular file: ${resolvedLedger}`);
|
|
104
|
-
}
|
|
105
|
-
const written = writeSync(ledgerFd, bytes, 0, bytes.length, null);
|
|
106
|
-
if (written !== bytes.length) {
|
|
107
|
-
throw new ActivationLedgerError(`activation ledger short write: wrote ${written} of ${bytes.length} bytes to ${resolvedLedger}`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
catch (error) {
|
|
111
|
-
primaryFailure = error;
|
|
112
|
-
}
|
|
113
|
-
if (ledgerFd !== undefined) {
|
|
114
|
-
try {
|
|
115
|
-
closeSync(ledgerFd);
|
|
116
|
-
}
|
|
117
|
-
catch (closeFailure) {
|
|
118
|
-
if (primaryFailure !== undefined) {
|
|
119
|
-
throw new AggregateError([primaryFailure, closeFailure], "activation ledger operation and close failed", { cause: primaryFailure });
|
|
120
|
-
}
|
|
121
|
-
throw closeFailure;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
if (primaryFailure !== undefined)
|
|
125
|
-
throw primaryFailure;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Append one complete JSONL record with one O_APPEND write of the full record.
|
|
129
|
-
* Shared-ledger contract: concurrent successful append-only producers cannot
|
|
130
|
-
* overwrite one another. A short write is an honest infrastructure failure
|
|
131
|
-
* (ADR 0049) — no non-append rollback/truncate. Close failure cannot mask the
|
|
132
|
-
* primary write cause; simultaneous close evidence is retained.
|
|
133
|
-
*/
|
|
134
|
-
export function appendAcceptedActivationFact(ledgerPath, fact, options) {
|
|
135
|
-
appendActivationLedgerLine(ledgerPath, Buffer.from(serializeAcceptedActivationFact(fact), "utf8"), { ledgerHome: options.ledgerHome });
|
|
136
|
-
}
|
|
137
|
-
export function appendAcceptedActivationToBook(options) {
|
|
138
|
-
appendAcceptedActivationFact(activationWaitingLedgerPath(options.ledgerHome, options.fact.bookKey), options.fact, { ledgerHome: options.ledgerHome });
|
|
139
|
-
}
|
package/dist/activation-trace.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { Type } from "typebox";
|
|
2
|
-
const causeSchema = Type.Object({
|
|
3
|
-
identity: Type.String({ minLength: 1 }),
|
|
4
|
-
name: Type.String({ minLength: 1 }),
|
|
5
|
-
message: Type.String(),
|
|
6
|
-
evidenceId: Type.Optional(Type.String({ minLength: 1 })),
|
|
7
|
-
}, { additionalProperties: false });
|
|
8
|
-
export const activationTraceRecordSchema = Type.Object({
|
|
9
|
-
role: Type.String({ minLength: 1 }),
|
|
10
|
-
stageId: Type.String({ pattern: "^[a-z][a-z0-9-]*$" }),
|
|
11
|
-
status: Type.Literal("failed"),
|
|
12
|
-
timestamp: Type.String({ format: "date-time" }),
|
|
13
|
-
cause: causeSchema,
|
|
14
|
-
}, { additionalProperties: false });
|
|
15
|
-
let activationCauseEvidence = 0;
|
|
16
|
-
const retainedActivationCauses = new Map();
|
|
17
|
-
export function namedActivationCause(error) {
|
|
18
|
-
const evidenceId = `activation-cause-${++activationCauseEvidence}`;
|
|
19
|
-
retainedActivationCauses.set(evidenceId, error);
|
|
20
|
-
if (error instanceof Error) {
|
|
21
|
-
const code = error.code;
|
|
22
|
-
const name = error.name || "Error";
|
|
23
|
-
return { identity: typeof code === "string" && code.length > 0 ? code : name, name, message: error.message, evidenceId };
|
|
24
|
-
}
|
|
25
|
-
let message;
|
|
26
|
-
try {
|
|
27
|
-
message = typeof error === "string" ? error : JSON.stringify(error) ?? String(error);
|
|
28
|
-
return { identity: "UnknownThrownCause", name: "UnknownThrownCause", message, evidenceId };
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
return { identity: "UnknownThrownCause", name: "UnknownThrownCause", message: String(error), evidenceId };
|
|
32
|
-
}
|
|
33
|
-
}
|