gsd-pi 2.73.0 → 2.73.1
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/cli.js +0 -47
- package/dist/help-text.js +1 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +5 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +9 -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/bootstrap/system-context.js +6 -1
- 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/startup-model-validation.js +8 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- 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 +9 -9
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +27 -0
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +25 -68
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- 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 +22 -9
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +63 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +38 -0
- package/packages/pi-coding-agent/src/core/auth-storage.ts +1 -1
- package/packages/pi-coding-agent/src/core/model-resolver.ts +26 -70
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +71 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +23 -9
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +5 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +9 -3
- 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/bootstrap/system-context.ts +8 -1
- 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/tests/crash-handler-secondary.test.ts +235 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +59 -1
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +64 -0
- package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +267 -0
- /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → Qr27MOHx0lxRGnJvlhxxu}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{KSZ2dcC3p4z6lOmUpPpzr → Qr27MOHx0lxRGnJvlhxxu}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,6 @@ import { initResources, buildResourceLoader, getNewerManagedResourceVersion } fr
|
|
|
6
6
|
import { ensureManagedTools } from './tool-bootstrap.js';
|
|
7
7
|
import { loadStoredEnvKeys } from './wizard.js';
|
|
8
8
|
import { migratePiCredentials, getPiDefaultModelAndProvider } from './pi-migration.js';
|
|
9
|
-
import { shouldMigrateAnthropicToClaudeCode } from './provider-migrations.js';
|
|
10
9
|
import { shouldRunOnboarding, runOnboarding } from './onboarding.js';
|
|
11
10
|
import chalk from 'chalk';
|
|
12
11
|
import { checkForUpdates } from './update-check.js';
|
|
@@ -455,29 +454,6 @@ if (isPrintMode) {
|
|
|
455
454
|
isClaudeCodeReady: () => modelRegistry.isProviderRequestReady('claude-code'),
|
|
456
455
|
});
|
|
457
456
|
markStartup('createAgentSession');
|
|
458
|
-
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
|
|
459
|
-
// Anthropic blocks third-party apps from using subscription quotas — routing through
|
|
460
|
-
// the local claude CLI binary is TOS-compliant.
|
|
461
|
-
if (shouldMigrateAnthropicToClaudeCode({
|
|
462
|
-
authStorage,
|
|
463
|
-
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
|
|
464
|
-
defaultProvider: settingsManager.getDefaultProvider(),
|
|
465
|
-
})) {
|
|
466
|
-
const currentModelId = settingsManager.getDefaultModel();
|
|
467
|
-
if (currentModelId) {
|
|
468
|
-
const ccModel = modelRegistry.find('claude-code', currentModelId);
|
|
469
|
-
if (ccModel) {
|
|
470
|
-
try {
|
|
471
|
-
await session.setModel(ccModel);
|
|
472
|
-
// Only persist after successful session switch to avoid desync
|
|
473
|
-
settingsManager.setDefaultModelAndProvider('claude-code', currentModelId);
|
|
474
|
-
}
|
|
475
|
-
catch {
|
|
476
|
-
// claude-code provider not ready — leave both session and settings unchanged
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
457
|
// Validate configured model AFTER extensions have registered their models (#2626).
|
|
482
458
|
// Before this, extension-provided models (e.g. claude-code/*) were not yet in the
|
|
483
459
|
// registry, causing the user's valid choice to be silently overwritten.
|
|
@@ -647,29 +623,6 @@ const { session, extensionsResult, modelFallbackMessage: interactiveFallbackMsg
|
|
|
647
623
|
isClaudeCodeReady: () => modelRegistry.isProviderRequestReady('claude-code'),
|
|
648
624
|
});
|
|
649
625
|
markStartup('createAgentSession');
|
|
650
|
-
// Migrate anthropic OAuth users to claude-code provider when CLI is available (#3772).
|
|
651
|
-
// Anthropic blocks third-party apps from using subscription quotas — routing through
|
|
652
|
-
// the local claude CLI binary is TOS-compliant.
|
|
653
|
-
if (shouldMigrateAnthropicToClaudeCode({
|
|
654
|
-
authStorage,
|
|
655
|
-
isClaudeCodeReady: modelRegistry.isProviderRequestReady('claude-code'),
|
|
656
|
-
defaultProvider: settingsManager.getDefaultProvider(),
|
|
657
|
-
})) {
|
|
658
|
-
const currentModelId = settingsManager.getDefaultModel();
|
|
659
|
-
if (currentModelId) {
|
|
660
|
-
const ccModel = modelRegistry.find('claude-code', currentModelId);
|
|
661
|
-
if (ccModel) {
|
|
662
|
-
try {
|
|
663
|
-
await session.setModel(ccModel);
|
|
664
|
-
// Only persist after successful session switch to avoid desync
|
|
665
|
-
settingsManager.setDefaultModelAndProvider('claude-code', currentModelId);
|
|
666
|
-
}
|
|
667
|
-
catch {
|
|
668
|
-
// claude-code provider not ready — leave both session and settings unchanged
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
626
|
// Validate configured model AFTER extensions have registered their models (#2626).
|
|
674
627
|
// Before this, extension-provided models (e.g. claude-code/*) were not yet in the
|
|
675
628
|
// registry, causing the user's valid choice to be silently overwritten.
|
package/dist/help-text.js
CHANGED
|
@@ -148,7 +148,7 @@ export function printHelp(version) {
|
|
|
148
148
|
process.stdout.write(' --print, -p Single-shot print mode\n');
|
|
149
149
|
process.stdout.write(' --continue, -c Resume the most recent session\n');
|
|
150
150
|
process.stdout.write(' --worktree, -w [name] Start in an isolated worktree (auto-named if omitted)\n');
|
|
151
|
-
process.stdout.write(' --model <id> Override model (e.g.
|
|
151
|
+
process.stdout.write(' --model <id> Override model (e.g. provider/model-id)\n');
|
|
152
152
|
process.stdout.write(' --no-session Disable session persistence\n');
|
|
153
153
|
process.stdout.write(' --extension <path> Load additional extension\n');
|
|
154
154
|
process.stdout.write(' --tools <a,b,c> Restrict available tools\n');
|
|
@@ -18,6 +18,7 @@ import { logWarning, logError } from "./workflow-logger.js";
|
|
|
18
18
|
import { join } from "node:path";
|
|
19
19
|
import { hasImplementationArtifacts } from "./auto-recovery.js";
|
|
20
20
|
import { buildDiscussMilestonePrompt, buildResearchMilestonePrompt, buildPlanMilestonePrompt, buildResearchSlicePrompt, buildPlanSlicePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReplanSlicePrompt, buildRunUatPrompt, buildReassessRoadmapPrompt, buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, buildParallelResearchSlicesPrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js";
|
|
21
|
+
import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
|
|
21
22
|
function missingSliceStop(mid, phase) {
|
|
22
23
|
return {
|
|
23
24
|
action: "stop",
|
|
@@ -330,7 +331,7 @@ export const DISPATCH_RULES = [
|
|
|
330
331
|
action: "dispatch",
|
|
331
332
|
unitType: "research-slice",
|
|
332
333
|
unitId: `${mid}/parallel-research`,
|
|
333
|
-
prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath),
|
|
334
|
+
prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary),
|
|
334
335
|
};
|
|
335
336
|
},
|
|
336
337
|
},
|
|
@@ -401,7 +402,7 @@ export const DISPATCH_RULES = [
|
|
|
401
402
|
action: "dispatch",
|
|
402
403
|
unitType: "gate-evaluate",
|
|
403
404
|
unitId: `${mid}/${sid}/gates+${pending.map(g => g.gate_id).join(",")}`,
|
|
404
|
-
prompt: await buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, basePath),
|
|
405
|
+
prompt: await buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, basePath, resolveModelWithFallbacksForUnit("subagent")?.primary),
|
|
405
406
|
};
|
|
406
407
|
},
|
|
407
408
|
},
|
|
@@ -436,6 +437,7 @@ export const DISPATCH_RULES = [
|
|
|
436
437
|
const sid = state.activeSlice.id;
|
|
437
438
|
const sTitle = state.activeSlice.title;
|
|
438
439
|
const maxParallel = reactiveConfig.max_parallel ?? 2;
|
|
440
|
+
const subagentModel = reactiveConfig.subagent_model ?? resolveModelWithFallbacksForUnit("subagent")?.primary;
|
|
439
441
|
// Dry-run mode: max_parallel=1 means graph is derived and logged but
|
|
440
442
|
// execution remains sequential
|
|
441
443
|
if (maxParallel <= 1)
|
|
@@ -478,7 +480,7 @@ export const DISPATCH_RULES = [
|
|
|
478
480
|
action: "dispatch",
|
|
479
481
|
unitType: "reactive-execute",
|
|
480
482
|
unitId: `${mid}/${sid}/reactive+${batchSuffix}`,
|
|
481
|
-
prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath),
|
|
483
|
+
prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel),
|
|
482
484
|
};
|
|
483
485
|
}
|
|
484
486
|
catch (err) {
|
|
@@ -1712,7 +1712,7 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
|
|
|
1712
1712
|
});
|
|
1713
1713
|
}
|
|
1714
1714
|
// ─── Reactive Execute Prompt ──────────────────────────────────────────────
|
|
1715
|
-
export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, readyTaskIds, base) {
|
|
1715
|
+
export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, readyTaskIds, base, subagentModel) {
|
|
1716
1716
|
const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
|
|
1717
1717
|
// Build graph for context
|
|
1718
1718
|
const taskIO = await loadSliceTaskIO(base, mid, sid);
|
|
@@ -1744,10 +1744,11 @@ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, rea
|
|
|
1744
1744
|
const depPaths = await getDependencyTaskSummaryPaths(mid, sid, tid, node?.dependsOn ?? [], base);
|
|
1745
1745
|
// Build a full execute-task prompt with dependency-based carry-forward
|
|
1746
1746
|
const taskPrompt = await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base, { carryForwardPaths: depPaths });
|
|
1747
|
+
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
1747
1748
|
subagentSections.push([
|
|
1748
1749
|
`### ${tid}: ${tTitle}`,
|
|
1749
1750
|
"",
|
|
1750
|
-
|
|
1751
|
+
`Use this as the prompt for a \`subagent\` call${modelSuffix}:`,
|
|
1751
1752
|
"",
|
|
1752
1753
|
"```",
|
|
1753
1754
|
taskPrompt,
|
|
@@ -1806,15 +1807,16 @@ function renderGatesToCloseBlock(gates, opts) {
|
|
|
1806
1807
|
}
|
|
1807
1808
|
return lines.join("\n").trimEnd();
|
|
1808
1809
|
}
|
|
1809
|
-
export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, basePath) {
|
|
1810
|
+
export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, basePath, subagentModel) {
|
|
1810
1811
|
// Build individual research-slice prompts for each slice
|
|
1811
1812
|
const subagentSections = [];
|
|
1813
|
+
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
1812
1814
|
for (const slice of slices) {
|
|
1813
1815
|
const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath);
|
|
1814
1816
|
subagentSections.push([
|
|
1815
1817
|
`### ${slice.id}: ${slice.title}`,
|
|
1816
1818
|
"",
|
|
1817
|
-
|
|
1819
|
+
`Use this as the prompt for a \`subagent\` call${modelSuffix} (agent: \`gsd-executor\` or the default agent):`,
|
|
1818
1820
|
"",
|
|
1819
1821
|
"```",
|
|
1820
1822
|
slicePrompt,
|
|
@@ -1829,7 +1831,7 @@ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, b
|
|
|
1829
1831
|
subagentPrompts: subagentSections.join("\n\n---\n\n"),
|
|
1830
1832
|
});
|
|
1831
1833
|
}
|
|
1832
|
-
export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base) {
|
|
1834
|
+
export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base, subagentModel) {
|
|
1833
1835
|
// Pull only the gates this turn actually owns (Q3/Q4). Filter via the
|
|
1834
1836
|
// registry so that scope:"slice" gates owned by other turns (Q8) can't
|
|
1835
1837
|
// leak into this prompt and can't block dispatch via silent skip.
|
|
@@ -1873,10 +1875,11 @@ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base)
|
|
|
1873
1875
|
"- `rationale`: one-sentence justification",
|
|
1874
1876
|
"- `findings`: detailed markdown findings (or empty if omitted)",
|
|
1875
1877
|
].join("\n");
|
|
1878
|
+
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
1876
1879
|
subagentSections.push([
|
|
1877
1880
|
`### ${def.id}: ${def.question}`,
|
|
1878
1881
|
"",
|
|
1879
|
-
|
|
1882
|
+
`Use this as the prompt for a \`subagent\` call${modelSuffix}:`,
|
|
1880
1883
|
"",
|
|
1881
1884
|
"```",
|
|
1882
1885
|
subPrompt,
|
|
@@ -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);
|
|
@@ -6,6 +6,7 @@ import { debugTime } from "../debug-logger.js";
|
|
|
6
6
|
import { loadPrompt, getTemplatesDir } from "../prompt-loader.js";
|
|
7
7
|
import { readForensicsMarker } from "../forensics.js";
|
|
8
8
|
import { resolveAllSkillReferences, renderPreferencesForSystemPrompt, loadEffectiveGSDPreferences } from "../preferences.js";
|
|
9
|
+
import { resolveModelWithFallbacksForUnit } from "../preferences-models.js";
|
|
9
10
|
import { resolveSkillReference } from "../preferences-skills.js";
|
|
10
11
|
import { resolveGsdRootFile, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTaskFiles, resolveTasksDir, relSliceFile, relSlicePath, relTaskFile } from "../paths.js";
|
|
11
12
|
import { ensureCodebaseMapFresh, readCodebaseMap } from "../codebase-generator.js";
|
|
@@ -147,7 +148,11 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
147
148
|
// Re-inject forensics context on follow-up turns (#2941)
|
|
148
149
|
const forensicsInjection = !injection ? buildForensicsContextInjection(process.cwd(), event.prompt) : null;
|
|
149
150
|
const worktreeBlock = buildWorktreeContextBlock();
|
|
150
|
-
const
|
|
151
|
+
const subagentModelConfig = resolveModelWithFallbacksForUnit("subagent");
|
|
152
|
+
const subagentModelBlock = subagentModelConfig
|
|
153
|
+
? `\n\n## Subagent Model\n\nWhen spawning subagents via the \`subagent\` tool, always pass \`model: "${subagentModelConfig.primary}"\` in the tool call parameters. Never omit this — always specify it explicitly.`
|
|
154
|
+
: "";
|
|
155
|
+
const fullSystem = `${event.systemPrompt}\n\n[SYSTEM CONTEXT — GSD]\n\n${systemContent}${preferenceBlock}${knowledgeBlock}${codebaseBlock}${memoryBlock}${newSkillsBlock}${worktreeBlock}${subagentModelBlock}`;
|
|
151
156
|
stopContextTimer({
|
|
152
157
|
systemPromptSize: fullSystem.length,
|
|
153
158
|
injectionSize: injection?.length ?? forensicsInjection?.length ?? 0,
|
|
@@ -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
|
}
|
|
@@ -34,15 +34,18 @@ export function validateConfiguredModel(modelRegistry, settingsManager) {
|
|
|
34
34
|
if (!configuredModel || !configuredExists) {
|
|
35
35
|
// Model not configured at all, or removed from registry — pick a fallback.
|
|
36
36
|
// Only fires when the model is genuinely unknown (not just temporarily unavailable).
|
|
37
|
+
//
|
|
38
|
+
// Model-agnostic selection order:
|
|
39
|
+
// 1. Pi migration default (preserves migration from ~/.pi install)
|
|
40
|
+
// 2. Any model from the user's previously-chosen provider (provider stickiness)
|
|
41
|
+
// 3. First available model in registry order (user-controlled via models.json)
|
|
37
42
|
const piDefault = getPiDefaultModelAndProvider();
|
|
38
43
|
const preferred = (piDefault
|
|
39
44
|
? availableModels.find((m) => m.provider === piDefault.provider && m.id === piDefault.model)
|
|
40
45
|
: undefined) ||
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
availableModels.find((m) => m.provider === 'anthropic' && m.id.includes('opus')) ||
|
|
45
|
-
availableModels.find((m) => m.provider === 'anthropic') ||
|
|
46
|
+
(configuredProvider
|
|
47
|
+
? availableModels.find((m) => m.provider === configuredProvider)
|
|
48
|
+
: undefined) ||
|
|
46
49
|
availableModels[0];
|
|
47
50
|
if (preferred) {
|
|
48
51
|
settingsManager.setDefaultModelAndProvider(preferred.provider, preferred.id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
Qr27MOHx0lxRGnJvlhxxu
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"/_not-found/page": "/_not-found",
|
|
3
3
|
"/_global-error/page": "/_global-error",
|
|
4
|
-
"/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
|
|
5
4
|
"/api/bridge-terminal/input/route": "/api/bridge-terminal/input",
|
|
5
|
+
"/api/bridge-terminal/resize/route": "/api/bridge-terminal/resize",
|
|
6
6
|
"/api/boot/route": "/api/boot",
|
|
7
7
|
"/api/bridge-terminal/stream/route": "/api/bridge-terminal/stream",
|
|
8
8
|
"/api/dev-mode/route": "/api/dev-mode",
|
|
@@ -19,28 +19,28 @@
|
|
|
19
19
|
"/api/knowledge/route": "/api/knowledge",
|
|
20
20
|
"/api/notifications/route": "/api/notifications",
|
|
21
21
|
"/api/live-state/route": "/api/live-state",
|
|
22
|
-
"/api/preferences/route": "/api/preferences",
|
|
23
22
|
"/api/experimental/route": "/api/experimental",
|
|
24
|
-
"/api/
|
|
23
|
+
"/api/preferences/route": "/api/preferences",
|
|
25
24
|
"/api/recovery/route": "/api/recovery",
|
|
26
25
|
"/api/projects/route": "/api/projects",
|
|
26
|
+
"/api/onboarding/route": "/api/onboarding",
|
|
27
27
|
"/api/session/browser/route": "/api/session/browser",
|
|
28
28
|
"/api/session/command/route": "/api/session/command",
|
|
29
|
-
"/api/session/manage/route": "/api/session/manage",
|
|
30
|
-
"/api/settings-data/route": "/api/settings-data",
|
|
31
29
|
"/api/session/events/route": "/api/session/events",
|
|
30
|
+
"/api/settings-data/route": "/api/settings-data",
|
|
31
|
+
"/api/session/manage/route": "/api/session/manage",
|
|
32
32
|
"/api/shutdown/route": "/api/shutdown",
|
|
33
33
|
"/api/skill-health/route": "/api/skill-health",
|
|
34
34
|
"/api/steer/route": "/api/steer",
|
|
35
|
-
"/api/files/route": "/api/files",
|
|
36
35
|
"/api/terminal/input/route": "/api/terminal/input",
|
|
36
|
+
"/api/files/route": "/api/files",
|
|
37
37
|
"/api/terminal/resize/route": "/api/terminal/resize",
|
|
38
|
-
"/api/terminal/sessions/route": "/api/terminal/sessions",
|
|
39
38
|
"/api/switch-root/route": "/api/switch-root",
|
|
40
|
-
"/api/terminal/
|
|
39
|
+
"/api/terminal/sessions/route": "/api/terminal/sessions",
|
|
41
40
|
"/api/terminal/upload/route": "/api/terminal/upload",
|
|
42
|
-
"/api/
|
|
41
|
+
"/api/terminal/stream/route": "/api/terminal/stream",
|
|
43
42
|
"/api/undo/route": "/api/undo",
|
|
43
|
+
"/api/visualizer/route": "/api/visualizer",
|
|
44
44
|
"/api/update/route": "/api/update",
|
|
45
45
|
"/api/remote-questions/route": "/api/remote-questions",
|
|
46
46
|
"/page": "/"
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
],
|
|
5
5
|
"devFiles": [],
|
|
6
6
|
"lowPriorityFiles": [
|
|
7
|
-
"static/
|
|
8
|
-
"static/
|
|
7
|
+
"static/Qr27MOHx0lxRGnJvlhxxu/_buildManifest.js",
|
|
8
|
+
"static/Qr27MOHx0lxRGnJvlhxxu/_ssgManifest.js"
|
|
9
9
|
],
|
|
10
10
|
"rootMainFiles": [
|
|
11
11
|
"static/chunks/webpack-b868033a5834586d.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-b868033a5834586d.js"/><script src="/_next/static/chunks/4bd1b696-e356ca5ba0218e27.js" async=""></script><script src="/_next/static/chunks/3794-42fdce068d44fa4f.js" async=""></script><script src="/_next/static/chunks/main-app-d3d4c336195465f9.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-b868033a5834586d.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[57121,[],\"\"]\n3:I[74581,[],\"\"]\n4:I[90484,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[90484,[],\"ViewportBoundary\"]\na:I[90484,[],\"MetadataBoundary\"]\nc:I[27123,[],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"
|
|
1
|
+
<!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-b868033a5834586d.js"/><script src="/_next/static/chunks/4bd1b696-e356ca5ba0218e27.js" async=""></script><script src="/_next/static/chunks/3794-42fdce068d44fa4f.js" async=""></script><script src="/_next/static/chunks/main-app-d3d4c336195465f9.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-b868033a5834586d.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[57121,[],\"\"]\n3:I[74581,[],\"\"]\n4:I[90484,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[90484,[],\"ViewportBoundary\"]\na:I[90484,[],\"MetadataBoundary\"]\nc:I[27123,[],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"Qr27MOHx0lxRGnJvlhxxu\"}\n"])</script><script>self.__next_f.push([1,"d:[]\n7:\"$Wd\"\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\nb:[]\n"])</script></body></html>
|