@stage5/lumine 0.3.23 → 0.3.24
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-daily.js +2 -0
- package/lib/admin-identity-inspection.js +66 -0
- package/lib/admin.js +51 -0
- package/lib/commands.js +3 -0
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +4 -4
- package/sdk/LUMINE_ADMIN.md +72 -1
package/lib/admin-daily.js
CHANGED
|
@@ -73,6 +73,7 @@ export function runAdmin(
|
|
|
73
73
|
"reward-review": ["list"],
|
|
74
74
|
approvals: ["list"],
|
|
75
75
|
todo: ["list"],
|
|
76
|
+
followup: ["list"],
|
|
76
77
|
escalation: ["list"],
|
|
77
78
|
featured: ["list", "history"],
|
|
78
79
|
sponsor: ["applications"],
|
|
@@ -192,6 +193,7 @@ export async function dailyRunGather(options) {
|
|
|
192
193
|
// flags: a required report section (agent-guides/lumine-admin.md)
|
|
193
194
|
step("approvals-pending", ["approvals", "list", "--status", "pending"], "approvals-pending.json");
|
|
194
195
|
step("todos", ["todo", "list"], "todos.json");
|
|
196
|
+
step("personal-followups", ["followup", "list"], "personal-followups.json");
|
|
195
197
|
step("escalations", ["escalation", "list"], "escalations.json");
|
|
196
198
|
step("featured-list", ["featured", "list"], "featured-start.json");
|
|
197
199
|
step(
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function seenAt(value) {
|
|
2
|
+
if (value == null || !Number.isFinite(Number(value)) || Number(value) <= 0)
|
|
3
|
+
return "unknown";
|
|
4
|
+
const date = new Date(Number(value) * 1000);
|
|
5
|
+
return Number.isNaN(date.getTime()) ? "unknown" : date.toISOString();
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function eventTypes(counts) {
|
|
9
|
+
const entries = Object.entries(counts || {}).sort(([a], [b]) =>
|
|
10
|
+
a.localeCompare(b),
|
|
11
|
+
);
|
|
12
|
+
return entries.length
|
|
13
|
+
? `; ${entries.map(([type, count]) => `${type}: ${count}`).join(", ")}`
|
|
14
|
+
: "";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Display observed device events separately from accounts and physical visits. */
|
|
18
|
+
export function formatIdentityDeviceUsage(inspection) {
|
|
19
|
+
const devices = inspection.sharedDevices || [];
|
|
20
|
+
const coverage = inspection.evidenceCoverage || {};
|
|
21
|
+
const lines = [];
|
|
22
|
+
if (devices.length) {
|
|
23
|
+
lines.push(
|
|
24
|
+
`Device history (${coverage.deviceLookbackDays || "unknown"} days): recorded account events, not visits or unique sessions.`,
|
|
25
|
+
);
|
|
26
|
+
for (const device of devices) {
|
|
27
|
+
const usage = device.usage;
|
|
28
|
+
if (!usage) {
|
|
29
|
+
lines.push(
|
|
30
|
+
` Device ${device.key}: usage counts unavailable from this API; ${device.accountCount ?? "unknown"} observed account(s).`,
|
|
31
|
+
);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (usage.status === "not_read" || usage.eventCount == null) {
|
|
35
|
+
lines.push(
|
|
36
|
+
` Device ${device.key}: usage and sharing not read (${usage.truncatedBy?.join(", ") || "incomplete evidence"}).`,
|
|
37
|
+
);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const partial = usage.status !== "complete";
|
|
41
|
+
const prefix = partial ? "at least " : "";
|
|
42
|
+
lines.push(
|
|
43
|
+
` Device ${device.key}: ${prefix}${usage.eventCount} recorded event(s), ${prefix}${device.accountCount} account(s)${eventTypes(usage.eventsByType)}.`,
|
|
44
|
+
` First observed ${seenAt(usage.firstSeenAt)}; last observed ${seenAt(usage.lastSeenAt)}.`,
|
|
45
|
+
);
|
|
46
|
+
if (partial) {
|
|
47
|
+
lines.push(
|
|
48
|
+
` Partial history (${usage.truncatedBy?.join(", ") || "incomplete evidence"}); older events and other accounts may be missing.`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
for (const account of usage.accounts || []) {
|
|
52
|
+
lines.push(
|
|
53
|
+
` #${account.userId} ${account.username || "(unknown account)"}: ${prefix}${account.eventCount} recorded event(s)${eventTypes(account.eventsByType)}; first ${seenAt(account.firstSeenAt)}, last ${seenAt(account.lastSeenAt)}.`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (usage.accountsTruncated)
|
|
57
|
+
lines.push(" Account detail list capped; totals include omitted accounts.");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (coverage.truncated) {
|
|
61
|
+
lines.push(
|
|
62
|
+
`Device discovery/evidence is incomplete (${coverage.truncatedBy?.join(", ") || "history limit"}); additional devices or accounts may exist.`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
package/lib/admin.js
CHANGED
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
printOwnerTrace,
|
|
40
40
|
} from "./admin-owner-trace.js";
|
|
41
41
|
import { formatChatReportResult } from "./admin-chat-reports.js";
|
|
42
|
+
import { formatIdentityDeviceUsage } from "./admin-identity-inspection.js";
|
|
42
43
|
import {
|
|
43
44
|
buildTeacherBranchOperation,
|
|
44
45
|
printTeacherBranchResult,
|
|
@@ -2478,6 +2479,21 @@ export function parseAdminOperation(options) {
|
|
|
2478
2479
|
},
|
|
2479
2480
|
);
|
|
2480
2481
|
}
|
|
2482
|
+
if (action === "device" && target === "block-signup") {
|
|
2483
|
+
if (!/^[a-f0-9]{16}$/.test(options.adminDeviceKey || "")) {
|
|
2484
|
+
throw cliValidationError("Pass --device-key with the exact key from identity inspect.");
|
|
2485
|
+
}
|
|
2486
|
+
return writeOperation(
|
|
2487
|
+
"ai-bucket.device.signup.block", "POST",
|
|
2488
|
+
`/cli/admin/ai-buckets/${bucketId}/signup-devices`,
|
|
2489
|
+
{
|
|
2490
|
+
sourceUserId: parseRequiredInteger(options.adminUser, "--user", 1),
|
|
2491
|
+
deviceKey: options.adminDeviceKey,
|
|
2492
|
+
expectedUserIds: parseAiBucketUserIds(options.adminUserIds),
|
|
2493
|
+
note: parseAiBucketNote(options.note),
|
|
2494
|
+
},
|
|
2495
|
+
);
|
|
2496
|
+
}
|
|
2481
2497
|
if (action === "note" && target === "set") {
|
|
2482
2498
|
return writeOperation(
|
|
2483
2499
|
"ai-bucket.note.set",
|
|
@@ -3174,6 +3190,25 @@ export function parseAdminOperation(options) {
|
|
|
3174
3190
|
);
|
|
3175
3191
|
}
|
|
3176
3192
|
|
|
3193
|
+
if (namespace === "followup" || namespace === "followups") {
|
|
3194
|
+
if (!action || action === "list") {
|
|
3195
|
+
return readOperation("followup.list", withQuery("/cli/admin/personal-followups", {
|
|
3196
|
+
cursor: options.cursor || undefined,
|
|
3197
|
+
status: options.adminStatus === "all" ? "all" : "pending",
|
|
3198
|
+
}));
|
|
3199
|
+
}
|
|
3200
|
+
if (action === "update") {
|
|
3201
|
+
const id = parseRequiredInteger(target, "Follow-up ID", 1);
|
|
3202
|
+
const revision = parseRequiredInteger(options.followupRevision, "--revision", 1);
|
|
3203
|
+
const note = String(options.note || "").trim();
|
|
3204
|
+
if (!note || note.length > MAX_TODO_NOTE_LENGTH) throw cliValidationError("Record the actual work, evidence and reply outcome (or blocker) in --note, up to 4000 characters.");
|
|
3205
|
+
return writeOperation("followup.update", "PUT", `/cli/admin/personal-followups/${id}`, {
|
|
3206
|
+
revision, status: parseTodoStatus(options.adminStatus), note,
|
|
3207
|
+
});
|
|
3208
|
+
}
|
|
3209
|
+
throw cliValidationError("Usage: lumine admin followup list [--cursor <id>] [--status all] | followup update <id> --revision <n> --status <status> --note <evidence and outcome>.");
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3177
3212
|
if (namespace === "todo" || namespace === "todos") {
|
|
3178
3213
|
if (!action || action === "list") {
|
|
3179
3214
|
return readOperation(
|
|
@@ -6814,6 +6849,7 @@ function printAdminResult({ operation, result }) {
|
|
|
6814
6849
|
);
|
|
6815
6850
|
} else {
|
|
6816
6851
|
printTodoItems(report.carryoverTodos?.items || [], "Unfinished work");
|
|
6852
|
+
printPersonalFollowups(report.personalFollowups);
|
|
6817
6853
|
}
|
|
6818
6854
|
if (report.sponsorIntegrity) {
|
|
6819
6855
|
const pendingApplications = Number.isSafeInteger(
|
|
@@ -6870,6 +6906,7 @@ function printAdminResult({ operation, result }) {
|
|
|
6870
6906
|
);
|
|
6871
6907
|
}
|
|
6872
6908
|
}
|
|
6909
|
+
for (const line of formatIdentityDeviceUsage(inspection)) console.log(line);
|
|
6873
6910
|
return;
|
|
6874
6911
|
}
|
|
6875
6912
|
if (Array.isArray(data.escalations)) {
|
|
@@ -6912,6 +6949,8 @@ function printAdminResult({ operation, result }) {
|
|
|
6912
6949
|
}
|
|
6913
6950
|
return;
|
|
6914
6951
|
}
|
|
6952
|
+
if (data.personalFollowups) printPersonalFollowups(data.personalFollowups);
|
|
6953
|
+
if (data.personalFollowup) printPersonalFollowups({ items: [data.personalFollowup], nextCursor: null });
|
|
6915
6954
|
if (Array.isArray(data.todos)) {
|
|
6916
6955
|
printTodoItems(data.todos, "Private carry-over work");
|
|
6917
6956
|
if (data.truncated) {
|
|
@@ -7191,6 +7230,18 @@ function printFeaturedHistoryEvent(event) {
|
|
|
7191
7230
|
);
|
|
7192
7231
|
}
|
|
7193
7232
|
|
|
7233
|
+
function printPersonalFollowups(handoff) {
|
|
7234
|
+
if (!handoff) return;
|
|
7235
|
+
console.log(`Personal follow-ups: ${handoff.items.length}${handoff.nextCursor ? "+" : ""}`);
|
|
7236
|
+
for (const item of handoff.items) {
|
|
7237
|
+
console.log(` #${item.id} r${item.revision} ${item.status} — ${item.title}`);
|
|
7238
|
+
console.log(` ${item.details}`);
|
|
7239
|
+
console.log(` Source: ${item.source.type} ${item.source.id}, ${item.source.rootType} ${item.source.rootId}; member ${item.requesterUserId}`);
|
|
7240
|
+
if (item.lastProgressNote) console.log(` Progress: ${item.lastProgressNote}`);
|
|
7241
|
+
}
|
|
7242
|
+
if (handoff.nextCursor) console.log(` Continue: lumine admin followup list --cursor ${handoff.nextCursor}`);
|
|
7243
|
+
}
|
|
7244
|
+
|
|
7194
7245
|
function printTodoItems(items, heading) {
|
|
7195
7246
|
console.log(`${heading}: ${items.length} item(s).`);
|
|
7196
7247
|
for (const todo of items) {
|
package/lib/commands.js
CHANGED
|
@@ -2592,6 +2592,7 @@ export function parseArgs(args) {
|
|
|
2592
2592
|
adminReviewed: Boolean(raw.reviewed),
|
|
2593
2593
|
adminSeverity: raw.severity ? String(raw.severity) : "",
|
|
2594
2594
|
adminStatus: raw.status ? String(raw.status) : "",
|
|
2595
|
+
followupRevision: raw.revision ? String(raw.revision) : "",
|
|
2595
2596
|
adminDecision: raw.decision ? String(raw.decision) : "",
|
|
2596
2597
|
adminWaitMs: raw.waitMs ? String(raw.waitMs) : "",
|
|
2597
2598
|
adminBrowserPath: raw.browserPath ? String(raw.browserPath) : "",
|
|
@@ -2673,6 +2674,7 @@ export function parseArgs(args) {
|
|
|
2673
2674
|
? String(raw.addIds)
|
|
2674
2675
|
: "",
|
|
2675
2676
|
adminBucketId: raw.bucketId ? String(raw.bucketId) : "",
|
|
2677
|
+
adminDeviceKey: raw.deviceKey ? String(raw.deviceKey) : "",
|
|
2676
2678
|
adminLabel: raw.label ? String(raw.label) : "",
|
|
2677
2679
|
adminUserIds: raw.userIds ? String(raw.userIds) : "",
|
|
2678
2680
|
adminUser: raw.user ? String(raw.user) : "",
|
|
@@ -3104,6 +3106,7 @@ export function printHelp() {
|
|
|
3104
3106
|
lumine admin ai-bucket create --label <name> --note <text> [--json]
|
|
3105
3107
|
lumine admin ai-bucket get --bucket-id <id> [--json]
|
|
3106
3108
|
lumine admin ai-bucket accounts add --bucket-id <id> --user-ids <id,id,...> [--note <text>] [--json]
|
|
3109
|
+
lumine admin ai-bucket device block-signup --bucket-id <id> --user <source-user-id> --device-key <inspection-key> --user-ids <reviewed-device-users> --note <reason> [--json]
|
|
3107
3110
|
lumine admin ai-bucket note set --bucket-id <id> --note <text> [--json]
|
|
3108
3111
|
lumine admin ai-email-policy get --email <address> [--json]
|
|
3109
3112
|
lumine admin ai-email-policy set --email <address> --mode <automatic|separate_accounts> --note <text> [--json]
|
package/package.json
CHANGED
package/sdk/BUILD_SDK_INDEX.md
CHANGED
|
@@ -502,9 +502,9 @@ const result = await Twinkle.ai.chat({ message, history: chatHistory, systemProm
|
|
|
502
502
|
- Template values describe types, not answers to copy. All object keys are required and extra keys are rejected, including inside nested objects; {} means an exactly empty object, not a free-form dictionary. A non-empty array uses only its first item as the template for every returned item, with no fixed length; mixed examples do not declare a union. [] allows arbitrary JSON items. A null template accepts any JSON primitive (string, number, boolean or null), but the key is still required. Use 'number' for fractional numbers; 0 and other integer examples require integers. State content requirements in prompt or instructions. GPT routes retain descriptive string examples as field guidance, not enforced enum values.
|
|
503
503
|
- GPT models prefer native JSON Schema output for compatible shapes. Shapes outside the strict provider subset, such as [] with arbitrary items or deeply nested structures, use prompt-based JSON generation with the same final validation. A provider rejection specifically of the native schema triggers one prompt-based fallback; auth, quota, safety and unrelated request failures do not trigger that fallback. Validation retries preserve the original task and instructions. The returned object/result shape and streaming callbacks are unchanged.
|
|
504
504
|
- mode is accepted as an alias for thinkingMode, and mid is accepted as an alias for medium.
|
|
505
|
-
- Omit model to use the normal Lite/Medium/High routing. model
|
|
506
|
-
- thinkingMode low uses
|
|
507
|
-
- thinkingMode medium uses
|
|
505
|
+
- Omit model to use the normal Lite/Medium/High routing. Lumine Light defaults to claude-haiku-5-5; gpt-6-luna remains its second choice. Both model IDs accept Low, Medium or High thinking. Explicit claude-sonnet-5-5, gpt-6.1-sol or claude-opus-5-5 still require thinkingMode: 'high'; unknown model IDs reject instead of silently falling back. Retired IDs still work and run on their replacement: claude-sonnet-5 runs claude-sonnet-5-5; gpt-6-sol and gpt-5.6-sol run gpt-6.1-sol; gpt-6-astra, claude-opus-5 and claude-fable-5-1 run claude-opus-5-5.
|
|
506
|
+
- thinkingMode low uses Claude Haiku 5.5 with low adaptive thinking and consumes the viewer's AI Energy from confirmed provider usage. Set model: 'gpt-6-luna' to choose Luna instead.
|
|
507
|
+
- thinkingMode medium is Lumine Light: it uses Claude Haiku 5.5 with medium adaptive thinking and consumes normal AI Energy. Set model: 'gpt-6-luna' to keep Luna as the second choice.
|
|
508
508
|
- thinkingMode high without model uses Claude Sonnet 5.5 with high adaptive thinking and consumes high AI Energy. Explicit model: 'gpt-6.1-sol' selects GPT-6.1 Sol with xhigh reasoning at the same High AI Energy tier; model: 'claude-sonnet-5-5' names Sonnet 5.5 directly.
|
|
509
509
|
- claude-sonnet-5-5 and claude-opus-5-5 use Anthropic adaptive High thinking and debit confirmed provider usage at the High tier.
|
|
510
510
|
- Pass onStatus, onReasoning, and/or onText to stream progress from the same structured generation. onStatus receives high-level phases such as thinking, searching_web, responding, validating, and completed.
|
|
@@ -513,7 +513,7 @@ const result = await Twinkle.ai.chat({ message, history: chatHistory, systemProm
|
|
|
513
513
|
- Put a user-facing field such as producerNotes in expectedStructure when commentary must be part of the validated final object rather than transient reasoning progress.
|
|
514
514
|
- When AI Energy is empty, every automatic or named model choice rejects before new provider work; there is no free fallback mode.
|
|
515
515
|
- Live web search is enabled by default in Medium and High modes. Pass webSearch: false to disable it for the app. Low/Lite Mode remains tool-free; explicitly forcing webSearch: true in Low Mode returns an error.
|
|
516
|
-
- The server validates the final shape;
|
|
516
|
+
- The server validates the final shape; OpenAI/xAI routes can retry malformed output, while Anthropic routes (claude-haiku-5-5, claude-sonnet-5-5 and claude-opus-5-5) use native JSON Schema output and retry one malformed or shape-invalid result. App code should still validate business-specific enum values.
|
|
517
517
|
- Example: const { object } = await Twinkle.ai.generateObject({ thinkingMode: 'high', model: 'claude-opus-5-5', prompt: 'Plan the next section from: ' + currentState, expectedStructure: { producerNotes: 'string', action: 'string', confidence: 'number' }, onStatus: (phase) => showPhase(phase), onReasoning: (summary, meta) => showReasoningProgress(summary, meta), onText: (partialJson, meta) => showStructuredProgress(partialJson, meta) });
|
|
518
518
|
- onChatStatus(listener) | scopes: none
|
|
519
519
|
- Returns: unsubscribe function
|
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -130,13 +130,35 @@ private Lumine audit log; no public bot identity is involved.
|
|
|
130
130
|
canonical bucket. Use it to distinguish quota bookkeeping from moderation;
|
|
131
131
|
the note itself changes no access, ban, or identity rules.
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
The commands above are quota bookkeeping only. They cannot ban accounts, block signup,
|
|
134
134
|
add IP/device/risk-key rules, or infer an account family. Identification
|
|
135
135
|
remains a human/LLM evidence judgment and must be explicitly requested by
|
|
136
136
|
Mikey; routine administrator runs still escalate suspected alternate accounts
|
|
137
137
|
and never auto-enforce. `accounts add` and `note set` still accept only an
|
|
138
138
|
existing **unbanned** bucket.
|
|
139
139
|
|
|
140
|
+
For an exact device Mikey explicitly authorizes blocking, use the separate
|
|
141
|
+
signup-only action after reviewing `identity inspect`:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
lumine admin ai-bucket device block-signup --bucket-id 10 --user 3127 \
|
|
145
|
+
--device-key 0123456789abcdef --user-ids 3127 \
|
|
146
|
+
--note "Mikey approved this reviewed device for the household signup block" --json
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
`--user` is the source account ID; `--user-ids` is the complete reviewed list
|
|
150
|
+
of accounts observed on that device, not every member of the household.
|
|
151
|
+
The API resolves the inspection handle internally, rechecks bounded 365-day
|
|
152
|
+
device evidence, requires the exact expected account set, and rejects devices
|
|
153
|
+
used by anyone outside the existing bucket. Incomplete history, unresolved
|
|
154
|
+
handles and a rule belonging to another bucket stop the action. The bucket
|
|
155
|
+
must already have an active **signup-only** restriction. This cannot turn on
|
|
156
|
+
a bucket ban, change full-site bans, add network rules or infer membership.
|
|
157
|
+
The response confirms the canonical rule and restriction without returning the
|
|
158
|
+
raw device ID. The private audit records the request before evidence is read
|
|
159
|
+
and records successful completion separately. Routine reviews still never
|
|
160
|
+
auto-enforce; this action requires Mikey's explicit instruction for the device.
|
|
161
|
+
|
|
140
162
|
### Shared verified-email policies
|
|
141
163
|
|
|
142
164
|
A verified address proves that an account can use that mailbox; it does not
|
|
@@ -198,6 +220,22 @@ IP evidence, private messages, or unrelated activity. `--include-private-evidenc
|
|
|
198
220
|
adds DOB values and verified email addresses only; exact device IDs are never
|
|
199
221
|
returned.
|
|
200
222
|
|
|
223
|
+
`sharedDevices[].usage` reports recorded event counts, first/last observed Unix
|
|
224
|
+
timestamps, an `eventsByType` breakdown, and the same details per account. These
|
|
225
|
+
come from `ai_usage_session_evidence`, which records the exact device identifier;
|
|
226
|
+
`users_actions` has account/IP activity but no device identifier. Counts include
|
|
227
|
+
events such as login, logout, socket binding and AI use, **not unique visits or
|
|
228
|
+
physical-device sessions**. Only events attached to an account are counted.
|
|
229
|
+
|
|
230
|
+
Usage `status: complete` means the queried device's records fit inside the
|
|
231
|
+
365-day lookback and read limit. `partial` counts are lower bounds, with observed
|
|
232
|
+
dates and `truncatedBy` explaining the gap. `not_read` has a null event count,
|
|
233
|
+
never zero. `accountsTruncated` separately marks a capped account-detail list;
|
|
234
|
+
its totals still include omitted accounts. Device discovery can be truncated
|
|
235
|
+
even when an individual device's usage is complete: always check the overall
|
|
236
|
+
`evidenceCoverage` too. Human-readable CLI output includes these distinctions;
|
|
237
|
+
older APIs explicitly report that usage counts are unavailable.
|
|
238
|
+
|
|
201
239
|
The CLI sends `identity inspect` and `identity network` as reads, so it keeps
|
|
202
240
|
no copy of the result on disk; the server still records who looked, when and
|
|
203
241
|
why, one audit row per call. When a case needs a kept copy (a bullying or police
|
|
@@ -1647,6 +1685,39 @@ to a child):
|
|
|
1647
1685
|
|
|
1648
1686
|
## Private carry-over todos
|
|
1649
1687
|
|
|
1688
|
+
### Zero and Ciel's personal follow-ups
|
|
1689
|
+
|
|
1690
|
+
Each bot has a separate persistent list of commitments made to members. The
|
|
1691
|
+
API reply tools save a concrete task before saying it is on the bot's list;
|
|
1692
|
+
they link the real source comment/chat and keep the requesting member and bot
|
|
1693
|
+
server-owned. Retries of the same message return the same task. Members can
|
|
1694
|
+
ask that bot to read or cancel their own tasks. This does not approve any
|
|
1695
|
+
administrative action, deployment, reward, or publication.
|
|
1696
|
+
|
|
1697
|
+
Full `daily-run start` returns `personalFollowups` for the assigned bot;
|
|
1698
|
+
`daily-run gather` saves its first page in `personal-followups.json`. Follow
|
|
1699
|
+
`nextCursor` to exhaustion. Other bots' tasks wait for their own turns, and
|
|
1700
|
+
scoped sessions do not surface or modify the list.
|
|
1701
|
+
|
|
1702
|
+
```bash
|
|
1703
|
+
lumine admin followup list --json
|
|
1704
|
+
lumine admin followup list --cursor 100 --json
|
|
1705
|
+
lumine admin followup update 17 --revision 2 --status completed --note "Played the requested path on published version 400; observed ...; replied in original comment 91 (reply 103)." --json
|
|
1706
|
+
```
|
|
1707
|
+
|
|
1708
|
+
Read the original context, do the promised work, keep evidence, and report the
|
|
1709
|
+
real result to the member in the original context under the existing reply
|
|
1710
|
+
rules. Do not move a private request into a public post. Build replies still
|
|
1711
|
+
require exact-version review evidence. Record `blocked` with a concrete next
|
|
1712
|
+
step when access, the creator, or Mikey's decision is needed. A completed
|
|
1713
|
+
daily run never completes promises automatically. Update requires the current
|
|
1714
|
+
revision and the assigned bot's active full run; stale or sibling updates are
|
|
1715
|
+
refused. Daily reports include pending follow-ups and their latest progress.
|
|
1716
|
+
|
|
1717
|
+
Apply `add-ai-daily-followups.sql` before deploying this API/CLI capability.
|
|
1718
|
+
|
|
1719
|
+
### Operator carryovers
|
|
1720
|
+
|
|
1650
1721
|
```bash
|
|
1651
1722
|
lumine admin todo list --json
|
|
1652
1723
|
lumine admin todo list --status all --json
|