@stage5/lumine 0.2.69 → 0.2.71
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 +27 -6
- package/lib/admin-featured.js +33 -0
- package/lib/admin-runtime-logs.js +22 -3
- package/lib/admin.js +22 -0
- package/lib/assets.js +11 -8
- package/lib/build-review.js +609 -8
- package/lib/commands.js +12 -6
- package/lib/constants.js +5 -1
- package/lib/sdk.js +108 -4
- package/lib/thumbnail.js +2 -2
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +7 -3
- package/sdk/LUMINE_ADMIN.md +129 -9
package/lib/commands.js
CHANGED
|
@@ -2403,6 +2403,10 @@ export function parseArgs(args) {
|
|
|
2403
2403
|
adminDecision: raw.decision ? String(raw.decision) : "",
|
|
2404
2404
|
adminWaitMs: raw.waitMs ? String(raw.waitMs) : "",
|
|
2405
2405
|
adminBrowserPath: raw.browserPath ? String(raw.browserPath) : "",
|
|
2406
|
+
adminInteract: raw.interact ? String(raw.interact) : "",
|
|
2407
|
+
adminDecisionsTemplate: raw.decisionsTemplate
|
|
2408
|
+
? String(raw.decisionsTemplate)
|
|
2409
|
+
: "",
|
|
2406
2410
|
adminEffort: raw.effort ? String(raw.effort) : "",
|
|
2407
2411
|
agentEffort: command === "agent" && raw.effort ? String(raw.effort) : "",
|
|
2408
2412
|
sponsorArgs: command === "sponsor" ? positional : [],
|
|
@@ -2831,12 +2835,12 @@ export function printHelp() {
|
|
|
2831
2835
|
lumine sdk call <namespace.method> [jsonArgs]
|
|
2832
2836
|
lumine assets [list]
|
|
2833
2837
|
lumine assets upload <file...>
|
|
2834
|
-
lumine assets generate "<prompt>" --model <gpt-image-2|nano-banana>
|
|
2838
|
+
lumine assets generate "<prompt>" --model <gpt-image-2.5-flare|gpt-image-2.5-sunburst|gpt-image-2|nano-banana>
|
|
2835
2839
|
lumine assets delete <assetId>
|
|
2836
2840
|
lumine assets prune [--yes]
|
|
2837
2841
|
lumine thumbnail set <file>
|
|
2838
2842
|
lumine thumbnail capture [--out <file>]
|
|
2839
|
-
lumine thumbnail generate ["<prompt>"] --model <gpt-image-2|nano-banana>
|
|
2843
|
+
lumine thumbnail generate ["<prompt>"] --model <gpt-image-2.5-flare|gpt-image-2.5-sunburst|gpt-image-2|nano-banana>
|
|
2840
2844
|
lumine doctor runtime-assets
|
|
2841
2845
|
lumine admin identity list|status|use <zero|ciel|auto> [--json]
|
|
2842
2846
|
lumine admin identity inspect <user-id|username> --reason <management-reason> [--include-private-evidence] [--json]
|
|
@@ -2865,7 +2869,7 @@ export function printHelp() {
|
|
|
2865
2869
|
lumine admin sponsor integrity review <case-id> --decision clear|hold|flag|disqualify [--note <evidence>] [--json]
|
|
2866
2870
|
lumine admin recommendations list [--since-run|--after <date>|--include-legacy] [--all --checkpoint <file> [--resume]] [--content-types comment,dailyReflection] [--unviewed|--viewed] [--cursor <cursor>] [--json]
|
|
2867
2871
|
lumine admin builds candidates [--since-run|--after <date>|--include-legacy] [--all --checkpoint <file> [--resume]] [--cursor <cursor>] [--limit <number>] [--json]
|
|
2868
|
-
lumine admin builds review <build-url-or-id> [--output-dir <dir>] [--wait-ms <ms>] [--browser-path <path>] [--json]
|
|
2872
|
+
lumine admin builds review <build-url-or-id> [--output-dir <dir>] [--wait-ms <ms>] [--interact <steps.json>] [--browser-path <path>] [--json]
|
|
2869
2873
|
lumine admin subjects candidates [--since-run|--after <date>|--include-legacy] [--effort unassigned] [--unviewed|--viewed] [--all --checkpoint <file> [--resume]] [--cursor <cursor>] [--json]
|
|
2870
2874
|
lumine admin subject get|reveal <subject-url-or-id> [--json]
|
|
2871
2875
|
lumine admin subject comments <subject-url-or-id> [--unviewed|--viewed] [--all --checkpoint <file> [--resume]] [--cursor <cursor>] [--json]
|
|
@@ -2880,7 +2884,7 @@ export function printHelp() {
|
|
|
2880
2884
|
lumine admin featured plan --remove-subject-ids <ids> --add-subject-ids <ids> [--subject-ids <final-order>] --posted-after <timestamp> --output <plan.json> [--json]
|
|
2881
2885
|
lumine admin featured apply --file <plan.json> --approve <exact-plan-hash> [--json]
|
|
2882
2886
|
lumine admin featured comments scan --checkpoint <file> [--resume] [--json]
|
|
2883
|
-
lumine admin featured comments acknowledge --checkpoint <scan-file> --reviewed [--json]
|
|
2887
|
+
lumine admin featured comments acknowledge --checkpoint <scan-file> --reviewed [--decisions-template <file>] [--json]
|
|
2884
2888
|
lumine admin featured comments recommend --file <decisions.json> --checkpoint <batch-file> [--resume] [--json]
|
|
2885
2889
|
lumine admin featured comments report --checkpoint <scan-file> [--json]
|
|
2886
2890
|
lumine admin post get <target> [--type subject|comment|aiStory|dailyReflection] [--json]
|
|
@@ -3006,6 +3010,8 @@ Options:
|
|
|
3006
3010
|
--severity <level> Run escalation severity: attention or urgent
|
|
3007
3011
|
--status <state> Private escalation or todo lifecycle filter/state
|
|
3008
3012
|
--wait-ms <ms> Managed Build observation or sponsor watch duration
|
|
3013
|
+
--interact <file> Managed Build review: bounded JSON step script (click/type/press/wait/screenshot) run inside the app frame after the start screenshot
|
|
3014
|
+
--decisions-template <file> Featured acknowledge: write a ready {reviewId, coverageId, selections: []} decisions file
|
|
3009
3015
|
--browser-path <path> Chrome/Chromium executable for managed Build review
|
|
3010
3016
|
--effort unassigned Admin subjects: show only unassigned effort
|
|
3011
3017
|
--unviewed Admin content lists: retain unviewed and unknown items
|
|
@@ -3048,8 +3054,8 @@ Options:
|
|
|
3048
3054
|
--json Print machine-readable output where supported
|
|
3049
3055
|
--keep-assets Keep doctor probe assets instead of deleting them
|
|
3050
3056
|
--no-browser Skip doctor browser probes
|
|
3051
|
-
--model <model> Image model for generate: gpt-image-2 or nano-banana (required
|
|
3052
|
-
--quality <q>
|
|
3057
|
+
--model <model> Image model for generate: gpt-image-2.5-flare, gpt-image-2.5-sunburst, gpt-image-2, or nano-banana (required)
|
|
3058
|
+
--quality <q> GPT Image quality: low, medium, high, xhigh, max (default high; xhigh/max require 2.5)
|
|
3053
3059
|
--name <fileName> File name hint for a generated asset
|
|
3054
3060
|
--out <path> With thumbnail capture: also save the capture locally
|
|
3055
3061
|
--yes Skip confirmation prompts (assets prune/generate, thumbnail)
|
package/lib/constants.js
CHANGED
|
@@ -16,11 +16,15 @@ export const DEFAULT_TIMEOUT_MS = 20000;
|
|
|
16
16
|
export const ASSET_GENERATE_TIMEOUT_MS = 6 * 60 * 1000;
|
|
17
17
|
export const THUMBNAIL_CAPTURE_TIMEOUT_MS = 90 * 1000;
|
|
18
18
|
export const GENERATE_MODEL_ALIASES = {
|
|
19
|
+
"gpt-image-2.5-flare": "gpt-image-2.5-flare",
|
|
20
|
+
"gpt-image-2.5-sunburst": "gpt-image-2.5-sunburst",
|
|
21
|
+
"flare": "gpt-image-2.5-flare",
|
|
22
|
+
"sunburst": "gpt-image-2.5-sunburst",
|
|
19
23
|
"gpt-image-2": "gpt-image-2",
|
|
20
24
|
"nano-banana": "gemini-3-pro-image-preview",
|
|
21
25
|
"gemini-3-pro-image-preview": "gemini-3-pro-image-preview",
|
|
22
26
|
};
|
|
23
|
-
export const GENERATE_QUALITIES = new Set(["low", "medium", "high"]);
|
|
27
|
+
export const GENERATE_QUALITIES = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
24
28
|
// Server accepts only these thumbnail content types (8MB max).
|
|
25
29
|
export const THUMBNAIL_CONTENT_TYPE_BY_EXTENSION = {
|
|
26
30
|
".jpg": "image/jpeg",
|
package/lib/sdk.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from "path";
|
|
|
2
2
|
|
|
3
3
|
import { ensureAuth, assertAuthScope } from "./auth.js";
|
|
4
4
|
import { mintBuildApiToken } from "./api.js";
|
|
5
|
-
import { requestText } from "./http.js";
|
|
5
|
+
import { requestJson, requestText } from "./http.js";
|
|
6
6
|
import { parseJson, resolveBuildId } from "./util.js";
|
|
7
7
|
import { findLocalProjectMetadata } from "./workspace.js";
|
|
8
8
|
|
|
@@ -152,6 +152,37 @@ export const SDK_CLI_METHODS = {
|
|
|
152
152
|
"notifications.getSubjectUpdateSubscription": { path: "api/notifications/subject-update-subscription", scopes: ["notifications:read"] },
|
|
153
153
|
"notifications.subscribeToSubjectUpdates": { path: "api/notifications/subject-update-subscription/subscribe", scopes: ["notifications:write"], write: true },
|
|
154
154
|
"notifications.unsubscribeFromSubjectUpdates": { path: "api/notifications/subject-update-subscription/unsubscribe", scopes: ["notifications:write"], write: true },
|
|
155
|
+
// Rewards go through the same server-verified endpoint the published app
|
|
156
|
+
// runtime uses: POST api/rewards/<operation> with the rewards:claim build
|
|
157
|
+
// token PLUS the server-issued published-runtime grant (fetched from the
|
|
158
|
+
// canonical GET /build/:id/runtime payload, never minted locally). The CLI
|
|
159
|
+
// holds no award logic; the server checks approval, version and budget.
|
|
160
|
+
// getStatus is read-only: the endpoint only accepts rewards:claim, so that
|
|
161
|
+
// scope is minted for it, but only the status operation is ever sent.
|
|
162
|
+
"rewards.getStatus": {
|
|
163
|
+
path: "api/rewards/status",
|
|
164
|
+
special: "rewards",
|
|
165
|
+
operation: "status",
|
|
166
|
+
scopes: ["rewards:claim"],
|
|
167
|
+
readOnly: true,
|
|
168
|
+
mapArgs: () => ({}),
|
|
169
|
+
},
|
|
170
|
+
"rewards.start": {
|
|
171
|
+
path: "api/rewards/start",
|
|
172
|
+
special: "rewards",
|
|
173
|
+
operation: "start",
|
|
174
|
+
scopes: ["rewards:claim"],
|
|
175
|
+
write: true,
|
|
176
|
+
mapArgs: (args) => ({ ruleId: args.ruleId }),
|
|
177
|
+
},
|
|
178
|
+
"rewards.claim": {
|
|
179
|
+
path: "api/rewards/claim",
|
|
180
|
+
special: "rewards",
|
|
181
|
+
operation: "claim",
|
|
182
|
+
scopes: ["rewards:claim"],
|
|
183
|
+
write: true,
|
|
184
|
+
mapArgs: (args) => ({ challengeId: args.challengeId, answers: args.answers }),
|
|
185
|
+
},
|
|
155
186
|
// Leaderboards use the public leaderboard routes (regular login auth, no
|
|
156
187
|
// build API token). args.boardKey selects the board; remaining args are
|
|
157
188
|
// query params (get) or the POST body (submit).
|
|
@@ -221,7 +252,9 @@ export const SDK_CLI_READ_SCOPES = [
|
|
|
221
252
|
];
|
|
222
253
|
|
|
223
254
|
export function isWriteCapableScope(scope) {
|
|
224
|
-
|
|
255
|
+
// rewards:claim is the only rewards scope and it can award XP/Coins, so a
|
|
256
|
+
// --scopes override naming it is write-capable like any :write scope.
|
|
257
|
+
return /:(write|emit|claim)$/.test(String(scope));
|
|
225
258
|
}
|
|
226
259
|
|
|
227
260
|
export async function sdkCommand(options) {
|
|
@@ -352,7 +385,13 @@ export async function sdkCall(options) {
|
|
|
352
385
|
const requestedScopes = options.sdkScopes.length
|
|
353
386
|
? options.sdkScopes
|
|
354
387
|
: endpoint.scopes || [];
|
|
355
|
-
|
|
388
|
+
// A curated readOnly method (rewards.getStatus) needs a scope whose name is
|
|
389
|
+
// write-capable but only ever sends its read operation; its own default
|
|
390
|
+
// scopes do not trip the gate. Explicit --scopes overrides always do.
|
|
391
|
+
const writeScopes =
|
|
392
|
+
endpoint.readOnly && !options.sdkScopes.length
|
|
393
|
+
? []
|
|
394
|
+
: requestedScopes.filter(isWriteCapableScope);
|
|
356
395
|
// writeWhen covers endpoints that mutate under a read scope depending on
|
|
357
396
|
// their args (e.g. reminders.getDue acknowledging due reminders).
|
|
358
397
|
const writeByArgs =
|
|
@@ -410,6 +449,39 @@ export async function sdkCall(options) {
|
|
|
410
449
|
attempts.push(lastResult);
|
|
411
450
|
printSdkAttemptLine(attempts.length, lastResult);
|
|
412
451
|
}
|
|
452
|
+
} else if (endpoint.special === "rewards") {
|
|
453
|
+
const scopes = requestedScopes.length ? requestedScopes : endpoint.scopes;
|
|
454
|
+
const grant = await loadRewardRuntimeGrant({
|
|
455
|
+
options,
|
|
456
|
+
auth,
|
|
457
|
+
buildId,
|
|
458
|
+
methodName,
|
|
459
|
+
});
|
|
460
|
+
const tokenResult = await mintBuildApiToken({
|
|
461
|
+
options,
|
|
462
|
+
auth,
|
|
463
|
+
buildId,
|
|
464
|
+
scopes,
|
|
465
|
+
});
|
|
466
|
+
console.error(
|
|
467
|
+
`token minted in ${tokenResult.ms}ms (scopes: ${
|
|
468
|
+
tokenResult.scopes.join(", ") || "default"
|
|
469
|
+
}); published-runtime reward grant loaded from the server`,
|
|
470
|
+
);
|
|
471
|
+
const body = endpoint.mapArgs ? endpoint.mapArgs(args) : args;
|
|
472
|
+
for (let attempt = 0; attempt < options.repeat; attempt += 1) {
|
|
473
|
+
lastResult = await executeSdkHttpCall({
|
|
474
|
+
options,
|
|
475
|
+
method: "POST",
|
|
476
|
+
url: `${options.apiUrl}/build/${buildId}/api/rewards/${endpoint.operation}`,
|
|
477
|
+
authToken: auth.token,
|
|
478
|
+
body,
|
|
479
|
+
buildApiToken: tokenResult.token,
|
|
480
|
+
headers: { "x-build-reward-runtime": grant },
|
|
481
|
+
});
|
|
482
|
+
attempts.push(lastResult);
|
|
483
|
+
printSdkAttemptLine(attempts.length, lastResult);
|
|
484
|
+
}
|
|
413
485
|
} else if (
|
|
414
486
|
endpoint.special === "userDbQuery" ||
|
|
415
487
|
endpoint.special === "userDbExec"
|
|
@@ -492,6 +564,34 @@ export function printSdkAttemptLine(attemptNumber, result) {
|
|
|
492
564
|
);
|
|
493
565
|
}
|
|
494
566
|
|
|
567
|
+
// The published-runtime reward grant is a server-issued, short-lived JWT that
|
|
568
|
+
// the canonical runtime payload carries only for the current approved
|
|
569
|
+
// published release. Reading it from that payload keeps every rewards call on
|
|
570
|
+
// the same verified path the app itself uses; the CLI never fabricates one.
|
|
571
|
+
export async function loadRewardRuntimeGrant({
|
|
572
|
+
options,
|
|
573
|
+
auth,
|
|
574
|
+
buildId,
|
|
575
|
+
methodName,
|
|
576
|
+
request = requestJson,
|
|
577
|
+
}) {
|
|
578
|
+
const payload = await request({
|
|
579
|
+
url: `${options.apiUrl}/build/${buildId}/runtime?runtimeSource=published`,
|
|
580
|
+
authToken: auth.token,
|
|
581
|
+
timeoutMs: options.timeoutMs,
|
|
582
|
+
});
|
|
583
|
+
const grant = payload?.build?.rewardRuntimeGrant;
|
|
584
|
+
if (typeof grant !== "string" || !grant) {
|
|
585
|
+
throw new Error(
|
|
586
|
+
`${methodName}: the server issued no published-runtime reward grant for build ${buildId}. ` +
|
|
587
|
+
"Rewards exist only for the current approved public release (drafts, " +
|
|
588
|
+
"private apps and unapproved releases return preview mode in the app), " +
|
|
589
|
+
"and the grant is issued to the signed-in account. Nothing was called.",
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
return grant;
|
|
593
|
+
}
|
|
594
|
+
|
|
495
595
|
export async function executeSdkHttpCall({
|
|
496
596
|
options,
|
|
497
597
|
method,
|
|
@@ -499,6 +599,7 @@ export async function executeSdkHttpCall({
|
|
|
499
599
|
authToken,
|
|
500
600
|
body,
|
|
501
601
|
buildApiToken,
|
|
602
|
+
headers = {},
|
|
502
603
|
}) {
|
|
503
604
|
const startedAt = Date.now();
|
|
504
605
|
const { response, text } = await requestText({
|
|
@@ -507,7 +608,10 @@ export async function executeSdkHttpCall({
|
|
|
507
608
|
authToken,
|
|
508
609
|
body,
|
|
509
610
|
timeoutMs: options.timeoutMs,
|
|
510
|
-
headers:
|
|
611
|
+
headers: {
|
|
612
|
+
...(buildApiToken ? { "x-build-api-token": buildApiToken } : {}),
|
|
613
|
+
...headers,
|
|
614
|
+
},
|
|
511
615
|
});
|
|
512
616
|
return {
|
|
513
617
|
ok: response.ok,
|
package/lib/thumbnail.js
CHANGED
|
@@ -42,7 +42,7 @@ export async function thumbnailCommand(options) {
|
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
throw new Error(
|
|
45
|
-
'Usage: lumine thumbnail set <file> | lumine thumbnail capture [--out <file>] | lumine thumbnail generate "<prompt>" --model <gpt-image-2|nano-banana>',
|
|
45
|
+
'Usage: lumine thumbnail set <file> | lumine thumbnail capture [--out <file>] | lumine thumbnail generate "<prompt>" --model <gpt-image-2.5-flare|gpt-image-2.5-sunburst|gpt-image-2|nano-banana>',
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -276,7 +276,7 @@ export async function thumbnailGenerate(options) {
|
|
|
276
276
|
}
|
|
277
277
|
if (selectedOption) {
|
|
278
278
|
console.log(
|
|
279
|
-
` Estimated
|
|
279
|
+
` Estimated image output: ${formatBatteryPercent(selectedOption.energyUnits, estimate?.fullBatteryUnits)} of a full AI battery (~$${Number(selectedOption.estimatedUsd || 0).toFixed(2)}). Prompt input uses additional energy.`,
|
|
280
280
|
);
|
|
281
281
|
}
|
|
282
282
|
if (estimate) {
|
package/package.json
CHANGED
package/sdk/BUILD_SDK_INDEX.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Version: 1.41.0
|
|
4
4
|
Updated: 2026-09-08
|
|
5
|
-
Generated: 2026-09-
|
|
5
|
+
Generated: 2026-09-09T02:13:12.499Z
|
|
6
6
|
|
|
7
7
|
## Notes
|
|
8
8
|
- This SDK is injected into Build iframes via the Build preview/runtime.
|
|
@@ -488,8 +488,8 @@ const result = await Twinkle.ai.chat({ message, history: chatHistory, systemProm
|
|
|
488
488
|
- Listen to shared runtime AI chat stream events.
|
|
489
489
|
- Usually prefer per-call onText/onStatus callbacks on Twinkle.ai.chat.
|
|
490
490
|
- Events include requestId plus type status, text, done, or error.
|
|
491
|
-
- async generateImage({ prompt, referenceImageB64, previousResponseId, previousImageId, engine, quality, requestId, onStatus, timeoutMs } = {}) | scopes: none
|
|
492
|
-
- Returns: { success, imageUrl, responseId, imageId, engine, quality, aiUsagePolicy } or { success: false, error, reason, code, aiUsagePolicy }
|
|
491
|
+
- async generateImage({ prompt, referenceImageB64, previousResponseId, previousImageId, engine, model, quality, requestId, onStatus, timeoutMs } = {}) | scopes: none
|
|
492
|
+
- Returns: { success, imageUrl, responseId, imageId, engine, model, quality, aiUsagePolicy } or { success: false, error, reason, code, aiUsagePolicy }
|
|
493
493
|
- Generate or edit an image from a prompt and optional base64/data-URL reference image.
|
|
494
494
|
- Signed-in viewers only.
|
|
495
495
|
- Each successful image generation consumes AI Energy from the signed-in viewer.
|
|
@@ -502,6 +502,10 @@ const result = await Twinkle.ai.chat({ message, history: chatHistory, systemProm
|
|
|
502
502
|
- Pass requestId when you need to correlate browser logs, backend logs, and iframe status events for one generation.
|
|
503
503
|
- partial_image statuses may include partialImageB64 for progressive preview UI before the final imageUrl arrives.
|
|
504
504
|
- referenceImageB64 may be a raw base64 string or a data:image/...;base64 URL.
|
|
505
|
+
- Optional model: gpt-image-2.5-flare or gpt-image-2.5-sunburst. Without a model, OpenAI uses Flare for new images and Sunburst when a reference image or continuation is supplied. Explicit gpt-image-2 remains supported.
|
|
506
|
+
- Quality accepts low, medium, high, xhigh, or max. xhigh and max require a GPT Image 2.5 model. Gemini has one quality tier.
|
|
507
|
+
- GPT Image 2.5 battery spending uses actual image-model input and output token usage. The confirmation shows an image-output estimate; prompts and reference images use additional energy.
|
|
508
|
+
- responseId and imageId are opaque continuation handles. Pass them back unchanged to edit a prior result; do not assume an OpenAI ID format. Existing GPT Image 2 continuations remain usable.
|
|
505
509
|
- Example: const result = await Twinkle.ai.generateImage({ prompt: 'Create a fashion guide portrait for this face with flattering colors and outfit ideas', referenceImageB64, quality: 'high', onStatus: (status) => console.log(status.stage) });
|
|
506
510
|
- onImageGenerationStatus(listener) | scopes: none
|
|
507
511
|
- Returns: unsubscribe function
|
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -912,6 +912,27 @@ type DailyRunComplete = Success<{
|
|
|
912
912
|
rotationAdvanced: false; // legacy field; calendar schedules never advance by run
|
|
913
913
|
}>;
|
|
914
914
|
type DailyRunFail = DailyRunComplete;
|
|
915
|
+
// `escalation add` echoes the same open-item shape `escalation list` returns,
|
|
916
|
+
// so the run.escalation audit ID that `escalation set <auditId>` needs is in
|
|
917
|
+
// the add response; no list round-trip is required. `recordedAt` (the audit
|
|
918
|
+
// row's timestamp) appears only in `list`.
|
|
919
|
+
type DailyRunEscalationAdd = Success<{
|
|
920
|
+
escalation: {
|
|
921
|
+
auditId: number; // run.escalation audit event ID = escalation identity
|
|
922
|
+
runId: number;
|
|
923
|
+
targetType: string | null;
|
|
924
|
+
targetId: number | null;
|
|
925
|
+
url: string | null;
|
|
926
|
+
summary: string;
|
|
927
|
+
severity: "attention" | "urgent";
|
|
928
|
+
status: "open";
|
|
929
|
+
decisionNote: null;
|
|
930
|
+
decisionAuditId: null;
|
|
931
|
+
decisionRevision: 0;
|
|
932
|
+
decisionUpdatedAt: null;
|
|
933
|
+
decisionByUserId: null;
|
|
934
|
+
};
|
|
935
|
+
}>;
|
|
915
936
|
```
|
|
916
937
|
|
|
917
938
|
### Build Workshop sponsor applications and integrity
|
|
@@ -1169,6 +1190,8 @@ lumine admin subjects candidates --effort unassigned --json
|
|
|
1169
1190
|
lumine admin subjects candidates --unviewed --json
|
|
1170
1191
|
lumine admin builds candidates --all --limit 50 --json
|
|
1171
1192
|
lumine admin builds review build:884 --output-dir ./build-review --json
|
|
1193
|
+
lumine admin builds review build:884 --output-dir ./build-review \
|
|
1194
|
+
--interact ./build-review/steps.json --json
|
|
1172
1195
|
```
|
|
1173
1196
|
|
|
1174
1197
|
Schemas:
|
|
@@ -1294,6 +1317,17 @@ All-history traversal is deliberately available only through
|
|
|
1294
1317
|
boundary for bounded modes, so deploying a new CLI against an older API cannot
|
|
1295
1318
|
silently fall back to a million-row historical scan.
|
|
1296
1319
|
|
|
1320
|
+
`recommendations list` is the Earn Recommend picker, not a "new comments"
|
|
1321
|
+
feed: it returns only comments on subjects with an assigned effort level,
|
|
1322
|
+
whose length exceeds that level's threshold (>100 / >250 / >450 / >700
|
|
1323
|
+
characters for effort ≤2 / 3 / 4 / 5), with no skip row and no existing
|
|
1324
|
+
recommendation from any effective Level 5+ user (1000+ AP or Teacher
|
|
1325
|
+
authority), Zero, or Ciel. Community-recommended and short comments are
|
|
1326
|
+
deliberately absent, so an empty page or an empty window is normal and is
|
|
1327
|
+
not evidence of a broken walk; Featured-subject comments are reviewed through
|
|
1328
|
+
the Featured comment scan instead. A successful `post recommend` does not
|
|
1329
|
+
imply the target was queue-eligible.
|
|
1330
|
+
|
|
1297
1331
|
After upgrading the API and CLI to the stable run-start window, start a fresh
|
|
1298
1332
|
`--since-run` scan with a new checkpoint path, without `--resume`. Older
|
|
1299
1333
|
since-run checkpoints are rejected even when already exhausted: they may have
|
|
@@ -1342,6 +1376,53 @@ learned during that review. The receipt binds the draft to the exact reviewed
|
|
|
1342
1376
|
artifact without copying a version number by hand; the server owns the Build,
|
|
1343
1377
|
version, method, and review-time fields around that understanding.
|
|
1344
1378
|
|
|
1379
|
+
Without `--interact` the review captures only the start screen after
|
|
1380
|
+
`--wait-ms`. `--interact <steps.json>` adds a bounded, ordered interaction
|
|
1381
|
+
script that runs inside the app's runtime iframe after that start screenshot,
|
|
1382
|
+
so the receipt can show what happens when the app is actually used. The file is
|
|
1383
|
+
a JSON array (or `{ "steps": [...] }`) of at most **12** steps, each exactly one
|
|
1384
|
+
of:
|
|
1385
|
+
|
|
1386
|
+
```json
|
|
1387
|
+
[
|
|
1388
|
+
{ "click": "text=Start" },
|
|
1389
|
+
{ "wait": 1500 },
|
|
1390
|
+
{ "screenshot": "after-start" },
|
|
1391
|
+
{ "type": { "selector": "input[name=name]", "text": "Zero" } },
|
|
1392
|
+
{ "press": "Enter" },
|
|
1393
|
+
{ "press": "ArrowLeft" },
|
|
1394
|
+
{ "screenshot": "moved" }
|
|
1395
|
+
]
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
- `click`: a CSS selector, or `text=<visible text>` (case-insensitive; the
|
|
1399
|
+
smallest visible element whose text matches, then a bounded contains match).
|
|
1400
|
+
Dispatched as a trusted mouse click at the element's centre, so canvas games
|
|
1401
|
+
and buttons both receive it.
|
|
1402
|
+
- `type`: `{ selector, text }` — clicks the element, then inserts single-line
|
|
1403
|
+
text (at most 200 characters) as trusted input.
|
|
1404
|
+
- `press`: `Enter`, `Space`, `Escape`, `Tab`, `Backspace`, `ArrowUp/Down/Left/Right`,
|
|
1405
|
+
a letter, or a digit. The frame is focused first if nothing was clicked yet.
|
|
1406
|
+
- `wait`: 1–5000 ms.
|
|
1407
|
+
- `screenshot`: a unique label (1–40 letters/digits/`-`/`_`, not `runtime` or
|
|
1408
|
+
`review`); saved as `<label>.png` beside `runtime.png`.
|
|
1409
|
+
|
|
1410
|
+
The whole script is capped at **60 s**; it stops at the first failed step
|
|
1411
|
+
(element not found/not visible, budget exhausted, frame unreachable). Console
|
|
1412
|
+
evidence stays bounded exactly as before. `review.json` gains
|
|
1413
|
+
`screenshots: [{ label, path, bytes }]` (script screenshots only; the start
|
|
1414
|
+
screen stays in `screenshot`) and
|
|
1415
|
+
`interaction: { path, stepsPlanned, stepsCompleted, status, failedStep, frame,
|
|
1416
|
+
elapsedMs, steps }` with a per-step record (coordinates for clicks, the saved
|
|
1417
|
+
path for screenshots, the error for a failed step). Both are `[]`/`null`
|
|
1418
|
+
without `--interact`. The review remains one receipt bound to one artifact: the
|
|
1419
|
+
published version is re-read after the script finishes, and a script that did
|
|
1420
|
+
not complete makes the receipt `failed` with
|
|
1421
|
+
`CLI_ADMIN_BUILD_REVIEW_INTERACTION_FAILED` (the completed steps and their
|
|
1422
|
+
screenshots are still listed) so a draft can never cite interactions that did
|
|
1423
|
+
not happen. `comment draft --review-receipt` accepts a receipt only when every
|
|
1424
|
+
listed screenshot still exists unchanged and the script completed.
|
|
1425
|
+
|
|
1345
1426
|
During every full daily management review, scan recent Build candidates back through the
|
|
1346
1427
|
run's review window alongside Subjects and the recommendation queue. An app
|
|
1347
1428
|
that is thin, broken, private, unchanged since a prior substantive bot
|
|
@@ -1736,7 +1817,8 @@ lumine admin featured comments scan --checkpoint featured-read.json --json
|
|
|
1736
1817
|
# On interruption: repeat with --resume, in the same active run.
|
|
1737
1818
|
# Read ALL pageFiles, including full root context and nested replies.
|
|
1738
1819
|
lumine admin featured comments acknowledge \
|
|
1739
|
-
--checkpoint featured-read.json --reviewed
|
|
1820
|
+
--checkpoint featured-read.json --reviewed \
|
|
1821
|
+
--decisions-template featured-decisions.json --json
|
|
1740
1822
|
```
|
|
1741
1823
|
|
|
1742
1824
|
The scan snapshots every current Featured Subject (up to 100) and its maximum
|
|
@@ -1757,6 +1839,26 @@ missing Subject IDs and comment counts, and distinguishes complete from partial
|
|
|
1757
1839
|
coverage. A download alone never counts as a read or grants recommendations.
|
|
1758
1840
|
After a resumed scan fills a gap, read those pages and acknowledge again.
|
|
1759
1841
|
|
|
1842
|
+
Acknowledge returns `data.reviewedCoverage` (the coverage receipt; its `id` is
|
|
1843
|
+
the `coverageId` the recommend step needs — it is a different audit row from
|
|
1844
|
+
the review ID) and a ready-to-fill `data.decisionsTemplate`
|
|
1845
|
+
`{ reviewId, coverageId, selections: [] }`. With `--decisions-template <file>`
|
|
1846
|
+
the CLI also writes that template as a private mode-0600 file; only
|
|
1847
|
+
`acknowledge --reviewed` writes it (a scan rejects the flag). Start the
|
|
1848
|
+
decisions file from the template rather than assembling the identifiers by
|
|
1849
|
+
hand. `readFeaturedSelections` refuses a file whose `coverageId` equals its
|
|
1850
|
+
`reviewId` before any request is sent, and the API answers a wrong receipt
|
|
1851
|
+
with `CLI_ADMIN_FEATURED_COVERAGE_MISMATCH` naming the expected receipt, e.g.
|
|
1852
|
+
`coverageId 5719 is not the acknowledged coverage receipt for review 5719
|
|
1853
|
+
(expected 5749; 5719 is the review ID itself).` with
|
|
1854
|
+
`details: { reviewId, suppliedCoverageId, expectedCoverageId,
|
|
1855
|
+
suppliedReceiptAction, suppliedCoverageReviewId }`, or
|
|
1856
|
+
`CLI_ADMIN_FEATURED_COVERAGE_MISSING` when the review has no acknowledged
|
|
1857
|
+
coverage in this run. Other receipt failures (a page ID from another run,
|
|
1858
|
+
operator, or actor) keep the generic
|
|
1859
|
+
`Receipt #<id> is not a completed <action> receipt of this active run,
|
|
1860
|
+
operator and actor.` rejection.
|
|
1861
|
+
|
|
1760
1862
|
Compose a decisions JSON file from the genuinely reviewed comments, using the
|
|
1761
1863
|
returned review ID, coverage receipt ID, and each selected comment's page ID:
|
|
1762
1864
|
|
|
@@ -2232,7 +2334,9 @@ streak — "I'm telling you: Stop", guilt framing, ordering him to quit Daily
|
|
|
2232
2334
|
Reflections — and it surfaced only because the kid showed Mikey).
|
|
2233
2335
|
|
|
2234
2336
|
`bot-output` returns, windowed since the operator's last completed full run
|
|
2235
|
-
(`--days 1..30` overrides
|
|
2337
|
+
(`--days 1..30` overrides; the bare form sends no `days` parameter at all, so
|
|
2338
|
+
the API applies that default window — an older CLI wrongly validated the empty
|
|
2339
|
+
default and failed with "--days must be an integer"): `chatMessages` (every stored Zero/Ciel chat and
|
|
2236
2340
|
reflection reply, with full text and recipient metadata when its best-effort
|
|
2237
2341
|
prompt audit exists) and `comments`
|
|
2238
2342
|
(every public bot comment/reply). Individual utterances are returned in full;
|
|
@@ -2399,7 +2503,16 @@ checking, and in-place truncation occur on the same open descriptor. It then
|
|
|
2399
2503
|
returns `post_clear_review_required` with another immutable snapshot. That
|
|
2400
2504
|
snapshot also captures normal-output bytes that arrived after the prior
|
|
2401
2505
|
acknowledged cutoff, so routine stdout traffic cannot make the review infinite.
|
|
2402
|
-
Read it and run the same `finish --reviewed` command again.
|
|
2506
|
+
Read it and run the same `finish --reviewed` command again. **Only
|
|
2507
|
+
`data.completionStatus: "completed"` means the review is done.** The top-level
|
|
2508
|
+
`status` mirrors it: `"needs_review"` for both non-terminal outcomes
|
|
2509
|
+
(`needs_review` and `post_clear_review_required`, including a recovered
|
|
2510
|
+
pending snapshot), `"success"` only when `completed`, and `"already_done"` for
|
|
2511
|
+
a replay of an already-finished review. `ok` stays `true` in every case; a
|
|
2512
|
+
`needs_review` result is a valid response that requires another read plus
|
|
2513
|
+
finish, not an error. The CLI derives the top-level status from
|
|
2514
|
+
`completionStatus`, so it is correct against an API that still answers the
|
|
2515
|
+
older `success` envelope. A review clears
|
|
2403
2516
|
`twinkle-api.err.log` at most once. The lease closes when the reviewed error
|
|
2404
2517
|
boundary is still stable, i.e. every byte now in the API error log arrived
|
|
2405
2518
|
after that clear and was captured and acknowledged; errors that arrive before
|
|
@@ -3003,12 +3116,19 @@ farm-signal sections added that day; AI Card summon watch added 2026-08-24):
|
|
|
3003
3116
|
is never called a dodge. Offers without redemptions are a reason to inspect
|
|
3004
3117
|
sample size, event type, and offer age — not proof of a broken funnel by
|
|
3005
3118
|
themselves.
|
|
3006
|
-
- `goneQuiet` — the inverse of `notableCandidates
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3119
|
+
- `goneQuiet` — the inverse of `notableCandidates`, and window-relative: a
|
|
3120
|
+
user "went quiet" the moment 7 days of silence passed since their
|
|
3121
|
+
`lastActive`, and the section lists only the users whose quiet moment fell
|
|
3122
|
+
inside the brief's window (`lastActive` between `sinceTs - 7d` and
|
|
3123
|
+
`now - 7d`). A one-day brief therefore reports one day of crossings and
|
|
3124
|
+
contiguous daily runs list each user once; it never re-lists everyone seen
|
|
3125
|
+
in the past fortnight. `totals.wentQuiet` is that cohort; `previouslyRegular`
|
|
3126
|
+
is the subset with at least 7 distinct active days (completed daily tasks or
|
|
3127
|
+
Wordle plays) in the 30 days ending on their own last active day, and
|
|
3128
|
+
`users` is that subset ranked by `regularityScore` (`dailyTasks * 2 +
|
|
3129
|
+
wordlePlays`, both measured over the same per-user span), capped at 15 with
|
|
3130
|
+
`daysQuiet`. Use it for product signal (what did they stop doing?) and
|
|
3131
|
+
gentle outreach candidates; never guilt a child in public about absence.
|
|
3012
3132
|
- `newUserFunnel` — signups in the window with `activeOnDayOne` (any
|
|
3013
3133
|
XP-ledger event within 24h of joining) and `returnedAfterDayOne`
|
|
3014
3134
|
(`lastActive` beyond their first day), plus the newest few accounts.
|