@stage5/lumine 0.2.53 → 0.2.55

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.
@@ -77,7 +77,10 @@ export async function runCodexAgentPass({
77
77
  tools: runtime.tools,
78
78
  toolSession,
79
79
  });
80
- return { finalText: result.finalText };
80
+ return {
81
+ finalText: result.finalText,
82
+ provenance: result.provenance,
83
+ };
81
84
  }
82
85
 
83
86
  export async function reviewCodexAgentLoop({
@@ -120,6 +123,7 @@ async function runCodexAppServerTurn({
120
123
  binary,
121
124
  cwd: isolationDir,
122
125
  environment: providerEnvironment,
126
+ signal: options.agentAbortSignal,
123
127
  });
124
128
  } catch (error) {
125
129
  throw normalizeProviderLaunchError(error, binary, "Codex");
@@ -128,12 +132,22 @@ async function runCodexAppServerTurn({
128
132
  cwd: isolationDir,
129
133
  stdio: ["pipe", "pipe", "pipe"],
130
134
  env: providerEnvironment,
135
+ ...(options.agentAbortSignal ? { signal: options.agentAbortSignal } : {}),
131
136
  });
132
137
  child.stderr.on("data", (chunk) => process.stderr.write(chunk));
138
+ let childInputError = null;
139
+ child.stdin.on("error", (error) => {
140
+ childInputError ||= error;
141
+ });
133
142
 
134
143
  let nextRequestId = 1;
135
144
  const pendingRequests = new Map();
136
145
  let completedTurn = null;
146
+ let initializedServer = null;
147
+ let startedThreadResult = null;
148
+ let latestThreadSettings = null;
149
+ let latestTokenUsage = null;
150
+ let reroutedModel = null;
137
151
  let completedTurnResolve;
138
152
  const completedTurnPromise = new Promise((resolve) => {
139
153
  completedTurnResolve = resolve;
@@ -143,10 +157,19 @@ async function runCodexAppServerTurn({
143
157
  const inheritedMcpServers = new Set();
144
158
 
145
159
  function send(message) {
160
+ if (childInputError || child.stdin.destroyed || child.stdin.writableEnded) {
161
+ return false;
162
+ }
146
163
  // App-server uses JSON-RPC semantics but intentionally omits the jsonrpc
147
164
  // header on its JSONL wire format.
148
165
  const { jsonrpc: _jsonrpc, ...wireMessage } = message;
149
- child.stdin.write(`${JSON.stringify(wireMessage)}\n`);
166
+ try {
167
+ child.stdin.write(`${JSON.stringify(wireMessage)}\n`);
168
+ return true;
169
+ } catch (error) {
170
+ childInputError ||= error;
171
+ return false;
172
+ }
150
173
  }
151
174
 
152
175
  function request(method, params) {
@@ -154,7 +177,10 @@ async function runCodexAppServerTurn({
154
177
  nextRequestId += 1;
155
178
  return new Promise((resolve, reject) => {
156
179
  pendingRequests.set(id, { resolve, reject });
157
- send({ jsonrpc: "2.0", id, method, params });
180
+ if (!send({ jsonrpc: "2.0", id, method, params })) {
181
+ pendingRequests.delete(id);
182
+ reject(new Error("Codex app-server input closed."));
183
+ }
158
184
  });
159
185
  }
160
186
 
@@ -199,9 +225,7 @@ async function runCodexAppServerTurn({
199
225
  jsonrpc: "2.0",
200
226
  id: message.id,
201
227
  result: {
202
- contentItems: [
203
- { type: "inputText", text: JSON.stringify(output) },
204
- ],
228
+ contentItems: [{ type: "inputText", text: JSON.stringify(output) }],
205
229
  success,
206
230
  },
207
231
  });
@@ -269,6 +293,18 @@ async function runCodexAppServerTurn({
269
293
  }
270
294
  return;
271
295
  }
296
+ if (message.method === "thread/settings/updated") {
297
+ latestThreadSettings = message.params?.threadSettings || null;
298
+ return;
299
+ }
300
+ if (message.method === "thread/tokenUsage/updated") {
301
+ latestTokenUsage = message.params?.tokenUsage || null;
302
+ return;
303
+ }
304
+ if (message.method === "model/rerouted") {
305
+ reroutedModel = firstNonEmptyString(message.params?.toModel);
306
+ return;
307
+ }
272
308
  if (
273
309
  message.method === "item/completed" &&
274
310
  message.params?.item?.type === "agentMessage"
@@ -296,7 +332,7 @@ async function runCodexAppServerTurn({
296
332
  });
297
333
 
298
334
  try {
299
- await Promise.race([
335
+ initializedServer = await Promise.race([
300
336
  request("initialize", {
301
337
  clientInfo: {
302
338
  name: "lumine-cli",
@@ -312,6 +348,7 @@ async function runCodexAppServerTurn({
312
348
  send({ jsonrpc: "2.0", method: "initialized", params: {} });
313
349
  const threadResult = await request("thread/start", {
314
350
  ...(options.model ? { model: options.model } : {}),
351
+ ...(options.serviceTier ? { serviceTier: options.serviceTier } : {}),
315
352
  cwd: isolationDir,
316
353
  approvalPolicy: "never",
317
354
  sandbox: "read-only",
@@ -322,6 +359,7 @@ async function runCodexAppServerTurn({
322
359
  developerInstructions,
323
360
  dynamicTools: toCodexDynamicTools(tools),
324
361
  });
362
+ startedThreadResult = threadResult || null;
325
363
  const threadId = String(threadResult?.thread?.id || "");
326
364
  if (!threadId) throw new Error("Codex did not return a thread ID.");
327
365
  await delay(250);
@@ -330,9 +368,8 @@ async function runCodexAppServerTurn({
330
368
  detail: "toolsAndAuthOnly",
331
369
  limit: 100,
332
370
  });
333
- const inheritedToolServers = (Array.isArray(mcpInventory?.data)
334
- ? mcpInventory.data
335
- : []
371
+ const inheritedToolServers = (
372
+ Array.isArray(mcpInventory?.data) ? mcpInventory.data : []
336
373
  )
337
374
  .filter((server) => Object.keys(server?.tools || {}).length > 0)
338
375
  .map((server) => String(server?.name || "").trim())
@@ -364,7 +401,44 @@ async function runCodexAppServerTurn({
364
401
  }
365
402
  const finalText =
366
403
  completedMessages.at(-1) || String(deltaText || "").trim();
367
- return { finalText };
404
+ const resolvedModel = firstNonEmptyString(
405
+ reroutedModel,
406
+ latestThreadSettings?.model,
407
+ startedThreadResult?.model,
408
+ startedThreadResult?.thread?.model,
409
+ );
410
+ const resolvedEffort = firstNonEmptyString(
411
+ latestThreadSettings?.effort,
412
+ startedThreadResult?.reasoningEffort,
413
+ startedThreadResult?.thread?.effort,
414
+ startedThreadResult?.thread?.reasoningEffort,
415
+ );
416
+ const resolvedServiceTier = firstNonEmptyString(
417
+ latestThreadSettings?.serviceTier,
418
+ startedThreadResult?.serviceTier,
419
+ startedThreadResult?.thread?.serviceTier,
420
+ startedThreadResult?.thread?.service_tier,
421
+ );
422
+ return {
423
+ finalText,
424
+ provenance: {
425
+ resolvedModel,
426
+ resolvedEffort,
427
+ resolvedServiceTier,
428
+ runtimeVersion: firstNonEmptyString(
429
+ initializedServer?.userAgent,
430
+ initializedServer?.serverInfo?.version,
431
+ initializedServer?.server_info?.version,
432
+ ),
433
+ evidenceTier:
434
+ resolvedModel || resolvedEffort || resolvedServiceTier
435
+ ? "runtime_observed"
436
+ : "requested_only",
437
+ usage: normalizeUsageEvidence(
438
+ latestTokenUsage?.last || latestTokenUsage?.total || turn?.usage,
439
+ ),
440
+ },
441
+ };
368
442
  } catch (error) {
369
443
  throw normalizeProviderLaunchError(error, binary, "Codex");
370
444
  } finally {
@@ -378,13 +452,33 @@ async function runCodexAppServerTurn({
378
452
  }
379
453
  }
380
454
 
381
- async function loadCodexIsolationArgs({ binary, cwd, environment }) {
455
+ function firstNonEmptyString(...values) {
456
+ for (const value of values) {
457
+ const normalized = String(value || "").trim();
458
+ if (normalized) return normalized.slice(0, 160);
459
+ }
460
+ return null;
461
+ }
462
+
463
+ function normalizeUsageEvidence(value) {
464
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
465
+ return Object.fromEntries(
466
+ Object.entries(value)
467
+ .filter(([, amount]) =>
468
+ ["number", "string", "boolean"].includes(typeof amount),
469
+ )
470
+ .slice(0, 30),
471
+ );
472
+ }
473
+
474
+ async function loadCodexIsolationArgs({ binary, cwd, environment, signal }) {
382
475
  const [featureOutput, mcpOutput] = await Promise.all([
383
476
  runCodexInspectionCommand({
384
477
  binary,
385
478
  args: ["features", "list"],
386
479
  cwd,
387
480
  environment,
481
+ signal,
388
482
  label: "feature inventory",
389
483
  }),
390
484
  runCodexInspectionCommand({
@@ -392,13 +486,19 @@ async function loadCodexIsolationArgs({ binary, cwd, environment }) {
392
486
  args: ["mcp", "list", "--json"],
393
487
  cwd,
394
488
  environment,
489
+ signal,
395
490
  label: "MCP inventory",
396
491
  }),
397
492
  ]);
398
493
  const availableFeatures = new Set(
399
494
  featureOutput
400
495
  .split("\n")
401
- .map((line) => String(line || "").trim().split(/\s+/)[0])
496
+ .map(
497
+ (line) =>
498
+ String(line || "")
499
+ .trim()
500
+ .split(/\s+/)[0],
501
+ )
402
502
  .filter((name) => /^[a-z][a-z0-9_]*$/.test(name)),
403
503
  );
404
504
  let mcpServers;
@@ -437,6 +537,7 @@ async function runCodexInspectionCommand({
437
537
  args,
438
538
  cwd,
439
539
  environment,
540
+ signal,
440
541
  label,
441
542
  }) {
442
543
  return await new Promise((resolve, reject) => {
@@ -444,13 +545,16 @@ async function runCodexInspectionCommand({
444
545
  cwd,
445
546
  stdio: ["ignore", "pipe", "pipe"],
446
547
  env: environment,
548
+ ...(signal ? { signal } : {}),
447
549
  });
448
550
  let stdout = "";
449
551
  let settled = false;
450
552
  const timeout = setTimeout(() => {
451
553
  child.kill();
452
554
  finish(
453
- new Error(`Codex ${label} timed out; Lumine cannot prove tool isolation.`),
555
+ new Error(
556
+ `Codex ${label} timed out; Lumine cannot prove tool isolation.`,
557
+ ),
454
558
  );
455
559
  }, CODEX_PREFLIGHT_TIMEOUT_MS);
456
560
  const finish = (error, value = "") => {
package/lib/agent.js CHANGED
@@ -112,6 +112,8 @@ export async function agentCommand(options, { saveWorkspace }) {
112
112
  let currentFiles = workspace.files;
113
113
  let scopeBaseFiles = workspace.files;
114
114
  let finalText = "";
115
+ const providerRuns = [];
116
+ let saveResult = null;
115
117
  let scopeRepairAttempts = 0;
116
118
  let validationRepairAttempts = 0;
117
119
 
@@ -160,6 +162,10 @@ export async function agentCommand(options, { saveWorkspace }) {
160
162
  },
161
163
  });
162
164
  finalText = String(providerResult?.finalText || "").trim() || finalText;
165
+ providerRuns.push({
166
+ phase: runtime.phase,
167
+ ...(providerResult?.provenance || {}),
168
+ });
163
169
  await appendAgentTrace(artifacts.traceFile, {
164
170
  type: "provider_completed",
165
171
  provider: provider.id,
@@ -257,7 +263,7 @@ export async function agentCommand(options, { saveWorkspace }) {
257
263
 
258
264
  const changedPaths = listChangedPaths(workspace.files, currentFiles);
259
265
  if (changedPaths.length > 0) {
260
- await saveWorkspace({
266
+ saveResult = await saveWorkspace({
261
267
  ...options,
262
268
  dir: workspace.dir,
263
269
  publish: false,
@@ -304,6 +310,8 @@ export async function agentCommand(options, { saveWorkspace }) {
304
310
  finalText,
305
311
  traceFile: artifacts.traceFile,
306
312
  feedbackFile: feedback ? artifacts.feedbackFile : null,
313
+ saveResult,
314
+ providerRuns,
307
315
  };
308
316
  } catch (error) {
309
317
  await appendAgentTrace(artifacts.traceFile, {
package/lib/auth.js CHANGED
@@ -6,6 +6,9 @@ import { requestJson } from "./http.js";
6
6
  import { sleep, trimTrailingSlash } from "./util.js";
7
7
 
8
8
  export async function login(options) {
9
+ const report = options.loginProgressToStderr
10
+ ? (...args) => console.error(...args)
11
+ : (...args) => console.log(...args);
9
12
  const start = await requestJson({
10
13
  method: "POST",
11
14
  url: `${options.apiUrl}/cli/device/start`,
@@ -23,15 +26,15 @@ export async function login(options) {
23
26
  });
24
27
 
25
28
  const approvalUrl = start.verificationUriComplete || start.verificationUri;
26
- console.log("Connect Lumine CLI to Twinkle.");
29
+ report("Connect Lumine CLI to Twinkle.");
27
30
  if (options.openBrowser && approvalUrl) {
28
- console.log("Opening Twinkle in your browser...");
31
+ report("Opening Twinkle in your browser...");
29
32
  const opened = await openBrowser(approvalUrl);
30
- if (!opened) console.log("Could not open the browser automatically.");
33
+ if (!opened) report("Could not open the browser automatically.");
31
34
  }
32
- console.log(`Approval link: ${approvalUrl}`);
33
- console.log(`Code: ${start.userCode}`);
34
- console.log("Leave this terminal open. Waiting for approval...");
35
+ report(`Approval link: ${approvalUrl}`);
36
+ report(`Code: ${start.userCode}`);
37
+ report("Leave this terminal open. Waiting for approval...");
35
38
 
36
39
  const intervalMs = Math.max(Number(start.interval || 3), 1) * 1000;
37
40
  const startedAt = Date.now();
@@ -52,10 +55,10 @@ export async function login(options) {
52
55
  userId: tokenResponse.user?.id || null,
53
56
  expiresAt: Date.now() + Number(tokenResponse.expiresIn || 0) * 1000,
54
57
  });
55
- console.log(
58
+ report(
56
59
  `Logged in${tokenResponse.user?.username ? ` as ${tokenResponse.user.username}` : ""}.`,
57
60
  );
58
- console.log("You can now run `lumine` to choose a project.");
61
+ report("You can now run `lumine` to choose a project.");
59
62
  return;
60
63
  }
61
64
 
@@ -112,7 +115,10 @@ export async function ensureAuth(options) {
112
115
  } catch (error) {
113
116
  if (options.authToken || !isMissingLoginError(error)) throw error;
114
117
  }
115
- await login(options);
118
+ await login({
119
+ ...options,
120
+ loginProgressToStderr: Boolean(options.json),
121
+ });
116
122
  return await resolveAuth(options);
117
123
  }
118
124
 
package/lib/commands.js CHANGED
@@ -67,6 +67,7 @@ import { runBuildForumCommand } from "./forum.js";
67
67
  import { agentCommand } from "./agent.js";
68
68
  import { agentMcpCommand } from "./agent/mcp-server.js";
69
69
  import { appMcpCommand } from "./app-mcp/server.js";
70
+ import { sponsorCommand } from "./sponsor.js";
70
71
  import {
71
72
  defaultMainCheckoutDir,
72
73
  defaultReferenceDir,
@@ -130,6 +131,14 @@ export async function main() {
130
131
  return;
131
132
  }
132
133
 
134
+ if (options.command === "sponsor") {
135
+ await sponsorCommand(options, {
136
+ saveWorkspace: save,
137
+ pullWorkspace: pullBuildFiles,
138
+ });
139
+ return;
140
+ }
141
+
133
142
  if (options.command === "agent") {
134
143
  await agentCommand(options, { saveWorkspace: save });
135
144
  return;
@@ -1122,7 +1131,7 @@ export async function save(options) {
1122
1131
  console.log(
1123
1132
  "Saved to your branch. The project owner can merge or replace main from Twinkle.",
1124
1133
  );
1125
- return;
1134
+ return result;
1126
1135
  }
1127
1136
  const publish = await publishBuild({ options, buildId, auth });
1128
1137
  if (publish.skipped) {
@@ -1135,6 +1144,7 @@ export async function save(options) {
1135
1144
  );
1136
1145
  console.log(`App: ${options.siteUrl}/app/${buildId}`);
1137
1146
  }
1147
+ return result;
1138
1148
  }
1139
1149
 
1140
1150
  export async function check(options) {
@@ -1502,32 +1512,34 @@ export async function pullBuildFiles({ options, auth, buildId }) {
1502
1512
  // exists (the workspace UI gives Lumine the same context). Never fail the
1503
1513
  // pull over it.
1504
1514
  let assetCount = null;
1505
- try {
1506
- const { token } = await mintBuildApiToken({
1507
- options,
1508
- auth,
1509
- buildId: Number(build.id || buildId),
1510
- scopes: ["files:read"],
1511
- });
1512
- const { assets, usage, projectAssets } = await fetchAllRuntimeAssets({
1513
- options,
1514
- auth,
1515
- buildId: Number(build.id || buildId),
1516
- buildApiToken: token,
1517
- });
1518
- await writeAssetsManifest({
1519
- dir,
1520
- buildId: Number(build.id || buildId),
1521
- assets,
1522
- usage,
1523
- projectAssets,
1524
- codeFiles: files,
1525
- });
1526
- assetCount = assets.length + (projectAssets?.length || 0);
1527
- } catch (error) {
1528
- console.error(
1529
- `lumine: skipped ${ASSETS_METADATA_FILE} (${error?.message || error})`,
1530
- );
1515
+ if (!options.skipAssetManifest) {
1516
+ try {
1517
+ const { token } = await mintBuildApiToken({
1518
+ options,
1519
+ auth,
1520
+ buildId: Number(build.id || buildId),
1521
+ scopes: ["files:read"],
1522
+ });
1523
+ const { assets, usage, projectAssets } = await fetchAllRuntimeAssets({
1524
+ options,
1525
+ auth,
1526
+ buildId: Number(build.id || buildId),
1527
+ buildApiToken: token,
1528
+ });
1529
+ await writeAssetsManifest({
1530
+ dir,
1531
+ buildId: Number(build.id || buildId),
1532
+ assets,
1533
+ usage,
1534
+ projectAssets,
1535
+ codeFiles: files,
1536
+ });
1537
+ assetCount = assets.length + (projectAssets?.length || 0);
1538
+ } catch (error) {
1539
+ console.error(
1540
+ `lumine: skipped ${ASSETS_METADATA_FILE} (${error?.message || error})`,
1541
+ );
1542
+ }
1531
1543
  }
1532
1544
  return {
1533
1545
  build,
@@ -2249,6 +2261,7 @@ export function parseArgs(args) {
2249
2261
  "includeLegacy",
2250
2262
  "includePrivateEvidence",
2251
2263
  "noReviewLoop",
2264
+ "acceptAgreement",
2252
2265
  ]);
2253
2266
 
2254
2267
  for (let i = 0; i < rest.length; i += 1) {
@@ -2366,10 +2379,33 @@ export function parseArgs(args) {
2366
2379
  adminReviewContext: raw.reviewContext ? String(raw.reviewContext) : "",
2367
2380
  adminSeverity: raw.severity ? String(raw.severity) : "",
2368
2381
  adminStatus: raw.status ? String(raw.status) : "",
2382
+ adminDecision: raw.decision ? String(raw.decision) : "",
2369
2383
  adminWaitMs: raw.waitMs ? String(raw.waitMs) : "",
2370
2384
  adminBrowserPath: raw.browserPath ? String(raw.browserPath) : "",
2371
2385
  adminEffort: raw.effort ? String(raw.effort) : "",
2372
2386
  agentEffort: command === "agent" && raw.effort ? String(raw.effort) : "",
2387
+ sponsorArgs: command === "sponsor" ? positional : [],
2388
+ sponsorMotivation: raw.motivation ? String(raw.motivation) : "",
2389
+ sponsorAvailability: raw.availability ? String(raw.availability) : "",
2390
+ sponsorProviders: raw.providers
2391
+ ? String(raw.providers)
2392
+ : raw.provider
2393
+ ? String(raw.provider)
2394
+ : "",
2395
+ sponsorCapabilityNotes: raw.capabilityNotes
2396
+ ? String(raw.capabilityNotes)
2397
+ : "",
2398
+ sponsorAcceptAgreement: Boolean(raw.acceptAgreement),
2399
+ sponsorPersona: raw.persona ? String(raw.persona) : "",
2400
+ sponsorConcurrency: raw.concurrency,
2401
+ sponsorHelpers: raw.helpers,
2402
+ sponsorDailyLimit: raw.dailyLimit,
2403
+ sponsorWeeklyLimit: raw.weeklyLimit,
2404
+ sponsorEffort: raw.effort ? String(raw.effort) : "",
2405
+ sponsorServiceTier: raw.serviceTier ? String(raw.serviceTier) : "",
2406
+ sponsorPollMs: raw.pollMs
2407
+ ? Math.min(Math.max(Number(raw.pollMs) || 0, 1_000), 60_000)
2408
+ : 3_000,
2373
2409
  provider: raw.provider ? String(raw.provider) : "",
2374
2410
  providerPath: raw.providerPath ? String(raw.providerPath) : "",
2375
2411
  agentPrompt:
@@ -2692,6 +2728,11 @@ export function printHelp() {
2692
2728
  lumine whoami
2693
2729
  lumine logout
2694
2730
  lumine agent --provider <codex|claude-code> "<build request>"
2731
+ lumine sponsor agreement|apply|status|withdraw
2732
+ lumine sponsor capacity [--concurrency <n>] [--helpers <n>] [--daily-limit <n>] [--weekly-limit <n>]
2733
+ lumine sponsor duty start <zero|ciel> --provider <codex|claude-code> [--model <name>] [--effort <level>] [--service-tier <tier>]
2734
+ lumine sponsor duty pause|resume|stop <zero|ciel>
2735
+ lumine sponsor jobs
2695
2736
  lumine app-mcp <published-app-url-or-id> [--no-open]
2696
2737
  lumine new [title]
2697
2738
  lumine rename [title] [--target <twinkle-build-url-or-id>]
@@ -2750,6 +2791,13 @@ export function printHelp() {
2750
2791
  lumine admin todo list [--status pending|open|in_progress|blocked|completed|cancelled|all] [--limit <number>] [--json]
2751
2792
  lumine admin todo add --title <title> --note <handoff-and-acceptance-criteria> [--kind task|experiment] [--status open|in_progress|blocked] [--json]
2752
2793
  lumine admin todo update <todo-id> --status open|in_progress|blocked|completed|cancelled --note <progress-or-evidence> [--json]
2794
+ lumine admin sponsor applications list [--status pending|approved|rejected|withdrawn] [--json]
2795
+ lumine admin sponsor applications review <application-id> --decision approve|reject [--note <text>] [--json]
2796
+ lumine admin sponsor status set <user-id> --status probationary|trusted|suspended|revoked [--note <text>] [--json]
2797
+ lumine admin sponsor integrity status|scan [--json]
2798
+ lumine admin sponsor integrity cases [--status open|pending|held|flagged|cleared|disqualified] [--json]
2799
+ lumine admin sponsor integrity get <case-id> [--json]
2800
+ lumine admin sponsor integrity review <case-id> --decision clear|hold|flag|disqualify [--note <evidence>] [--json]
2753
2801
  lumine admin recommendations list [--since-run|--after <date>|--include-legacy] [--all --checkpoint <file> [--resume]] [--content-types comment,dailyReflection] [--unviewed|--viewed] [--cursor <cursor>] [--json]
2754
2802
  lumine admin builds candidates [--all --checkpoint <file> [--resume]] [--cursor <cursor>] [--limit <number>] [--json]
2755
2803
  lumine admin builds review <build-url-or-id> [--output-dir <dir>] [--wait-ms <ms>] [--browser-path <path>] [--json]
@@ -2859,7 +2907,7 @@ Options:
2859
2907
  --since-run Recommendations since the previous completed run (default)
2860
2908
  --include-legacy Explicitly include the full historical recommendation queue
2861
2909
  --all Automatically read every page in the canonical snapshot
2862
- --checkpoint <file> Persist automatic pagination or batch progress
2910
+ --checkpoint <file> Persist bounded pagination metadata or batch progress
2863
2911
  --resume Resume the exact request recorded by --checkpoint
2864
2912
  --output <file> Save an admin result or newspaper claim as JSON
2865
2913
  --output-dir <dir> Directory for managed Build review evidence
package/lib/constants.js CHANGED
@@ -371,6 +371,7 @@ export const MAIN_CHECKOUT_READONLY_COMMANDS = new Set([
371
371
  export const COMMANDS = new Set([
372
372
  "admin",
373
373
  "agent",
374
+ "sponsor",
374
375
  "agent-mcp",
375
376
  "app-mcp",
376
377
  "workspace",