gsd-pi 2.73.0-dev.e1c09f2 → 2.73.1-dev.6ddfa43
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/dist/resources/extensions/claude-code-cli/stream-adapter.js +9 -3
- package/dist/resources/extensions/gsd/auto-model-selection.js +54 -11
- package/dist/resources/extensions/gsd/auto-start.js +20 -6
- package/dist/resources/extensions/gsd/auto.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/crash-log.js +31 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +18 -7
- package/dist/resources/extensions/gsd/crash-recovery.js +51 -0
- package/dist/resources/extensions/gsd/gsd-db.js +36 -2
- package/dist/resources/extensions/gsd/milestone-actions.js +19 -1
- package/dist/resources/extensions/gsd/preferences-models.js +43 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +22 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/src/index.ts +4 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +175 -8
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +12 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +51 -26
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +73 -12
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +198 -8
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +62 -26
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +92 -17
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -4
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +23 -2
- package/src/resources/extensions/gsd/auto-model-selection.ts +85 -11
- package/src/resources/extensions/gsd/auto-start.ts +27 -6
- package/src/resources/extensions/gsd/auto.ts +5 -0
- package/src/resources/extensions/gsd/bootstrap/crash-log.ts +32 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +19 -7
- package/src/resources/extensions/gsd/crash-recovery.ts +59 -0
- package/src/resources/extensions/gsd/gsd-db.ts +52 -2
- package/src/resources/extensions/gsd/milestone-actions.ts +19 -1
- package/src/resources/extensions/gsd/preferences-models.ts +41 -0
- package/src/resources/extensions/gsd/preferences-types.ts +12 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +23 -0
- package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +51 -2
- package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +235 -0
- package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +137 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
- package/src/resources/extensions/gsd/tests/model-isolation.test.ts +91 -2
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/preferences.test.ts +47 -0
- /package/dist/web/standalone/.next/static/{_XD_gUDcZNBbWV5rI8RgS → r6AvNu-aMwn4nwqjHqAfw}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{_XD_gUDcZNBbWV5rI8RgS → r6AvNu-aMwn4nwqjHqAfw}/_ssgManifest.js +0 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* AssistantMessageEvents for TUI rendering, then strips tool-call blocks from
|
|
7
7
|
* the final AssistantMessage so GSD's agent loop doesn't try to dispatch them.
|
|
8
8
|
*/
|
|
9
|
-
import { EventStream } from "@gsd/pi-ai";
|
|
9
|
+
import { EventStream, mapThinkingLevelToEffort, supportsAdaptiveThinking } from "@gsd/pi-ai";
|
|
10
10
|
import { execSync } from "node:child_process";
|
|
11
11
|
import { PartialMessageBuilder, ZERO_USAGE, mapUsage } from "./partial-builder.js";
|
|
12
12
|
import { buildWorkflowMcpServers } from "../gsd/workflow-mcp.js";
|
|
@@ -437,6 +437,7 @@ export async function resolveClaudePermissionMode(env = process.env) {
|
|
|
437
437
|
* behaviour pass `permissionMode: "bypassPermissions"` explicitly.
|
|
438
438
|
*/
|
|
439
439
|
export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
440
|
+
const { reasoning, ...sdkExtraOptions } = extraOptions;
|
|
440
441
|
const mcpServers = buildWorkflowMcpServers();
|
|
441
442
|
const permissionMode = overrides?.permissionMode ?? "bypassPermissions";
|
|
442
443
|
const disallowedTools = ["AskUserQuestion"];
|
|
@@ -455,6 +456,9 @@ export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
|
455
456
|
"Bash(pwd)",
|
|
456
457
|
...(mcpServers ? Object.keys(mcpServers).map((serverName) => `mcp__${serverName}__*`) : []),
|
|
457
458
|
];
|
|
459
|
+
const effort = reasoning && supportsAdaptiveThinking(modelId)
|
|
460
|
+
? mapThinkingLevelToEffort(reasoning, modelId)
|
|
461
|
+
: undefined;
|
|
458
462
|
return {
|
|
459
463
|
pathToClaudeCodeExecutable: getClaudePath(),
|
|
460
464
|
model: modelId,
|
|
@@ -469,7 +473,8 @@ export function buildSdkOptions(modelId, prompt, overrides, extraOptions = {}) {
|
|
|
469
473
|
...(allowedTools.length > 0 ? { allowedTools } : {}),
|
|
470
474
|
...(mcpServers ? { mcpServers } : {}),
|
|
471
475
|
betas: modelId.includes("sonnet") ? ["context-1m-2025-08-07"] : [],
|
|
472
|
-
...
|
|
476
|
+
...(effort ? { effort } : {}),
|
|
477
|
+
...sdkExtraOptions,
|
|
473
478
|
};
|
|
474
479
|
}
|
|
475
480
|
function normalizeToolResultContent(content) {
|
|
@@ -620,9 +625,10 @@ async function pumpSdkMessages(model, context, options, stream) {
|
|
|
620
625
|
const permissionMode = await resolveClaudePermissionMode();
|
|
621
626
|
const sdkOpts = buildSdkOptions(modelId, prompt, { permissionMode }, typeof options?.extensionUIContext === "object"
|
|
622
627
|
? {
|
|
628
|
+
reasoning: options?.reasoning,
|
|
623
629
|
onElicitation: createClaudeCodeElicitationHandler(options?.extensionUIContext),
|
|
624
630
|
}
|
|
625
|
-
: {});
|
|
631
|
+
: { reasoning: options?.reasoning });
|
|
626
632
|
const queryResult = sdk.query({
|
|
627
633
|
prompt,
|
|
628
634
|
options: {
|
|
@@ -9,10 +9,8 @@ import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabil
|
|
|
9
9
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
10
10
|
import { unitPhaseLabel } from "./auto-dashboard.js";
|
|
11
11
|
import { getSessionModelOverride } from "./session-model-override.js";
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */
|
|
15
|
-
isAutoMode = true) {
|
|
12
|
+
import { logWarning } from "./workflow-logger.js";
|
|
13
|
+
export function resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode = true) {
|
|
16
14
|
const explicitConfig = resolveModelWithFallbacksForUnit(unitType);
|
|
17
15
|
if (explicitConfig)
|
|
18
16
|
return explicitConfig;
|
|
@@ -24,7 +22,7 @@ isAutoMode = true) {
|
|
|
24
22
|
if (!routingConfig.enabled || !routingConfig.tier_models)
|
|
25
23
|
return undefined;
|
|
26
24
|
// Don't synthesize a routing config for flat-rate providers (#3453).
|
|
27
|
-
if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider))
|
|
25
|
+
if (autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx))
|
|
28
26
|
return undefined;
|
|
29
27
|
const ceilingModel = routingConfig.tier_models.heavy
|
|
30
28
|
?? (autoModeStartModel ? `${autoModeStartModel.provider}/${autoModeStartModel.id}` : undefined);
|
|
@@ -51,6 +49,17 @@ sessionModelOverride) {
|
|
|
51
49
|
const effectiveSessionModelOverride = sessionModelOverride === undefined
|
|
52
50
|
? getSessionModelOverride(ctx.sessionManager.getSessionId())
|
|
53
51
|
: (sessionModelOverride ?? undefined);
|
|
52
|
+
// Enrich the start model with a flat-rate context up front so routing
|
|
53
|
+
// synthesis and the dispatch-time guard see the same signals (built-in
|
|
54
|
+
// list + user `flat_rate_providers` preference + externalCli auto-
|
|
55
|
+
// detection). The dispatch-time primary-model check below builds its
|
|
56
|
+
// own per-provider context when it has a resolved primary model.
|
|
57
|
+
if (autoModeStartModel) {
|
|
58
|
+
autoModeStartModel = {
|
|
59
|
+
...autoModeStartModel,
|
|
60
|
+
flatRateCtx: buildFlatRateContext(autoModeStartModel.provider, ctx, prefs),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
54
63
|
const modelConfig = effectiveSessionModelOverride
|
|
55
64
|
? undefined
|
|
56
65
|
: resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
|
|
@@ -76,12 +85,13 @@ sessionModelOverride) {
|
|
|
76
85
|
if (routingConfig.enabled) {
|
|
77
86
|
const primaryModel = resolveModelId(modelConfig.primary, availableModels, ctx.model?.provider);
|
|
78
87
|
if (primaryModel) {
|
|
79
|
-
|
|
88
|
+
const primaryFlatRateCtx = buildFlatRateContext(primaryModel.provider, ctx, prefs);
|
|
89
|
+
if (isFlatRateProvider(primaryModel.provider, primaryFlatRateCtx)) {
|
|
80
90
|
routingConfig.enabled = false;
|
|
81
91
|
}
|
|
82
92
|
}
|
|
83
|
-
else if ((autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider))
|
|
84
|
-
|| (ctx.model?.provider && isFlatRateProvider(ctx.model.provider))) {
|
|
93
|
+
else if ((autoModeStartModel && isFlatRateProvider(autoModeStartModel.provider, autoModeStartModel.flatRateCtx))
|
|
94
|
+
|| (ctx.model?.provider && isFlatRateProvider(ctx.model.provider, buildFlatRateContext(ctx.model.provider, ctx, prefs)))) {
|
|
85
95
|
// Primary model unresolvable but provider signals indicate flat-rate —
|
|
86
96
|
// disable routing to prevent quality degradation.
|
|
87
97
|
routingConfig.enabled = false;
|
|
@@ -331,7 +341,40 @@ export function resolveModelId(modelId, availableModels, currentProvider) {
|
|
|
331
341
|
* Uses case-insensitive matching with alias support to prevent fail-open on
|
|
332
342
|
* provider naming variations (e.g. "copilot" vs "github-copilot").
|
|
333
343
|
*/
|
|
334
|
-
const
|
|
335
|
-
export function isFlatRateProvider(provider) {
|
|
336
|
-
|
|
344
|
+
const BUILTIN_FLAT_RATE = new Set(["github-copilot", "copilot", "claude-code"]);
|
|
345
|
+
export function isFlatRateProvider(provider, opts) {
|
|
346
|
+
const p = provider.toLowerCase();
|
|
347
|
+
if (BUILTIN_FLAT_RATE.has(p))
|
|
348
|
+
return true;
|
|
349
|
+
if (opts?.userFlatRate?.some(id => id.toLowerCase() === p))
|
|
350
|
+
return true;
|
|
351
|
+
if (opts?.authMode === "externalCli")
|
|
352
|
+
return true;
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Build a FlatRateContext for a given provider from live runtime state.
|
|
357
|
+
* Safe to call when ctx or prefs are undefined — missing pieces are
|
|
358
|
+
* treated as "no signal".
|
|
359
|
+
*/
|
|
360
|
+
export function buildFlatRateContext(provider, ctx, prefs) {
|
|
361
|
+
let authMode;
|
|
362
|
+
const getAuthMode = ctx?.modelRegistry?.getProviderAuthMode;
|
|
363
|
+
if (typeof getAuthMode === "function") {
|
|
364
|
+
try {
|
|
365
|
+
const mode = getAuthMode(provider);
|
|
366
|
+
if (mode === "apiKey" || mode === "oauth" || mode === "externalCli" || mode === "none") {
|
|
367
|
+
authMode = mode;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
// Registry lookup failure must never break flat-rate detection —
|
|
372
|
+
// fall through with authMode undefined and surface the cause.
|
|
373
|
+
logWarning("dispatch", `flat-rate auth-mode lookup failed for ${provider}: ${err instanceof Error ? err.message : String(err)}`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
authMode,
|
|
378
|
+
userFlatRate: prefs?.flat_rate_providers,
|
|
379
|
+
};
|
|
337
380
|
}
|
|
@@ -38,7 +38,7 @@ import { existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync, } fro
|
|
|
38
38
|
import { join } from "node:path";
|
|
39
39
|
import { sep as pathSep } from "node:path";
|
|
40
40
|
import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
|
|
41
|
-
import { resolveDefaultSessionModel, resolveDynamicRoutingConfig } from "./preferences-models.js";
|
|
41
|
+
import { isCustomProvider, resolveDefaultSessionModel, resolveDynamicRoutingConfig, } from "./preferences-models.js";
|
|
42
42
|
import { getSessionModelOverride } from "./session-model-override.js";
|
|
43
43
|
/**
|
|
44
44
|
* Bootstrap a fresh auto-mode session. Handles everything from git init
|
|
@@ -195,8 +195,18 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
195
195
|
//
|
|
196
196
|
// This preserves #3517 defaults while honoring explicit runtime model
|
|
197
197
|
// selection for subsequent /gsd runs in the same session.
|
|
198
|
+
//
|
|
199
|
+
// Exception (#4122): when the session provider is a custom provider declared
|
|
200
|
+
// in ~/.gsd/agent/models.json (Ollama, vLLM, OpenAI-compatible proxy, etc.),
|
|
201
|
+
// PREFERENCES.md is skipped entirely. PREFERENCES.md cannot reference custom
|
|
202
|
+
// providers, so honoring it would silently reroute auto-mode to a built-in
|
|
203
|
+
// provider the user is not logged into and surface as "Not logged in · Please
|
|
204
|
+
// run /login" before pausing and resetting to claude-code/claude-sonnet-4-6.
|
|
198
205
|
const manualSessionOverride = getSessionModelOverride(ctx.sessionManager.getSessionId());
|
|
199
|
-
const
|
|
206
|
+
const sessionProviderIsCustom = isCustomProvider(ctx.model?.provider);
|
|
207
|
+
const preferredModel = sessionProviderIsCustom
|
|
208
|
+
? null
|
|
209
|
+
: resolveDefaultSessionModel(ctx.model?.provider);
|
|
200
210
|
// Validate the preferred model against the live registry + provider auth so
|
|
201
211
|
// an unconfigured PREFERENCES.md entry (no API key / OAuth) can't become the
|
|
202
212
|
// start-model snapshot. Without this, every subsequent unit would try to
|
|
@@ -636,12 +646,16 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
|
|
|
636
646
|
const startModelLabel = s.autoModeStartModel
|
|
637
647
|
? `${s.autoModeStartModel.provider}/${s.autoModeStartModel.id}`
|
|
638
648
|
: ctx.model ? `${ctx.model.provider}/${ctx.model.id}` : "default";
|
|
639
|
-
// Flat-rate providers (e.g. GitHub Copilot, claude-code
|
|
640
|
-
//
|
|
641
|
-
|
|
649
|
+
// Flat-rate providers (e.g. GitHub Copilot, claude-code, user-declared
|
|
650
|
+
// subscription proxies, externalCli CLIs) suppress routing at dispatch
|
|
651
|
+
// time (#3453) — reflect that in the banner. Thread the same
|
|
652
|
+
// FlatRateContext used by selectAndApplyModel so user-declared
|
|
653
|
+
// flat-rate providers and externalCli auto-detection are respected.
|
|
654
|
+
const { isFlatRateProvider, buildFlatRateContext } = await import("./auto-model-selection.js");
|
|
655
|
+
const bannerPrefs = loadEffectiveGSDPreferences()?.preferences;
|
|
642
656
|
const effectiveProvider = s.autoModeStartModel?.provider ?? ctx.model?.provider;
|
|
643
657
|
const effectivelyEnabled = routingConfig.enabled
|
|
644
|
-
&& !(effectiveProvider && isFlatRateProvider(effectiveProvider));
|
|
658
|
+
&& !(effectiveProvider && isFlatRateProvider(effectiveProvider, buildFlatRateContext(effectiveProvider, ctx, bannerPrefs)));
|
|
645
659
|
// The actual ceiling may come from tier_models.heavy, not the start model.
|
|
646
660
|
const effectiveCeiling = (routingConfig.enabled && routingConfig.tier_models?.heavy)
|
|
647
661
|
? routingConfig.tier_models.heavy
|
|
@@ -19,7 +19,7 @@ import { gsdRoot, resolveMilestoneFile, resolveMilestonePath, resolveDir, milest
|
|
|
19
19
|
import { invalidateAllCaches } from "./cache.js";
|
|
20
20
|
import { clearActivityLogState } from "./activity-log.js";
|
|
21
21
|
import { synthesizeCrashRecovery, getDeepDiagnostic, readActiveMilestoneId, } from "./session-forensics.js";
|
|
22
|
-
import { writeLock, clearLock, readCrashLock, isLockProcessAlive, formatCrashInfo, } from "./crash-recovery.js";
|
|
22
|
+
import { writeLock, clearLock, readCrashLock, isLockProcessAlive, formatCrashInfo, emitCrashRecoveredUnitEnd, } from "./crash-recovery.js";
|
|
23
23
|
import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
|
|
24
24
|
import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
|
|
25
25
|
import { sendDesktopNotification } from "./notifications.js";
|
|
@@ -1014,6 +1014,10 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
|
|
|
1014
1014
|
s.stepMode = requestedStepMode;
|
|
1015
1015
|
}
|
|
1016
1016
|
if (freshStartAssessment.lock) {
|
|
1017
|
+
// Emit a synthetic unit-end for any unit-start that has no closing event.
|
|
1018
|
+
// This closes the journal gap reported in #3348 where the worker wrote side
|
|
1019
|
+
// effects (SUMMARY.md, DB updates) but died before emitting unit-end.
|
|
1020
|
+
emitCrashRecoveredUnitEnd(base, freshStartAssessment.lock);
|
|
1017
1021
|
clearLock(base);
|
|
1018
1022
|
}
|
|
1019
1023
|
if (!s.paused) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* crash-log.ts — Write crash diagnostics to ~/.gsd/crash/<timestamp>.log
|
|
3
|
+
*
|
|
4
|
+
* Zero cross-dependencies: only uses Node.js built-ins so it can be imported
|
|
5
|
+
* safely from uncaughtException / unhandledRejection handlers and from tests
|
|
6
|
+
* without pulling in the full extension dependency tree.
|
|
7
|
+
*/
|
|
8
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
/**
|
|
12
|
+
* Write a crash log to ~/.gsd/crash/<timestamp>.log (or $GSD_HOME/crash/).
|
|
13
|
+
* Never throws — must be safe to call from any error handler.
|
|
14
|
+
*/
|
|
15
|
+
export function writeCrashLog(err, source) {
|
|
16
|
+
try {
|
|
17
|
+
const crashDir = join(process.env.GSD_HOME ?? join(homedir(), ".gsd"), "crash");
|
|
18
|
+
mkdirSync(crashDir, { recursive: true });
|
|
19
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
20
|
+
const logPath = join(crashDir, `${ts}.log`);
|
|
21
|
+
const lines = [
|
|
22
|
+
`[gsd] ${source}: ${err.message}`,
|
|
23
|
+
`timestamp: ${new Date().toISOString()}`,
|
|
24
|
+
`pid: ${process.pid}`,
|
|
25
|
+
err.stack ?? "(no stack trace available)",
|
|
26
|
+
"",
|
|
27
|
+
];
|
|
28
|
+
appendFileSync(logPath, lines.join("\n"));
|
|
29
|
+
}
|
|
30
|
+
catch { /* never throw from crash handler */ }
|
|
31
|
+
}
|
|
@@ -8,6 +8,8 @@ import { registerJournalTools } from "./journal-tools.js";
|
|
|
8
8
|
import { registerQueryTools } from "./query-tools.js";
|
|
9
9
|
import { registerHooks } from "./register-hooks.js";
|
|
10
10
|
import { registerShortcuts } from "./register-shortcuts.js";
|
|
11
|
+
import { writeCrashLog } from "./crash-log.js";
|
|
12
|
+
export { writeCrashLog } from "./crash-log.js";
|
|
11
13
|
export function handleRecoverableExtensionProcessError(err) {
|
|
12
14
|
if (err.code === "EPIPE") {
|
|
13
15
|
process.exit(0);
|
|
@@ -28,17 +30,26 @@ export function handleRecoverableExtensionProcessError(err) {
|
|
|
28
30
|
function installEpipeGuard() {
|
|
29
31
|
if (!process.listeners("uncaughtException").some((listener) => listener.name === "_gsdEpipeGuard")) {
|
|
30
32
|
const _gsdEpipeGuard = (err) => {
|
|
31
|
-
if (handleRecoverableExtensionProcessError(err))
|
|
33
|
+
if (handleRecoverableExtensionProcessError(err))
|
|
32
34
|
return;
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
process.stderr.write(`${err.stack}\n`);
|
|
35
|
+
// Write crash log and exit cleanly for unrecoverable errors.
|
|
36
|
+
// Logging and continuing was the original double-fault fix (#3163), but
|
|
37
|
+
// continuing in an indeterminate state is worse than a clean exit (#3348).
|
|
38
|
+
writeCrashLog(err, "uncaughtException");
|
|
39
|
+
process.exit(1);
|
|
39
40
|
};
|
|
40
41
|
process.on("uncaughtException", _gsdEpipeGuard);
|
|
41
42
|
}
|
|
43
|
+
if (!process.listeners("unhandledRejection").some((listener) => listener.name === "_gsdRejectionGuard")) {
|
|
44
|
+
const _gsdRejectionGuard = (reason, _promise) => {
|
|
45
|
+
const err = reason instanceof Error ? reason : new Error(String(reason));
|
|
46
|
+
if (handleRecoverableExtensionProcessError(err))
|
|
47
|
+
return;
|
|
48
|
+
writeCrashLog(err, "unhandledRejection");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
};
|
|
51
|
+
process.on("unhandledRejection", _gsdRejectionGuard);
|
|
52
|
+
}
|
|
42
53
|
}
|
|
43
54
|
export function registerGsdExtension(pi) {
|
|
44
55
|
registerGSDCommand(pi);
|
|
@@ -14,6 +14,7 @@ import { join } from "node:path";
|
|
|
14
14
|
import { gsdRoot } from "./paths.js";
|
|
15
15
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
16
16
|
import { effectiveLockFile } from "./session-lock.js";
|
|
17
|
+
import { emitJournalEvent, queryJournal } from "./journal.js";
|
|
17
18
|
function lockPath(basePath) {
|
|
18
19
|
return join(gsdRoot(basePath), effectiveLockFile());
|
|
19
20
|
}
|
|
@@ -110,3 +111,53 @@ export function formatCrashInfo(lock) {
|
|
|
110
111
|
}
|
|
111
112
|
return lines.join("\n");
|
|
112
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Emit a synthetic unit-end event for a unit that crashed without emitting its own.
|
|
116
|
+
*
|
|
117
|
+
* Queries the journal to find the most recent unit-start for the crashed unit.
|
|
118
|
+
* If a matching unit-end already exists (e.g. the hard timeout fired), this is a
|
|
119
|
+
* no-op. Called during crash recovery, before clearing the stale lock.
|
|
120
|
+
*
|
|
121
|
+
* Addresses the gap reported in #3348 where `unit-start` was emitted but no
|
|
122
|
+
* `unit-end` followed — side effects landed but the worker died before closeout.
|
|
123
|
+
*/
|
|
124
|
+
export function emitCrashRecoveredUnitEnd(basePath, lock) {
|
|
125
|
+
// Skip bootstrap / starting pseudo-units — they have no meaningful unit-start event.
|
|
126
|
+
if (!lock.unitType || !lock.unitId || lock.unitType === "starting")
|
|
127
|
+
return;
|
|
128
|
+
try {
|
|
129
|
+
const all = queryJournal(basePath);
|
|
130
|
+
// Find the most recent unit-start for this unitId
|
|
131
|
+
const starts = all.filter((e) => e.eventType === "unit-start" && e.data?.unitId === lock.unitId);
|
|
132
|
+
if (starts.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
const lastStart = starts[starts.length - 1];
|
|
135
|
+
// Check if a unit-end was already emitted (e.g. hard timeout fired after the crash)
|
|
136
|
+
const alreadyClosed = all.some((e) => e.eventType === "unit-end" &&
|
|
137
|
+
e.data?.unitId === lock.unitId &&
|
|
138
|
+
e.causedBy?.flowId === lastStart.flowId &&
|
|
139
|
+
e.causedBy?.seq === lastStart.seq);
|
|
140
|
+
if (alreadyClosed)
|
|
141
|
+
return;
|
|
142
|
+
// Find the highest seq in this flow for monotonic ordering
|
|
143
|
+
const maxSeq = all
|
|
144
|
+
.filter((e) => e.flowId === lastStart.flowId)
|
|
145
|
+
.reduce((max, e) => Math.max(max, e.seq), lastStart.seq);
|
|
146
|
+
emitJournalEvent(basePath, {
|
|
147
|
+
ts: new Date().toISOString(),
|
|
148
|
+
flowId: lastStart.flowId,
|
|
149
|
+
seq: maxSeq + 1,
|
|
150
|
+
eventType: "unit-end",
|
|
151
|
+
data: {
|
|
152
|
+
unitType: lock.unitType,
|
|
153
|
+
unitId: lock.unitId,
|
|
154
|
+
status: "crash-recovered",
|
|
155
|
+
artifactVerified: false,
|
|
156
|
+
},
|
|
157
|
+
causedBy: { flowId: lastStart.flowId, seq: lastStart.seq },
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Never throw from crash recovery path — journal failure must not block recovery
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -1352,6 +1352,25 @@ export function setSliceSummaryMd(milestoneId, sliceId, summaryMd, uatMd) {
|
|
|
1352
1352
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1353
1353
|
currentDb.prepare(`UPDATE slices SET full_summary_md = :summary_md, full_uat_md = :uat_md WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId, ":summary_md": summaryMd, ":uat_md": uatMd });
|
|
1354
1354
|
}
|
|
1355
|
+
function parseTaskArrayColumn(raw) {
|
|
1356
|
+
if (typeof raw !== "string" || raw.trim() === "")
|
|
1357
|
+
return [];
|
|
1358
|
+
try {
|
|
1359
|
+
const parsed = JSON.parse(raw);
|
|
1360
|
+
if (Array.isArray(parsed))
|
|
1361
|
+
return parsed.map((value) => String(value));
|
|
1362
|
+
if (parsed === null || parsed === undefined || parsed === "")
|
|
1363
|
+
return [];
|
|
1364
|
+
return [String(parsed)];
|
|
1365
|
+
}
|
|
1366
|
+
catch {
|
|
1367
|
+
// Older/corrupt rows may contain comma-separated strings instead of JSON.
|
|
1368
|
+
return raw
|
|
1369
|
+
.split(",")
|
|
1370
|
+
.map((value) => value.trim())
|
|
1371
|
+
.filter(Boolean);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1355
1374
|
function rowToTask(row) {
|
|
1356
1375
|
const parseTaskArray = (value) => {
|
|
1357
1376
|
if (Array.isArray(value)) {
|
|
@@ -1390,8 +1409,8 @@ function rowToTask(row) {
|
|
|
1390
1409
|
blocker_discovered: row["blocker_discovered"] === 1,
|
|
1391
1410
|
deviations: row["deviations"],
|
|
1392
1411
|
known_issues: row["known_issues"],
|
|
1393
|
-
key_files:
|
|
1394
|
-
key_decisions:
|
|
1412
|
+
key_files: parseTaskArrayColumn(row["key_files"]),
|
|
1413
|
+
key_decisions: parseTaskArrayColumn(row["key_decisions"]),
|
|
1395
1414
|
full_summary_md: row["full_summary_md"],
|
|
1396
1415
|
description: row["description"] ?? "",
|
|
1397
1416
|
estimate: row["estimate"] ?? "",
|
|
@@ -1855,6 +1874,21 @@ export function deleteSlice(milestoneId, sliceId) {
|
|
|
1855
1874
|
currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid AND id = :sid`).run({ ":mid": milestoneId, ":sid": sliceId });
|
|
1856
1875
|
});
|
|
1857
1876
|
}
|
|
1877
|
+
export function deleteMilestone(milestoneId) {
|
|
1878
|
+
if (!currentDb)
|
|
1879
|
+
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
1880
|
+
transaction(() => {
|
|
1881
|
+
currentDb.prepare(`DELETE FROM verification_evidence WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1882
|
+
currentDb.prepare(`DELETE FROM quality_gates WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1883
|
+
currentDb.prepare(`DELETE FROM tasks WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1884
|
+
currentDb.prepare(`DELETE FROM slice_dependencies WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1885
|
+
currentDb.prepare(`DELETE FROM slices WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1886
|
+
currentDb.prepare(`DELETE FROM replan_history WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1887
|
+
currentDb.prepare(`DELETE FROM assessments WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1888
|
+
currentDb.prepare(`DELETE FROM artifacts WHERE milestone_id = :mid`).run({ ":mid": milestoneId });
|
|
1889
|
+
currentDb.prepare(`DELETE FROM milestones WHERE id = :mid`).run({ ":mid": milestoneId });
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1858
1892
|
export function updateSliceFields(milestoneId, sliceId, fields) {
|
|
1859
1893
|
if (!currentDb)
|
|
1860
1894
|
throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
@@ -15,7 +15,8 @@ import { join } from "node:path";
|
|
|
15
15
|
import { resolveMilestonePath, resolveMilestoneFile, buildMilestoneFileName, } from "./paths.js";
|
|
16
16
|
import { invalidateAllCaches } from "./cache.js";
|
|
17
17
|
import { loadQueueOrder, saveQueueOrder } from "./queue-order.js";
|
|
18
|
-
import { getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
|
|
18
|
+
import { deleteMilestone, getMilestone, isDbAvailable, updateMilestoneStatus } from "./gsd-db.js";
|
|
19
|
+
import { removeWorktree } from "./worktree-manager.js";
|
|
19
20
|
import { logWarning } from "./workflow-logger.js";
|
|
20
21
|
// ─── Park ──────────────────────────────────────────────────────────────────
|
|
21
22
|
/**
|
|
@@ -99,12 +100,29 @@ export function discardMilestone(basePath, milestoneId) {
|
|
|
99
100
|
const mDir = resolveMilestonePath(basePath, milestoneId);
|
|
100
101
|
if (!mDir || !existsSync(mDir))
|
|
101
102
|
return false;
|
|
103
|
+
try {
|
|
104
|
+
removeWorktree(basePath, milestoneId, {
|
|
105
|
+
branch: `milestone/${milestoneId}`,
|
|
106
|
+
deleteBranch: true,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
logWarning("engine", `discardMilestone worktree cleanup failed for ${milestoneId}: ${err.message}`);
|
|
111
|
+
}
|
|
102
112
|
rmSync(mDir, { recursive: true, force: true });
|
|
103
113
|
// Prune from queue order if present
|
|
104
114
|
const order = loadQueueOrder(basePath);
|
|
105
115
|
if (order && order.includes(milestoneId)) {
|
|
106
116
|
saveQueueOrder(basePath, order.filter(id => id !== milestoneId));
|
|
107
117
|
}
|
|
118
|
+
if (isDbAvailable()) {
|
|
119
|
+
try {
|
|
120
|
+
deleteMilestone(milestoneId);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
logWarning("engine", `discardMilestone DB cleanup failed for ${milestoneId}: ${err.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
108
126
|
invalidateAllCaches();
|
|
109
127
|
return true;
|
|
110
128
|
}
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* and dynamic routing configuration.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
9
11
|
import { defaultRoutingConfig } from "./model-router.js";
|
|
10
12
|
import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./preferences.js";
|
|
11
13
|
/**
|
|
@@ -161,6 +163,47 @@ export function resolveDefaultSessionModel(sessionProvider) {
|
|
|
161
163
|
}
|
|
162
164
|
return undefined;
|
|
163
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* Returns true if `provider` is defined as a custom provider in the user's
|
|
168
|
+
* `~/.gsd/agent/models.json` (Ollama, vLLM, LM Studio, OpenAI-compatible
|
|
169
|
+
* proxies, etc.).
|
|
170
|
+
*
|
|
171
|
+
* Used by auto-mode bootstrap to decide whether the session model
|
|
172
|
+
* (set via `/gsd model`) should override `PREFERENCES.md`. Custom providers
|
|
173
|
+
* are never reachable from `PREFERENCES.md` (which only knows built-in
|
|
174
|
+
* providers), so when the user has explicitly selected one, it must take
|
|
175
|
+
* priority — otherwise auto-mode tries to start the built-in provider from
|
|
176
|
+
* PREFERENCES.md and fails with "Not logged in · Please run /login" (#4122).
|
|
177
|
+
*
|
|
178
|
+
* Reads models.json directly with a lightweight JSON parse to avoid
|
|
179
|
+
* pulling in the full model-registry at this call site. Falls back to
|
|
180
|
+
* `~/.pi/agent/models.json` for parity with `resolveModelsJsonPath()`.
|
|
181
|
+
* Any read or parse error yields `false` (treat as not-custom) so a
|
|
182
|
+
* malformed models.json never breaks the session bootstrap.
|
|
183
|
+
*/
|
|
184
|
+
export function isCustomProvider(provider) {
|
|
185
|
+
if (!provider)
|
|
186
|
+
return false;
|
|
187
|
+
const candidates = [
|
|
188
|
+
join(homedir(), ".gsd", "agent", "models.json"),
|
|
189
|
+
join(homedir(), ".pi", "agent", "models.json"),
|
|
190
|
+
];
|
|
191
|
+
for (const path of candidates) {
|
|
192
|
+
if (!existsSync(path))
|
|
193
|
+
continue;
|
|
194
|
+
try {
|
|
195
|
+
const raw = readFileSync(path, "utf-8");
|
|
196
|
+
const parsed = JSON.parse(raw);
|
|
197
|
+
if (parsed?.providers && Object.prototype.hasOwnProperty.call(parsed.providers, provider)) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// Ignore — malformed models.json must not break bootstrap.
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
164
207
|
/**
|
|
165
208
|
* Determines the next fallback model to try when the current model fails.
|
|
166
209
|
* If the current model is not in the configured list, returns the primary model.
|
|
@@ -155,6 +155,28 @@ export function validatePreferences(preferences) {
|
|
|
155
155
|
errors.push(`search_provider must be one of: brave, tavily, ollama, native, auto`);
|
|
156
156
|
}
|
|
157
157
|
}
|
|
158
|
+
// ─── Flat-rate Providers ────────────────────────────────────────────
|
|
159
|
+
// User-declared flat-rate providers for dynamic routing suppression.
|
|
160
|
+
// Built-in providers (github-copilot, copilot, claude-code) and any
|
|
161
|
+
// externalCli provider are already auto-detected; this list layers on
|
|
162
|
+
// top for private subscription proxies and custom CLI wrappers.
|
|
163
|
+
if (preferences.flat_rate_providers !== undefined) {
|
|
164
|
+
if (Array.isArray(preferences.flat_rate_providers)) {
|
|
165
|
+
const allStrings = preferences.flat_rate_providers.every((item) => typeof item === "string");
|
|
166
|
+
if (allStrings) {
|
|
167
|
+
// Strip empty/whitespace-only entries to avoid false matches.
|
|
168
|
+
validated.flat_rate_providers = preferences.flat_rate_providers
|
|
169
|
+
.map((s) => s.trim())
|
|
170
|
+
.filter((s) => s.length > 0);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
errors.push("flat_rate_providers must be an array of strings");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
errors.push("flat_rate_providers must be an array of strings");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
158
180
|
// ─── Phase Skip Preferences ─────────────────────────────────────────
|
|
159
181
|
if (preferences.phases !== undefined) {
|
|
160
182
|
if (typeof preferences.phases === "object" && preferences.phases !== null) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
r6AvNu-aMwn4nwqjHqAfw
|
|
@@ -9,39 +9,39 @@
|
|
|
9
9
|
"/api/cleanup/route": "/api/cleanup",
|
|
10
10
|
"/api/doctor/route": "/api/doctor",
|
|
11
11
|
"/api/export-data/route": "/api/export-data",
|
|
12
|
-
"/api/browse-directories/route": "/api/browse-directories",
|
|
13
12
|
"/api/captures/route": "/api/captures",
|
|
14
13
|
"/api/forensics/route": "/api/forensics",
|
|
15
|
-
"/api/
|
|
14
|
+
"/api/browse-directories/route": "/api/browse-directories",
|
|
15
|
+
"/api/experimental/route": "/api/experimental",
|
|
16
16
|
"/api/git/route": "/api/git",
|
|
17
|
+
"/api/history/route": "/api/history",
|
|
17
18
|
"/api/hooks/route": "/api/hooks",
|
|
18
|
-
"/api/knowledge/route": "/api/knowledge",
|
|
19
19
|
"/api/inspect/route": "/api/inspect",
|
|
20
|
-
"/api/
|
|
20
|
+
"/api/knowledge/route": "/api/knowledge",
|
|
21
21
|
"/api/live-state/route": "/api/live-state",
|
|
22
22
|
"/api/notifications/route": "/api/notifications",
|
|
23
23
|
"/api/preferences/route": "/api/preferences",
|
|
24
24
|
"/api/recovery/route": "/api/recovery",
|
|
25
25
|
"/api/onboarding/route": "/api/onboarding",
|
|
26
|
-
"/api/session/browser/route": "/api/session/browser",
|
|
27
26
|
"/api/projects/route": "/api/projects",
|
|
27
|
+
"/api/session/browser/route": "/api/session/browser",
|
|
28
28
|
"/api/session/command/route": "/api/session/command",
|
|
29
|
-
"/api/
|
|
30
|
-
"/api/session/events/route": "/api/session/events",
|
|
29
|
+
"/api/files/route": "/api/files",
|
|
31
30
|
"/api/settings-data/route": "/api/settings-data",
|
|
32
31
|
"/api/shutdown/route": "/api/shutdown",
|
|
32
|
+
"/api/session/events/route": "/api/session/events",
|
|
33
33
|
"/api/skill-health/route": "/api/skill-health",
|
|
34
|
+
"/api/session/manage/route": "/api/session/manage",
|
|
34
35
|
"/api/steer/route": "/api/steer",
|
|
35
|
-
"/api/files/route": "/api/files",
|
|
36
36
|
"/api/terminal/input/route": "/api/terminal/input",
|
|
37
|
-
"/api/terminal/resize/route": "/api/terminal/resize",
|
|
38
37
|
"/api/switch-root/route": "/api/switch-root",
|
|
38
|
+
"/api/terminal/resize/route": "/api/terminal/resize",
|
|
39
39
|
"/api/terminal/sessions/route": "/api/terminal/sessions",
|
|
40
40
|
"/api/terminal/stream/route": "/api/terminal/stream",
|
|
41
|
+
"/api/update/route": "/api/update",
|
|
41
42
|
"/api/undo/route": "/api/undo",
|
|
42
43
|
"/api/visualizer/route": "/api/visualizer",
|
|
43
44
|
"/api/terminal/upload/route": "/api/terminal/upload",
|
|
44
|
-
"/api/update/route": "/api/update",
|
|
45
45
|
"/api/remote-questions/route": "/api/remote-questions",
|
|
46
46
|
"/page": "/"
|
|
47
47
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
],
|
|
5
5
|
"devFiles": [],
|
|
6
6
|
"lowPriorityFiles": [
|
|
7
|
-
"static/
|
|
8
|
-
"static/
|
|
7
|
+
"static/r6AvNu-aMwn4nwqjHqAfw/_buildManifest.js",
|
|
8
|
+
"static/r6AvNu-aMwn4nwqjHqAfw/_ssgManifest.js"
|
|
9
9
|
],
|
|
10
10
|
"rootMainFiles": [
|
|
11
11
|
"static/chunks/webpack-b868033a5834586d.js",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"dynamicRoutes": {},
|
|
79
79
|
"notFoundRoutes": [],
|
|
80
80
|
"preview": {
|
|
81
|
-
"previewModeId": "
|
|
82
|
-
"previewModeSigningKey": "
|
|
83
|
-
"previewModeEncryptionKey": "
|
|
81
|
+
"previewModeId": "1ad2aeb0370b98cc01860d4aa27c2cc6",
|
|
82
|
+
"previewModeSigningKey": "d1af9f025aa721de88357535f1bcb3b731aa041032a52fdda4caf17b0a7d3960",
|
|
83
|
+
"previewModeEncryptionKey": "812d78941c2b0688748da1f190f3e6071d14c1206bf1b75b1407d9e101368741"
|
|
84
84
|
}
|
|
85
85
|
}
|