@stage5/lumine 0.3.40 → 0.3.41

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 CHANGED
@@ -2922,7 +2922,22 @@ export function parseAdminOperation(options) {
2922
2922
  if (!Array.isArray(items) || !items.length || items.length > 100) throw cliValidationError("Import 1 to 100 reviewed words, with definitions and examples.");
2923
2923
  return writeOperation("word-games.import", "POST", "/cli/admin/word-games/import", { items, dryRun: Boolean(options.adminDryRun) }, { requiresRun: false, mutates: !options.adminDryRun, timeoutMs: 120000 });
2924
2924
  }
2925
- throw cliValidationError("Use: lumine admin word-games status | candidates <1-5> [--after <id>] [--limit <n>] | import <reviewed-words.json> [--dry-run]");
2925
+ // Word Search monthly champions: evidence first, then a verdict (Mikey 10-17).
2926
+ if (action === "champions") return readOperation("word-games.champions", withQuery("/cli/admin/word-games/champions", {
2927
+ buildId: parseRequiredInteger(options.adminBuild, "--build", 1),
2928
+ }), { requiresRun: false });
2929
+ if ((action === "confirm" || action === "disqualify") && target) {
2930
+ const reason = String(options.adminReason || "").trim();
2931
+ if (!/^\d{4}-\d{2}$/.test(target) || reason.length < 8) throw cliValidationError(`Use: lumine admin word-games ${action} <YYYY-MM> <userId> --build <id> --reason "<evidence>"`);
2932
+ return writeOperation("word-games.champion-review", "POST", "/cli/admin/word-games/champions/review", {
2933
+ buildId: parseRequiredInteger(options.adminBuild, "--build", 1),
2934
+ month: target,
2935
+ userId: parseRequiredInteger(extra, "user id", 1),
2936
+ verdict: action === "confirm" ? "confirmed" : "disqualified",
2937
+ reason,
2938
+ }, { requiresRun: false, mutates: true });
2939
+ }
2940
+ throw cliValidationError("Use: lumine admin word-games status | candidates <1-5> [--after <id>] [--limit <n>] | import <reviewed-words.json> [--dry-run] | champions --build <id> | confirm|disqualify <YYYY-MM> <userId> --build <id> --reason \"<evidence>\"");
2926
2941
  }
2927
2942
  if (namespace === "english") {
2928
2943
  return buildEnglishOperation({ action, target, options });
package/lib/sdk.js CHANGED
@@ -50,6 +50,9 @@ export const SDK_CLI_METHODS = {
50
50
  "wordMaster.answerQuiz": { path: "api/content/word-master/answer", scopes: ["content:write"], write: true },
51
51
  "wordMaster.getItems": { path: "api/content/word-master/items", scopes: ["content:read"] },
52
52
  "wordMaster.useItem": { path: "api/content/word-master/use-item", scopes: ["content:write"], write: true },
53
+ "wordMaster.startPuzzle": { path: "api/content/word-master/puzzle-start", scopes: ["content:write"], write: true },
54
+ "wordMaster.sendPuzzleEvents": { path: "api/content/word-master/puzzle-events", scopes: ["content:write"], write: true },
55
+ "wordMaster.getPuzzleHint": { path: "api/content/word-master/puzzle-hint", scopes: ["content:write"], write: true },
53
56
  "grammarbles.listQuestions": { path: "api/content/grammarbles/questions", scopes: ["content:read"] },
54
57
  "grammarbles.getMyQuestionHistory": { path: "api/content/grammarbles/history", scopes: ["content:read"] },
55
58
  "minecraft.getWorlds": { path: "api/minecraft/worlds", scopes: ["content:read"] },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.3.40",
3
+ "version": "0.3.41",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,8 +1,8 @@
1
1
  # Build SDK Index
2
2
 
3
- Version: 1.67.0
3
+ Version: 1.68.0
4
4
  Updated: 2026-10-16
5
- Generated: 2026-10-16T15:37:18.409Z
5
+ Generated: 2026-10-17T04:41:51.966Z
6
6
 
7
7
  ## Notes
8
8
  - This SDK is injected into Build iframes via the Build preview/runtime.
@@ -40,6 +40,7 @@ Generated: 2026-10-16T15:37:18.409Z
40
40
  - word-game-quiz-v1 is a completion reward proof for a correctly answered, Battery-settled Twinkle.wordMaster quiz from today in the same app/viewer/runtime mode. Pass quiz.completionToken to rewards.claim; each quiz pays once across all rules. A series of identical rules allows a bounded number of distinct daily quizzes. Normal review and publication approval still apply; a contribution preview never pays real XP or Coins.
41
41
  - word-game-entry-v1 verifies a Battery-settled missing-definition/example contribution made today in this app/runtime mode, paying each contribution once. Its platform cap is three claims per viewer/app/day. word-game-quiz-v1 is capped at five. Declare matching completion rules and normal reward review; these proofs do not approve the app themselves.
42
42
  - word-game-puzzle-v1 preserves Word Search streak bonuses using canonical puzzle reward history: daily-easy/medium/hard with optional -s10 and -s20 tiers. A learner earns each difficulty once per UTC day across every tier and release. rewards.getStatus returns wordGamePuzzle { streakDays, ruleIds, earnedLevels }; choose its ruleIds and display canonical rule amounts. Streaks count consecutive days with a puzzle reward, capped at 30. This profile checks streak eligibility and duplicate difficulty claims in addition to the approved timer; it does not simulate or prove the puzzle solve.
43
+ - word-game-puzzle-v2 is the same daily rule and also requires a server-verified finished solve of that difficulty today (Twinkle.wordMaster.startPuzzle and sendPuzzleEvents). word-search-champion-v1 pays only the rank-1 viewer of a completed UTC month on the server-written verified:month:YYYY-MM board (score, then earliest), once per month and only after the month is final (07:00 UTC on the 2nd, when its last boards can no longer take scores); once anyone collected a month, nobody else can; a removed or fully banned #1 passes the prize to the next eligible player; The daily Twinkle review confirms each month’s winner from the server’s record of their solves before the prize opens (a disqualified winner passes it to the next eligible player). rewards.getStatus returns wordSearchChampion: { month, months, claimable, checking } — months are ready to collect; checking lists wins still being confirmed (show a checking message, no collect button). Show the champion banner from that status, never from app storage.
43
44
 
44
45
  ## AI decision design
45
46
  - Twinkle.ai.decide runs JEV, a model for narrow decisions over supplied text or structured state, and it is free for viewers: decisions are covered by Twinkle and never use their AI Energy. Whenever an app makes a judgment that ordinary code can only approximate, consider JEV first. Proven patterns: an opponent or boss coach that reads the player's habits (favourite lane, repeated trick, cheese move) and picks a counter-plan from a fixed list, as in Red Square's boss and Open Football's opponent coach; NPCs that understand what a player types and choose an action; classifying or scoring user submissions; ranking supplied candidates; and picking the next exercise or difficulty from evidence about the learner. Any app with an AI opponent, NPC, or adaptive difficulty should decide explicitly whether JEV improves it. These are examples to reason from, not a keyword checklist or a requirement to add AI to every app.
@@ -761,6 +762,21 @@ const result = await Twinkle.characters.chat({ character: 'zero', thinkingMode:
761
762
  - Returns: { item: { id, itemType, usedAt, requestId, puzzleKey }, mode }
762
763
  - Consume an earned item once and return a receipt for its game effect.
763
764
  - puzzleKey starts items: and has at most 128 letters/digits/:|_-. Persist requestId and puzzleKey before sending. Retry the same IDs after a lost response; apply each receipt once and persist it with the session. Reuse on another puzzle is rejected. Item use is free of AI Battery.
765
+ - async startPuzzle({ level, play, dayKey, revision, gridHash, requestId }) | scopes: content:write
766
+ - Returns: { sessionId, gridHash, words, ranked }
767
+ - Start a verified Word Search solve when the timer starts.
768
+ - Server-verified solves (Mikey 10-17): the server rebuilds the same deterministic board, checks every found word and times the solve itself; it computes the score with the same rules as the app.
769
+ - play: classic (Original themes board for dayKey and level), daily (Word Master mix from the packet with this revision) or items. dayKey is today or yesterday (UTC). gridHash is the board hash the app built; a mismatch returns code word_search_board_mismatch (play unranked and ask the viewer to reload).
770
+ - Start the session when the board appears (the ranked clock starts then; the board is the same for everyone all day). Pass a requestId (8–64 letters, digits, - or _) saved before the call: retrying with it returns the same session, so a lost response never costs the ranked attempt. Only the viewer’s first session for a board ranks, whatever the play mode (Items and Word Master mix share boards) and in live or preview; later sessions are practice. Ranked solves allow up to 10 minutes of pauses in total.
771
+ - async sendPuzzleEvents({ sessionId, events }) | scopes: content:write
772
+ - Returns: { sessionId, status, found, total, ranked, result }
773
+ - Report found words, misses, pauses and resumes for a verified solve.
774
+ - Events: { kind: "word", t, cells: [row0, col0, row1, col1] } (the selection’s end cells, either direction), { kind: "miss", t }, { kind: "pause" }, { kind: "resume" }. t is wall-clock milliseconds since the timer started; send each word right away. Pauses are timed by the server clock.
775
+ - When the last word arrives the server finishes the solve: status finished (result.finalScore, elapsedSeconds, hintsUsed, streakBonusPoints, ranked) or timeout. Show the server result as the official score.
776
+ - Ranked results need human pacing: send one word per call, with the app’s t at least one second apart. A solve with words under a second apart by t, words arriving under 250 ms apart, several words in one call, or a word reported more than 8 seconds late still counts toward daily rewards but is left off the leaderboards (ranked: false). Boards come from public inputs, so this limits bots rather than making them impossible.
777
+ - async getPuzzleHint({ sessionId, word, t }) | scopes: content:write
778
+ - Returns: { sessionId, word, row, col, hintsUsed }
779
+ - Reveal one letter of a word in a verified Classic solve.
764
780
  - async getWord({ wordId, word }) | scopes: content:write
765
781
  - Returns: { word: { id, word, definition, example, wordLevel, canGenerate }, mode }
766
782
  - Open an existing Word Master word’s reviewed definition and example, and add it to the viewer’s book.
@@ -1287,6 +1303,7 @@ const result = await Twinkle.characters.chat({ character: 'zero', thinkingMode:
1287
1303
  - Submit only after computing the final score when a run, shift, match, or level attempt ends; do not submit every frame or every tick.
1288
1304
  - Only improved personal-best scores replace the existing score row.
1289
1305
  - meta is optional JSON object data, max 2 KB.
1306
+ - Boards whose key starts with verified: are written by the server from checked results (Word Search verified solves: verified:day:{dayKey}:{level} for Classic, verified:day:{dayKey}:wm-{level} for Word Master mix, verified:month:YYYY-MM, verified:all, verified:all:{level}, verified:points). Read them with get; submit refuses them.
1290
1307
 
1291
1308
  ### Twinkle.sharedDb
1292
1309
  - async getTopics() | scopes: sharedDb:read
@@ -5364,3 +5364,7 @@ Every decision requires Mikey's authorization for that crew and action. Mutation
5364
5364
  ## Word Search vocabulary upkeep
5365
5365
 
5366
5366
  Every full daily website-management run adds reviewed Word Master words across all five difficulty bands, with accurate definitions and natural example sentences. Use `lumine admin word-games status`, `candidates <1-5> --after <id> --limit 100`, and `import <reviewed-words.json> --dry-run` followed by the reviewed import. File shape: `{items:[{wordId,word,wordLevel,definition,example}]}` (up to 100). Candidate exports are unreviewed; use agent judgment and check the current censor table before importing. No paid batch APIs. The import is audited and preserves active generation and today's existing puzzles; future banks include the additions. The app receives all five ready banks in its normal HTML and has a bundled checked fallback, so word selection never creates a loading screen or separate word request. Report additions per difficulty and text corrections. Repository agents follow `agent-guides/word-search-vocabulary.md` for the full procedure. A scoped management request does not start this full-run duty.
5367
+
5368
+ ## Word Search monthly champions
5369
+
5370
+ Word Search rankings come from server-verified solves, but every board comes from public inputs, so a script playing at a human-looking pace can still rank. A month's champion prize (word-search-champion-v1) opens only after review. From 07:00 UTC on the 2nd, run `lumine admin word-games champions --build <id>`. It lists the last three final months: the current candidate, whether the win is confirmed or collected, and each top-three player's best solves (score, seconds, words, gaps between words, gap variation (near 0 means machine-even spacing), misses, pauses, hints, best streak, days played). Confirm a believable human with `lumine admin word-games confirm <YYYY-MM> <userId> --build <id> --reason "<evidence>"`. Disqualify a scripted winner with `disqualify`; the next eligible player becomes the candidate, so review them too. Removed and fully banned accounts are skipped automatically. Both writes are audited, and neither works after the prize is collected. Until confirmation the winner's game shows that the win is being checked.