@maka/maka-cli 5.190.0 → 5.191.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/factories/scene-seed-generator.js +23 -2
- package/bundle/typescript/src/commands/game/sideQuest/game.js +20 -3
- package/bundle/typescript/src/commands/game/sideQuest/utilities/draft-failure-note.js +25 -1
- package/bundle/typescript/src/tools/ai/ai.class.js +26 -4
- package/bundle/typescript/src/tools/https/https.class.js +14 -9
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.191.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.",
|
|
@@ -7,6 +7,7 @@ import { Logger } from '../utilities/logger.js';
|
|
|
7
7
|
import { clampDeviceKind } from '../utilities/affordances.js';
|
|
8
8
|
import { GenerationCapture } from '../utilities/generation-capture.js';
|
|
9
9
|
import { fetchCanonContext } from '../utilities/canon-lore.js';
|
|
10
|
+
import { providerUnavailable } from '../utilities/draft-failure-note.js';
|
|
10
11
|
import { SceneSynthesizer } from './scene-factory.js';
|
|
11
12
|
import { buildSkeletonPrompt, buildRoomsPrompt, buildNpcsPrompt, buildDevicesPrompt, buildItemsPrompt, pickRunType, parseJsonReply, validateSkeleton, normalizeSkeleton, assembleScene, classifyRepair, VAULT_HOST_RATING, } from './scene-chunks.js';
|
|
12
13
|
// The fixer name convention and the player dossier live with the prompt
|
|
@@ -169,6 +170,26 @@ export class SceneSeedGenerator {
|
|
|
169
170
|
catch (err) {
|
|
170
171
|
carriedError = err instanceof Error ? err.message : String(err);
|
|
171
172
|
logger.write(`SceneSeedGenerator: pipeline ${pipeline} failed: ${carriedError}`);
|
|
173
|
+
// A PROVIDER THAT IS NOT ANSWERING WILL NOT ANSWER THREE TIMES
|
|
174
|
+
// (po7qSJycJrcfWbgaA, 2026-09-14). The pipeline budget exists for
|
|
175
|
+
// a model that wrote a bad SCENE -- a fresh skeleton is a real
|
|
176
|
+
// second chance at that. It is worth nothing against a provider
|
|
177
|
+
// that never started: DeepSeek refused this run's generation with
|
|
178
|
+
// "unable to start processing your request within the 900-second
|
|
179
|
+
// timeout limit", and the loop dutifully spent that wait twice
|
|
180
|
+
// more. Three-quarters of an hour, for a player standing in the
|
|
181
|
+
// hub, to arrive at the same refusal the first attempt already
|
|
182
|
+
// had in hand.
|
|
183
|
+
//
|
|
184
|
+
// So an unavailable provider ends the generation instead of
|
|
185
|
+
// re-buying it. The message survives in carriedError and reaches
|
|
186
|
+
// the player through draftFailureNote, which tells them it is the
|
|
187
|
+
// provider and not their key -- which is the whole point of
|
|
188
|
+
// stopping early rather than looking flaky for 45 minutes.
|
|
189
|
+
if (providerUnavailable(carriedError)) {
|
|
190
|
+
logger.write(`SceneSeedGenerator: provider is not answering -- abandoning the remaining ${this.maxPipelineAttempts - pipeline} pipeline attempt(s).`);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
172
193
|
}
|
|
173
194
|
}
|
|
174
195
|
logger.write(`SceneSeedGenerator: giving up (${carriedError ?? 'unknown'}) -- falling back to a static scene`);
|
|
@@ -188,7 +209,7 @@ export class SceneSeedGenerator {
|
|
|
188
209
|
let lastError = previousError;
|
|
189
210
|
for (let attempt = 1; attempt <= this.maxCallAttempts; attempt++) {
|
|
190
211
|
const prompt = buildSkeletonPrompt(tier, player, lastError, rumor, crew, canonBlock, runType);
|
|
191
|
-
const reply = await AI.ask(prompt, { maxTokens: this.SKELETON_TOKENS });
|
|
212
|
+
const reply = await AI.ask(prompt, { maxTokens: this.SKELETON_TOKENS, timeoutMs: AI.GENERATION_TIMEOUT_MS });
|
|
192
213
|
try {
|
|
193
214
|
const skeleton = parseJsonReply(reply);
|
|
194
215
|
skeleton.devices = skeleton.devices ?? [];
|
|
@@ -231,7 +252,7 @@ export class SceneSeedGenerator {
|
|
|
231
252
|
const fullPrompt = lastError
|
|
232
253
|
? `${prompt}\n\nYour previous attempt failed with this error -- fix it and try again: ${lastError}`
|
|
233
254
|
: prompt;
|
|
234
|
-
const reply = await AI.ask(fullPrompt, { maxTokens });
|
|
255
|
+
const reply = await AI.ask(fullPrompt, { maxTokens, timeoutMs: AI.GENERATION_TIMEOUT_MS });
|
|
235
256
|
try {
|
|
236
257
|
const parsed = parseJsonReply(reply);
|
|
237
258
|
logger.write(`SceneSeedGenerator: chunk "${label}" ok (attempt ${attempt})`);
|
|
@@ -191,7 +191,7 @@ import { clockTime, worldNow } from './utilities/world-clock.js';
|
|
|
191
191
|
import { CommandFactory } from './factories/command-factory.js';
|
|
192
192
|
import { SceneSynthesizer } from './factories/scene-factory.js';
|
|
193
193
|
import { SceneSeedGenerator, FIXER_NAME, isFixerName } from './factories/scene-seed-generator.js';
|
|
194
|
-
import { draftFailureNote } from './utilities/draft-failure-note.js';
|
|
194
|
+
import { draftFailureNote, providerUnavailable } from './utilities/draft-failure-note.js';
|
|
195
195
|
import { CALL_ICON, CALL_COLOR, END_CALL_SENTINEL } from './utilities/comm-style.js';
|
|
196
196
|
import { AI } from '../../../tools/ai/ai.class.js';
|
|
197
197
|
import { BULLET } from './utilities/log-style.js';
|
|
@@ -4646,8 +4646,25 @@ ${client.name} won't be remembering anything. Dead Johnsons pay nothing -- and t
|
|
|
4646
4646
|
// caller but the ask-for-data-work door passes.
|
|
4647
4647
|
const nextSeed = await SceneSeedGenerator.generate(this._logFilePath, nextTier, this.player, this.activeRumor, localCrew, undefined, runType);
|
|
4648
4648
|
if (!nextSeed) {
|
|
4649
|
-
const
|
|
4650
|
-
|
|
4649
|
+
const why = SceneSeedGenerator.lastFailure ?? '';
|
|
4650
|
+
const opsNote = draftFailureNote(why);
|
|
4651
|
+
// DO NOT INVENT A CLIENT WHO GOT COLD FEET (po7qSJycJrcfWbgaA).
|
|
4652
|
+
// That line is fine for a draft that came back malformed -- the
|
|
4653
|
+
// fiction has to say something, and "the deal soured" is as true
|
|
4654
|
+
// as anything. It is a LIE when the provider never answered: it
|
|
4655
|
+
// names a cause inside the story for something that happened
|
|
4656
|
+
// entirely outside it, and the advice it gives ("call me in a
|
|
4657
|
+
// bit") walks the player straight back into the same wall. This
|
|
4658
|
+
// codebase refuses invented causes everywhere else -- a fixer may
|
|
4659
|
+
// not name a venue it does not know, an NPC may not name a runner
|
|
4660
|
+
// who is not there -- and the rule holds here too.
|
|
4661
|
+
//
|
|
4662
|
+
// So an outage gets its own line: Krow cannot reach anyone, which
|
|
4663
|
+
// is the honest shape of it in his voice, and the OOC note under
|
|
4664
|
+
// it says the rest.
|
|
4665
|
+
return providerUnavailable(why)
|
|
4666
|
+
? `Incoming call -- ${FIXER_NAME}: "Can't raise anybody tonight, chummer -- whole street's gone quiet on me. Sit tight; I'll call when the lines come back."${opsNote}`
|
|
4667
|
+
: `Incoming call -- ${FIXER_NAME}: "Job fell through, chummer. Client got cold feet before the ink dried. Give me another call in a bit -- I'll shake something loose."${opsNote}`;
|
|
4651
4668
|
}
|
|
4652
4669
|
// HUB sessions: the draft lands as a MESSAGE, never a teleport (real
|
|
4653
4670
|
// playtest note: being yanked into the run the instant generation
|
|
@@ -23,6 +23,30 @@
|
|
|
23
23
|
* codes need word boundaries so they can't match inside some other
|
|
24
24
|
* number.
|
|
25
25
|
*/
|
|
26
|
+
/**
|
|
27
|
+
* IS THE PROVIDER SIMPLY NOT ANSWERING? Exported because two decisions
|
|
28
|
+
* hang off the same question and must never disagree: what the player is
|
|
29
|
+
* told (below), and whether SceneSeedGenerator bothers with its remaining
|
|
30
|
+
* pipeline attempts (po7qSJycJrcfWbgaA). Two separately-written lists
|
|
31
|
+
* would drift, and the drift would be invisible -- the generator would
|
|
32
|
+
* keep re-buying a fifteen-minute wait while the note said the provider
|
|
33
|
+
* was down.
|
|
34
|
+
*
|
|
35
|
+
* Matched on PROSE, not status codes: only the prose crosses the wire,
|
|
36
|
+
* and each provider words it differently.
|
|
37
|
+
*
|
|
38
|
+
* The 2026-09-14 addition is DeepSeek's own queue refusal -- "We were
|
|
39
|
+
* unable to start processing your request within the 900-second timeout
|
|
40
|
+
* limit. Please try again later." Nothing in the old list came close to
|
|
41
|
+
* it, so a real outage read to the player as "Job fell through, chummer.
|
|
42
|
+
* Client got cold feet" with no note at all, and sent them straight back
|
|
43
|
+
* to call into the same wall. `Request timeout` is this codebase's own
|
|
44
|
+
* transport giving up (https.class.ts), which means the same thing from
|
|
45
|
+
* the other end of the socket.
|
|
46
|
+
*/
|
|
47
|
+
export function providerUnavailable(why) {
|
|
48
|
+
return /overloaded or down|rate-limited by its provider|took too long to answer|could not (?:complete that request|reach its ai provider)|unable to start processing|\b\d{2,4}-second timeout limit|Request timeout|ETIMEDOUT|ECONNRESET|socket hang up/i.test(why);
|
|
49
|
+
}
|
|
26
50
|
export function draftFailureNote(why) {
|
|
27
51
|
// NAME THE CAUSE WHEN IT'S THE PROVIDER, NOT LUCK (real session: six
|
|
28
52
|
// Krow calls into an out-of-credits key, indistinguishable from flaky
|
|
@@ -50,7 +74,7 @@ export function draftFailureNote(why) {
|
|
|
50
74
|
// answering. Matched on the SITE'S OWN wording (ai-error-envelope.ts
|
|
51
75
|
// classifyUpstream/classifyTransportFailure) rather than status
|
|
52
76
|
// codes, since only the prose crosses the wire.
|
|
53
|
-
const providerOutage = !providerDown &&
|
|
77
|
+
const providerOutage = !providerDown && providerUnavailable(why);
|
|
54
78
|
// A structural failure is OURS, not the player's -- name it as the
|
|
55
79
|
// generator's problem so nobody goes hunting their billing page, and
|
|
56
80
|
// keep it short: the fiction already covers the beat.
|
|
@@ -377,6 +377,27 @@ export class AI {
|
|
|
377
377
|
* runs 3-5x a dialogue reply, and the old fixed caps truncated it
|
|
378
378
|
* mid-string). Leave unset for ordinary dialogue-sized replies.
|
|
379
379
|
*/
|
|
380
|
+
/**
|
|
381
|
+
* HOW LONG A GENERATION CALL WAITS (po7qSJycJrcfWbgaA, 2026-09-14).
|
|
382
|
+
*
|
|
383
|
+
* DeepSeek answered a run's scene generation with "We were unable to
|
|
384
|
+
* start processing your request within the 900-second timeout limit"
|
|
385
|
+
* -- fifteen minutes of queue before a refusal, and the generator's
|
|
386
|
+
* pipeline budget took that three times over. Forty-five minutes of a
|
|
387
|
+
* player standing in the hub waiting for a call that, when it finally
|
|
388
|
+
* came, said the job fell through.
|
|
389
|
+
*
|
|
390
|
+
* The transport's own default is the module's TIMEOUT (300s), which
|
|
391
|
+
* is a sane ceiling for a big download and far too patient for a
|
|
392
|
+
* prompt somebody is waiting on. A scene chunk that has not started
|
|
393
|
+
* coming back in 90 seconds is not going to save the commission.
|
|
394
|
+
*
|
|
395
|
+
* Deliberately NOT applied to every AI.ask: dialogue calls are short
|
|
396
|
+
* and already bounded by the same default, and an NPC that takes an
|
|
397
|
+
* extra beat is not a player staring at nothing. Generation asks for
|
|
398
|
+
* it explicitly.
|
|
399
|
+
*/
|
|
400
|
+
static GENERATION_TIMEOUT_MS = 90_000;
|
|
380
401
|
static async ask(prompt, opts) {
|
|
381
402
|
const config = this.resolveConfig();
|
|
382
403
|
if (!config) {
|
|
@@ -384,13 +405,14 @@ export class AI {
|
|
|
384
405
|
'or log in to maka-cli.com (`maka login`) to use the server relay ' +
|
|
385
406
|
'(optionally MAKA_AI_PROVIDER to choose explicitly).');
|
|
386
407
|
}
|
|
408
|
+
const timeoutMs = opts?.timeoutMs;
|
|
387
409
|
const result = config.provider === 'openai'
|
|
388
|
-
? await HTTPS.askGPT({ prompt, openApiKey: config.apiKey, model: config.model, maxTokens: opts?.maxTokens })
|
|
410
|
+
? await HTTPS.askGPT({ prompt, openApiKey: config.apiKey, model: config.model, maxTokens: opts?.maxTokens, timeoutMs })
|
|
389
411
|
: config.provider === 'anthropic'
|
|
390
|
-
? await HTTPS.askClaude({ prompt, apiKey: config.apiKey, model: config.model, maxTokens: opts?.maxTokens })
|
|
412
|
+
? await HTTPS.askClaude({ prompt, apiKey: config.apiKey, model: config.model, maxTokens: opts?.maxTokens, timeoutMs })
|
|
391
413
|
: config.provider === 'deepseek'
|
|
392
|
-
? await HTTPS.askDeepSeek({ prompt, apiKey: config.apiKey, model: config.model, maxTokens: opts?.maxTokens })
|
|
393
|
-
: await HTTPS.askMakaServer({ prompt, authToken: config.apiKey, model: config.model, maxTokens: opts?.maxTokens });
|
|
414
|
+
? await HTTPS.askDeepSeek({ prompt, apiKey: config.apiKey, model: config.model, maxTokens: opts?.maxTokens, timeoutMs })
|
|
415
|
+
: await HTTPS.askMakaServer({ prompt, authToken: config.apiKey, model: config.model, maxTokens: opts?.maxTokens, timeoutMs });
|
|
394
416
|
// A BODY THAT IS NOT JSON IS AN HTTP PROBLEM, NOT A PARSE PROBLEM.
|
|
395
417
|
// This parse used to run bare, AHEAD of the statusCode guard below
|
|
396
418
|
// -- so that guard only ever protected JSON-shaped failures. When
|
|
@@ -258,7 +258,7 @@ export class HTTPS {
|
|
|
258
258
|
req.end();
|
|
259
259
|
});
|
|
260
260
|
}
|
|
261
|
-
static async askGPT({ prompt, openApiKey, certPath, model = 'gpt-4.1', maxTokens = 3000 }) {
|
|
261
|
+
static async askGPT({ prompt, openApiKey, certPath, model = 'gpt-4.1', maxTokens = 3000, timeoutMs }) {
|
|
262
262
|
const headers = {
|
|
263
263
|
'Authorization': `Bearer ${openApiKey}`,
|
|
264
264
|
'Content-Type': 'application/json',
|
|
@@ -283,10 +283,11 @@ export class HTTPS {
|
|
|
283
283
|
path: '/v1/chat/completions',
|
|
284
284
|
data,
|
|
285
285
|
headers,
|
|
286
|
-
cert: certPath
|
|
286
|
+
cert: certPath,
|
|
287
|
+
timeout: timeoutMs
|
|
287
288
|
});
|
|
288
289
|
}
|
|
289
|
-
static async askClaude({ prompt, apiKey, certPath, model = 'claude-sonnet-5', maxTokens = 4096 }) {
|
|
290
|
+
static async askClaude({ prompt, apiKey, certPath, model = 'claude-sonnet-5', maxTokens = 4096, timeoutMs }) {
|
|
290
291
|
const headers = {
|
|
291
292
|
'x-api-key': apiKey,
|
|
292
293
|
'anthropic-version': '2023-06-01',
|
|
@@ -328,7 +329,8 @@ export class HTTPS {
|
|
|
328
329
|
path: '/v1/messages',
|
|
329
330
|
data,
|
|
330
331
|
headers,
|
|
331
|
-
cert: certPath
|
|
332
|
+
cert: certPath,
|
|
333
|
+
timeout: timeoutMs
|
|
332
334
|
});
|
|
333
335
|
}
|
|
334
336
|
/**
|
|
@@ -349,7 +351,7 @@ export class HTTPS {
|
|
|
349
351
|
* reasoning tokens that never reach the player. The same trap the
|
|
350
352
|
* Claude path above documents, sprung by a different vendor.
|
|
351
353
|
*/
|
|
352
|
-
static async askDeepSeek({ prompt, apiKey, certPath, model = 'deepseek-v4-flash', maxTokens = 4096 }) {
|
|
354
|
+
static async askDeepSeek({ prompt, apiKey, certPath, model = 'deepseek-v4-flash', maxTokens = 4096, timeoutMs }) {
|
|
353
355
|
return await this.post({
|
|
354
356
|
hostname: 'api.deepseek.com',
|
|
355
357
|
path: '/anthropic/v1/messages',
|
|
@@ -364,7 +366,8 @@ export class HTTPS {
|
|
|
364
366
|
'anthropic-version': '2023-06-01',
|
|
365
367
|
'Content-Type': 'application/json',
|
|
366
368
|
},
|
|
367
|
-
cert: certPath
|
|
369
|
+
cert: certPath,
|
|
370
|
+
timeout: timeoutMs
|
|
368
371
|
});
|
|
369
372
|
}
|
|
370
373
|
/**
|
|
@@ -375,7 +378,7 @@ export class HTTPS {
|
|
|
375
378
|
* resume token, not an API key. Production host only -- the relay is
|
|
376
379
|
* an account nicety, not a dev-loop dependency.
|
|
377
380
|
*/
|
|
378
|
-
static async askMakaServer({ prompt, authToken, model, maxTokens = 4096 }) {
|
|
381
|
+
static async askMakaServer({ prompt, authToken, model, maxTokens = 4096, timeoutMs }) {
|
|
379
382
|
// MAKA_DEV TARGETING (kvhsgn3ouEG2znsha). Every other maka-cli.com
|
|
380
383
|
// transport in this codebase already swings to a local server --
|
|
381
384
|
// backlog, canon-lore, catalog, cloud-saves, session-telemetry,
|
|
@@ -401,8 +404,10 @@ export class HTTPS {
|
|
|
401
404
|
data: { prompt, model, maxTokens },
|
|
402
405
|
headers: { 'x-auth-token': authToken },
|
|
403
406
|
// Generation-length calls ride this too; outlive the server's own
|
|
404
|
-
// 150s upstream timeout so its error body reaches us.
|
|
405
|
-
|
|
407
|
+
// 150s upstream timeout so its error body reaches us. A caller
|
|
408
|
+
// asking for LESS patience still wins -- it is the one that knows
|
|
409
|
+
// whether somebody is waiting on the answer.
|
|
410
|
+
timeout: timeoutMs ?? 180000,
|
|
406
411
|
});
|
|
407
412
|
}
|
|
408
413
|
static async askGPTStreaming(prompt, model = 'gpt-4.1') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maka/maka-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.191.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.",
|