@stage5/lumine 0.3.43 → 0.3.45

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
@@ -382,7 +382,7 @@ function buildApprovalOperation({ action, target, options }) {
382
382
  // Code agent passes wrote (no model is called), then review what they could
383
383
  // not settle (Mikey 2026-10-06: bulk AI work runs on Claude Code agents).
384
384
  const ENGLISH_USAGE =
385
- "Use: lumine admin english import <verdicts.json> --pass a|b | adjudicate <verdicts.json> | add-questions <questions.json> | relevel <moves.json> | mastery <userId> --reason <why> | backfill-distractors | summary | review [--limit <n>] | accept|retire <questionId> [--reason <text>] [--fault] | retag <questionId> --skill <code> [--reason <text>] | build-candidates [--after <questionId>] [--limit <n>] | build-import <verdicts.json> | check-candidates [--after <questionId>] [--limit <n>] | check-import <verdicts.json> | check-broken [--after <questionId>] [--limit <n>] | check-apply <fixes.json> | check-verify <checked.json> | check-follow-faulty [--after <next>] [--limit <n>] | lesson-candidates [--after <skillCode>] [--limit <n>] | lesson-import <lessons.json> | lesson-approve <approvals.json> | lessons [--status draft|approved]";
385
+ "Use: lumine admin english import <verdicts.json> --pass a|b | adjudicate <verdicts.json> | add-questions <questions.json> | relevel <moves.json> | mastery <userId> --reason <why> | backfill-distractors | summary | review [--limit <n>] | accept|retire <questionId> [--reason <text>] [--fault] | retag <questionId> --skill <code> [--reason <text>] | build-candidates [--after <questionId>] [--limit <n>] | build-import <verdicts.json> | check-candidates [--after <questionId>] [--limit <n>] | check-import <verdicts.json> | check-broken [--after <questionId>] [--limit <n>] | check-apply <fixes.json> | check-verify <checked.json> | check-follow-faulty [--after <next>] [--limit <n>] | audit-backlog [--after <questionId>] [--limit <n>] | audit-mark <marks.json> | lesson-candidates [--after <skillCode>] [--limit <n>] | lesson-import <lessons.json> | lesson-approve <approvals.json> | lessons [--status draft|approved]";
386
386
  const ENGLISH_IMPORT_MAX = 500;
387
387
 
388
388
  function buildEnglishOperation({ action, target, options }) {
@@ -464,6 +464,25 @@ function buildEnglishOperation({ action, target, options }) {
464
464
  if (after && !/^(?:[ufc]:)?\d+:\d+$/.test(after)) throw cliValidationError("--after takes the last reply's next (<phase:id:version>).");
465
465
  return writeOperation("english.check_follow_faulty", "POST", "/cli/admin/english/item-checks/follow-faulty", { after, limit: limitOf(200, 100) }, { requiresRun: false, timeoutMs: 300000 });
466
466
  }
467
+ // Grammar Quest audit passes (Mikey 2026-10-17): the checked items no pass
468
+ // has looked at yet, first; and marking what a pass read,
469
+ // { audit: "<name>", items: [{ questionId, version, contentHash, outcome }] }
470
+ if (action === "audit-backlog") {
471
+ // the parser leaves an omitted --after as ""
472
+ const after = options.adminAfter ? parseRequiredInteger(options.adminAfter, "--after", 0, 2147483647) : 0;
473
+ return readOperation("english.audit_backlog", withQuery("/cli/admin/english/item-audits/backlog", { after, limit: limitOf(1000, 200) }), { requiresRun: false });
474
+ }
475
+ if (action === "audit-mark") {
476
+ if (!target) throw cliValidationError(ENGLISH_USAGE);
477
+ const raw = readAdminJsonFile(target, "<marks.json>", { maxBytes: 4 * 1024 * 1024 });
478
+ const audit = String(raw?.audit || "");
479
+ if (!/^[a-z0-9][a-z0-9-]{2,63}$/.test(audit)) throw cliValidationError('The file needs "audit": the pass\'s name (a-z, 0-9, -), e.g. crack-ambiguity-20261017.');
480
+ const items = Array.isArray(raw?.items) ? raw.items : null;
481
+ if (!items || !items.length || items.length > ENGLISH_IMPORT_MAX) {
482
+ throw cliValidationError(`The file must hold { audit, items: [...] } with 1 to ${ENGLISH_IMPORT_MAX} marks.`);
483
+ }
484
+ return writeOperation("english.audit_mark", "POST", "/cli/admin/english/item-audits/mark", { audit, items }, { requiresRun: false, timeoutMs: 180000 });
485
+ }
467
486
  // Grammar Quest lessons (Mikey 2026-10-11): what the writing pass draws on
468
487
  // (each rule-first point with live sound questions on it), importing
469
488
  // drafts, approving reviewed versions [{ skillCode, version }], the list
package/lib/constants.js CHANGED
@@ -135,6 +135,7 @@ export const BUNDLED_SDK_REFERENCE_URL = new URL(
135
135
  );
136
136
  export const PACKAGE_METADATA_URL = new URL("../package.json", import.meta.url);
137
137
  export const LUMINE_MOBILE_SELECTION_GUIDANCE = `- Mobile long-press must not select game UI or open the browser's Copy/Look Up/image menu. The SDK protects standard buttons, button-like ARIA controls, and canvases. Mark the entire gameplay wrapper data-twinkle-no-select (including HUD, labels, scores, menus, controls, and empty play space); also apply user-select: none, -webkit-user-select: none, and -webkit-touch-callout: none for local previews. Protecting only one button or the canvas is insufficient.
138
+ - On a played game page (a play-sized canvas, or data-twinkle-game on <html>/<body>) the SDK also blocks text selection, the long-press menu and page pinch-zoom everywhere except inputs, contenteditable and data-twinkle-selectable text. A game that sizes itself with Twinkle.preview should set data-twinkle-game; data-twinkle-document keeps a canvas page (charts, drawing pad) an ordinary page; data-twinkle-allow-zoom keeps pinch-zoom on a game.
138
139
  - Keep inputs/contenteditable usable. Mark genuinely copyable story/chat/user-written text data-twinkle-selectable and style it with user-select: text, -webkit-user-select: text, and -webkit-touch-callout: default. Do not blanket-disable selection on document/readers or preventDefault on document-wide touch/pointer events. Test holding controls, the HUD, and empty play space in mobile Safari and Chromium, then verify release/cancel, scrolling, typing, and copying. lumine check only detects a missing no-selection rule; a pass does not prove selector coverage or mobile behavior.`;
139
140
  export const LUMINE_WORLD_UPDATE_GUIDANCE = `- For Twinkle.world realtime presence, keep render/input loops local. Queue an update only when relevant state changes, replace any queued snapshot with the newest one, and flush on a fixed 5-15 updates-per-second schedule with at most one updatePresence request in flight. Never call or await updatePresence every animation frame, resend unchanged snapshots, overlap requests, or build a backlog.
140
141
  - Send Twinkle.world actions only when the discrete action happens; do not poll or automatically retry them. On WORLD_EVENT_RATE_LIMITED or another recoverable non-session-ended error, drop that attempted presence/action update without an immediate retry and keep the session. Reconnect with backoff only after session.ended or Twinkle.world.isSessionEndedError(error).`;
@@ -349,7 +350,7 @@ lumine save --summary "Describe the change"
349
350
  - Use local project files with relative or root-local imports only. Do not add package imports, CDN scripts, external network calls, or app-local /api/* routes.
350
351
  - Build apps run in sandboxed iframes without allow-forms. Do not use <form> elements, native form submission, requestSubmit(), or browser form navigation. Build input flows with JavaScript-handled inputs and buttons instead.
351
352
  ${LUMINE_MOBILE_SELECTION_GUIDANCE}
352
- - CAUTION: the preview runtime AUTO-DETECTS "game apps" — any <canvas> in the body (even a decorative background canvas) or game-y words in visible text switch the app to viewport-app mode: html/body get overflow:hidden !important and body becomes a centering flexbox, so tall document-flow pages clip and stop scrolling. Document-style apps that use a canvas must call Twinkle.preview.subscribe (or getLayout/reserveInsets) early at boot — any of those opts out of auto game mode — then pad by layout.safeInsets and scroll within layout.viewport.height.
353
+ - CAUTION: the preview runtime AUTO-DETECTS "game apps" — any <canvas> in the body (even a decorative background canvas) switches the app to viewport-app mode: html/body get overflow:hidden !important and body becomes a centering flexbox, so tall document-flow pages clip and stop scrolling. Document-style apps that use a canvas must call Twinkle.preview.subscribe (or getLayout/reserveInsets) early at boot — any of those opts out of auto game mode — then pad by layout.safeInsets and scroll within layout.viewport.height.
353
354
  - For canvas, WebGL, Three.js, fullscreen, or game builds, use Twinkle.preview for layout. Do not size roots from 100vh, 100vw, 100dvh, 100dvw, window.innerWidth, window.innerHeight, visualViewport, or document viewport dimensions.
354
355
  ${LUMINE_THREE_VENDOR_GUIDANCE}
355
356
  ${LUMINE_MATH_VENDOR_GUIDANCE}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.3.43",
3
+ "version": "0.3.45",
4
4
  "description": "Command line tools for launching Lumine builds on Twinkle.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  Version: 1.68.0
4
4
  Updated: 2026-10-16
5
- Generated: 2026-10-17T04:41:51.966Z
5
+ Generated: 2026-10-18T01:06:28.603Z
6
6
 
7
7
  ## Notes
8
8
  - This SDK is injected into Build iframes via the Build preview/runtime.
@@ -25,6 +25,7 @@ Generated: 2026-10-17T04:41:51.966Z
25
25
  - Twinkle.ai.chat history entries must use { role, content }; map local message.text fields to content before passing history.
26
26
  - Live web search is enabled by default for Twinkle.ai.chat and for Medium/High Twinkle.ai.generateObject and Twinkle.characters.chat requests. App authors can pass webSearch: false to disable it for their app. Search uses the provider's live web-search tool and is included in AI Energy usage; structured and character Lite Mode remains tool-free.
27
27
  - Mobile long-press must not select game UI or open browser Copy/Look Up/image menus. The SDK provides no-selection/callout defaults for standard buttons, button-like ARIA controls, and canvases. Mark the entire custom gameplay wrapper data-twinkle-no-select, including HUD, labels, scores, menus, controls, and empty play space; also style it with user-select: none, -webkit-user-select: none, and -webkit-touch-callout: none for local previews. Protecting only one button or the canvas is insufficient. This behavior is independent of Twinkle.preview layout mode.
28
+ - Played game pages block text selection, the long-press menu and page pinch-zoom everywhere except inputs, textareas, contenteditable and data-twinkle-selectable text, so text a game shows for copying (a chat log, a word to copy) needs data-twinkle-selectable. A page is a played game when the app sets data-twinkle-game on <html> or <body>, or, for apps that do not lay themselves out with Twinkle.preview, when a play-sized canvas is on screen: rendered, not pointer-events:none, and covering at least 30% of the viewport by its layout size; the page then stays a played game while that canvas is in the document, even hidden between screens. Set data-twinkle-game on a game that sizes itself with Twinkle.preview, a DOM-only game, or a small-canvas game; set data-twinkle-document to keep a canvas page (charts, drawing pad) an ordinary page; set data-twinkle-allow-zoom to keep page pinch-zoom on a game. Page layout is not affected by any of this.
28
29
  - Keep inputs/contenteditable usable. Mark genuinely copyable story/chat/user-written text data-twinkle-selectable and style it with user-select: text, -webkit-user-select: text, and -webkit-touch-callout: default. SDK defaults have low specificity so existing explicit copyable-text styles remain effective. Preserve document/reader selection; do not block document-wide touch/pointer events or disable scrolling/zoom to prevent selection.
29
30
  - Verify mobile long presses on controls, HUD, and empty play space in Safari and Chromium; confirm held controls still work and release/cancel correctly, and scrolling, typing, and copying still work. lumine check only detects a missing no-selection rule, not selector coverage or mobile behavior.
30
31
  - Build app tab mute is enforced by the host runtime automatically for standard media elements and Web Audio connections to AudioContext.destination. Apps with custom audio engines can also observe Twinkle.onAudioMuteChange and check Twinkle.isAudioMuted.
@@ -94,8 +95,9 @@ files:read, media:read, media:write, live:read, live:write, user:read, users:rea
94
95
 
95
96
  ### Twinkle.app
96
97
  - async getInfo() | scopes: none
97
- - Returns: App info object from the parent (includes appUrl) or null
98
+ - Returns: App info object from the parent ({ buildId, appUrl, source, artifactVersionId }) or null
98
99
  - Cached after the first call for the iframe session.
100
+ - source is 'published' on the app page and 'workspace' in the editor preview; artifactVersionId is the published release this frame runs (null in the workspace). Stamp it on your own telemetry rows instead of a hand-written release label, so data can be tied to an exact version. Hosts older than website 2.3.113 omit source and artifactVersionId; treat a missing value as unknown.
99
101
  - async getShareUrl(pathSegment) | scopes: none
100
102
  - Returns: Canonical shareable deep-link URL string, or null when app info is unavailable
101
103
  - Builds a canonical shareable deep link into this app, e.g. https://www.twin-kle.com/app/884/432-the-great-gatsby.