@stage5/lumine 0.2.59 → 0.2.60
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 +6 -1
- package/lib/commands.js +3 -0
- package/lib/sponsor-duty.js +87 -16
- package/lib/sponsor.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -283,13 +283,18 @@ works:
|
|
|
283
283
|
|
|
284
284
|
```bash
|
|
285
285
|
lumine sponsor job begin 42
|
|
286
|
+
lumine sponsor job update 42 --file /path/to/lumine-update.txt --phase building
|
|
286
287
|
lumine sponsor job pulse 42
|
|
287
288
|
lumine sponsor job relay-applied 42 101
|
|
288
289
|
lumine sponsor job complete 42 --summary "Implemented and tested the approved change"
|
|
289
290
|
```
|
|
290
291
|
|
|
291
292
|
`pulse` renews the duty/job leases and returns any newly approved follow-up.
|
|
292
|
-
|
|
293
|
+
`update` sends the exact file text back to Zero or Ciel as a user-visible
|
|
294
|
+
message from Lumine, their added brain. Use it for concise, deliberate
|
|
295
|
+
milestones only; never send hidden reasoning, raw terminal output, credentials,
|
|
296
|
+
tokens, private paths, or unrelated data. Run `pulse` between substantial work
|
|
297
|
+
steps. `relay-applied` is an explicit receipt;
|
|
293
298
|
completion is rejected until every delivered relay has actually been applied
|
|
294
299
|
and acknowledged. Use `helper-start` and `helper-complete` to record native
|
|
295
300
|
same-session subagents within the configured helper limit—the CLI records them
|
package/lib/commands.js
CHANGED
|
@@ -2409,6 +2409,8 @@ export function parseArgs(args) {
|
|
|
2409
2409
|
sponsorAgentOrdinal: raw.ordinal,
|
|
2410
2410
|
sponsorOutcome: raw.outcome ? String(raw.outcome) : "",
|
|
2411
2411
|
sponsorFailureReason: raw.reason ? String(raw.reason) : "",
|
|
2412
|
+
sponsorUpdateFile: raw.file ? String(raw.file) : "",
|
|
2413
|
+
sponsorUpdatePhase: raw.phase ? String(raw.phase) : "",
|
|
2412
2414
|
sponsorResolvedModel: raw.resolvedModel
|
|
2413
2415
|
? String(raw.resolvedModel)
|
|
2414
2416
|
: "",
|
|
@@ -2749,6 +2751,7 @@ export function printHelp() {
|
|
|
2749
2751
|
lumine sponsor duty watch [--wait-ms <ms>] [--json]
|
|
2750
2752
|
lumine sponsor duty pause|resume|stop
|
|
2751
2753
|
lumine sponsor job status|pulse|begin <job-id>
|
|
2754
|
+
lumine sponsor job update <job-id> --file <path> [--phase <name>]
|
|
2752
2755
|
lumine sponsor job relay-applied <job-id> <relay-id...>
|
|
2753
2756
|
lumine sponsor job helper-start|helper-complete <job-id> [options]
|
|
2754
2757
|
lumine sponsor job complete <job-id> --summary <text>
|
package/lib/sponsor-duty.js
CHANGED
|
@@ -66,6 +66,13 @@ export async function sponsorJobCommand(options, args, commandServices) {
|
|
|
66
66
|
await withSponsorStateLock(options, () => beginJob(options, jobId));
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
+
if (action === "update") {
|
|
70
|
+
assertNoExtraArgs(args, 2);
|
|
71
|
+
await withSponsorStateLock(options, () =>
|
|
72
|
+
publishDialogueUpdate(options, jobId),
|
|
73
|
+
);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
69
76
|
if (action === "relay-applied") {
|
|
70
77
|
const relayIds = args
|
|
71
78
|
.slice(2)
|
|
@@ -539,10 +546,61 @@ async function beginJob(options, jobId) {
|
|
|
539
546
|
printJsonOrLines(options, { job: canonicalJobState.job, coordinator: agent }, [
|
|
540
547
|
`Workshop job #${jobId} is now being handled by this same on-duty agent session.`,
|
|
541
548
|
`Apply the approved plan in ${jobState.workspaceDir}.`,
|
|
549
|
+
`Introduce yourself to ${displayPersona(jobState.job.persona)} as Lumine with: lumine sponsor job update ${jobId} --file <message-file> --phase starting`,
|
|
542
550
|
`Check in with: lumine sponsor job pulse ${jobId}`,
|
|
543
551
|
]);
|
|
544
552
|
}
|
|
545
553
|
|
|
554
|
+
async function publishDialogueUpdate(options, jobId) {
|
|
555
|
+
const messageFile = String(options.sponsorUpdateFile || "").trim();
|
|
556
|
+
if (!messageFile) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
"Write the deliberate user-facing Lumine update to a file and pass --file <path>.",
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
const phase = String(options.sponsorUpdatePhase || "").trim();
|
|
562
|
+
if (phase.length > 40) {
|
|
563
|
+
throw new Error("--phase must be at most 40 characters.");
|
|
564
|
+
}
|
|
565
|
+
const messagePath = path.resolve(messageFile);
|
|
566
|
+
const messageStat = await fs.stat(messagePath);
|
|
567
|
+
if (!messageStat.isFile() || messageStat.size > 8_000) {
|
|
568
|
+
throw new Error("The Lumine update file must be a text file under 8 KB.");
|
|
569
|
+
}
|
|
570
|
+
const message = String(await fs.readFile(messagePath, "utf8")).trim();
|
|
571
|
+
if (!message) throw new Error("The Lumine update file is empty.");
|
|
572
|
+
if (message.length > 2_000) {
|
|
573
|
+
throw new Error("The Lumine update must be at most 2,000 characters.");
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
const auth = await ensureSponsorAuth(options);
|
|
577
|
+
let state = await loadOwnedState({ options, auth });
|
|
578
|
+
({ state } = await heartbeatJob({ options, auth, state, jobId }));
|
|
579
|
+
const jobState = requireJobState(state, jobId);
|
|
580
|
+
const clientUpdateKey = randomBytes(18).toString("base64url");
|
|
581
|
+
const result = await retrySponsorCheckIn(() =>
|
|
582
|
+
sponsorJobRequest({
|
|
583
|
+
options,
|
|
584
|
+
auth,
|
|
585
|
+
state,
|
|
586
|
+
jobState,
|
|
587
|
+
path: "/dialogue",
|
|
588
|
+
body: {
|
|
589
|
+
clientUpdateKey,
|
|
590
|
+
phase: phase || null,
|
|
591
|
+
message,
|
|
592
|
+
},
|
|
593
|
+
}),
|
|
594
|
+
);
|
|
595
|
+
if (!result?.update?.message) {
|
|
596
|
+
throw new Error("Twinkle did not confirm the canonical Lumine update.");
|
|
597
|
+
}
|
|
598
|
+
printJsonOrLines(options, result, [
|
|
599
|
+
`Lumine → ${displayPersona(jobState.job.persona)}:`,
|
|
600
|
+
result.update.message,
|
|
601
|
+
]);
|
|
602
|
+
}
|
|
603
|
+
|
|
546
604
|
async function startHelper(options, jobId) {
|
|
547
605
|
const auth = await ensureSponsorAuth(options);
|
|
548
606
|
let state = await loadOwnedState({ options, auth });
|
|
@@ -1177,25 +1235,36 @@ async function writeAssignment(jobState, state) {
|
|
|
1177
1235
|
const unapplied = new Set(unappliedRelayIds(jobState));
|
|
1178
1236
|
const relays = (jobState.relays || [])
|
|
1179
1237
|
.map((relay) => {
|
|
1180
|
-
const
|
|
1181
|
-
relay.
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1238
|
+
const dialogueText =
|
|
1239
|
+
String(relay.dialogueText || "").trim() ||
|
|
1240
|
+
[
|
|
1241
|
+
relay.summary,
|
|
1242
|
+
relay.projectTitleHint
|
|
1243
|
+
? `Project: ${relay.projectTitleHint}`
|
|
1244
|
+
: "",
|
|
1245
|
+
relay.requestedOutcome
|
|
1246
|
+
? `What to build: ${relay.requestedOutcome}`
|
|
1247
|
+
: "",
|
|
1248
|
+
relay.constraints?.length
|
|
1249
|
+
? `Keeping in mind:\n${relay.constraints.map((item) => `• ${item}`).join("\n")}`
|
|
1250
|
+
: "",
|
|
1251
|
+
relay.acceptanceCriteria?.length
|
|
1252
|
+
? `Done means:\n${relay.acceptanceCriteria.map((item) => `• ${item}`).join("\n")}`
|
|
1253
|
+
: "",
|
|
1254
|
+
]
|
|
1255
|
+
.filter(Boolean)
|
|
1256
|
+
.join("\n\n");
|
|
1257
|
+
return `## Relay #${relay.id}${unapplied.has(Number(relay.id)) ? " (not yet acknowledged as applied)" : " (applied receipt confirmed)"}\n\n${dialogueText}`;
|
|
1191
1258
|
})
|
|
1192
1259
|
.join("\n\n");
|
|
1193
1260
|
const content = `# Lumine Build Workshop assignment #${jobState.job.id}
|
|
1194
1261
|
|
|
1195
|
-
You are the same live ${displayProvider(state.operatorSession.provider)} agent session that opened sponsor duty. Zero or Ciel is the user's visible teammate; you are lending that teammate an added brain. Perform this work in this session. Do not launch a replacement coding provider or leave an unattended heartbeat process standing in for you.
|
|
1262
|
+
You are the same live ${displayProvider(state.operatorSession.provider)} agent session that opened sponsor duty. Zero or Ciel is the user's visible teammate; you are lending that teammate an added brain. In every user-facing Workshop update, you are Lumine: the added brain talking with that teammate. Perform this work in this session. Do not launch a replacement coding provider or leave an unattended heartbeat process standing in for you.
|
|
1196
1263
|
|
|
1197
1264
|
The user approved sharing only this structured plan, active-job follow-ups, the assigned Build project, and its scoped Forum. Never inspect or infer from their private Zero/Ciel chat. Edit only contribution Build #${jobState.job.contributionBuild.id}; never merge Main, publish, or contact the user directly.
|
|
1198
1265
|
|
|
1266
|
+
Lumine updates are a deliberate public channel. Write concise messages about what you are checking, what you found, or what happens next. Never publish hidden chain-of-thought, raw terminal output, credentials, tokens, private paths, or unrelated data. The exact file text you submit is shown in Twinkle and echoed back by the CLI.
|
|
1267
|
+
|
|
1199
1268
|
- User: @${jobState.job.requester.username}
|
|
1200
1269
|
- Visible teammate: ${displayPersona(jobState.job.persona)}
|
|
1201
1270
|
- Main project: ${jobState.job.rootBuild.title} (#${jobState.job.rootBuild.id})
|
|
@@ -1208,10 +1277,11 @@ ${jobState.forumContext ? `## Scoped Build Forum snapshot\n\n${jobState.forumCon
|
|
|
1208
1277
|
## Duty protocol
|
|
1209
1278
|
|
|
1210
1279
|
1. Run \`lumine sponsor job begin ${jobState.job.id}\` before editing.
|
|
1211
|
-
2.
|
|
1212
|
-
3.
|
|
1213
|
-
4.
|
|
1214
|
-
5.
|
|
1280
|
+
2. Introduce yourself and publish meaningful milestones with \`lumine sponsor job update ${jobState.job.id} --file <message-file> --phase <name>\`. Speak as Lumine, never as the underlying provider.
|
|
1281
|
+
3. Work only in the workspace above. Use your native same-session subagents only after registering each with \`helper-start\`. The coordinator alone runs sponsor CLI commands; helpers report their results back to it.
|
|
1282
|
+
4. Run \`lumine sponsor job pulse ${jobState.job.id}\` between substantial work steps to keep the lease alive and receive approved follow-ups.
|
|
1283
|
+
5. After applying a relay, record its exact ID with \`lumine sponsor job relay-applied ${jobState.job.id} <relay-id...>\`.
|
|
1284
|
+
6. Finish with \`lumine sponsor job complete ${jobState.job.id} --summary "..."\`.
|
|
1215
1285
|
`;
|
|
1216
1286
|
await fs.writeFile(jobState.assignmentPath, content, {
|
|
1217
1287
|
encoding: "utf8",
|
|
@@ -1865,6 +1935,7 @@ function sponsorJobUsage() {
|
|
|
1865
1935
|
"Usage:",
|
|
1866
1936
|
" lumine sponsor job status|pulse <job-id>",
|
|
1867
1937
|
" lumine sponsor job begin <job-id>",
|
|
1938
|
+
" lumine sponsor job update <job-id> --file <path> [--phase <name>]",
|
|
1868
1939
|
" lumine sponsor job relay-applied <job-id> <relay-id...>",
|
|
1869
1940
|
" lumine sponsor job helper-start <job-id> [--ordinal <n>]",
|
|
1870
1941
|
" lumine sponsor job helper-complete <job-id> --ordinal <n> --outcome <text> [--resolved-model <name>] [--resolved-effort <level>]",
|
package/lib/sponsor.js
CHANGED
|
@@ -391,6 +391,7 @@ function sponsorUsage() {
|
|
|
391
391
|
" lumine sponsor duty watch [--wait-ms <ms>] [--json]",
|
|
392
392
|
" lumine sponsor duty status|pause|resume|stop",
|
|
393
393
|
" lumine sponsor job status|pulse|begin <job-id>",
|
|
394
|
+
" lumine sponsor job update <job-id> --file <path> [--phase <name>]",
|
|
394
395
|
" lumine sponsor job relay-applied <job-id> <relay-id...>",
|
|
395
396
|
" lumine sponsor job helper-start|helper-complete <job-id> [options]",
|
|
396
397
|
" lumine sponsor job complete <job-id> --summary <text>",
|