@stage5/lumine 0.2.35 → 0.2.37
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 +64 -29
- package/lib/commands.js +5 -1
- package/package.json +1 -1
- package/sdk/BUILD_SDK_INDEX.md +4 -4
- package/sdk/LUMINE_ADMIN.md +39 -5
package/lib/admin.js
CHANGED
|
@@ -4,19 +4,17 @@ import { assertAuthScope, resolveAuth } from "./auth.js";
|
|
|
4
4
|
import { requestJson } from "./http.js";
|
|
5
5
|
|
|
6
6
|
const MAX_EDITORIAL_FILE_BYTES = 256 * 1024;
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const MAX_COMPOSED_TEXT_FILE_BYTES = 64 * 1024;
|
|
8
|
+
const MAX_COMPOSED_TEXT_LENGTH = 10_000;
|
|
9
9
|
const MAX_NOTABLE_NOTE_LENGTH = 2_000;
|
|
10
10
|
|
|
11
|
-
// Operator-composed persona
|
|
12
|
-
//
|
|
11
|
+
// Operator-composed persona text (plain UTF-8, not JSON). The agent writes
|
|
12
|
+
// the content in the bot's persona itself; the server never invokes
|
|
13
13
|
// its model and no AI Energy is spent.
|
|
14
|
-
function
|
|
14
|
+
function readComposedTextFile(filePath) {
|
|
15
15
|
const normalizedPath = String(filePath || "").trim();
|
|
16
16
|
if (!normalizedPath) {
|
|
17
|
-
throw cliValidationError(
|
|
18
|
-
"Pass composed comment text with --file <comment.md>.",
|
|
19
|
-
);
|
|
17
|
+
throw cliValidationError("Pass composed text with --file <file.md>.");
|
|
20
18
|
}
|
|
21
19
|
let contents;
|
|
22
20
|
try {
|
|
@@ -24,18 +22,18 @@ function readComposedCommentFile(filePath) {
|
|
|
24
22
|
} catch {
|
|
25
23
|
throw cliValidationError(`Could not read ${normalizedPath}.`);
|
|
26
24
|
}
|
|
27
|
-
if (Buffer.byteLength(contents, "utf8") >
|
|
28
|
-
throw cliValidationError("The composed
|
|
25
|
+
if (Buffer.byteLength(contents, "utf8") > MAX_COMPOSED_TEXT_FILE_BYTES) {
|
|
26
|
+
throw cliValidationError("The composed text file must be under 64KB.");
|
|
29
27
|
}
|
|
30
28
|
const normalized = contents.trim();
|
|
31
29
|
if (!normalized) {
|
|
32
30
|
throw cliValidationError(
|
|
33
|
-
`${normalizedPath} is empty;
|
|
31
|
+
`${normalizedPath} is empty; composed text is required.`,
|
|
34
32
|
);
|
|
35
33
|
}
|
|
36
|
-
if (normalized.length >
|
|
34
|
+
if (normalized.length > MAX_COMPOSED_TEXT_LENGTH) {
|
|
37
35
|
throw cliValidationError(
|
|
38
|
-
`
|
|
36
|
+
`Composed text must be at most ${MAX_COMPOSED_TEXT_LENGTH} characters.`,
|
|
39
37
|
);
|
|
40
38
|
}
|
|
41
39
|
return normalized;
|
|
@@ -401,6 +399,17 @@ export function parseAdminOperation(options) {
|
|
|
401
399
|
}
|
|
402
400
|
|
|
403
401
|
if (namespace === "ai-bucket" || namespace === "ai-buckets") {
|
|
402
|
+
if (action === "create") {
|
|
403
|
+
return writeOperation(
|
|
404
|
+
"ai-bucket.create",
|
|
405
|
+
"POST",
|
|
406
|
+
"/cli/admin/ai-buckets",
|
|
407
|
+
{
|
|
408
|
+
label: parseAiBucketLabel(options.adminLabel),
|
|
409
|
+
note: parseAiBucketNote(options.note),
|
|
410
|
+
},
|
|
411
|
+
);
|
|
412
|
+
}
|
|
404
413
|
const bucketId = parseRequiredInteger(
|
|
405
414
|
options.adminBucketId,
|
|
406
415
|
"AI bucket ID",
|
|
@@ -424,22 +433,11 @@ export function parseAdminOperation(options) {
|
|
|
424
433
|
);
|
|
425
434
|
}
|
|
426
435
|
if (action === "note" && target === "set") {
|
|
427
|
-
const note = String(options.note || "").trim();
|
|
428
|
-
if (!note) {
|
|
429
|
-
throw cliValidationError(
|
|
430
|
-
"Pass the quota-only context with --note <text>.",
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
if (note.length > 255) {
|
|
434
|
-
throw cliValidationError(
|
|
435
|
-
"An AI bucket note can be at most 255 characters.",
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
436
|
return writeOperation(
|
|
439
437
|
"ai-bucket.note.set",
|
|
440
438
|
"PUT",
|
|
441
439
|
`/cli/admin/ai-buckets/${bucketId}/note`,
|
|
442
|
-
{ note },
|
|
440
|
+
{ note: parseAiBucketNote(options.note) },
|
|
443
441
|
);
|
|
444
442
|
}
|
|
445
443
|
}
|
|
@@ -724,6 +722,17 @@ export function parseAdminOperation(options) {
|
|
|
724
722
|
);
|
|
725
723
|
}
|
|
726
724
|
|
|
725
|
+
if (namespace === "announcement" && action === "post") {
|
|
726
|
+
return writeOperation(
|
|
727
|
+
"announcement.post",
|
|
728
|
+
"POST",
|
|
729
|
+
"/cli/admin/announcements",
|
|
730
|
+
{
|
|
731
|
+
content: readComposedTextFile(options.adminFile),
|
|
732
|
+
},
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
|
|
727
736
|
if (namespace === "chat" && action === "send") {
|
|
728
737
|
const rawTarget = String(target || "").trim();
|
|
729
738
|
if (!rawTarget) {
|
|
@@ -735,7 +744,7 @@ export function parseAdminOperation(options) {
|
|
|
735
744
|
// the bot's voice; the server never invokes a model for it.
|
|
736
745
|
return writeOperation("chat.send", "POST", "/cli/admin/chat-messages", {
|
|
737
746
|
target: rawTarget,
|
|
738
|
-
content:
|
|
747
|
+
content: readComposedTextFile(options.adminFile),
|
|
739
748
|
});
|
|
740
749
|
}
|
|
741
750
|
|
|
@@ -821,7 +830,7 @@ export function parseAdminOperation(options) {
|
|
|
821
830
|
? parseIdentity(options.adminIdentity)
|
|
822
831
|
: undefined,
|
|
823
832
|
...(options.adminFile
|
|
824
|
-
? { content:
|
|
833
|
+
? { content: readComposedTextFile(options.adminFile) }
|
|
825
834
|
: {}),
|
|
826
835
|
...(reviewedBuildVersionId ? { reviewedBuildVersionId } : {}),
|
|
827
836
|
...(buildReviewMethod ? { buildReviewMethod } : {}),
|
|
@@ -849,7 +858,7 @@ export function parseAdminOperation(options) {
|
|
|
849
858
|
"comment.edit",
|
|
850
859
|
"PUT",
|
|
851
860
|
`/cli/admin/comments/${commentId}`,
|
|
852
|
-
{ content:
|
|
861
|
+
{ content: readComposedTextFile(options.adminFile) },
|
|
853
862
|
);
|
|
854
863
|
}
|
|
855
864
|
if (action === "post") {
|
|
@@ -868,7 +877,7 @@ export function parseAdminOperation(options) {
|
|
|
868
877
|
}
|
|
869
878
|
|
|
870
879
|
throw cliValidationError(
|
|
871
|
-
"Usage: lumine admin identity|daily-run|recommendations|builds|post|subjects|subject|featured|comment|chat|news|audit|brief|bot-output|notable ...",
|
|
880
|
+
"Usage: lumine admin identity|daily-run|recommendations|builds|post|subjects|subject|featured|comment|announcement|chat|news|audit|brief|bot-output|notable ...",
|
|
872
881
|
);
|
|
873
882
|
}
|
|
874
883
|
|
|
@@ -1181,6 +1190,32 @@ function parseOrderedIds(value) {
|
|
|
1181
1190
|
return ids;
|
|
1182
1191
|
}
|
|
1183
1192
|
|
|
1193
|
+
function parseAiBucketLabel(value) {
|
|
1194
|
+
const label = String(value || "").trim();
|
|
1195
|
+
if (!label) {
|
|
1196
|
+
throw cliValidationError("Pass the bucket name with --label <name>.");
|
|
1197
|
+
}
|
|
1198
|
+
if (label.length > 120) {
|
|
1199
|
+
throw cliValidationError("An AI bucket name can be at most 120 characters.");
|
|
1200
|
+
}
|
|
1201
|
+
return label;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
function parseAiBucketNote(value) {
|
|
1205
|
+
const note = String(value || "").trim();
|
|
1206
|
+
if (!note) {
|
|
1207
|
+
throw cliValidationError(
|
|
1208
|
+
"Pass the quota-only context with --note <text>.",
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
if (note.length > 255) {
|
|
1212
|
+
throw cliValidationError(
|
|
1213
|
+
"An AI bucket note can be at most 255 characters.",
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1216
|
+
return note;
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1184
1219
|
function parseAiBucketUserIds(value) {
|
|
1185
1220
|
const raw = String(value || "").trim();
|
|
1186
1221
|
if (!raw) {
|
package/lib/commands.js
CHANGED
|
@@ -2173,6 +2173,7 @@ export function parseArgs(args) {
|
|
|
2173
2173
|
? String(raw.ids)
|
|
2174
2174
|
: "",
|
|
2175
2175
|
adminBucketId: raw.bucketId ? String(raw.bucketId) : "",
|
|
2176
|
+
adminLabel: raw.label ? String(raw.label) : "",
|
|
2176
2177
|
adminUserIds: raw.userIds ? String(raw.userIds) : "",
|
|
2177
2178
|
adminType: raw.type ? String(raw.type) : "",
|
|
2178
2179
|
adminKind: raw.kind ? String(raw.kind) : "",
|
|
@@ -2513,6 +2514,7 @@ export function printHelp() {
|
|
|
2513
2514
|
lumine thumbnail generate ["<prompt>"] --model <gpt-image-2|nano-banana>
|
|
2514
2515
|
lumine doctor runtime-assets
|
|
2515
2516
|
lumine admin identity list|status|use <zero|ciel|auto> [--json]
|
|
2517
|
+
lumine admin ai-bucket create --label <name> --note <text> [--json]
|
|
2516
2518
|
lumine admin ai-bucket get --bucket-id <id> [--json]
|
|
2517
2519
|
lumine admin ai-bucket accounts add --bucket-id <id> --user-ids <id,id,...> [--note <text>] [--json]
|
|
2518
2520
|
lumine admin ai-bucket note set --bucket-id <id> --note <text> [--json]
|
|
@@ -2539,6 +2541,7 @@ export function printHelp() {
|
|
|
2539
2541
|
lumine admin comment edit <comment-id> --file <comment.md> [--json]
|
|
2540
2542
|
lumine admin brief [--days <1..30>] [--json]
|
|
2541
2543
|
lumine admin bot-output [--days <1..30>] [--json]
|
|
2544
|
+
lumine admin announcement post --file <announcement.md> [--json]
|
|
2542
2545
|
lumine admin chat send <user-id|username> --file <message.md> [--json]
|
|
2543
2546
|
lumine admin notable add <user-id|username> --note <text> [--json]
|
|
2544
2547
|
lumine admin audit [list] [--run current|last|<run-id>] [--target <target>] [--actions <a,b>] [--full] [--cursor <cursor>] [--json]
|
|
@@ -2616,6 +2619,7 @@ Options:
|
|
|
2616
2619
|
--level <1|2|3> Admin subject effort level
|
|
2617
2620
|
--subject-ids <ids> Complete ordered Featured subject IDs
|
|
2618
2621
|
--bucket-id <id> Unbanned AI identity bucket for account consolidation
|
|
2622
|
+
--label <name> Name for a new unbanned AI identity bucket
|
|
2619
2623
|
--user-ids <ids> Explicit user IDs for an AI bucket batch (up to 500)
|
|
2620
2624
|
--type <type> Admin target: subject, comment, build, aiStory, or dailyReflection
|
|
2621
2625
|
--kind recommend Admin recommendation queue kind
|
|
@@ -2623,7 +2627,7 @@ Options:
|
|
|
2623
2627
|
--reward-twinkles 3 Pair a recommendation with exactly 3 Twinkles
|
|
2624
2628
|
--twinkles 3 Give exactly 3 Twinkles through the normal economy
|
|
2625
2629
|
--draft-id <id> Canonical delegated comment draft ID
|
|
2626
|
-
--file <path> Editorial JSON or composed
|
|
2630
|
+
--file <path> Editorial JSON or composed text file
|
|
2627
2631
|
--reviewed-version <id> Published Build artifact version actually reviewed
|
|
2628
2632
|
--reviewed-via <method> Build review method: runtime or code
|
|
2629
2633
|
--reason <text> Reason when marking an admin run failed
|
package/package.json
CHANGED
package/sdk/BUILD_SDK_INDEX.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Version: 1.32.0
|
|
4
4
|
Updated: 2026-08-02
|
|
5
|
-
Generated: 2026-08-
|
|
5
|
+
Generated: 2026-08-13T00:13:49.827Z
|
|
6
6
|
|
|
7
7
|
## Notes
|
|
8
8
|
- This SDK is injected into Build iframes via the Build preview/runtime.
|
|
@@ -287,7 +287,7 @@ console.log(analysis.bestMove, analysis.evaluation, analysis.mate);
|
|
|
287
287
|
- Returns: { text, response, model, webSearch, aiUsagePolicy }
|
|
288
288
|
- Generate text with the default Lumine text model, optionally using live web search and streaming text updates through onText.
|
|
289
289
|
- Signed-in viewers only.
|
|
290
|
-
- Uses Grok 4.
|
|
290
|
+
- Uses Grok 4.6 by default.
|
|
291
291
|
- Each successful text generation consumes AI Energy from the signed-in viewer.
|
|
292
292
|
- history must be an array of { role: 'user' | 'assistant', content: string }. Twinkle.ai.chat does not read a text field.
|
|
293
293
|
- The server keeps the latest 12 valid history entries.
|
|
@@ -307,7 +307,7 @@ const result = await Twinkle.ai.chat({ message, history: chatHistory, systemProm
|
|
|
307
307
|
- expectedStructure must be a JSON object that describes the exact returned object shape.
|
|
308
308
|
- mode is accepted as an alias for thinkingMode, and mid is accepted as an alias for medium.
|
|
309
309
|
- thinkingMode low uses GPT-5.6 Luna and consumes the viewer's AI Energy from confirmed provider usage; its smaller model is usually cheaper than Medium or High.
|
|
310
|
-
- thinkingMode medium uses Grok 4.
|
|
310
|
+
- thinkingMode medium uses Grok 4.6 with medium reasoning and consumes normal AI Energy.
|
|
311
311
|
- thinkingMode high uses GPT-5.6 Sol with high reasoning and consumes high AI Energy.
|
|
312
312
|
- When AI Energy is empty, Low, Medium, and High all reject before new provider work; there is no free fallback mode.
|
|
313
313
|
- 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.
|
|
@@ -353,7 +353,7 @@ const result = await Twinkle.ai.chat({ message, history: chatHistory, systemProm
|
|
|
353
353
|
- Pass onText/onStatus for streaming dialogue. Omit callbacks for non-streaming dialogue where the promise resolves with the final response.
|
|
354
354
|
- Inside Build character chat, thinkingMode low uses Lite Mode: Zero and Ciel both use GPT-5.6 Luna with reasoning disabled; confirmed provider usage consumes the viewer's AI Energy and is usually cheaper than High.
|
|
355
355
|
- Inside Build character chat, thinkingMode medium uses the same normal chat model routing: Zero and Ciel both use GPT-5.6 Luna with reasoning disabled and normal AI Energy.
|
|
356
|
-
- Inside Build character chat, thinkingMode high uses Think Hard chat routing and high AI Energy: Zero uses Grok 4.
|
|
356
|
+
- Inside Build character chat, thinkingMode high uses Think Hard chat routing and high AI Energy: Zero uses Grok 4.6 with high reasoning and Ciel uses GPT-5.6 Terra with high reasoning.
|
|
357
357
|
- When AI Energy is empty, Low, Medium, and High all reject before new provider work; there is no free fallback mode.
|
|
358
358
|
- Pass roomContext as a short shared scene transcript so Zero and Ciel can know what happened in the same room.
|
|
359
359
|
- includeWebsiteContext defaults to true. Set includeWebsiteContext: false for in-world NPC dialogue that should only use Zero/Ciel's basic character identity plus your scene/instructions.
|
package/sdk/LUMINE_ADMIN.md
CHANGED
|
@@ -55,6 +55,8 @@ AI identity buckets are private operator bookkeeping, not a Zero/Ciel public
|
|
|
55
55
|
action. They therefore do not require or attach to a delegated daily run:
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
+
lumine admin ai-bucket create --label Lemon \
|
|
59
|
+
--note "Quota accounting only; not a moderation flag." --json
|
|
58
60
|
lumine admin ai-bucket get --bucket-id 10 --json
|
|
59
61
|
lumine admin ai-bucket accounts add --bucket-id 10 \
|
|
60
62
|
--user-ids 3127,13037,15410,16288 \
|
|
@@ -63,6 +65,13 @@ lumine admin ai-bucket note set --bucket-id 10 \
|
|
|
63
65
|
--note "Quota accounting only; not a moderation flag." --json
|
|
64
66
|
```
|
|
65
67
|
|
|
68
|
+
`create` inserts a new unbanned quota bucket through the same helper the
|
|
69
|
+
management page uses. `--label` is required (at most 120 characters). `--note`
|
|
70
|
+
is required and follows the same 255-character quota-context rule as
|
|
71
|
+
`note set`. It cannot create a banned bucket, copy an existing one, or infer
|
|
72
|
+
members. The response returns the canonical bucket, including its id for
|
|
73
|
+
later `get` / `accounts add` / `note set` calls.
|
|
74
|
+
|
|
66
75
|
`accounts add` accepts 1-500 unique positive user IDs, preflights the complete
|
|
67
76
|
batch before writing, adds the canonical user and durable verified-email rules,
|
|
68
77
|
re-attributes current-day AI usage, and returns the canonical bucket members.
|
|
@@ -73,11 +82,12 @@ Lumine audit log; no public bot identity is involved.
|
|
|
73
82
|
canonical bucket. Use it to distinguish quota bookkeeping from moderation;
|
|
74
83
|
the note itself changes no access, ban, or identity rules.
|
|
75
84
|
|
|
76
|
-
This surface
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
This surface is quota bookkeeping only. It cannot ban accounts, block signup,
|
|
86
|
+
add IP/device/risk-key rules, or infer an account family. Identification
|
|
87
|
+
remains a human/LLM evidence judgment and must be explicitly requested by
|
|
88
|
+
Mikey; routine administrator runs still escalate suspected alternate accounts
|
|
89
|
+
and never auto-enforce. `accounts add` and `note set` still accept only an
|
|
90
|
+
existing **unbanned** bucket.
|
|
81
91
|
|
|
82
92
|
## Editorial priorities
|
|
83
93
|
|
|
@@ -1611,6 +1621,30 @@ Ordinary warm follow-ups (checking on a member the bots already know after
|
|
|
1611
1621
|
something the run surfaced) are within a run's judgment, sparingly, and are
|
|
1612
1622
|
always reported in the run report.
|
|
1613
1623
|
|
|
1624
|
+
## Official announcements
|
|
1625
|
+
|
|
1626
|
+
```bash
|
|
1627
|
+
lumine admin announcement post --file announcement.md --json
|
|
1628
|
+
```
|
|
1629
|
+
|
|
1630
|
+
The run's selected bot posts one composed message to General's announcement
|
|
1631
|
+
subchannel (`channelId` 2, `subchannelId` 2). This is the public official
|
|
1632
|
+
board, not a DM and not a Home comment. Mechanics:
|
|
1633
|
+
|
|
1634
|
+
- requires the `chat:post` scope of a comment-mode `post` run;
|
|
1635
|
+
- composed-only (`--file`, same 10,000-character limit as `chat send`);
|
|
1636
|
+
- authors as Zero or Ciel only — the ordinary chat post route and the
|
|
1637
|
+
announcement socket relay now treat those two IDs as allowed announcement
|
|
1638
|
+
authors, same as management-level 3 humans;
|
|
1639
|
+
- persists through the ordinary `msg_chats` insert (channel lock +
|
|
1640
|
+
visibility restore), writes the Twinkle Newspaper `announcement:<messageId>`
|
|
1641
|
+
event, and relays `new_chat_message` on General. No bot socket, session, or
|
|
1642
|
+
presence;
|
|
1643
|
+
- audited as `announcement.post` and idempotent per request key.
|
|
1644
|
+
|
|
1645
|
+
Use this only when Mikey asks for an official announcement. Do not treat a
|
|
1646
|
+
management run as a standing license to post there.
|
|
1647
|
+
|
|
1614
1648
|
## Audit history
|
|
1615
1649
|
|
|
1616
1650
|
```bash
|