@stage5/lumine 0.2.53 → 0.2.54
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/README.md +52 -1
- package/lib/admin.js +191 -1
- package/lib/agent/providers/claude-code.js +104 -3
- package/lib/agent/providers/codex.js +118 -14
- package/lib/agent.js +9 -1
- package/lib/commands.js +75 -27
- package/lib/constants.js +1 -0
- package/lib/sponsor.js +1291 -0
- package/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +34 -0
package/README.md
CHANGED
|
@@ -229,6 +229,46 @@ claude "Read CLAUDE.md, then make the requested change."
|
|
|
229
229
|
The login command uses a browser approval code and stores a scoped token and the
|
|
230
230
|
selected project at `~/.twinkle/lumine-cli-auth.json`.
|
|
231
231
|
|
|
232
|
+
## Sponsoring Zero or Ciel Build Workshop duty
|
|
233
|
+
|
|
234
|
+
Sponsorship is an approved contribution role, not an open switch and not a
|
|
235
|
+
website-management role. Applications are submitted only through Lumine CLI;
|
|
236
|
+
the Twinkle website provides instructions, but no application form.
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
lumine sponsor agreement
|
|
240
|
+
lumine sponsor apply --providers codex --motivation "Why I want to help" \
|
|
241
|
+
--availability "My usual duty window" --accept-agreement
|
|
242
|
+
lumine sponsor status
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
After Mikey approves an application, configure conservative limits based on
|
|
246
|
+
the subscription you are contributing, then start one foreground duty:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
lumine sponsor capacity --concurrency 1 --helpers 0 \
|
|
250
|
+
--daily-limit 3 --weekly-limit 10
|
|
251
|
+
lumine sponsor duty start zero --provider codex \
|
|
252
|
+
--model gpt-5.6-sol --effort max --service-tier priority
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
While that foreground process has a fresh server lease, Zero or Ciel exposes a
|
|
256
|
+
Build Workshop in chat and users may see the named sponsor and canonical queue.
|
|
257
|
+
With no live duty, the website stays in its ordinary chat state and shows none
|
|
258
|
+
of the Workshop UI. Press Ctrl-C to stop accepting work and drain already
|
|
259
|
+
claimed jobs before duty ends; `pause`, `resume`, and `stop` are also available.
|
|
260
|
+
|
|
261
|
+
Zero or Ciel remains the user-facing teammate. The local coding agent receives
|
|
262
|
+
only the initial relay and active-job Build follow-ups covered by the user’s
|
|
263
|
+
explicit Workshop consent, the assigned contribution branch, and its scoped
|
|
264
|
+
Build Forum—not the raw assistant chat. It
|
|
265
|
+
cannot merge into Main, publish, or use website-management APIs. Lumine records
|
|
266
|
+
the requested and provider-reported model, effort, service tier, runtime/usage
|
|
267
|
+
evidence, coordinator/helper tree, saved artifact, and branch notice. Every
|
|
268
|
+
completed handoff enters the daily integrity flow; probationary, hard-flagged,
|
|
269
|
+
and sampled work requires review. Each unique cleared handoff earns a flat 50
|
|
270
|
+
Karma Points, while retries and helper agents do not multiply the award.
|
|
271
|
+
|
|
232
272
|
## Privileged website administration
|
|
233
273
|
|
|
234
274
|
`lumine admin` reuses Mikey's saved CLI login while keeping the visible public
|
|
@@ -248,6 +288,13 @@ lumine admin economy trace lock --days 3 \
|
|
|
248
288
|
lumine admin rescue wordle-audit --days 30 \
|
|
249
289
|
--reason "Identify recorded Wordle breaks and rescue status" --json
|
|
250
290
|
lumine admin daily-run start --identity auto --comment-mode off --json
|
|
291
|
+
lumine admin sponsor applications list --status pending --json
|
|
292
|
+
lumine admin sponsor applications review 12 --decision approve \
|
|
293
|
+
--note "Approved for probationary duty" --json
|
|
294
|
+
lumine admin sponsor integrity scan --json
|
|
295
|
+
lumine admin sponsor integrity cases --status open --json
|
|
296
|
+
lumine admin sponsor integrity get 34 --json
|
|
297
|
+
lumine admin sponsor integrity review 34 --decision clear --json
|
|
251
298
|
lumine admin todo list --json
|
|
252
299
|
lumine admin todo add --kind experiment --status in_progress \
|
|
253
300
|
--title "Validate Zero/Ciel cost optimization" \
|
|
@@ -355,7 +402,11 @@ with `todo update`; completing a run does not complete its todos. Experiments
|
|
|
355
402
|
must meet their stated acceptance criteria—lower AI cost with weaker user
|
|
356
403
|
responses is not a successful optimization. `daily-run report` summarizes
|
|
357
404
|
confirmed mutations, completed queue coverage, explicitly recorded escalations,
|
|
358
|
-
unfinished todos, and the run brief before the run is
|
|
405
|
+
unfinished todos, sponsor-integrity state, and the run brief before the run is
|
|
406
|
+
completed. A run cannot complete until its bounded sponsor-integrity snapshot
|
|
407
|
+
has been scanned and every selected case is cleared or disqualified. `hold` and
|
|
408
|
+
`flag` deliberately keep the run open for human judgment; scans never suspend,
|
|
409
|
+
revoke, or disqualify a sponsor automatically.
|
|
359
410
|
|
|
360
411
|
Identity inspection, escalation dispositions, AI-bucket maintenance, and
|
|
361
412
|
approved Notable User additions and todos are private operator bookkeeping and
|
package/lib/admin.js
CHANGED
|
@@ -814,6 +814,108 @@ export function parseAdminOperation(options) {
|
|
|
814
814
|
);
|
|
815
815
|
}
|
|
816
816
|
|
|
817
|
+
if (namespace === "sponsor" || namespace === "sponsors") {
|
|
818
|
+
if (action === "applications") {
|
|
819
|
+
if (!target || target === "list") {
|
|
820
|
+
return readOperation(
|
|
821
|
+
"sponsor.applications.list",
|
|
822
|
+
withQuery("/cli/admin/sponsors/applications", {
|
|
823
|
+
status: options.adminStatus || "pending",
|
|
824
|
+
limit: options.limit,
|
|
825
|
+
}),
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
if (target === "review") {
|
|
829
|
+
const applicationId = parseRequiredInteger(
|
|
830
|
+
extra,
|
|
831
|
+
"Sponsor application ID",
|
|
832
|
+
1,
|
|
833
|
+
);
|
|
834
|
+
const decision = parseChoice(
|
|
835
|
+
options.adminDecision || options.adminStatus,
|
|
836
|
+
"--decision",
|
|
837
|
+
["approve", "reject"],
|
|
838
|
+
);
|
|
839
|
+
return writeOperation(
|
|
840
|
+
"sponsor.application.review",
|
|
841
|
+
"POST",
|
|
842
|
+
`/cli/admin/sponsors/applications/${applicationId}/review`,
|
|
843
|
+
{ decision, note: options.note || undefined },
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (action === "status" && target === "set") {
|
|
848
|
+
const sponsorUserId = parseRequiredInteger(extra, "Sponsor user ID", 1);
|
|
849
|
+
const status = parseChoice(options.adminStatus, "--status", [
|
|
850
|
+
"probationary",
|
|
851
|
+
"trusted",
|
|
852
|
+
"suspended",
|
|
853
|
+
"revoked",
|
|
854
|
+
]);
|
|
855
|
+
return writeOperation(
|
|
856
|
+
"sponsor.status.set",
|
|
857
|
+
"PUT",
|
|
858
|
+
`/cli/admin/sponsors/${sponsorUserId}/status`,
|
|
859
|
+
{ status, note: options.note || undefined },
|
|
860
|
+
);
|
|
861
|
+
}
|
|
862
|
+
if (action === "integrity") {
|
|
863
|
+
if (target === "status") {
|
|
864
|
+
return readOperation(
|
|
865
|
+
"sponsor.integrity.status",
|
|
866
|
+
"/cli/admin/daily-runs/sponsor-integrity/status",
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
if (target === "scan") {
|
|
870
|
+
return writeOperation(
|
|
871
|
+
"sponsor.integrity.scan",
|
|
872
|
+
"POST",
|
|
873
|
+
"/cli/admin/daily-runs/sponsor-integrity/scan",
|
|
874
|
+
{},
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
if (target === "cases") {
|
|
878
|
+
return readOperation(
|
|
879
|
+
"sponsor.integrity.cases",
|
|
880
|
+
withQuery("/cli/admin/daily-runs/sponsor-integrity/cases", {
|
|
881
|
+
status: options.adminStatus || "open",
|
|
882
|
+
limit: options.limit,
|
|
883
|
+
}),
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
if (target === "get") {
|
|
887
|
+
const caseId = parseRequiredInteger(extra, "Integrity case ID", 1);
|
|
888
|
+
return readOperation(
|
|
889
|
+
"sponsor.integrity.get",
|
|
890
|
+
`/cli/admin/daily-runs/sponsor-integrity/cases/${caseId}`,
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
if (target === "review") {
|
|
894
|
+
const caseId = parseRequiredInteger(extra, "Integrity case ID", 1);
|
|
895
|
+
const decision = parseChoice(options.adminDecision, "--decision", [
|
|
896
|
+
"clear",
|
|
897
|
+
"hold",
|
|
898
|
+
"flag",
|
|
899
|
+
"disqualify",
|
|
900
|
+
]);
|
|
901
|
+
if (decision !== "clear" && !String(options.note || "").trim()) {
|
|
902
|
+
throw cliValidationError(
|
|
903
|
+
"Sponsor-integrity hold, flag, and disqualify decisions require --note <evidence>.",
|
|
904
|
+
);
|
|
905
|
+
}
|
|
906
|
+
return writeOperation(
|
|
907
|
+
"sponsor.integrity.review",
|
|
908
|
+
"POST",
|
|
909
|
+
`/cli/admin/daily-runs/sponsor-integrity/cases/${caseId}/review`,
|
|
910
|
+
{ decision, note: options.note || undefined },
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
throw cliValidationError(
|
|
915
|
+
"Usage: lumine admin sponsor applications list|review | sponsor status set | sponsor integrity status|scan|cases|get|review.",
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
|
|
817
919
|
if (namespace === "daily-run") {
|
|
818
920
|
if (action === "start") {
|
|
819
921
|
return writeOperation(
|
|
@@ -1490,7 +1592,7 @@ export function parseAdminOperation(options) {
|
|
|
1490
1592
|
}
|
|
1491
1593
|
|
|
1492
1594
|
throw cliValidationError(
|
|
1493
|
-
"Usage: lumine admin identity|economy|rescue|daily-run|escalation|todo|recommendations|builds|post|subjects|subject|featured|comment|announcement|chat|news|audit|brief|ai-costs|media-costs|bot-output|notable ...",
|
|
1595
|
+
"Usage: lumine admin identity|economy|rescue|sponsor|daily-run|escalation|todo|recommendations|builds|post|subjects|subject|featured|comment|announcement|chat|news|audit|brief|ai-costs|media-costs|bot-output|notable ...",
|
|
1494
1596
|
);
|
|
1495
1597
|
}
|
|
1496
1598
|
|
|
@@ -2049,6 +2151,16 @@ function parseRequiredInteger(
|
|
|
2049
2151
|
return number;
|
|
2050
2152
|
}
|
|
2051
2153
|
|
|
2154
|
+
function parseChoice(value, label, choices) {
|
|
2155
|
+
const normalized = String(value || "")
|
|
2156
|
+
.trim()
|
|
2157
|
+
.toLowerCase();
|
|
2158
|
+
if (!choices.includes(normalized)) {
|
|
2159
|
+
throw cliValidationError(`${label} must be ${choices.join(", ")}.`);
|
|
2160
|
+
}
|
|
2161
|
+
return normalized;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2052
2164
|
function defaultDailyRunKey() {
|
|
2053
2165
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
2054
2166
|
timeZone: "Asia/Bangkok",
|
|
@@ -2265,6 +2377,79 @@ function printAdminMonthlyMediaCosts(monthlyMediaCosts) {
|
|
|
2265
2377
|
|
|
2266
2378
|
function printAdminResult({ operation, result }) {
|
|
2267
2379
|
const data = result?.data || {};
|
|
2380
|
+
if (Array.isArray(data.applications)) {
|
|
2381
|
+
console.log(`${data.applications.length} sponsor application(s):`);
|
|
2382
|
+
for (const application of data.applications) {
|
|
2383
|
+
console.log(
|
|
2384
|
+
` #${application.id} ${String(application.status).toUpperCase()} · ${application.username || `user ${application.userId}`} · agreement ${application.agreementVersion || "unknown"}`,
|
|
2385
|
+
);
|
|
2386
|
+
}
|
|
2387
|
+
return;
|
|
2388
|
+
}
|
|
2389
|
+
if (data.application) {
|
|
2390
|
+
console.log(
|
|
2391
|
+
`Sponsor application #${data.application.id}: ${String(data.application.status).toUpperCase()} · ${data.application.username || `user ${data.application.userId}`}`,
|
|
2392
|
+
);
|
|
2393
|
+
return;
|
|
2394
|
+
}
|
|
2395
|
+
if (data.sponsor) {
|
|
2396
|
+
console.log(
|
|
2397
|
+
`Sponsor ${data.sponsor.username || `user ${data.sponsor.userId}`}: ${String(data.sponsor.status).toUpperCase()}.`,
|
|
2398
|
+
);
|
|
2399
|
+
return;
|
|
2400
|
+
}
|
|
2401
|
+
if (data.scan) {
|
|
2402
|
+
const scan = data.scan;
|
|
2403
|
+
console.log(
|
|
2404
|
+
`Sponsor-integrity scan #${scan.id || "not started"}: ${scan.status || "not started"} · ${scan.scannedCount || 0} scanned · ${scan.selectedReviewCount || 0} selected · ${data.openCaseCount || 0} open.`,
|
|
2405
|
+
);
|
|
2406
|
+
if (Array.isArray(data.casesCreated) && data.casesCreated.length > 0) {
|
|
2407
|
+
console.log(
|
|
2408
|
+
`Created ${data.casesCreated.length} review case(s) on this page.`,
|
|
2409
|
+
);
|
|
2410
|
+
}
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
if (Array.isArray(data.cases)) {
|
|
2414
|
+
console.log(`${data.cases.length} sponsor-integrity case(s):`);
|
|
2415
|
+
for (const item of data.cases) {
|
|
2416
|
+
const flags =
|
|
2417
|
+
Array.isArray(item.hardFlags) && item.hardFlags.length
|
|
2418
|
+
? ` · flags=${item.hardFlags.join(",")}`
|
|
2419
|
+
: "";
|
|
2420
|
+
console.log(
|
|
2421
|
+
` #${item.id} ${String(item.status).toUpperCase()} · job #${item.jobId} · ${item.sponsorUsername || `sponsor ${item.sponsorUserId}`}${flags}`,
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
if (data.case) {
|
|
2427
|
+
const item = data.case;
|
|
2428
|
+
console.log(
|
|
2429
|
+
`Sponsor-integrity case #${item.id}: ${String(item.status).toUpperCase()} · job #${item.job?.id} · ${item.sponsorUsername || `sponsor ${item.sponsorUserId}`}.`,
|
|
2430
|
+
);
|
|
2431
|
+
const relays = Array.isArray(item.relays) && item.relays.length > 0
|
|
2432
|
+
? item.relays
|
|
2433
|
+
: item.relay
|
|
2434
|
+
? [item.relay]
|
|
2435
|
+
: [];
|
|
2436
|
+
console.log(
|
|
2437
|
+
`Relays: ${relays.map((relay) => `${relay.kind || "initial_request"}: ${relay.summary || "(missing)"}`).join(" | ") || "(missing)"}`,
|
|
2438
|
+
);
|
|
2439
|
+
console.log(
|
|
2440
|
+
`Provenance: ${(item.agents || []).map((agent) => `${agent.role}:${agent.provider}:${agent.resolvedModel || "unresolved"}:${agent.resolvedEffort || "unresolved"}`).join(" · ") || "missing"}`,
|
|
2441
|
+
);
|
|
2442
|
+
console.log(
|
|
2443
|
+
`Artifact files: ${(item.artifactFiles || []).map((file) => file.path).join(", ") || "none"}`,
|
|
2444
|
+
);
|
|
2445
|
+
return;
|
|
2446
|
+
}
|
|
2447
|
+
if (data.caseId) {
|
|
2448
|
+
console.log(
|
|
2449
|
+
`Sponsor-integrity case #${data.caseId}: ${data.decision}${data.scanCompleted ? "; scan complete" : ""}.`,
|
|
2450
|
+
);
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2268
2453
|
if (data.review) {
|
|
2269
2454
|
console.log(
|
|
2270
2455
|
`Confirmed Build #${data.review.buildId} runtime at published artifact #${data.review.publishedArtifactVersionId}.`,
|
|
@@ -2313,6 +2498,11 @@ function printAdminResult({ operation, result }) {
|
|
|
2313
2498
|
);
|
|
2314
2499
|
}
|
|
2315
2500
|
printTodoItems(report.carryoverTodos?.items || [], "Unfinished work");
|
|
2501
|
+
if (report.sponsorIntegrity) {
|
|
2502
|
+
console.log(
|
|
2503
|
+
`Sponsor integrity: ${report.sponsorIntegrity.scan?.status || "not started"}; ${report.sponsorIntegrity.cases?.open || 0} open case(s); ${report.sponsorIntegrity.pendingApplications || 0} pending application(s).`,
|
|
2504
|
+
);
|
|
2505
|
+
}
|
|
2316
2506
|
const surfaces = report.brief?.engagementPulse?.surfaces;
|
|
2317
2507
|
if (surfaces && typeof surfaces === "object") {
|
|
2318
2508
|
const deltas = Object.entries(surfaces)
|
|
@@ -79,8 +79,12 @@ export async function runClaudeCodeAgentPass({
|
|
|
79
79
|
args,
|
|
80
80
|
cwd: isolationDir,
|
|
81
81
|
streamJson: true,
|
|
82
|
+
signal: options.agentAbortSignal,
|
|
82
83
|
});
|
|
83
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
finalText: result.finalText,
|
|
86
|
+
provenance: result.provenance,
|
|
87
|
+
};
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
export async function reviewClaudeCodeAgentLoop({
|
|
@@ -96,6 +100,7 @@ export async function reviewClaudeCodeAgentLoop({
|
|
|
96
100
|
binary,
|
|
97
101
|
cwd: isolationDir,
|
|
98
102
|
streamJson: false,
|
|
103
|
+
signal: options.agentAbortSignal,
|
|
99
104
|
args: [
|
|
100
105
|
"-p",
|
|
101
106
|
"--output-format",
|
|
@@ -129,16 +134,22 @@ export async function reviewClaudeCodeAgentLoop({
|
|
|
129
134
|
}
|
|
130
135
|
}
|
|
131
136
|
|
|
132
|
-
async function runClaudeProcess({ binary, args, cwd, streamJson }) {
|
|
137
|
+
async function runClaudeProcess({ binary, args, cwd, streamJson, signal }) {
|
|
133
138
|
const child = spawn(binary, args, {
|
|
134
139
|
cwd,
|
|
135
140
|
stdio: ["ignore", "pipe", "pipe"],
|
|
136
141
|
env: createSubscriptionAgentEnvironment(),
|
|
142
|
+
...(signal ? { signal } : {}),
|
|
137
143
|
});
|
|
138
144
|
child.stderr.on("data", (chunk) => process.stderr.write(chunk));
|
|
139
145
|
let finalText = "";
|
|
140
146
|
let structuredOutput = null;
|
|
141
147
|
let rawOutput = "";
|
|
148
|
+
let providerModel = null;
|
|
149
|
+
let providerEffort = null;
|
|
150
|
+
let providerUsage = {};
|
|
151
|
+
let providerServiceTier = null;
|
|
152
|
+
let providerRuntimeVersion = null;
|
|
142
153
|
|
|
143
154
|
if (streamJson) {
|
|
144
155
|
const lines = readline.createInterface({
|
|
@@ -155,13 +166,53 @@ async function runClaudeProcess({ binary, args, cwd, streamJson }) {
|
|
|
155
166
|
return;
|
|
156
167
|
}
|
|
157
168
|
if (event?.type === "assistant") {
|
|
169
|
+
providerModel =
|
|
170
|
+
firstNonEmptyString(event.message?.model, providerModel) || null;
|
|
171
|
+
providerEffort =
|
|
172
|
+
firstNonEmptyString(
|
|
173
|
+
event.message?.effort,
|
|
174
|
+
event.message?.reasoning_effort,
|
|
175
|
+
providerEffort,
|
|
176
|
+
) || null;
|
|
177
|
+
providerUsage = mergeUsageEvidence(providerUsage, event.message?.usage);
|
|
158
178
|
const texts = (event.message?.content || [])
|
|
159
179
|
.filter((block) => block?.type === "text")
|
|
160
180
|
.map((block) => String(block.text || "").trim())
|
|
161
181
|
.filter(Boolean);
|
|
162
182
|
if (texts.length > 0) finalText = texts.join("\n\n");
|
|
163
183
|
}
|
|
184
|
+
if (event?.type === "system") {
|
|
185
|
+
providerModel = firstNonEmptyString(event.model, providerModel) || null;
|
|
186
|
+
providerRuntimeVersion =
|
|
187
|
+
firstNonEmptyString(
|
|
188
|
+
event.claude_code_version,
|
|
189
|
+
event.claudeCodeVersion,
|
|
190
|
+
event.version,
|
|
191
|
+
providerRuntimeVersion,
|
|
192
|
+
) || null;
|
|
193
|
+
}
|
|
164
194
|
if (event?.type === "result") {
|
|
195
|
+
providerModel = firstNonEmptyString(event.model, providerModel) || null;
|
|
196
|
+
providerEffort =
|
|
197
|
+
firstNonEmptyString(
|
|
198
|
+
event.effort,
|
|
199
|
+
event.reasoning_effort,
|
|
200
|
+
providerEffort,
|
|
201
|
+
) || null;
|
|
202
|
+
providerServiceTier =
|
|
203
|
+
firstNonEmptyString(
|
|
204
|
+
event.service_tier,
|
|
205
|
+
event.serviceTier,
|
|
206
|
+
providerServiceTier,
|
|
207
|
+
) || null;
|
|
208
|
+
providerUsage = mergeUsageEvidence(providerUsage, event.usage);
|
|
209
|
+
providerRuntimeVersion =
|
|
210
|
+
firstNonEmptyString(
|
|
211
|
+
event.claude_code_version,
|
|
212
|
+
event.claudeCodeVersion,
|
|
213
|
+
event.version,
|
|
214
|
+
providerRuntimeVersion,
|
|
215
|
+
) || null;
|
|
165
216
|
if (typeof event.result === "string" && event.result.trim()) {
|
|
166
217
|
finalText = event.result.trim();
|
|
167
218
|
}
|
|
@@ -200,8 +251,58 @@ async function runClaudeProcess({ binary, args, cwd, streamJson }) {
|
|
|
200
251
|
}
|
|
201
252
|
finalText = String(parsed?.result || "").trim();
|
|
202
253
|
structuredOutput = parsed?.structured_output || null;
|
|
254
|
+
providerModel = firstNonEmptyString(parsed?.model) || null;
|
|
255
|
+
providerEffort =
|
|
256
|
+
firstNonEmptyString(parsed?.effort, parsed?.reasoning_effort) || null;
|
|
257
|
+
providerServiceTier =
|
|
258
|
+
firstNonEmptyString(parsed?.service_tier, parsed?.serviceTier) || null;
|
|
259
|
+
providerRuntimeVersion =
|
|
260
|
+
firstNonEmptyString(
|
|
261
|
+
parsed?.claude_code_version,
|
|
262
|
+
parsed?.claudeCodeVersion,
|
|
263
|
+
parsed?.version,
|
|
264
|
+
) || null;
|
|
265
|
+
providerUsage = mergeUsageEvidence(providerUsage, parsed?.usage);
|
|
203
266
|
}
|
|
204
|
-
return {
|
|
267
|
+
return {
|
|
268
|
+
finalText,
|
|
269
|
+
structuredOutput,
|
|
270
|
+
provenance: {
|
|
271
|
+
resolvedModel: providerModel,
|
|
272
|
+
resolvedEffort: providerEffort,
|
|
273
|
+
resolvedServiceTier: providerServiceTier,
|
|
274
|
+
runtimeVersion: providerRuntimeVersion,
|
|
275
|
+
evidenceTier:
|
|
276
|
+
providerModel || providerEffort || providerServiceTier
|
|
277
|
+
? "provider_reported"
|
|
278
|
+
: "requested_only",
|
|
279
|
+
usage: providerUsage,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function firstNonEmptyString(...values) {
|
|
285
|
+
for (const value of values) {
|
|
286
|
+
const normalized = String(value || "").trim();
|
|
287
|
+
if (normalized) return normalized.slice(0, 160);
|
|
288
|
+
}
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function mergeUsageEvidence(current, value) {
|
|
293
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
294
|
+
return current;
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
...current,
|
|
298
|
+
...Object.fromEntries(
|
|
299
|
+
Object.entries(value)
|
|
300
|
+
.filter(([, amount]) =>
|
|
301
|
+
["number", "string", "boolean"].includes(typeof amount),
|
|
302
|
+
)
|
|
303
|
+
.slice(0, 30),
|
|
304
|
+
),
|
|
305
|
+
};
|
|
205
306
|
}
|
|
206
307
|
|
|
207
308
|
function normalizeProviderLaunchError(error, binary) {
|