@maka/maka-cli 5.213.0 → 5.215.0
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/bundle/typescript/package.json +1 -1
- package/bundle/typescript/src/commands/game/sideQuest/commands/backlog.js +16 -5
- package/bundle/typescript/src/commands/game/sideQuest/engine-version.js +59 -1
- package/bundle/typescript/src/commands/game/sideQuest/models/npc.js +8 -0
- package/bundle/typescript/src/commands/game/sideQuest/utilities/backlog.js +23 -8
- package/bundle/typescript/src/commands/game/sideQuest/utilities/narration-limits.js +83 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.215.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|
|
@@ -150,7 +150,7 @@ export class BacklogCommand extends Command {
|
|
|
150
150
|
// patch-probe exists to catch. game.ts already imports this module
|
|
151
151
|
// lazily for the same reason (streetTransport).
|
|
152
152
|
const { cliVersion, engineVersion } = await import('../utilities/shared-run.js');
|
|
153
|
-
const
|
|
153
|
+
const filed = await submitBacklog(text, {
|
|
154
154
|
runnerName: this.actor?.name,
|
|
155
155
|
// Links the item back to the play session it came out of -- the
|
|
156
156
|
// same id the uploaded logs are keyed by, so an item and its
|
|
@@ -177,10 +177,21 @@ export class BacklogCommand extends Command {
|
|
|
177
177
|
client: currentSession()?.client ?? 'cli',
|
|
178
178
|
});
|
|
179
179
|
// A real call classifies the account for free -- no second request.
|
|
180
|
-
noteBacklogOutcome(outcome);
|
|
181
|
-
if (outcome !== 'ok')
|
|
182
|
-
return this.explain(outcome);
|
|
183
|
-
|
|
180
|
+
noteBacklogOutcome(filed.outcome);
|
|
181
|
+
if (filed.outcome !== 'ok')
|
|
182
|
+
return this.explain(filed.outcome);
|
|
183
|
+
// NAME THE THING YOU JUST FILED (X3tZu4rspsa5CQW38, Maka: 'Let's
|
|
184
|
+
// change this to read "Logged - <ID>"'). "Filed. It'll come back
|
|
185
|
+
// checked against the books." told the reporter a report exists
|
|
186
|
+
// somewhere and gave them no way to refer to it -- and the id was on
|
|
187
|
+
// the wire the whole time, thrown away by submitBacklog.
|
|
188
|
+
//
|
|
189
|
+
// The old sentence is kept for the case where the server answered
|
|
190
|
+
// 200 with a body we could not read: the item IS filed, and saying
|
|
191
|
+
// "Logged - undefined" would be worse than saying less.
|
|
192
|
+
return filed.id
|
|
193
|
+
? `{cyan-fg}Logged - ${filed.id}${hint(` ("backlog list" reads the board.)`)}{/cyan-fg}`
|
|
194
|
+
: `{cyan-fg}Filed. It'll come back checked against the books.${hint(` ("backlog list" reads the board.)`)}{/cyan-fg}`;
|
|
184
195
|
}
|
|
185
196
|
/**
|
|
186
197
|
* THE REVIEW QUEUE (user request 2026-08-27). Bare "backlog review"
|
|
@@ -1045,5 +1045,63 @@
|
|
|
1045
1045
|
// at the top of execute() has charged for the phase before it knows
|
|
1046
1046
|
// whether it has a target, and a verb that bills before delegating has
|
|
1047
1047
|
// charged for a refusal it cannot see.
|
|
1048
|
-
|
|
1048
|
+
// 1.74.0 (2026-09-16): A BEAT IS WHAT THE ROOM CAN SEE AND HEAR
|
|
1049
|
+
// (BDYYpN9EZJtTJ5B6A, Maka: 'I shouldn't "notice" skitter's inner
|
|
1050
|
+
// monolog: "Skitter's eyes narrow--sharp as a razor. The kid just
|
|
1051
|
+
// walked off with product without settling up, and now he's shouting
|
|
1052
|
+
// about it like it's some kinda joke."'). The first sentence is what
|
|
1053
|
+
// a beat is FOR. The second is Skitter's private assessment, printed
|
|
1054
|
+
// to a player with no way to know it -- and a player who reads an
|
|
1055
|
+
// NPC's judgement of them cannot un-know it.
|
|
1056
|
+
// - The brief said only "Any exposition should be in 3rd person",
|
|
1057
|
+
// which constrains grammatical person and says nothing about
|
|
1058
|
+
// observability -- it licensed exactly this. A new bullet draws the
|
|
1059
|
+
// line where it belongs: exposition is what someone standing in the
|
|
1060
|
+
// room could see or hear, never what you know, have worked out,
|
|
1061
|
+
// think of them, or are about to do.
|
|
1062
|
+
// - An INTERIOR rule joins CONTACT, COMPELLED_MOVEMENT and
|
|
1063
|
+
// SELF_LOCATION in narration-limits.ts, because an instruction is
|
|
1064
|
+
// advice and this file is the engine. It catches interiority that
|
|
1065
|
+
// ANNOUNCES itself: a mind named as a place ("in his head", "in her
|
|
1066
|
+
// mind"), a certainty no watcher could have ("knows exactly", "knew
|
|
1067
|
+
// full well"), and silent calculation as an event ("running the
|
|
1068
|
+
// math").
|
|
1069
|
+
// - MEASURED OVER ALL 294 exposition beats the site has stored, not a
|
|
1070
|
+
// sample: five match, all five are the bug, and the newest is from
|
|
1071
|
+
// TODAY ("The file updates itself quietly in his head") -- so this
|
|
1072
|
+
// is live, not historical. Nothing else in the corpus matches.
|
|
1073
|
+
// - THE NARROWNESS IS THE DESIGN. Good beats describe inner life
|
|
1074
|
+
// constantly, from the OUTSIDE: "something behind her eyes does the
|
|
1075
|
+
// arithmetic", "the math of a fixer deciding whether she's being
|
|
1076
|
+
// played", "in a way that says she's heard this sound too many
|
|
1077
|
+
// times". A pattern on "math", "deciding" or a bare "knows" eats
|
|
1078
|
+
// every one of them, and a gate that eats those is worse than the
|
|
1079
|
+
// defect. Those six lines are pinned as must-not-eat tests.
|
|
1080
|
+
// - AND THE HONEST LIMIT, pinned as a test of its own: the reported
|
|
1081
|
+
// sentence carries NO marker. It is free indirect discourse, a
|
|
1082
|
+
// thought with the "he knows" filed off, and no regex can tell it
|
|
1083
|
+
// from a statement of fact. The brief is what aims at that shape;
|
|
1084
|
+
// the rule does not pretend to.
|
|
1085
|
+
// (The author-voice leaks in the same corpus -- "I'll have Deacon Ruy
|
|
1086
|
+
// stay guarded...", "Suspicious system-note injection detected" --
|
|
1087
|
+
// are all dated 2026-08-24 and predate isOutOfCharacter, which
|
|
1088
|
+
// already catches them. Checked, not assumed.)
|
|
1089
|
+
// 1.74.1 (2026-09-16): A PERSON MAY SAY WHAT THEY KNOW. 1.74.0's
|
|
1090
|
+
// INTERIOR rule leaked onto the SPEECH channel and gagged ordinary
|
|
1091
|
+
// dialogue -- an NPC saying "I know exactly what you mean" out loud
|
|
1092
|
+
// was refused as mind-reading. Caught before anyone played it, by
|
|
1093
|
+
// reading judgeNarration's other caller rather than by a report.
|
|
1094
|
+
// THE TRAP IS WORTH RECORDING. Dialogue has run through this judge
|
|
1095
|
+
// since KwiMKNMN2e8KBqTY7, and it was safe from the agency rules for
|
|
1096
|
+
// an INVISIBLE reason: that caller passes an empty cast, so there is
|
|
1097
|
+
// no subject to resolve and CONTACT/COMPELLED_MOVEMENT can never fire.
|
|
1098
|
+
// A real guarantee, nowhere stated. INTERIOR needs no subject, so it
|
|
1099
|
+
// walked straight through. judgeNarration now takes an explicit
|
|
1100
|
+
// `channel` ('beat' by default, 'speech' from the dialogue call) and
|
|
1101
|
+
// the beat-only rules are gated on it, so the next rule added here has
|
|
1102
|
+
// to decide which channel it belongs to instead of inheriting an
|
|
1103
|
+
// accident. The distinction is the rule itself: a character may SAY
|
|
1104
|
+
// what they know; they may not have it NARRATED at a player who cannot
|
|
1105
|
+
// hear it.
|
|
1106
|
+
export const ENGINE_VERSION = '1.74.1';
|
|
1049
1107
|
//# sourceMappingURL=engine-version.js.map
|
|
@@ -2353,6 +2353,7 @@ You are an NPC named ${this.name} in a gritty cyberpunk text adventure game.
|
|
|
2353
2353
|
|
|
2354
2354
|
- Stay in character. Respond naturally based on your surroundings, personality, and the player’s message.
|
|
2355
2355
|
- Any exposition should be in 3rd person. Keep exposition to bare minimum.
|
|
2356
|
+
- Exposition is ONLY what someone standing in the room could see or hear. Never your own thoughts, judgements, conclusions, or memories -- not what you know, not what you have worked out, not what you think of them, not what you are about to do. Write the OUTSIDE of it: the look, the pause, the hand that stops moving. "His eyes narrow" is yours to write. "He knows the kid never paid" is not, and neither is the same thought with the "he knows" filed off.
|
|
2356
2357
|
- Do NOT repeat, rephrase, or re-ask anything you have already said (check the recent-activity history below). If you already asked a question and it hasn't been answered, wait for the answer -- do not ask it again in different words.
|
|
2357
2358
|
- Silence is a valid response: if this event doesn't genuinely need words from you, do not include a say command at all.
|
|
2358
2359
|
- Keep spoken lines SHORT -- one or two sentences. People don't monologue at strangers.
|
|
@@ -2514,6 +2515,13 @@ Your objective is to drive the story line.`}
|
|
|
2514
2515
|
const placeVerdict = judgeNarration(said, this.name, [], {
|
|
2515
2516
|
here: this.currentLocation?.name,
|
|
2516
2517
|
worldRooms: Object.values(this._scene?.getRooms?.() ?? {}).map(r => r.name),
|
|
2518
|
+
// SAYS WHICH CHANNEL THIS IS, rather than relying on the empty
|
|
2519
|
+
// `others` above to keep the beat-only rules from firing. That
|
|
2520
|
+
// guarantee held for the agency rules, which need a subject to
|
|
2521
|
+
// resolve -- and broke the moment a rule arrived that does not
|
|
2522
|
+
// (INTERIOR), refusing an ordinary spoken "I know exactly what
|
|
2523
|
+
// you mean" as mind-reading. A person may SAY what they know.
|
|
2524
|
+
channel: 'speech',
|
|
2517
2525
|
});
|
|
2518
2526
|
if (placeVerdict.refused) {
|
|
2519
2527
|
this.logger.write(`${this.name} speech refused (claimed to be elsewhere): ${said.slice(0, 160)}`);
|
|
@@ -65,24 +65,39 @@ function parseData(raw) {
|
|
|
65
65
|
return undefined;
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* File one, and SAY WHICH ONE (X3tZu4rspsa5CQW38, Maka: 'Let's change
|
|
70
|
+
* this to read "Logged - <ID>"').
|
|
71
|
+
*
|
|
72
|
+
* The id was always on the wire -- the route has answered
|
|
73
|
+
* `{ id, status, vetting }` since it was written -- and this function
|
|
74
|
+
* threw it away, returning a bare outcome word. So the only thing the
|
|
75
|
+
* player who filed a report could say about it afterwards was "I filed
|
|
76
|
+
* something", which is not enough to look it up, quote it, or ask about
|
|
77
|
+
* it. Returns the shape its siblings already use.
|
|
78
|
+
*/
|
|
68
79
|
export async function submitBacklog(text, meta) {
|
|
69
80
|
const token = authToken();
|
|
70
81
|
if (!token)
|
|
71
|
-
return 'no-login';
|
|
82
|
+
return { outcome: 'no-login' };
|
|
72
83
|
try {
|
|
73
84
|
const res = await request('POST', `/${API_BASE}/item`, token, { text, ...meta });
|
|
74
|
-
if (res.statusCode === 200)
|
|
75
|
-
|
|
85
|
+
if (res.statusCode === 200) {
|
|
86
|
+
// An id we cannot read is not a failure to file -- the item IS on
|
|
87
|
+
// the board. The caller falls back to wording that does not
|
|
88
|
+
// promise one rather than reporting a filing that worked as broken.
|
|
89
|
+
return { outcome: 'ok', id: parseData(res.data)?.id };
|
|
90
|
+
}
|
|
76
91
|
if (res.statusCode === 401)
|
|
77
|
-
return 'expired';
|
|
92
|
+
return { outcome: 'expired' };
|
|
78
93
|
if (res.statusCode === 403)
|
|
79
|
-
return 'forbidden';
|
|
94
|
+
return { outcome: 'forbidden' };
|
|
80
95
|
if (res.statusCode === 400)
|
|
81
|
-
return 'rejected';
|
|
82
|
-
return 'offline';
|
|
96
|
+
return { outcome: 'rejected' };
|
|
97
|
+
return { outcome: 'offline' };
|
|
83
98
|
}
|
|
84
99
|
catch {
|
|
85
|
-
return 'offline';
|
|
100
|
+
return { outcome: 'offline' };
|
|
86
101
|
}
|
|
87
102
|
}
|
|
88
103
|
/**
|
|
@@ -121,6 +121,64 @@ const SELF_LOCATION = new RegExp([
|
|
|
121
121
|
// "I'm standing/sitting/waiting in X".
|
|
122
122
|
String.raw `\bI(?:'|’)?m\s+(?:stand|sitt|wait|post|hang|hold)\w*\s+(?:up\s+|out\s+|around\s+)*(?:at|in|by|outside)\s+(?:the\s+)?([^.,;!?"'’]{2,48})`,
|
|
123
123
|
].join('|'), 'i');
|
|
124
|
+
/**
|
|
125
|
+
* READING THE NPC'S MIND (BDYYpN9EZJtTJ5B6A, Maka: 'I shouldn't "notice"
|
|
126
|
+
* skitter's inner monolog: "Skitter's eyes narrow—sharp as a razor. The
|
|
127
|
+
* kid just walked off with product without settling up, and now he's
|
|
128
|
+
* shouting about it like it's some kinda joke."').
|
|
129
|
+
*
|
|
130
|
+
* A beat is what a person standing in the room can SEE and HEAR. The
|
|
131
|
+
* first sentence of that line is exactly that and is good writing. The
|
|
132
|
+
* second is Skitter's private assessment, printed to a player who has no
|
|
133
|
+
* way to know it -- and a player who reads an NPC's judgement of them
|
|
134
|
+
* cannot un-know it, which is half the game gone.
|
|
135
|
+
*
|
|
136
|
+
* WHAT THIS CATCHES AND WHAT IT CANNOT. The reported sentence carries no
|
|
137
|
+
* marker at all: it is free indirect discourse, a thought written as
|
|
138
|
+
* plain narration, and nothing short of a model could tell it from a
|
|
139
|
+
* statement of fact. So this rule does NOT claim to catch it -- the
|
|
140
|
+
* brief in models/npc.ts is what aims at that shape. What is catchable
|
|
141
|
+
* is interiority that ANNOUNCES itself: a mind named, or a mental act
|
|
142
|
+
* given as a fact.
|
|
143
|
+
*
|
|
144
|
+
* MEASURED, NOT GUESSED, over all 294 exposition beats the site has
|
|
145
|
+
* stored (GameSessionEvents, channel 'log'). Five lines match and all
|
|
146
|
+
* five are the bug:
|
|
147
|
+
*
|
|
148
|
+
* "...already sliding out of the booth's shadow in her mind if not
|
|
149
|
+
* her body."
|
|
150
|
+
* "...that's not natural, and Ilsen knows exactly what it means."
|
|
151
|
+
* "...but he's already running the math -- two of them, close
|
|
152
|
+
* quarters, his crew somewhere outside earshot."
|
|
153
|
+
* "Forty years on the pier means he knows exactly how far a man has
|
|
154
|
+
* to be standing before he can lie about why he's here."
|
|
155
|
+
* "The file updates itself quietly in his head."
|
|
156
|
+
*
|
|
157
|
+
* Nothing else in the corpus matches, and the near misses are the
|
|
158
|
+
* reason the patterns are this narrow. Good beats constantly describe
|
|
159
|
+
* inner life FROM THE OUTSIDE -- "something behind her eyes does the
|
|
160
|
+
* arithmetic", "the math of a fixer deciding whether she's being
|
|
161
|
+
* played", "like he's reading a bill of sale", "in a way that says
|
|
162
|
+
* she's heard this sound too many times". Every one of those is an
|
|
163
|
+
* observer's inference about visible behaviour, which is precisely what
|
|
164
|
+
* a beat is FOR. A pattern on the word "math" or "deciding" would eat
|
|
165
|
+
* all of them, so the anchors are the mind itself ("in his head", "in
|
|
166
|
+
* her mind"), certainty no observer could have ("knows exactly"), and
|
|
167
|
+
* the one idiom that always means silent calculation ("running the
|
|
168
|
+
* math").
|
|
169
|
+
*/
|
|
170
|
+
const INTERIOR = new RegExp([
|
|
171
|
+
// The mind, named as a place something happens. "in his head", "in
|
|
172
|
+
// her mind", "inside their thoughts". NOT "in the back of the shop".
|
|
173
|
+
String.raw `\b(?:in|inside|through|across)\s+(?:his|her|their|its)\s+(?:mind|head|thoughts)\b`,
|
|
174
|
+
// A certainty no watcher could have. "he knows exactly what it
|
|
175
|
+
// means", "she knows exactly how far". The adverb is load-bearing:
|
|
176
|
+
// "he knows the code" is a fact about the world a beat may state.
|
|
177
|
+
String.raw `\b(?:knows|knew)\s+(?:exactly|full\s+well|perfectly\s+well|damn\s+well)\b`,
|
|
178
|
+
// Silent calculation as an event. "already running the math",
|
|
179
|
+
// "running the numbers in his head" (caught twice over, harmlessly).
|
|
180
|
+
String.raw `\brunning\s+the\s+(?:math|numbers|odds)\b`,
|
|
181
|
+
].join('|'), 'i');
|
|
124
182
|
/** Every place a self-location claim names in one line. */
|
|
125
183
|
function placesClaimed(line) {
|
|
126
184
|
const out = [];
|
|
@@ -251,6 +309,21 @@ function aimedAtAPerson(text, matches, subject) {
|
|
|
251
309
|
* @param opts.holding who this NPC already has a grapple on, if anyone --
|
|
252
310
|
* narration about a hold the dice already settled is a
|
|
253
311
|
* description, not a claim.
|
|
312
|
+
* @param opts.channel which half of the reply this is. 'beat' (the
|
|
313
|
+
* default) is exposition and gets every rule. 'speech'
|
|
314
|
+
* is a line of DIALOGUE and gets the place rule only.
|
|
315
|
+
*
|
|
316
|
+
* WHY THE CHANNEL IS A PARAMETER AND NOT AN ACCIDENT. Dialogue has been
|
|
317
|
+
* passing through here since KwiMKNMN2e8KBqTY7 ("Whisper ... says he
|
|
318
|
+
* is"), and it survived the agency rules only because its caller passes
|
|
319
|
+
* an EMPTY `others` -- with nobody to resolve as the subject, CONTACT
|
|
320
|
+
* and COMPELLED_MOVEMENT can never fire. That is a real guarantee but an
|
|
321
|
+
* invisible one, and the INTERIOR rule broke it the moment it was
|
|
322
|
+
* written: interiority needs no subject, so a perfectly ordinary spoken
|
|
323
|
+
* "I know exactly what you mean" was refused as mind-reading. A person
|
|
324
|
+
* is allowed to say what they know. They are not allowed to have it
|
|
325
|
+
* NARRATED at a player who cannot hear it -- which is the whole
|
|
326
|
+
* distinction, and it now has a name in the signature.
|
|
254
327
|
*/
|
|
255
328
|
export function judgeNarration(line, speaker, others, opts = {}) {
|
|
256
329
|
const t = line.replace(/^[\s│|>*-]+/, '').trim();
|
|
@@ -268,6 +341,16 @@ export function judgeNarration(line, speaker, others, opts = {}) {
|
|
|
268
341
|
};
|
|
269
342
|
}
|
|
270
343
|
}
|
|
344
|
+
// ALSO BEFORE the agency rules, and for the same reason: this is about
|
|
345
|
+
// the SPEAKER's own head, so there is no subject to resolve -- which
|
|
346
|
+
// is exactly why it must be told to stay off the speech channel.
|
|
347
|
+
if (opts.channel !== 'speech' && INTERIOR.test(t)) {
|
|
348
|
+
return {
|
|
349
|
+
refused: true,
|
|
350
|
+
subject: speaker,
|
|
351
|
+
note: `A BEAT IS WHAT THE ROOM CAN SEE AND HEAR. Your last beat read your own mind out loud -- what you know, what you are working out, what is going on in your head -- so it did not happen and the player was not shown it. Nobody standing in front of you can see any of that. Write the OUTSIDE of it instead: the look, the pause, the hand that stops moving, the glance at the door. A player who watches you narrow your eyes and go quiet learns more than one who is handed your conclusion.`,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
271
354
|
const contactAt = t.match(CONTACT);
|
|
272
355
|
const movedAt = t.match(COMPELLED_MOVEMENT);
|
|
273
356
|
if (!contactAt && !movedAt)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.215.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"summary": "A command line tool for scaffolding Meteor 3.x applications using either React.",
|
|
6
6
|
"description": "A command line tool for scaffolding Meteor 3.x applications using React.",
|