@stage5/lumine 0.2.99 → 0.3.1
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/lib/admin.js +265 -4
- package/lib/commands.js +5 -0
- package/package.json +1 -1
- package/sdk/LUMINE_ADMIN.md +78 -0
package/lib/admin.js
CHANGED
|
@@ -640,6 +640,8 @@ export async function adminCommand(options) {
|
|
|
640
640
|
let runScope = null;
|
|
641
641
|
let correctionSessionId = 0;
|
|
642
642
|
let correctionSession = null;
|
|
643
|
+
let commentSessionId = 0;
|
|
644
|
+
let commentSession = null;
|
|
643
645
|
if (adminOperationRequiresRun(operation)) {
|
|
644
646
|
if (operation.correctionEligible) {
|
|
645
647
|
const correctionStatus = await requestJson({
|
|
@@ -657,6 +659,15 @@ export async function adminCommand(options) {
|
|
|
657
659
|
correctionSession = correction;
|
|
658
660
|
}
|
|
659
661
|
}
|
|
662
|
+
if (!correctionSessionId && adminOperationUsesCommentSession(operation)) {
|
|
663
|
+
commentSession = await loadActiveCommentSession({
|
|
664
|
+
options,
|
|
665
|
+
authToken: auth.token,
|
|
666
|
+
});
|
|
667
|
+
commentSessionId = Number(commentSession?.id || 0);
|
|
668
|
+
}
|
|
669
|
+
// Look up the daily run too: when both are active the caller must choose
|
|
670
|
+
// one (--via), and exactly one authority is sent.
|
|
660
671
|
const runStatus = correctionSessionId
|
|
661
672
|
? null
|
|
662
673
|
: await requestJson({
|
|
@@ -671,9 +682,39 @@ export async function adminCommand(options) {
|
|
|
671
682
|
].includes(operation.name)
|
|
672
683
|
? runStatus?.data?.lastRun || null
|
|
673
684
|
: null;
|
|
674
|
-
|
|
685
|
+
let selectedRun = activeRun || retryableFinishedRun;
|
|
686
|
+
const via = parseAdminVia(options.adminVia, operation);
|
|
687
|
+
if (via && correctionSessionId) {
|
|
688
|
+
throw cliValidationError(
|
|
689
|
+
`Correction session #${correctionSessionId} is active for this comment and handles it; drop --via or complete the correction first.`,
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
if (commentSessionId && selectedRun) {
|
|
693
|
+
let choice = via;
|
|
694
|
+
if (!choice) {
|
|
695
|
+
if (COMMENT_SESSION_WRITE_OPERATIONS.has(operation.name)) {
|
|
696
|
+
throw cliValidationError(
|
|
697
|
+
`Both comment session #${commentSessionId} (${commentSession.identity?.key || "unknown"}) and daily run #${selectedRun.id} (${selectedRun.identity?.key || "unknown"}) are active. Choose which one this ${operation.name} uses with --via session or --via run.`,
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
// Reads create nothing; without --via they keep using the daily run.
|
|
701
|
+
choice = "run";
|
|
702
|
+
}
|
|
703
|
+
if (choice === "session") {
|
|
704
|
+
selectedRun = null;
|
|
705
|
+
} else {
|
|
706
|
+
commentSessionId = 0;
|
|
707
|
+
commentSession = null;
|
|
708
|
+
}
|
|
709
|
+
} else if (via === "session" && !commentSessionId) {
|
|
710
|
+
throw cliValidationError(
|
|
711
|
+
"--via session: no comment session is active. Start one with lumine admin comment session start.",
|
|
712
|
+
);
|
|
713
|
+
} else if (via === "run" && !selectedRun && !correctionSessionId) {
|
|
714
|
+
throw noActiveRunError();
|
|
715
|
+
}
|
|
675
716
|
runId = Number(selectedRun?.id || 0);
|
|
676
|
-
if (!runId && !correctionSessionId) {
|
|
717
|
+
if (!runId && !correctionSessionId && !commentSessionId) {
|
|
677
718
|
if (operation.correctionEligible) {
|
|
678
719
|
const error = new Error(
|
|
679
720
|
`Start a correction session first: lumine admin correction start ${operation.correctionCommentId}.`,
|
|
@@ -687,7 +728,17 @@ export async function adminCommand(options) {
|
|
|
687
728
|
runScope = canonicalAdminRunScope(selectedRun);
|
|
688
729
|
assertAdminOperationAllowedForRunScope({ operation, runScope });
|
|
689
730
|
}
|
|
690
|
-
if (options.adminIdentity &&
|
|
731
|
+
if (options.adminIdentity && commentSession) {
|
|
732
|
+
const requestedIdentity = parseIdentity(options.adminIdentity);
|
|
733
|
+
if (
|
|
734
|
+
requestedIdentity !== "auto" &&
|
|
735
|
+
requestedIdentity !== commentSession.identity?.key
|
|
736
|
+
) {
|
|
737
|
+
throw cliValidationError(
|
|
738
|
+
`--identity ${requestedIdentity} does not match comment session #${commentSessionId} (${commentSession.identity?.key || "unknown"}).`,
|
|
739
|
+
);
|
|
740
|
+
}
|
|
741
|
+
} else if (options.adminIdentity && (selectedRun || correctionSession)) {
|
|
691
742
|
const requestedIdentity = parseIdentity(options.adminIdentity);
|
|
692
743
|
const canonicalIdentity = correctionSession
|
|
693
744
|
? correctionSession.identity?.key
|
|
@@ -722,6 +773,11 @@ export async function adminCommand(options) {
|
|
|
722
773
|
String(correctionSessionId),
|
|
723
774
|
}
|
|
724
775
|
: {}),
|
|
776
|
+
...(commentSessionId
|
|
777
|
+
? {
|
|
778
|
+
"x-lumine-admin-comment-session-id": String(commentSessionId),
|
|
779
|
+
}
|
|
780
|
+
: {}),
|
|
725
781
|
...(requestId ? { "x-lumine-idempotency-key": requestId } : {}),
|
|
726
782
|
},
|
|
727
783
|
// Long operations (reward-bank seed) carry their own timeout.
|
|
@@ -1529,6 +1585,81 @@ export function assertAdminOperationAllowedForRunScope({
|
|
|
1529
1585
|
);
|
|
1530
1586
|
}
|
|
1531
1587
|
|
|
1588
|
+
// Commands a standalone comment session can serve: drafting, replying,
|
|
1589
|
+
// publishing and editing its own comments, plus the reads needed to compose
|
|
1590
|
+
// on its targets. The API still enforces the session's target lock on each.
|
|
1591
|
+
const COMMENT_SESSION_WRITE_OPERATIONS = new Set([
|
|
1592
|
+
"comment.draft",
|
|
1593
|
+
"comment.post",
|
|
1594
|
+
"comment.edit",
|
|
1595
|
+
]);
|
|
1596
|
+
const COMMENT_SESSION_OPERATIONS = new Set([
|
|
1597
|
+
...COMMENT_SESSION_WRITE_OPERATIONS,
|
|
1598
|
+
"subject.get",
|
|
1599
|
+
"subject.comments",
|
|
1600
|
+
"post.get",
|
|
1601
|
+
"post.comments",
|
|
1602
|
+
]);
|
|
1603
|
+
|
|
1604
|
+
export function adminOperationUsesCommentSession(operation) {
|
|
1605
|
+
return COMMENT_SESSION_OPERATIONS.has(operation?.name);
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
export function parseAdminVia(value, operation) {
|
|
1609
|
+
const via = String(value || "").trim().toLowerCase();
|
|
1610
|
+
if (!via) return "";
|
|
1611
|
+
if (via !== "session" && via !== "run") {
|
|
1612
|
+
throw cliValidationError("--via must be session or run.");
|
|
1613
|
+
}
|
|
1614
|
+
if (!adminOperationUsesCommentSession(operation)) {
|
|
1615
|
+
throw cliValidationError(
|
|
1616
|
+
"--via applies only to comment draft/reply/post/edit and the subject/post/comment reads.",
|
|
1617
|
+
);
|
|
1618
|
+
}
|
|
1619
|
+
return via;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
async function loadActiveCommentSession({ options, authToken }) {
|
|
1623
|
+
let status;
|
|
1624
|
+
try {
|
|
1625
|
+
status = await requestJson({
|
|
1626
|
+
url: `${options.apiUrl}/cli/admin/comment-sessions/status`,
|
|
1627
|
+
authToken,
|
|
1628
|
+
timeoutMs: options.timeoutMs,
|
|
1629
|
+
});
|
|
1630
|
+
} catch (error) {
|
|
1631
|
+
// An API without comment sessions yet: keep daily runs working.
|
|
1632
|
+
if (error?.status === 404) return null;
|
|
1633
|
+
throw error;
|
|
1634
|
+
}
|
|
1635
|
+
const session = status?.data?.commentSession || null;
|
|
1636
|
+
return session?.status === "active" && Number(session.id) > 0
|
|
1637
|
+
? session
|
|
1638
|
+
: null;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
export function parseCommentSessionTargets(values) {
|
|
1642
|
+
const raw = values
|
|
1643
|
+
.flatMap((value) => String(value || "").split(","))
|
|
1644
|
+
.map((value) => value.trim())
|
|
1645
|
+
.filter(Boolean);
|
|
1646
|
+
if (!raw.length) {
|
|
1647
|
+
throw cliValidationError(
|
|
1648
|
+
"Name the targets with --target <subject:ID|comment:ID|build:ID|aiStory:ID|dailyReflection:ID>[,...].",
|
|
1649
|
+
);
|
|
1650
|
+
}
|
|
1651
|
+
const seen = new Set();
|
|
1652
|
+
const targets = [];
|
|
1653
|
+
for (const value of raw) {
|
|
1654
|
+
const target = parseAdminCommentTarget({ target: value });
|
|
1655
|
+
const key = `${target.type}:${target.id}`;
|
|
1656
|
+
if (seen.has(key)) continue;
|
|
1657
|
+
seen.add(key);
|
|
1658
|
+
targets.push(target);
|
|
1659
|
+
}
|
|
1660
|
+
return targets;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1532
1663
|
function adminOperationRequiresRun(operation) {
|
|
1533
1664
|
if (typeof operation.requiresRun === "boolean") {
|
|
1534
1665
|
return operation.requiresRun;
|
|
@@ -1547,6 +1678,9 @@ function adminOperationRequiresRun(operation) {
|
|
|
1547
1678
|
"correction.start",
|
|
1548
1679
|
"correction.status",
|
|
1549
1680
|
"correction.complete",
|
|
1681
|
+
"comment-session.start",
|
|
1682
|
+
"comment-session.status",
|
|
1683
|
+
"comment-session.close",
|
|
1550
1684
|
"escalation.list",
|
|
1551
1685
|
"escalation.set",
|
|
1552
1686
|
"notable.add",
|
|
@@ -2283,7 +2417,7 @@ export function parseAdminOperation(options) {
|
|
|
2283
2417
|
// long as two GPT-6 Sol calls, so it gets its own timeout. The daily
|
|
2284
2418
|
// website management run calls it in a bounded loop (Mikey, 2026-09-28).
|
|
2285
2419
|
const usage =
|
|
2286
|
-
"Usage: lumine admin reward-bank coverage <buildId> [--max-step 3] | seed <buildId> [--max-step 3].";
|
|
2420
|
+
"Usage: lumine admin reward-bank coverage <buildId> [--max-step 3] | seed <buildId> [--max-step 3] | report <buildId> [--days 1].";
|
|
2287
2421
|
const buildId = parseRequiredInteger(
|
|
2288
2422
|
target || options.adminBuild,
|
|
2289
2423
|
"Build ID",
|
|
@@ -2293,6 +2427,20 @@ export function parseAdminOperation(options) {
|
|
|
2293
2427
|
? parseRequiredInteger(options.adminMaxStep, "--max-step", 1)
|
|
2294
2428
|
: 3;
|
|
2295
2429
|
if (maxStep > 10) throw cliValidationError("--max-step must be at most 10.");
|
|
2430
|
+
if (action === "report") {
|
|
2431
|
+
const days = options.adminDays
|
|
2432
|
+
? parseRequiredInteger(options.adminDays, "--days", 1)
|
|
2433
|
+
: 1;
|
|
2434
|
+
if (days > 31) throw cliValidationError("--days must be at most 31.");
|
|
2435
|
+
return readOperation(
|
|
2436
|
+
"reward-bank.report",
|
|
2437
|
+
withQuery("/cli/admin/reward-bank/report", {
|
|
2438
|
+
buildId: String(buildId),
|
|
2439
|
+
days: String(days),
|
|
2440
|
+
}),
|
|
2441
|
+
{ requiresRun: false },
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2296
2444
|
if (action === "coverage" || action === "status") {
|
|
2297
2445
|
return readOperation(
|
|
2298
2446
|
"reward-bank.coverage",
|
|
@@ -3361,6 +3509,51 @@ export function parseAdminOperation(options) {
|
|
|
3361
3509
|
}
|
|
3362
3510
|
|
|
3363
3511
|
if (namespace === "comment") {
|
|
3512
|
+
if (action === "session") {
|
|
3513
|
+
const sessionAction = target || "status";
|
|
3514
|
+
if (sessionAction === "status") {
|
|
3515
|
+
return readOperation(
|
|
3516
|
+
"comment-session.status",
|
|
3517
|
+
"/cli/admin/comment-sessions/status",
|
|
3518
|
+
);
|
|
3519
|
+
}
|
|
3520
|
+
if (sessionAction === "start") {
|
|
3521
|
+
const identity = String(options.adminIdentity || "")
|
|
3522
|
+
.trim()
|
|
3523
|
+
.toLowerCase();
|
|
3524
|
+
if (identity !== "zero" && identity !== "ciel") {
|
|
3525
|
+
throw cliValidationError(
|
|
3526
|
+
"comment session start needs --identity zero|ciel.",
|
|
3527
|
+
);
|
|
3528
|
+
}
|
|
3529
|
+
const targets = parseCommentSessionTargets([
|
|
3530
|
+
options.adminTarget,
|
|
3531
|
+
...(options.positional || []).slice(3),
|
|
3532
|
+
]);
|
|
3533
|
+
const reason = String(options.adminReason || "").trim();
|
|
3534
|
+
return writeOperation(
|
|
3535
|
+
"comment-session.start",
|
|
3536
|
+
"POST",
|
|
3537
|
+
"/cli/admin/comment-sessions",
|
|
3538
|
+
{
|
|
3539
|
+
identity,
|
|
3540
|
+
targets,
|
|
3541
|
+
...(reason ? { reason } : {}),
|
|
3542
|
+
},
|
|
3543
|
+
);
|
|
3544
|
+
}
|
|
3545
|
+
if (sessionAction === "close") {
|
|
3546
|
+
return writeOperation(
|
|
3547
|
+
"comment-session.close",
|
|
3548
|
+
"POST",
|
|
3549
|
+
"/cli/admin/comment-sessions/close",
|
|
3550
|
+
{},
|
|
3551
|
+
);
|
|
3552
|
+
}
|
|
3553
|
+
throw cliValidationError(
|
|
3554
|
+
"Usage: lumine admin comment session start --identity zero|ciel --target <type:id>[,<type:id>...] [--reason <text>] | comment session status | comment session close.",
|
|
3555
|
+
);
|
|
3556
|
+
}
|
|
3364
3557
|
if (action === "draft" || action === "reply") {
|
|
3365
3558
|
const parsedTarget = parseAdminCommentTarget({
|
|
3366
3559
|
target,
|
|
@@ -5211,6 +5404,70 @@ function printReviewRequestResult({ operation, data, result }) {
|
|
|
5211
5404
|
printReviewEvents(data.events);
|
|
5212
5405
|
}
|
|
5213
5406
|
|
|
5407
|
+
// Human view of `lumine admin reward-bank report`; --json has everything.
|
|
5408
|
+
function printRewardBankReport(data) {
|
|
5409
|
+
const pct = (value) =>
|
|
5410
|
+
value === null || value === undefined ? "–" : `${Math.round(value * 100)}%`;
|
|
5411
|
+
console.log(`Question bank report · build #${data.buildId} · last ${data.days} day(s)`);
|
|
5412
|
+
if (data.coverage) {
|
|
5413
|
+
console.log(
|
|
5414
|
+
`Seeding coverage (steps 1-3): ${data.coverage.filled} / ${data.coverage.total}`,
|
|
5415
|
+
);
|
|
5416
|
+
}
|
|
5417
|
+
if (!data.generation) {
|
|
5418
|
+
console.log("Generation: no telemetry table yet.");
|
|
5419
|
+
} else {
|
|
5420
|
+
for (const row of data.generation.bySource) {
|
|
5421
|
+
if (!row.rounds) continue;
|
|
5422
|
+
console.log(
|
|
5423
|
+
`Generation (${row.source}): ${row.rounds} rounds · ${row.accepted} accepted (${pct(row.acceptRate)}) · off-brief ${row.offBrief} · answer mismatch ${row.answerMismatch} · malformed ${row.malformed} · errors ${row.errors} · $${row.costUsd} · avg ${row.avgSeconds ?? "–"}s (max ${row.maxSeconds ?? "–"}s)`,
|
|
5424
|
+
);
|
|
5425
|
+
}
|
|
5426
|
+
for (const row of data.generation.recentRejections.slice(0, 5)) {
|
|
5427
|
+
console.log(
|
|
5428
|
+
` rejected ${row.outcome} ${row.ruleId}/${row.topicKey} step ${row.step}: ${row.problems || "(no reason given)"}`,
|
|
5429
|
+
);
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
console.log(`Learners: ${data.learners.total}`);
|
|
5433
|
+
for (const row of data.learners.byRuleStep) {
|
|
5434
|
+
console.log(
|
|
5435
|
+
` ${row.ruleId} step ${row.step}: served ${row.served} · answered ${row.answered} · first-try ${pct(row.firstTryRate)} · solved ${row.solved} · skipped ${row.skipped}`,
|
|
5436
|
+
);
|
|
5437
|
+
}
|
|
5438
|
+
const paid = data.payouts.reduce(
|
|
5439
|
+
(sum, row) => ({ xp: sum.xp + row.xp, coins: sum.coins + row.coins, receipts: sum.receipts + row.receipts }),
|
|
5440
|
+
{ xp: 0, coins: 0, receipts: 0 },
|
|
5441
|
+
);
|
|
5442
|
+
console.log(
|
|
5443
|
+
`Payouts: ${paid.receipts} receipts · ${paid.xp.toLocaleString()} XP · ${paid.coins.toLocaleString()} Coins`,
|
|
5444
|
+
);
|
|
5445
|
+
for (const row of data.payouts) {
|
|
5446
|
+
console.log(
|
|
5447
|
+
` ${row.ruleId} step ${row.step}: ${row.receipts} (${row.firstTry} first try) · ${row.earners} earners · ${row.xp.toLocaleString()} XP · ${row.coins.toLocaleString()} Coins`,
|
|
5448
|
+
);
|
|
5449
|
+
}
|
|
5450
|
+
if (data.ladder.length) {
|
|
5451
|
+
console.log(
|
|
5452
|
+
`Ladder now: ${data.ladder.map((row) => `${row.ruleId} s${row.step}:${row.topicLadders}`).join(", ")}`,
|
|
5453
|
+
);
|
|
5454
|
+
}
|
|
5455
|
+
const hidden = data.bank.filter((row) => row.status === "hidden");
|
|
5456
|
+
console.log(
|
|
5457
|
+
`Bank: ${data.bank.filter((row) => row.status === "active").reduce((n, row) => n + row.questions, 0)} active · ${hidden.reduce((n, row) => n + row.questions, 0)} hidden${hidden.length ? ` (${hidden.map((row) => `${row.hiddenReason}: ${row.questions}`).join(", ")})` : ""} · ${data.bank.reduce((n, row) => n + row.createdInWindow, 0)} new in window`,
|
|
5458
|
+
);
|
|
5459
|
+
for (const row of data.questionsToReview.reported) {
|
|
5460
|
+
console.log(
|
|
5461
|
+
` reported #${row.id} (${row.reports}, ${row.status}) ${row.ruleId}/${row.topicKey} s${row.step}: ${row.reasons}`,
|
|
5462
|
+
);
|
|
5463
|
+
}
|
|
5464
|
+
for (const row of data.questionsToReview.hardest) {
|
|
5465
|
+
console.log(
|
|
5466
|
+
` hardest #${row.id} ${row.ruleId}/${row.topicKey} s${row.step}: solved ${pct(row.solveRate)} of ${row.learners}`,
|
|
5467
|
+
);
|
|
5468
|
+
}
|
|
5469
|
+
}
|
|
5470
|
+
|
|
5214
5471
|
function printAdminResult({ operation, result }) {
|
|
5215
5472
|
const data = result?.data || {};
|
|
5216
5473
|
if (printChatSafetyResult({ operation, data })) return;
|
|
@@ -5218,6 +5475,10 @@ function printAdminResult({ operation, result }) {
|
|
|
5218
5475
|
printRewardActivity(data);
|
|
5219
5476
|
return;
|
|
5220
5477
|
}
|
|
5478
|
+
if (operation.name === "reward-bank.report") {
|
|
5479
|
+
printRewardBankReport(data);
|
|
5480
|
+
return;
|
|
5481
|
+
}
|
|
5221
5482
|
if (operation.name.startsWith("reward-bank.")) {
|
|
5222
5483
|
if (operation.name === "reward-bank.seed") {
|
|
5223
5484
|
console.log(
|
package/lib/commands.js
CHANGED
|
@@ -2597,6 +2597,7 @@ export function parseArgs(args) {
|
|
|
2597
2597
|
adminUnviewed: Boolean(raw.unviewed),
|
|
2598
2598
|
adminViewed: Boolean(raw.viewed),
|
|
2599
2599
|
adminIdentity: raw.identity ? String(raw.identity) : "",
|
|
2600
|
+
adminVia: raw.via ? String(raw.via) : "",
|
|
2600
2601
|
adminScope: raw.scope ? String(raw.scope) : "",
|
|
2601
2602
|
commentMode: raw.commentMode ? String(raw.commentMode) : "",
|
|
2602
2603
|
runKey: raw.runKey ? String(raw.runKey) : "",
|
|
@@ -3059,9 +3060,13 @@ export function printHelp() {
|
|
|
3059
3060
|
lumine admin post reward <target> [--type subject|comment|aiStory|dailyReflection] --twinkles 3 [--json]
|
|
3060
3061
|
lumine admin reward-bank coverage <buildId> [--max-step 3] [--json]
|
|
3061
3062
|
lumine admin reward-bank seed <buildId> [--max-step 3] [--json] (daily run: writes ONE missing seed question, lowest step first, billed to Zero/Ciel's AI Energy)
|
|
3063
|
+
lumine admin reward-bank report <buildId> [--days 1] [--json] (question-bank telemetry: generation outcomes, learners, payouts, ladder, questions to review)
|
|
3062
3064
|
lumine admin comment draft <target> [--type subject|comment|build|aiStory|dailyReflection] [--file <comment.md>] [--review-receipt <review.json>|--reviewed-version <id> --reviewed-via runtime|code] [--review-context <context.json>] [--identity zero|ciel|auto] [--json]
|
|
3063
3065
|
lumine admin comment reply comment:<id> [--file <reply.md>] [--reviewed-version <id> --reviewed-via runtime|code] [--review-context <context.json>] [--identity zero|ciel|auto] [--json]
|
|
3064
3066
|
lumine admin comment post --draft-id <id> [--json]
|
|
3067
|
+
lumine admin comment session start --identity zero|ciel --target <subject:ID|comment:ID|build:ID|aiStory:ID>[,...] [--reason <text>] [--json]
|
|
3068
|
+
lumine admin comment session status|close [--json]
|
|
3069
|
+
(with both a comment session and a daily run active, comment draft/reply/post/edit need --via session|run)
|
|
3065
3070
|
lumine admin comment edit <comment-id> --file <comment.md> [--review-receipt <review.json> | --reviewed-version <id> --reviewed-via runtime|code] [--review-context <context.json>] [--json]
|
|
3066
3071
|
lumine admin brief [--days <1..30>] [--json]
|
|
3067
3072
|
lumine admin ai-costs monthly [--output <file.json>] [--json] (no active run required)
|
package/package.json
CHANGED
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -1304,8 +1304,18 @@ them so the first learner on a unit never waits or pays.
|
|
|
1304
1304
|
```bash
|
|
1305
1305
|
lumine admin reward-bank coverage <buildId> [--max-step 3] [--json] # read-only, no run
|
|
1306
1306
|
lumine admin reward-bank seed <buildId> [--max-step 3] [--json] # delegated full run
|
|
1307
|
+
lumine admin reward-bank report <buildId> [--days 1] [--json] # read-only telemetry
|
|
1307
1308
|
```
|
|
1308
1309
|
|
|
1310
|
+
`report` covers the last `--days` (1-31): generation rounds by source
|
|
1311
|
+
(learner or seed) with outcomes (`accepted`, `off_brief`: the checker judged
|
|
1312
|
+
the question outside the brief's language, level or LaTeX rule;
|
|
1313
|
+
`answer_mismatch`; `malformed`; `error`), cost and time, and recent rejection
|
|
1314
|
+
reasons; learners served/answered/first-try/solved/skipped per rule and step;
|
|
1315
|
+
payouts; the current ladder distribution; active/hidden questions; the
|
|
1316
|
+
hardest questions (3+ learners) and every reported question with reasons;
|
|
1317
|
+
and seeding coverage for steps 1-3 once the app is approved.
|
|
1318
|
+
|
|
1309
1319
|
- `seed` writes ONE question per call into the first missing slot, lowest
|
|
1310
1320
|
step first across every topic of every generated-quiz rule, up to
|
|
1311
1321
|
`--max-step` (default 3, at most 10). A slot is a (rule, topic, step) with
|
|
@@ -4171,6 +4181,74 @@ canonical review record ID before claiming success. Older APIs that still block
|
|
|
4171
4181
|
Build edits must be deployed first; do not silently substitute a duplicate reply
|
|
4172
4182
|
when Mikey requested an edit.
|
|
4173
4183
|
|
|
4184
|
+
### Standalone comment sessions
|
|
4185
|
+
|
|
4186
|
+
Posting a few new comments or replies as Zero or Ciel does not require a full
|
|
4187
|
+
daily run. An approved operator names the identity and the exact targets:
|
|
4188
|
+
|
|
4189
|
+
```bash
|
|
4190
|
+
lumine admin comment session start --identity ciel \
|
|
4191
|
+
--target subject:123,comment:456 --reason "Mikey asked for a reply" --json
|
|
4192
|
+
lumine admin subject get 123 --include-comments --json
|
|
4193
|
+
lumine admin comment draft subject:123 --file comment.md --json
|
|
4194
|
+
lumine admin comment reply comment:456 --file reply.md --json
|
|
4195
|
+
lumine admin comment post --draft-id <id> --json
|
|
4196
|
+
lumine admin comment edit <commentId> --file corrected.md --json
|
|
4197
|
+
lumine admin comment session status --json
|
|
4198
|
+
lumine admin comment session close --json
|
|
4199
|
+
```
|
|
4200
|
+
|
|
4201
|
+
`--target` takes a comma-separated list (extra space-separated targets after
|
|
4202
|
+
it also count); each is `subject:ID`, `comment:ID`, `build:ID`, `aiStory:ID` or
|
|
4203
|
+
`dailyReflection:ID`, at most ten. Pass them in one `--target`: a repeated
|
|
4204
|
+
`--target` flag keeps only its last value. The server checks every target when
|
|
4205
|
+
the session starts and refuses one the draft path would refuse later.
|
|
4206
|
+
|
|
4207
|
+
What the session authorizes:
|
|
4208
|
+
|
|
4209
|
+
- **Scopes:** only `content:read`, `comment:draft` and `comment:post`. No chat,
|
|
4210
|
+
Featured, reward, recommendation, subject, newspaper or run-completion
|
|
4211
|
+
command works through it; those still need a daily run.
|
|
4212
|
+
- **Target lock:** drafts, replies and the reads above work only on the named
|
|
4213
|
+
targets. A reply target counts when it is a named `comment:ID`, or a comment
|
|
4214
|
+
sitting directly in a named subject, Build, AI Story or Daily Reflection.
|
|
4215
|
+
Anything else fails with `CLI_ADMIN_COMMENT_SESSION_TARGET_FORBIDDEN` and
|
|
4216
|
+
the allowed list. `comment post` accepts only drafts created in the session.
|
|
4217
|
+
- **Edits:** `comment edit <commentId>` works under the session only for a
|
|
4218
|
+
comment this session itself posted, as the same identity
|
|
4219
|
+
(`CLI_ADMIN_COMMENT_SESSION_EDIT_FORBIDDEN` otherwise). The usual edit rules
|
|
4220
|
+
apply unchanged, including fresh Build review evidence for a Build comment.
|
|
4221
|
+
Edits do not count toward the post cap.
|
|
4222
|
+
- **Limits:** 30 minutes, at most five published comments
|
|
4223
|
+
(`CLI_ADMIN_COMMENT_SESSION_POST_CAP`), one active session per operator.
|
|
4224
|
+
`close` or the TTL ends it; start a new session for more.
|
|
4225
|
+
- **Same bar as a daily run:** drafting and publishing use the exact daily-run
|
|
4226
|
+
draft/publish path. Build comments still require composed text, a review
|
|
4227
|
+
receipt or `--reviewed-version/--reviewed-via` evidence of the current
|
|
4228
|
+
published version, and `--review-context`; context and persona revisions
|
|
4229
|
+
are checked at publish time as usual.
|
|
4230
|
+
- **Identity:** the operator must be an approved delegated administrator, and
|
|
4231
|
+
the session posts only as the identity named at start (`--identity auto` is
|
|
4232
|
+
not accepted). It does not change the Bangkok calendar assignment or the
|
|
4233
|
+
identity preference.
|
|
4234
|
+
|
|
4235
|
+
A comment session and a daily run can be active together, but every request
|
|
4236
|
+
carries exactly one authority. The session never claims the daily-run slot.
|
|
4237
|
+
|
|
4238
|
+
- When only one of them is active, the CLI uses it.
|
|
4239
|
+
- When both are active, `comment draft`, `comment reply`, `comment post` and
|
|
4240
|
+
`comment edit` refuse to run until you choose with `--via session` or
|
|
4241
|
+
`--via run`. Target reads (`subject get`, `post get`, `comments get`, their
|
|
4242
|
+
comment lists) default to the daily run and take `--via session` to read
|
|
4243
|
+
through the session instead.
|
|
4244
|
+
- The API refuses a request that names a comment session together with a
|
|
4245
|
+
daily run or correction session (`CLI_ADMIN_AMBIGUOUS_AUTHORITY`); a session
|
|
4246
|
+
request outside the session is refused, never handed to the run.
|
|
4247
|
+
- A draft records the authority that created it, and only that authority can
|
|
4248
|
+
publish it (`CLI_ADMIN_DRAFT_AUTHORITY_MISMATCH`).
|
|
4249
|
+
- A correction session still takes precedence for its one comment; `--via` is
|
|
4250
|
+
refused while it is active for that comment.
|
|
4251
|
+
|
|
4174
4252
|
**Editing the bot's own comments.** `comment edit <commentId> --file
|
|
4175
4253
|
<comment.md>` replaces the text of a comment the acting bot itself authored —
|
|
4176
4254
|
for correcting a factual error, an unfulfillable claim, or outdated guidance
|