@stage5/lumine 0.2.81 → 0.2.83

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
@@ -174,7 +174,10 @@ export function readRewardProposalDirectory(directory) {
174
174
  let bytes = 0;
175
175
  const walk = (dir) => {
176
176
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
177
- if (entry.name.startsWith(".") || REWARD_PROPOSAL_SKIPPED_DIRS.has(entry.name))
177
+ if (
178
+ entry.name.startsWith(".") ||
179
+ REWARD_PROPOSAL_SKIPPED_DIRS.has(entry.name)
180
+ )
178
181
  continue;
179
182
  const fullPath = path.join(dir, entry.name);
180
183
  if (entry.isSymbolicLink()) {
@@ -194,7 +197,10 @@ export function readRewardProposalDirectory(directory) {
194
197
  );
195
198
  }
196
199
  bytes += buffer.length;
197
- const relative = path.relative(realRoot, fullPath).split(path.sep).join("/");
200
+ const relative = path
201
+ .relative(realRoot, fullPath)
202
+ .split(path.sep)
203
+ .join("/");
198
204
  files.push({ path: `/${relative}`, content: buffer.toString("utf8") });
199
205
  }
200
206
  };
@@ -3918,7 +3924,9 @@ function printRewardReviewResult({ operation, data }) {
3918
3924
  console.log(` ${file.status}: ${file.path}`);
3919
3925
  }
3920
3926
  if (review.status === "rejected" && review.declinedByCreator) {
3921
- console.log("The creator declined the proposed changes; the request is closed.");
3927
+ console.log(
3928
+ "The creator declined the proposed changes; the request is closed.",
3929
+ );
3922
3930
  }
3923
3931
  if (review.status === "approved" && review.publishedArtifactVersionId) {
3924
3932
  console.log(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stage5/lumine",
3
- "version": "0.2.81",
3
+ "version": "0.2.83",
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.45.2
4
4
  Updated: 2026-09-14
5
- Generated: 2026-09-19T04:22:12.342Z
5
+ Generated: 2026-09-19T10:34:03.685Z
6
6
 
7
7
  ## Notes
8
8
  - This SDK is injected into Build iframes via the Build preview/runtime.
@@ -36,6 +36,7 @@ Generated: 2026-09-19T04:22:12.342Z
36
36
  - The creator's agent designs the rewards. Declare the economy in a project file `rewards.json` at the root: budgets (userDailyXP, userDailyCoins, optional userDailyClaims; there is no app-wide daily or lifetime budget, only what one learner can earn per day) and rules [{ id, title, xp, coins, verifier: 'numeric-quiz' | 'completion', maxAttempts?, retry?: { xpPercent, coinsPercent, paidAttempts? }, minSeconds? (completion), progression?: 'dated' | 'until-earned' (quiz) }]. Wire the matching Twinkle.rewards calls with those literal rule ids. Questions and answer keys NEVER go in project files (published source is readable by every player): quiz rules get them from the private question sheet uploaded with `lumine rewards sheet <file.json>` ({ rules: { <ruleId>: { questions?, sets? } } }); `lumine rewards check` validates both together. A review request freezes the code and proposes rewards.json merged with the sheet; the administrator reads the code, checks the amounts and whether the app is exploitable, may change any amount, and approves. Creators are kids and teens: show approval status and one Send for review action; do not ask them to fill in technical forms. Every code update that retains rewards needs a new approval before publishing. Removing the SDK automatically clears its gate. Apps read amounts, tries and sets from getStatus, never from their own file.
37
37
  - Verifiers: 'numeric-quiz' pays for server-checked numeric answers (retry share, attempt limits, dated sets or until-earned sets that stay up until somebody earns them, after-answer guides). 'completion' pays when the app reports an activity finished — a cleared stage, a finished round — at least minSeconds after start({ ruleId }); the server checks only the elapsed time, once per learner per site day (UTC midnight), and the budgets. Call start when the activity begins and claim({ challengeId }) with no answers when it ends; keep completion amounts and userDailyXP small enough that a player scripting the calls would not matter, because nothing else is verified.
38
38
  - Numeric quiz answers are verified on the server; client scores, privateDb state, timers and completion booleans are not verified reward evidence. Limits reset at UTC midnight. Rules are earned once per viewer per UTC day; attempt limits and retry payouts come from the approved rule. Challenges expire at the UTC day boundary. Budgets apply across release changes.
39
+ - Optional reward rule controls: maxLifetimeClaims caps one learner’s receipts for that rule across every day and release; completionProof: classic-tower-v1 requires a server-simulated Classic Tower finish in addition to minSeconds. These are server-enforced controls. Existing completion rules without completionProof still verify elapsed time only.
39
40
 
40
41
  ## Token Scopes
41
42
  files:read, media:read, media:write, live:read, live:write, user:read, users:read, dailyReflections:read, content:read, content:write, sharedDb:read, sharedDb:write, privateDb:read, privateDb:write, files:write, chat:read, chat:write, notifications:read, notifications:write, notifications:emit, reminders:read, reminders:write, rewards:claim
@@ -1017,6 +1018,7 @@ world.updatePresence({ x, y, z, facing });
1017
1018
  - rules[].available is false on a site day (UTC) the reviewer scheduled no questions for; show the rule as not available instead of starting it. xp/coins are the first-try amounts; retryReward is what a correct answer pays after a wrong one (equal to xp/coins unless the reviewer set a retry share). maxAttempts null means unlimited wrong answers until the site's daily reset (UTC midnight, 9:00 AM in Korea). retry.paidAttempts, when set, is the last attempt number a correct answer is still paid on: a later correct answer is recorded as solved (receipt xp 0, coins 0) and pays nothing — tell the learner before they pass it.
1018
1019
  - challenges lists challenges this viewer already started today with their questions, so an app can resume after a reload without calling start. A question's guide (reviewer-approved JSON teaching content: explanation, interactive-model configuration) is present only once the viewer has answered at least once, right or wrong; render it as the after-attempt lesson. claimsToday against userDailyClaims (null = uncapped) tells whether another bounty can still pay today.
1019
1020
  - Under progression 'until-earned' the same set stays up day after day until somebody earns it; setKey names the set currently up. Completion rules are always available and have questionCount 0.
1021
+ - Rules may set maxLifetimeClaims, a per-learner limit for that rule across days and releases. rules[].lifetime contains the server-confirmed limit and remaining claims. App storage never enforces this limit.
1020
1022
  - await Twinkle.rewards.getReceipt({ challengeId }) | scopes: rewards:claim
1021
1023
  - Returns: { mode: "live", status: "awarded" | "pending" | "expired" | "not_found", receipt: { id, challengeId, ruleId, reviewId, artifactVersionId, dayKey, xp, coins, attempt, createdAt } | null, balances: { xp, coins } } | { mode: "preview", status: "not_found", receipt: null, message }
1022
1024
  - Read an existing receipt for this app and signed-in viewer by server-issued challengeId, including previous UTC days and previous approved versions. Requires the current approved published release and runtime grant; a stale frame must reload first. Never awards, retries a claim, returns answer keys, or restores removed rewards permission.
@@ -1026,12 +1028,20 @@ world.updatePresence({ x, y, z, facing });
1026
1028
  - Creates or resumes a server-issued challenge for the signed-in viewer. Render its questions (prompt and optional hint) and collect numeric answers in the same order. One daily challenge per rule/review; repeat starts cannot reset attempts. A challenge stays open until the site's daily reset (UTC midnight, 9:00 AM in Korea) (expiresAt). Resuming after a wrong answer includes each question's guide.
1027
1029
  - Errors: build_reward_not_scheduled when the rule has no questions for today; build_reward_daily_claims_reached when the viewer already earned today’s cap. attemptsRemaining is null for unlimited rules.
1028
1030
  - For a completion rule call start when the activity begins (the moment the stage starts); the challenge's age is what the claim is measured against. In preview mode start also works for the owner (a stateless simulation).
1029
- - await Twinkle.rewards.claim({ challengeId, answers?: [number] }) | scopes: rewards:claim
1031
+ - For completionProof: classic-tower-v1, start also returns completion { profile, token, maxFrames, completed, failed }. A new start resets only the simulated climb to its canonical spawn; it cannot reset daily or lifetime rewards. Record inputs from the first physics frame. The completion token is bound to the viewer, challenge, rule and published release.
1032
+ - await Twinkle.rewards.progress({ challengeId, completionToken, frames }) | scopes: rewards:claim
1033
+ - Returns: { mode: "live" | "preview", completion: { profile: "classic-tower-v1", token, maxFrames, completed, failed } }
1034
+ - Verify a bounded batch of inputs for an approved server-simulated climb.
1035
+ - Only for completionProof: classic-tower-v1. Send 1 to 600 chronological physics frames, each [dt, moveX, moveY, cameraForwardX, cameraForwardZ, jumpPressed, jumpHeld, speedMultiplier]. dt is in seconds, at most 0.05; movement axes are -1 through 1; the camera values are the horizontal components before normalization; jump flags are 0 or 1; speedMultiplier is an existing Classic Tower trail speed (1, 1.1, 1.2, 1.3, 1.35 or 1.4). Geometry and player state are owned by the registered server simulation.
1036
+ - Send occasional batches with at most one request in flight. Keep the previous token and the exact batch until a response confirms it; an identical retry is safe. Use the returned token for the next batch. Simulation time cannot outrun wall time. A completed token is evidence of a legal simulated run, not proof that a human played or that inputs were not automated.
1037
+ - On respawn or a return from another world, begin a fresh run at the canonical spawn via start. Preserve other worlds and gameplay. Do not submit positions, scores, secret keys, or a client completion flag. Preview tokens can never be redeemed in the published app.
1038
+ - await Twinkle.rewards.claim({ challengeId, answers?: [number], completionToken?: string }) | scopes: rewards:claim
1030
1039
  - Returns: { awarded: false, attempts, attemptsRemaining, questions: [{ prompt, hint?, guide? }] } | { awarded: true, duplicate, receipt: { ruleId, xp, coins, attempt, firstTry }, questions: [{ prompt, hint?, guide? }], balances: { xp, coins } }
1031
1040
  - Twinkle verifies every answer, approval, current published artifact and budget before atomically recording XP and Coins. The receipt’s xp/coins are what was actually paid: the full amounts on a first try, the retry share after a wrong answer (attempt > 1). Retry the same challengeId after a lost response; a confirmed claim returns its original receipt without another award. Never update balance UI optimistically. Under retry.paidAttempts a correct answer past that attempt returns awarded: true with a zero receipt: solved, not paid.
1032
1041
  - Every claim response, wrong or right, returns the questions with their guides unlocked: show the teaching content right after the first answer. Answer keys are never returned.
1033
1042
  - A wrong answer within two seconds of the previous one is refused with build_reward_throttled (HTTP 429) and does not count; wait for the person to try again rather than retry-looping.
1034
1043
  - Completion rules take no answers: claim({ challengeId }) when the activity is finished. build_reward_too_fast (HTTP 409) means fewer than minSeconds passed since start; show nothing and let play continue. In preview mode the receipt carries preview: true and nothing is paid.
1044
+ - A completionProof rule also requires the signed completionToken from a successful rewards.progress response. The server simulates the registered game physics and must reach the goal. A timer, forged position, client win flag, altered inventory or token from another viewer, challenge or release cannot authorize payment. maxLifetimeClaims is enforced from receipts in the same award transaction. build_reward_lifetime_claims_reached means all rewards for this rule have been collected; do not retry it.
1035
1045
  - await Twinkle.rewards.getLeaderboard({ metric?: "xp" | "coins", period?: "day" | "week" | "all", limit? }) | scopes: rewards:claim
1036
1046
  - Returns: { mode: "live", metric, period, limit, dayKey, from, available: { xp, coins }, entries: [{ rank, userId, username, profilePicUrl, xp, coins, claims, lastAt }], me: { rank, xp, coins, claims } | null } | { mode: "preview", metric, period, available, entries: [], me: null, message }
1037
1047
  - Standings of who earned the most XP or Coins in THIS app, computed by Twinkle from its own receipts (never from anything the app submits). period 'day' is today (site day, UTC), 'week' the last 7 site days, 'all' (default) every day since approval. limit defaults to 20, max 100.
@@ -2752,8 +2752,10 @@ primary review does not cover the target. An open review does not block a
2752
2752
  deployment or host hold. Its files, lease and database boundaries persist;
2753
2753
  active requests use the normal drain. A held or unavailable owner returns a
2754
2754
  retryable failure, so keep the session and retry when that host is available
2755
- again. Release operators can review final shutdown deltas via management SSH
2756
- and record their own evidence. An active review keeps ownership of clearing;
2755
+ again. Release operators review final shutdown deltas through the deployment
2756
+ workflow's private SSM/S3 snapshots (or interactive management access during
2757
+ explicit recovery) and record their evidence. Raw logs never belong in GitHub
2758
+ output. An active review keeps ownership of clearing;
2757
2759
  otherwise API stderr is cleared with the existing guarded
2758
2760
  `npm run logs:clear-errors` plus post-clear re-read. A stopped target whose final logs
2759
2761
  were reviewed does not need to be started for daily management; starting EC2
@@ -2995,6 +2997,64 @@ Mikey"** section carrying only
2995
2997
  the deltas and anomalies worth his time, next to the escalation list. Never
2996
2998
  dump raw sections at him.
2997
2999
 
3000
+ ### Jev serving and audits (standing duty, every full daily review; updated 2026-09-20)
3001
+
3002
+ Read `data.jevPilot` from `lumine admin brief --json` and carry it into
3003
+ the full report for Mikey. The active `daily-run report --json` also includes
3004
+ `data.report.brief.jevPilot`. This duty does not authorize a separate full run.
3005
+ If the deployed API lacks the field, say the telemetry is not deployed; do not
3006
+ treat a missing section as zero traffic or a healthy pilot.
3007
+
3008
+ State the configuration and operating status even when off/blocked/awaiting
3009
+ samples. Headline the named last completed UTC day, compare with the trailing
3010
+ seven completed days, and keep the in-progress day separate. Include paired
3011
+ decision counts, disagreements (especially Jev react / baseline respond),
3012
+ p50/p95 latency for each model, provider errors/timeouts, pending observations,
3013
+ known incremental cost, unknown-cost requests, ledger gaps and cap status.
3014
+ Separate `bySurface.comment` from `bySurface.chat`. For chat, report
3015
+ `routingFieldDisagreements`, `candidateSkippedBaselineRequired`, and missing
3016
+ routing-comparison evidence. Equal reply actions do not prove equal tool/history
3017
+ routing. Chat's baseline also extracts structured plans, while Jev compares seven
3018
+ routing choices plus reaction emoji; these latencies do not establish an end-to-end speedup.
3019
+ Mikey authorized production serving on September 20 for the tested comment and
3020
+ text-chat routing decisions. Include `serving.jevDecisions`, baseline and
3021
+ unreserved fallbacks with reasons, audit coverage, served disagreements,
3022
+ `serving.decisionLatencyMs`, chat added wait, and comment baseline calls avoided.
3023
+ Chat retains the existing full planner for outputs outside Jev's tested scope;
3024
+ comments run a 5% independent background baseline audit. Distinguish actual
3025
+ selected routes from unused comparisons and identify Turtle's deployment tests.
3026
+ Chat's `baseline_requires_reply` fallback preserves the planner's written reply
3027
+ when Jev would only react; report its frequency and review those disagreements.
3028
+ Reaction-only chat responses require both models to agree.
3029
+ Jev chooses the emoji from all 18 supported reactions in `chat-routing-v2`.
3030
+ Report `reactionChoices` usage by source, paired emoji disagreements, and missing
3031
+ legacy evidence; review whether the chosen tone fits the canonical conversation.
3032
+ Candidate and served emoji are in `reviewCandidates` routing objects. Earlier
3033
+ `chat-routing-v1` rows have no emoji comparison and must not count as agreement.
3034
+ Mikey explicitly requested all eligible requests with no daily request cap
3035
+ (`dailyLimit: 0`) and a cost report during every full website-management run.
3036
+ Run `lumine admin ai-costs day YYYY-MM-DD --json` for the last completed UTC day.
3037
+ Report the canonical `data.dailyAiCosts.byOperation` USD totals for `jev_reply_gate_serve`,
3038
+ `jev_chat_routing_serve`, any `_shadow` operations, and `jev_reply_gate_audit`.
3039
+ Separate comment/chat provider spend from background baseline-audit spend.
3040
+ Report unfinished selection/baseline-audit telemetry; synthetic probes are
3041
+ excluded from performance metrics but included in daily request and cost counts.
3042
+ If `telemetryStatus: partial` or `telemetryComplete: false`, detail metrics are a
3043
+ bounded recent sample, not full-day performance or cost; use the canonical daily
3044
+ AI-cost report for complete cost totals and record the coverage gap.
3045
+ Known Jev spend and `jev_reply_gate_audit` calls are already in application AI
3046
+ costs: never add them again or infer net savings from Jev cost alone. Agreement
3047
+ is not accuracy; confidence is not a measured success rate.
3048
+
3049
+ Privately inspect the bounded `reviewCandidates` when needed, name what was
3050
+ actually reviewed, and account for edited comments or chat messages. Use the
3051
+ candidate's surface and target ID to find the correct canonical record. Ordinary model disagreements
3052
+ are evaluation findings; outages, stuck telemetry or missing ledger entries are
3053
+ operational findings. Report a recommendation to continue, adjust or stop, without
3054
+ automatically changing mode, scope or caps.
3055
+ See `twinkle-api/JEV_PILOT.md` for configuration,
3056
+ the synthetic evaluation step and release checks.
3057
+
2998
3058
  ### Application AI calendar-month cost (standing duty, every full daily review)
2999
3059
 
3000
3060
  Run `lumine admin ai-costs monthly --json` during every full daily management
@@ -3143,6 +3203,34 @@ reload while waiting in the queue. The current UTC day is returned with `inProgr
3143
3203
  **Headline `lastCompletedDay` (its exact `dayKey`) — never the in-progress
3144
3204
  day**, exactly as the closed-day AI-cost duty does.
3145
3205
 
3206
+ Since Mikey's 2026-09-20 decision, keep current Energy policy and worker
3207
+ capacity while observing. During every full run, supplement these counters
3208
+ with the read-only per-request diagnostic report (from the local API checkout):
3209
+
3210
+ ```bash
3211
+ ssh api-primary.twinkle.network \
3212
+ 'cd /home/ec2-user/server && timeout 75s node --max-old-space-size=128 -' \
3213
+ < scripts/build-energy-daily.cjs
3214
+ ```
3215
+
3216
+ This uses telemetry already recorded in queue jobs, canonical run sessions,
3217
+ provider-turn budget metadata and reservation usage; no new collection or API
3218
+ restart is needed. Save its JSON privately. Headline its last completed UTC
3219
+ day and compare the complete days in its seven-day window: queue wait p50/p90/
3220
+ maximum, starts waiting over 60 seconds, cancellations before start, unchanged
3221
+ budget stops, and the separate `handoff_only`, work-without-save and unknown
3222
+ patterns. Keep the two explicit denominators separate: unchanged stops / all
3223
+ budget stops, and unchanged stops / completed manual runs in the same cohort.
3224
+ Do not divide by usage reservations or assume busy-refusal counts measure waits.
3225
+
3226
+ Inspect the stop cases' observed starting budget, recorded work/handoff turns,
3227
+ remaining Energy and final-reservation spend. Missing lineage is unknown, not
3228
+ zero work or zero cost; final-reservation cost can exclude earlier planning
3229
+ reservations. The oldest day may be partial under rolling seven-day retention.
3230
+ These observations do not by themselves establish a bug or authorize an
3231
+ admission floor, extra worker capacity, budget cuts or model changes. Update
3232
+ todo 52 with the latest completed-day observations and any concrete regression.
3233
+
3146
3234
  `flags` lists every tripped check with its exact numbers: `overflow_usd`
3147
3235
  (overflow above $1 on a completed day), `budget_stop_unchanged_ratio` (more
3148
3236
  than 30% of at least 5 budget stops ended with nothing saved),