@stage5/lumine 0.2.55 → 0.2.57
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 +12 -8
- package/lib/commands.js +2 -2
- package/lib/sponsor.js +87 -34
- package/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +3 -1
package/README.md
CHANGED
|
@@ -229,7 +229,7 @@ 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
|
|
232
|
+
## Sponsoring shared Zero and Ciel Build Workshop help
|
|
233
233
|
|
|
234
234
|
Sponsorship is an approved contribution role, not an open switch and not a
|
|
235
235
|
website-management role. Applications are submitted only through Lumine CLI;
|
|
@@ -243,12 +243,12 @@ lumine sponsor status
|
|
|
243
243
|
```
|
|
244
244
|
|
|
245
245
|
After Mikey approves an application, configure conservative limits based on
|
|
246
|
-
the subscription you are contributing, then start one foreground duty:
|
|
246
|
+
the subscription you are contributing, then start one shared foreground duty:
|
|
247
247
|
|
|
248
248
|
```bash
|
|
249
249
|
lumine sponsor capacity --concurrency 1 --helpers 0 \
|
|
250
250
|
--daily-limit 3 --weekly-limit 10
|
|
251
|
-
lumine sponsor duty start
|
|
251
|
+
lumine sponsor duty start --provider codex \
|
|
252
252
|
--model gpt-5.6-sol --effort max --service-tier priority
|
|
253
253
|
```
|
|
254
254
|
|
|
@@ -259,11 +259,14 @@ account may authenticate this way, but login never grants sponsor duty:
|
|
|
259
259
|
`sponsor duty start` still requires that exact account to have a canonical,
|
|
260
260
|
server-approved sponsor profile.
|
|
261
261
|
|
|
262
|
-
While that foreground process has a fresh server lease, Zero
|
|
263
|
-
|
|
262
|
+
While that foreground process has a fresh server lease, both Zero and Ciel can
|
|
263
|
+
delegate user-approved Build work to the shared worker pool. The user chooses
|
|
264
|
+
the assistant for each request, and may see the named sponsor and canonical
|
|
265
|
+
shared queue.
|
|
264
266
|
With no live duty, the website stays in its ordinary chat state and shows none
|
|
265
267
|
of the Workshop UI. Press Ctrl-C to stop accepting work and drain already
|
|
266
|
-
claimed jobs before duty ends; `pause`, `resume`, and `stop` are also available
|
|
268
|
+
claimed jobs before duty ends; `pause`, `resume`, and `stop` are also available
|
|
269
|
+
without a Zero/Ciel argument.
|
|
267
270
|
|
|
268
271
|
Zero or Ciel remains the user-facing teammate. The local coding agent receives
|
|
269
272
|
only the initial relay and active-job Build follow-ups covered by the user’s
|
|
@@ -273,8 +276,9 @@ cannot merge into Main, publish, or use website-management APIs. Lumine records
|
|
|
273
276
|
the requested and provider-reported model, effort, service tier, runtime/usage
|
|
274
277
|
evidence, coordinator/helper tree, saved artifact, and branch notice. Every
|
|
275
278
|
completed handoff enters the daily integrity flow; probationary, hard-flagged,
|
|
276
|
-
and sampled work requires review. Each unique cleared
|
|
277
|
-
|
|
279
|
+
and sampled work requires review. Each unique cleared contribution to another
|
|
280
|
+
user earns a flat 50 Karma Points, while self-sponsored testing, retries, and
|
|
281
|
+
helper agents do not earn or multiply the award.
|
|
278
282
|
|
|
279
283
|
## Privileged website administration
|
|
280
284
|
|
package/lib/commands.js
CHANGED
|
@@ -2730,8 +2730,8 @@ export function printHelp() {
|
|
|
2730
2730
|
lumine agent --provider <codex|claude-code> "<build request>"
|
|
2731
2731
|
lumine sponsor agreement|apply|status|withdraw
|
|
2732
2732
|
lumine sponsor capacity [--concurrency <n>] [--helpers <n>] [--daily-limit <n>] [--weekly-limit <n>]
|
|
2733
|
-
lumine sponsor duty start
|
|
2734
|
-
lumine sponsor duty pause|resume|stop
|
|
2733
|
+
lumine sponsor duty start --provider <codex|claude-code> [--model <name>] [--effort <level>] [--service-tier <tier>]
|
|
2734
|
+
lumine sponsor duty pause|resume|stop
|
|
2735
2735
|
lumine sponsor jobs
|
|
2736
2736
|
lumine app-mcp <published-app-url-or-id> [--no-open]
|
|
2737
2737
|
lumine new [title]
|
package/lib/sponsor.js
CHANGED
|
@@ -212,24 +212,41 @@ async function sponsorDutyCommand(options, args, commandServices) {
|
|
|
212
212
|
.trim()
|
|
213
213
|
.toLowerCase();
|
|
214
214
|
if (action === "status") {
|
|
215
|
+
if (args.length > 1) throw new Error(sponsorUsage());
|
|
215
216
|
await printSponsorStatus(options);
|
|
216
217
|
return;
|
|
217
218
|
}
|
|
218
|
-
|
|
219
|
+
if (args.length > 1 || options.sponsorPersona) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
"Sponsor duty is shared by Zero and Ciel. Remove the persona argument.",
|
|
222
|
+
);
|
|
223
|
+
}
|
|
219
224
|
if (["pause", "resume", "stop"].includes(action)) {
|
|
220
225
|
const auth = await ensureAuth(options);
|
|
226
|
+
const status = await sponsorRequest({ options, auth, path: "/status" });
|
|
227
|
+
const currentDuties = (status.duties || []).filter((duty) =>
|
|
228
|
+
["active", "paused"].includes(String(duty.state || "")),
|
|
229
|
+
);
|
|
230
|
+
if (currentDuties.length === 0) {
|
|
231
|
+
throw new Error("No active sponsor duty session was found.");
|
|
232
|
+
}
|
|
233
|
+
if (currentDuties.length > 1) {
|
|
234
|
+
throw new Error(
|
|
235
|
+
`Multiple legacy duty sessions are still open (${currentDuties.map((duty) => `#${duty.id}`).join(", ")}). Stop them with the CLI version that started them before using shared duty.`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const dutyId = Number(currentDuties[0].id || 0);
|
|
239
|
+
if (!dutyId) throw new Error("Twinkle returned an invalid duty session.");
|
|
221
240
|
const state =
|
|
222
241
|
action === "resume" ? "active" : action === "stop" ? "stopped" : "paused";
|
|
223
242
|
const result = await sponsorRequest({
|
|
224
243
|
options,
|
|
225
244
|
auth,
|
|
226
245
|
method: "POST",
|
|
227
|
-
path:
|
|
228
|
-
body: { state },
|
|
246
|
+
path: "/duty/state",
|
|
247
|
+
body: { dutySessionId: dutyId, state },
|
|
229
248
|
});
|
|
230
|
-
printJsonOrLines(options, result, [
|
|
231
|
-
`${displayPersona(persona)} duty is now ${state}.`,
|
|
232
|
-
]);
|
|
249
|
+
printJsonOrLines(options, result, [`Sponsor duty is now ${state}.`]);
|
|
233
250
|
return;
|
|
234
251
|
}
|
|
235
252
|
if (action !== "start") throw new Error(sponsorUsage());
|
|
@@ -249,7 +266,7 @@ async function sponsorDutyCommand(options, args, commandServices) {
|
|
|
249
266
|
method: "POST",
|
|
250
267
|
path: "/duty/start",
|
|
251
268
|
body: {
|
|
252
|
-
|
|
269
|
+
scope: "shared",
|
|
253
270
|
provider,
|
|
254
271
|
requestedModel: options.model || null,
|
|
255
272
|
requestedEffort: options.sponsorEffort || null,
|
|
@@ -260,7 +277,6 @@ async function sponsorDutyCommand(options, args, commandServices) {
|
|
|
260
277
|
await runSponsorDuty({
|
|
261
278
|
options,
|
|
262
279
|
auth,
|
|
263
|
-
persona,
|
|
264
280
|
provider,
|
|
265
281
|
started,
|
|
266
282
|
commandServices,
|
|
@@ -270,7 +286,6 @@ async function sponsorDutyCommand(options, args, commandServices) {
|
|
|
270
286
|
async function runSponsorDuty({
|
|
271
287
|
options,
|
|
272
288
|
auth,
|
|
273
|
-
persona,
|
|
274
289
|
provider,
|
|
275
290
|
started,
|
|
276
291
|
commandServices,
|
|
@@ -280,6 +295,9 @@ async function runSponsorDuty({
|
|
|
280
295
|
if (!dutyId || !leaseToken) {
|
|
281
296
|
throw new Error("Twinkle did not return a valid sponsor duty lease.");
|
|
282
297
|
}
|
|
298
|
+
if (started.duty?.scope !== "shared") {
|
|
299
|
+
throw new Error("Twinkle did not return a shared sponsor duty lease.");
|
|
300
|
+
}
|
|
283
301
|
let canonicalDuty = started.duty;
|
|
284
302
|
let stopRequested = false;
|
|
285
303
|
let dutyLeaseError = null;
|
|
@@ -294,6 +312,9 @@ async function runSponsorDuty({
|
|
|
294
312
|
process.on("SIGINT", signalHandler);
|
|
295
313
|
process.on("SIGTERM", signalHandler);
|
|
296
314
|
|
|
315
|
+
const dutyHeartbeatEveryMs =
|
|
316
|
+
Math.max(10, Number(started.heartbeatEverySeconds || 30)) * 1_000;
|
|
317
|
+
let lastDutyHeartbeatSuccessAt = Date.now();
|
|
297
318
|
const heartbeatDuty = async () => {
|
|
298
319
|
if (dutyHeartbeatRunning) return;
|
|
299
320
|
dutyHeartbeatRunning = true;
|
|
@@ -306,6 +327,7 @@ async function runSponsorDuty({
|
|
|
306
327
|
body: { leaseToken },
|
|
307
328
|
});
|
|
308
329
|
dutyLeaseError = null;
|
|
330
|
+
lastDutyHeartbeatSuccessAt = Date.now();
|
|
309
331
|
if (canonicalDuty.state === "stopped") stopRequested = true;
|
|
310
332
|
} catch (error) {
|
|
311
333
|
const status = await sponsorRequest({
|
|
@@ -322,20 +344,27 @@ async function runSponsorDuty({
|
|
|
322
344
|
stopRequested = true;
|
|
323
345
|
return;
|
|
324
346
|
}
|
|
325
|
-
|
|
326
|
-
|
|
347
|
+
// Same lease policy as the per-job heartbeat: end the duty only when the
|
|
348
|
+
// server rejects the lease or the lease window has fully elapsed. A
|
|
349
|
+
// transient network failure retries on the next tick.
|
|
350
|
+
const terminalDutyError = !isRetryableSponsorDutyRequestError(error);
|
|
351
|
+
const leaseWindowElapsed =
|
|
352
|
+
Date.now() - lastDutyHeartbeatSuccessAt >=
|
|
353
|
+
Math.max(120_000, dutyHeartbeatEveryMs * 3);
|
|
354
|
+
if (terminalDutyError || leaseWindowElapsed) {
|
|
355
|
+
dutyLeaseError = error;
|
|
356
|
+
stopRequested = true;
|
|
357
|
+
}
|
|
327
358
|
} finally {
|
|
328
359
|
dutyHeartbeatRunning = false;
|
|
329
360
|
}
|
|
330
361
|
};
|
|
331
362
|
const dutyHeartbeat = setInterval(
|
|
332
363
|
() => void heartbeatDuty(),
|
|
333
|
-
|
|
364
|
+
dutyHeartbeatEveryMs,
|
|
334
365
|
);
|
|
335
366
|
|
|
336
|
-
console.log(
|
|
337
|
-
`${displayPersona(persona)} sponsor duty started with ${provider}.`,
|
|
338
|
-
);
|
|
367
|
+
console.log(`Shared Zero/Ciel sponsor duty started with ${provider}.`);
|
|
339
368
|
printDutyRuntime(canonicalDuty);
|
|
340
369
|
console.log(
|
|
341
370
|
"The Workshop is now discoverable to users. Press Ctrl-C for a graceful stop.",
|
|
@@ -352,20 +381,34 @@ async function runSponsorDuty({
|
|
|
352
381
|
Number(canonicalDuty.capacity?.maxConcurrentTasks || 1),
|
|
353
382
|
);
|
|
354
383
|
while (!stopRequested && activeJobs.size < concurrency) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
384
|
+
let claim;
|
|
385
|
+
try {
|
|
386
|
+
claim = await sponsorRequest({
|
|
387
|
+
options,
|
|
388
|
+
auth,
|
|
389
|
+
method: "POST",
|
|
390
|
+
path: "/jobs/claim",
|
|
391
|
+
body: { dutySessionId: dutyId, leaseToken },
|
|
392
|
+
});
|
|
393
|
+
} catch (error) {
|
|
394
|
+
if (!isRetryableSponsorDutyRequestError(error)) {
|
|
395
|
+
dutyLeaseError = error;
|
|
396
|
+
stopRequested = true;
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
// A failed claim must not kill the duty process; the heartbeat owns
|
|
400
|
+
// lease validity, and the outer loop polls again after a sleep.
|
|
401
|
+
console.error(
|
|
402
|
+
`Workshop claim failed; retrying next poll: ${error?.message || error}`,
|
|
403
|
+
);
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
362
406
|
if (!claim.job) break;
|
|
363
407
|
const work = processSponsorJob({
|
|
364
408
|
options,
|
|
365
409
|
sponsorAuth: auth,
|
|
366
410
|
dutyId,
|
|
367
411
|
dutyLeaseToken: leaseToken,
|
|
368
|
-
persona,
|
|
369
412
|
provider,
|
|
370
413
|
claim,
|
|
371
414
|
commandServices,
|
|
@@ -385,8 +428,8 @@ async function runSponsorDuty({
|
|
|
385
428
|
options,
|
|
386
429
|
auth,
|
|
387
430
|
method: "POST",
|
|
388
|
-
path:
|
|
389
|
-
body: { state: "paused" },
|
|
431
|
+
path: "/duty/state",
|
|
432
|
+
body: { dutySessionId: dutyId, state: "paused" },
|
|
390
433
|
});
|
|
391
434
|
canonicalDuty = paused.duty || canonicalDuty;
|
|
392
435
|
}
|
|
@@ -401,8 +444,8 @@ async function runSponsorDuty({
|
|
|
401
444
|
options,
|
|
402
445
|
auth,
|
|
403
446
|
method: "POST",
|
|
404
|
-
path:
|
|
405
|
-
body: { state: "stopped" },
|
|
447
|
+
path: "/duty/state",
|
|
448
|
+
body: { dutySessionId: dutyId, state: "stopped" },
|
|
406
449
|
}).catch((error) => {
|
|
407
450
|
if (!dutyLeaseError) {
|
|
408
451
|
console.error(
|
|
@@ -412,7 +455,7 @@ async function runSponsorDuty({
|
|
|
412
455
|
});
|
|
413
456
|
}
|
|
414
457
|
}
|
|
415
|
-
console.log(
|
|
458
|
+
console.log("Shared Zero/Ciel sponsor duty stopped.");
|
|
416
459
|
}
|
|
417
460
|
|
|
418
461
|
async function processSponsorJob({
|
|
@@ -420,12 +463,12 @@ async function processSponsorJob({
|
|
|
420
463
|
sponsorAuth,
|
|
421
464
|
dutyId,
|
|
422
465
|
dutyLeaseToken,
|
|
423
|
-
persona,
|
|
424
466
|
provider,
|
|
425
467
|
claim,
|
|
426
468
|
commandServices,
|
|
427
469
|
}) {
|
|
428
470
|
const job = claim.job;
|
|
471
|
+
const persona = normalizeJobPersona(job?.persona);
|
|
429
472
|
const jobId = Number(job.id);
|
|
430
473
|
const attemptToken = String(claim.attempt?.token || "");
|
|
431
474
|
const workspaceToken = String(claim.workspaceToken?.accessToken || "");
|
|
@@ -1118,6 +1161,12 @@ async function retrySponsorTransport(operation) {
|
|
|
1118
1161
|
throw lastError;
|
|
1119
1162
|
}
|
|
1120
1163
|
|
|
1164
|
+
function isRetryableSponsorDutyRequestError(error) {
|
|
1165
|
+
const status = Number(error?.status || 0);
|
|
1166
|
+
if (!status) return true;
|
|
1167
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1121
1170
|
async function sponsorRequest({
|
|
1122
1171
|
options,
|
|
1123
1172
|
auth,
|
|
@@ -1153,8 +1202,12 @@ async function printSponsorStatus(options) {
|
|
|
1153
1202
|
);
|
|
1154
1203
|
}
|
|
1155
1204
|
for (const duty of status.duties || []) {
|
|
1205
|
+
const scope =
|
|
1206
|
+
duty.scope === "shared"
|
|
1207
|
+
? "shared Zero/Ciel"
|
|
1208
|
+
: `legacy assistant user ${duty.personaUserId}`;
|
|
1156
1209
|
console.log(
|
|
1157
|
-
`Duty #${duty.id}:
|
|
1210
|
+
`Duty #${duty.id}: ${scope} · ${duty.state} · ${duty.provider} · model=${duty.requestedModel || "provider default"} · effort=${duty.requestedEffort || "provider default"}`,
|
|
1158
1211
|
);
|
|
1159
1212
|
}
|
|
1160
1213
|
}
|
|
@@ -1253,12 +1306,12 @@ function normalizeProvider(value) {
|
|
|
1253
1306
|
return provider;
|
|
1254
1307
|
}
|
|
1255
1308
|
|
|
1256
|
-
function
|
|
1309
|
+
function normalizeJobPersona(value) {
|
|
1257
1310
|
const persona = String(value || "")
|
|
1258
1311
|
.trim()
|
|
1259
1312
|
.toLowerCase();
|
|
1260
1313
|
if (!PERSONAS.has(persona)) {
|
|
1261
|
-
throw new Error("
|
|
1314
|
+
throw new Error("Twinkle returned an invalid Workshop job assistant.");
|
|
1262
1315
|
}
|
|
1263
1316
|
return persona;
|
|
1264
1317
|
}
|
|
@@ -1285,8 +1338,8 @@ function sponsorUsage() {
|
|
|
1285
1338
|
" lumine sponsor status",
|
|
1286
1339
|
" lumine sponsor withdraw [--yes]",
|
|
1287
1340
|
" lumine sponsor capacity [--concurrency <n>] [--helpers <n>] [--daily-limit <n>] [--weekly-limit <n>]",
|
|
1288
|
-
" lumine sponsor duty start
|
|
1289
|
-
" lumine sponsor duty pause|resume|stop
|
|
1341
|
+
" lumine sponsor duty start --provider <codex|claude-code> [--model <name>] [--effort <level>] [--service-tier <tier>]",
|
|
1342
|
+
" lumine sponsor duty pause|resume|stop",
|
|
1290
1343
|
" lumine sponsor jobs [--limit <n>]",
|
|
1291
1344
|
].join("\n");
|
|
1292
1345
|
}
|
package/package.json
CHANGED
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -663,7 +663,9 @@ artifact snapshot, branch-notice evidence, and requested/resolved provider,
|
|
|
663
663
|
model, effort, service-tier, runtime, usage, and agent-tree records. It never
|
|
664
664
|
includes raw Zero/Ciel chat.
|
|
665
665
|
|
|
666
|
-
`clear` qualifies
|
|
666
|
+
`clear` qualifies a contribution to another user for its flat 50 KP award;
|
|
667
|
+
self-sponsored testing remains ineligible even when its integrity evidence is
|
|
668
|
+
cleared.
|
|
667
669
|
`disqualify` makes it ineligible. `hold` and `flag` require an evidence note and
|
|
668
670
|
remain open. The scan itself never changes sponsor status or applies a sanction.
|
|
669
671
|
Use the separate, audited `sponsor status set` command for an explicit human
|