gsd-pi 2.75.0-dev.b6ad8c5f7 → 2.75.0-dev.e41b70b10
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/gsd/auto/phases.js +2 -0
- package/dist/resources/extensions/gsd/auto-dashboard.js +22 -1
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +8 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +11 -11
- package/dist/resources/extensions/gsd/auto-model-selection.js +3 -1
- package/dist/resources/extensions/gsd/auto-prompts.js +19 -9
- package/dist/resources/extensions/gsd/auto-worktree.js +16 -1
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +128 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +17 -4
- package/dist/resources/extensions/gsd/commands/handlers/onboarding.js +52 -92
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-memory.js +462 -0
- package/dist/resources/extensions/gsd/doctor-git-checks.js +22 -2
- package/dist/resources/extensions/gsd/gsd-db.js +237 -4
- package/dist/resources/extensions/gsd/memory-embeddings.js +219 -0
- package/dist/resources/extensions/gsd/memory-extractor.js +78 -27
- package/dist/resources/extensions/gsd/memory-ingest.js +218 -0
- package/dist/resources/extensions/gsd/memory-relations.js +189 -0
- package/dist/resources/extensions/gsd/memory-source-store.js +113 -0
- package/dist/resources/extensions/gsd/memory-store.js +299 -6
- package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -8
- package/dist/resources/extensions/gsd/prompts/add-tests.md +1 -0
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +306 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +14 -0
- package/dist/resources/extensions/search-the-web/command-search-provider.js +4 -1
- package/dist/resources/extensions/search-the-web/native-search.js +13 -2
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- 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/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +12 -10
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
- package/packages/mcp-server/dist/session-manager.js +8 -1
- package/packages/mcp-server/dist/session-manager.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +207 -71
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +40 -4
- package/packages/mcp-server/src/server.ts +12 -10
- package/packages/mcp-server/src/session-manager.ts +10 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +346 -1
- package/packages/mcp-server/src/workflow-tools.ts +228 -75
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +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/dist/providers/api-family.d.ts +27 -0
- package/packages/pi-ai/dist/providers/api-family.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/api-family.js +47 -0
- package/packages/pi-ai/dist/providers/api-family.js.map +1 -0
- package/packages/pi-ai/dist/providers/api-family.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/api-family.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/api-family.test.js +101 -0
- package/packages/pi-ai/dist/providers/api-family.test.js.map +1 -0
- package/packages/pi-ai/src/index.ts +1 -0
- package/packages/pi-ai/src/providers/api-family.test.ts +129 -0
- package/packages/pi-ai/src/providers/api-family.ts +57 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/cli/args.d.ts +6 -0
- package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.js +14 -4
- package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
- package/packages/pi-coding-agent/dist/cli/args.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/cli/args.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/cli/args.test.js +38 -0
- package/packages/pi-coding-agent/dist/cli/args.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +4 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +7 -1
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/main.js +3 -0
- package/packages/pi-coding-agent/dist/main.js.map +1 -1
- package/packages/pi-coding-agent/src/cli/args.test.ts +44 -0
- package/packages/pi-coding-agent/src/cli/args.ts +21 -6
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +4 -1
- package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -2
- package/packages/pi-coding-agent/src/core/retry-handler.ts +4 -1
- package/packages/pi-coding-agent/src/core/sdk.ts +17 -1
- package/packages/pi-coding-agent/src/main.ts +4 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -10
- package/src/resources/extensions/gsd/auto/phases.ts +3 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +25 -1
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +15 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +21 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +3 -1
- package/src/resources/extensions/gsd/auto-prompts.ts +33 -9
- package/src/resources/extensions/gsd/auto-worktree.ts +16 -1
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +158 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +20 -4
- package/src/resources/extensions/gsd/commands/handlers/onboarding.ts +65 -131
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-memory.ts +551 -0
- package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -2
- package/src/resources/extensions/gsd/gsd-db.ts +273 -4
- package/src/resources/extensions/gsd/memory-embeddings.ts +235 -0
- package/src/resources/extensions/gsd/memory-extractor.ts +100 -34
- package/src/resources/extensions/gsd/memory-ingest.ts +286 -0
- package/src/resources/extensions/gsd/memory-relations.ts +240 -0
- package/src/resources/extensions/gsd/memory-source-store.ts +138 -0
- package/src/resources/extensions/gsd/memory-store.ts +351 -7
- package/src/resources/extensions/gsd/pre-execution-checks.ts +12 -8
- package/src/resources/extensions/gsd/prompts/add-tests.md +1 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/doctor-git-symlink-cwd.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-embeddings.test.ts +213 -0
- package/src/resources/extensions/gsd/tests/memory-ingest.test.ts +153 -0
- package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/memory-relations.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +295 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +132 -8
- package/src/resources/extensions/gsd/tests/prompts-no-gitignored-test-refs.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +97 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +380 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +14 -0
- package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
- package/src/resources/extensions/search-the-web/command-search-provider.ts +4 -1
- package/src/resources/extensions/search-the-web/native-search.ts +13 -3
- package/src/resources/extensions/gsd/tests/onboarding-handler-loader.test.ts +0 -41
- /package/dist/web/standalone/.next/static/{J2z3GMC9QtSLr7gyoM38c → By_yegSJ-AA1OP0QjYbSl}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{J2z3GMC9QtSLr7gyoM38c → By_yegSJ-AA1OP0QjYbSl}/_ssgManifest.js +0 -0
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
VerificationContext,
|
|
17
17
|
VerificationResult,
|
|
18
18
|
} from "../auto-verification.js";
|
|
19
|
-
import type { DispatchAction } from "../auto-dispatch.js";
|
|
19
|
+
import type { DispatchAction, DispatchContext } from "../auto-dispatch.js";
|
|
20
20
|
import type { WorktreeResolver } from "../worktree-resolver.js";
|
|
21
21
|
import type { CmuxLogLevel } from "../../cmux/index.js";
|
|
22
22
|
import type { JournalEntry } from "../journal.js";
|
|
@@ -145,15 +145,7 @@ export interface LoopDeps {
|
|
|
145
145
|
} | null>;
|
|
146
146
|
|
|
147
147
|
// Dispatch
|
|
148
|
-
resolveDispatch: (dctx:
|
|
149
|
-
basePath: string;
|
|
150
|
-
mid: string;
|
|
151
|
-
midTitle: string;
|
|
152
|
-
state: GSDState;
|
|
153
|
-
prefs: GSDPreferences | undefined;
|
|
154
|
-
session?: AutoSession;
|
|
155
|
-
structuredQuestionsAvailable?: "true" | "false";
|
|
156
|
-
}) => Promise<DispatchAction>;
|
|
148
|
+
resolveDispatch: (dctx: DispatchContext) => Promise<DispatchAction>;
|
|
157
149
|
runPreDispatchHooks: (
|
|
158
150
|
unitType: string,
|
|
159
151
|
unitId: string,
|
|
@@ -50,6 +50,7 @@ import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "
|
|
|
50
50
|
import { getEligibleSlices } from "../slice-parallel-eligibility.js";
|
|
51
51
|
import { startSliceParallel } from "../slice-parallel-orchestrator.js";
|
|
52
52
|
import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
|
|
53
|
+
import type { MinimalModelRegistry } from "../context-budget.js";
|
|
53
54
|
import { ensurePlanV2Graph } from "../uok/plan-v2.js";
|
|
54
55
|
import { resolveUokFlags } from "../uok/flags.js";
|
|
55
56
|
import { UokGateRunner } from "../uok/gate-runner.js";
|
|
@@ -857,6 +858,8 @@ export async function runDispatch(
|
|
|
857
858
|
prefs,
|
|
858
859
|
session: s,
|
|
859
860
|
structuredQuestionsAvailable,
|
|
861
|
+
sessionContextWindow: ctx.model?.contextWindow,
|
|
862
|
+
modelRegistry: ctx.modelRegistry as MinimalModelRegistry | undefined,
|
|
860
863
|
});
|
|
861
864
|
|
|
862
865
|
if (dispatchResult.action === "stop") {
|
|
@@ -18,6 +18,7 @@ import { getCurrentBranch } from "./worktree.js";
|
|
|
18
18
|
import { getActiveHook } from "./post-unit-hooks.js";
|
|
19
19
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
20
20
|
import { getErrorMessage } from "./error-utils.js";
|
|
21
|
+
import { nativeIsRepo } from "./native-git-bridge.js";
|
|
21
22
|
import {
|
|
22
23
|
resolveMilestoneFile,
|
|
23
24
|
resolveSliceFile,
|
|
@@ -336,6 +337,10 @@ let lastCommitFetchedAt = 0;
|
|
|
336
337
|
|
|
337
338
|
function refreshLastCommit(basePath: string): void {
|
|
338
339
|
try {
|
|
340
|
+
if (!nativeIsRepo(basePath)) {
|
|
341
|
+
cachedLastCommit = null;
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
339
344
|
const raw = execFileSync("git", ["log", "-1", "--format=%cr|%s"], {
|
|
340
345
|
cwd: basePath,
|
|
341
346
|
encoding: "utf-8",
|
|
@@ -349,10 +354,12 @@ function refreshLastCommit(basePath: string): void {
|
|
|
349
354
|
message: raw.slice(sep + 1),
|
|
350
355
|
};
|
|
351
356
|
}
|
|
352
|
-
lastCommitFetchedAt = Date.now();
|
|
353
357
|
} catch (err) {
|
|
354
358
|
// Non-fatal — just skip last commit display
|
|
359
|
+
cachedLastCommit = null;
|
|
355
360
|
logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
361
|
+
} finally {
|
|
362
|
+
lastCommitFetchedAt = Date.now();
|
|
356
363
|
}
|
|
357
364
|
}
|
|
358
365
|
|
|
@@ -364,6 +371,23 @@ function getLastCommit(basePath: string): { timeAgo: string; message: string } |
|
|
|
364
371
|
return cachedLastCommit;
|
|
365
372
|
}
|
|
366
373
|
|
|
374
|
+
export function _resetLastCommitCacheForTests(): void {
|
|
375
|
+
cachedLastCommit = null;
|
|
376
|
+
lastCommitFetchedAt = 0;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
export function _refreshLastCommitForTests(basePath: string): void {
|
|
380
|
+
refreshLastCommit(basePath);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
export function _getLastCommitForTests(basePath: string): { timeAgo: string; message: string } | null {
|
|
384
|
+
return getLastCommit(basePath);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function _getLastCommitFetchedAtForTests(): number {
|
|
388
|
+
return lastCommitFetchedAt;
|
|
389
|
+
}
|
|
390
|
+
|
|
367
391
|
// ─── Footer Factory ───────────────────────────────────────────────────────────
|
|
368
392
|
|
|
369
393
|
/**
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
buildReplanSlicePrompt,
|
|
29
29
|
} from "./auto-prompts.js";
|
|
30
30
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
31
|
+
import type { MinimalModelRegistry } from "./context-budget.js";
|
|
31
32
|
import { pauseAuto } from "./auto.js";
|
|
32
33
|
import {
|
|
33
34
|
getWorkflowTransportSupportError,
|
|
@@ -104,7 +105,13 @@ export async function dispatchDirectPhase(
|
|
|
104
105
|
}
|
|
105
106
|
unitType = "plan-slice";
|
|
106
107
|
unitId = `${mid}/${sid}`;
|
|
107
|
-
prompt = await buildPlanSlicePrompt(
|
|
108
|
+
prompt = await buildPlanSlicePrompt(
|
|
109
|
+
mid, midTitle, sid, sTitle, base, undefined,
|
|
110
|
+
{
|
|
111
|
+
sessionContextWindow: ctx.model?.contextWindow,
|
|
112
|
+
modelRegistry: ctx.modelRegistry as MinimalModelRegistry | undefined,
|
|
113
|
+
},
|
|
114
|
+
);
|
|
108
115
|
} else {
|
|
109
116
|
unitType = "plan-milestone";
|
|
110
117
|
unitId = mid;
|
|
@@ -129,7 +136,13 @@ export async function dispatchDirectPhase(
|
|
|
129
136
|
}
|
|
130
137
|
unitType = "execute-task";
|
|
131
138
|
unitId = `${mid}/${sid}/${tid}`;
|
|
132
|
-
prompt = await buildExecuteTaskPrompt(
|
|
139
|
+
prompt = await buildExecuteTaskPrompt(
|
|
140
|
+
mid, sid, sTitle, tid, tTitle, base,
|
|
141
|
+
{
|
|
142
|
+
sessionContextWindow: ctx.model?.contextWindow,
|
|
143
|
+
modelRegistry: ctx.modelRegistry as MinimalModelRegistry | undefined,
|
|
144
|
+
},
|
|
145
|
+
);
|
|
133
146
|
break;
|
|
134
147
|
}
|
|
135
148
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import type { GSDState } from "./types.js";
|
|
13
13
|
import type { GSDPreferences } from "./preferences.js";
|
|
14
14
|
import type { UatType } from "./files.js";
|
|
15
|
+
import type { MinimalModelRegistry } from "./context-budget.js";
|
|
15
16
|
import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
|
|
16
17
|
import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone, updateMilestoneStatus } from "./gsd-db.js";
|
|
17
18
|
import { isClosedStatus } from "./status-guards.js";
|
|
@@ -81,6 +82,10 @@ export interface DispatchContext {
|
|
|
81
82
|
prefs: GSDPreferences | undefined;
|
|
82
83
|
session?: import("./auto/session.js").AutoSession;
|
|
83
84
|
structuredQuestionsAvailable?: "true" | "false";
|
|
85
|
+
/** Session model context window in tokens, forwarded to the budget engine's prompt builders. */
|
|
86
|
+
sessionContextWindow?: number;
|
|
87
|
+
/** Model registry forwarded to the budget engine so it can look up the configured executor model. */
|
|
88
|
+
modelRegistry?: MinimalModelRegistry;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
91
|
export interface DispatchRule {
|
|
@@ -521,7 +526,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
521
526
|
// auto-heal without either adding an explicit `setSliceSketchFlag(..., false)`
|
|
522
527
|
// call here or doing so inside the plan-slice tool handler.
|
|
523
528
|
name: "refining → refine-slice",
|
|
524
|
-
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
529
|
+
match: async ({ state, mid, midTitle, basePath, prefs, sessionContextWindow, modelRegistry }) => {
|
|
525
530
|
if (state.phase !== "refining") return null;
|
|
526
531
|
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
527
532
|
const sid = state.activeSlice.id;
|
|
@@ -546,7 +551,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
546
551
|
unitId: `${mid}/${sid}`,
|
|
547
552
|
prompt: await buildPlanSlicePrompt(
|
|
548
553
|
mid, midTitle, sid, sTitle, basePath, undefined,
|
|
549
|
-
softScopeHint ? { softScopeHint } :
|
|
554
|
+
{ ...(softScopeHint ? { softScopeHint } : {}), sessionContextWindow, modelRegistry },
|
|
550
555
|
),
|
|
551
556
|
};
|
|
552
557
|
}
|
|
@@ -554,13 +559,16 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
554
559
|
action: "dispatch",
|
|
555
560
|
unitType: "refine-slice",
|
|
556
561
|
unitId: `${mid}/${sid}`,
|
|
557
|
-
prompt: await buildRefineSlicePrompt(
|
|
562
|
+
prompt: await buildRefineSlicePrompt(
|
|
563
|
+
mid, midTitle, sid, sTitle, basePath, undefined,
|
|
564
|
+
{ sessionContextWindow, modelRegistry },
|
|
565
|
+
),
|
|
558
566
|
};
|
|
559
567
|
},
|
|
560
568
|
},
|
|
561
569
|
{
|
|
562
570
|
name: "planning → plan-slice",
|
|
563
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
571
|
+
match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry }) => {
|
|
564
572
|
if (state.phase !== "planning") return null;
|
|
565
573
|
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
566
574
|
const sid = state.activeSlice!.id;
|
|
@@ -575,6 +583,8 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
575
583
|
sid,
|
|
576
584
|
sTitle,
|
|
577
585
|
basePath,
|
|
586
|
+
undefined,
|
|
587
|
+
{ sessionContextWindow, modelRegistry },
|
|
578
588
|
),
|
|
579
589
|
};
|
|
580
590
|
},
|
|
@@ -635,7 +645,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
635
645
|
},
|
|
636
646
|
{
|
|
637
647
|
name: "executing → reactive-execute (parallel dispatch)",
|
|
638
|
-
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
648
|
+
match: async ({ state, mid, midTitle, basePath, prefs, sessionContextWindow, modelRegistry }) => {
|
|
639
649
|
if (state.phase !== "executing" || !state.activeTask) return null;
|
|
640
650
|
if (!state.activeSlice) return null; // fall through
|
|
641
651
|
|
|
@@ -726,6 +736,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
726
736
|
selected,
|
|
727
737
|
basePath,
|
|
728
738
|
subagentModel,
|
|
739
|
+
{ sessionContextWindow, modelRegistry },
|
|
729
740
|
),
|
|
730
741
|
};
|
|
731
742
|
} catch (err) {
|
|
@@ -737,7 +748,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
737
748
|
},
|
|
738
749
|
{
|
|
739
750
|
name: "executing → execute-task (recover missing task plan → plan-slice)",
|
|
740
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
751
|
+
match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry }) => {
|
|
741
752
|
if (state.phase !== "executing" || !state.activeTask) return null;
|
|
742
753
|
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
743
754
|
const sid = state.activeSlice!.id;
|
|
@@ -761,6 +772,8 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
761
772
|
sid,
|
|
762
773
|
sTitle,
|
|
763
774
|
basePath,
|
|
775
|
+
undefined,
|
|
776
|
+
{ sessionContextWindow, modelRegistry },
|
|
764
777
|
),
|
|
765
778
|
};
|
|
766
779
|
}
|
|
@@ -770,7 +783,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
770
783
|
},
|
|
771
784
|
{
|
|
772
785
|
name: "executing → execute-task",
|
|
773
|
-
match: async ({ state, mid, basePath }) => {
|
|
786
|
+
match: async ({ state, mid, basePath, sessionContextWindow, modelRegistry }) => {
|
|
774
787
|
if (state.phase !== "executing" || !state.activeTask) return null;
|
|
775
788
|
if (!state.activeSlice) return missingSliceStop(mid, state.phase);
|
|
776
789
|
const sid = state.activeSlice!.id;
|
|
@@ -789,6 +802,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|
|
789
802
|
tid,
|
|
790
803
|
tTitle,
|
|
791
804
|
basePath,
|
|
805
|
+
{ sessionContextWindow, modelRegistry },
|
|
792
806
|
),
|
|
793
807
|
};
|
|
794
808
|
},
|
|
@@ -488,7 +488,9 @@ export function resolveModelId<T extends { id: string; provider: string }>(
|
|
|
488
488
|
if (providerMatch) return providerMatch;
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
// Prefer "anthropic" as the canonical provider for Anthropic models
|
|
491
|
+
// Prefer "anthropic" as the canonical provider for Anthropic models.
|
|
492
|
+
// Transport-specific tiebreaker (ADR-012): intentionally keys on provider,
|
|
493
|
+
// not api — we want the plain Anthropic transport when multiple are available.
|
|
492
494
|
const anthropicMatch = candidates.find(m => m.provider === "anthropic");
|
|
493
495
|
if (anthropicMatch) return anthropicMatch;
|
|
494
496
|
|
|
@@ -23,7 +23,7 @@ import type { GSDPreferences } from "./preferences.js";
|
|
|
23
23
|
import { getLoadedSkills, type Skill } from "@gsd/pi-coding-agent";
|
|
24
24
|
import { join, basename } from "node:path";
|
|
25
25
|
import { existsSync } from "node:fs";
|
|
26
|
-
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
|
|
26
|
+
import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary, type MinimalModelRegistry } from "./context-budget.js";
|
|
27
27
|
import { getPendingGates, getPendingGatesForTurn } from "./gsd-db.js";
|
|
28
28
|
import {
|
|
29
29
|
GATE_REGISTRY,
|
|
@@ -94,14 +94,19 @@ function capPreamble(preamble: string): string {
|
|
|
94
94
|
* Uses the budget engine to compute task count ranges and inline context budgets
|
|
95
95
|
* based on the configured executor model's context window.
|
|
96
96
|
*/
|
|
97
|
-
function formatExecutorConstraints(
|
|
97
|
+
function formatExecutorConstraints(
|
|
98
|
+
sessionContextWindow?: number,
|
|
99
|
+
modelRegistry?: MinimalModelRegistry,
|
|
100
|
+
): string {
|
|
98
101
|
let windowTokens: number;
|
|
99
102
|
try {
|
|
100
103
|
const prefs = loadEffectiveGSDPreferences();
|
|
101
|
-
windowTokens = resolveExecutorContextWindow(
|
|
104
|
+
windowTokens = resolveExecutorContextWindow(modelRegistry, prefs?.preferences, sessionContextWindow);
|
|
102
105
|
} catch (e) {
|
|
103
106
|
logWarning("prompt", `resolveExecutorContextWindow failed: ${(e as Error).message}`);
|
|
104
|
-
|
|
107
|
+
// Delegate to the budget engine without prefs (the path that just threw)
|
|
108
|
+
// so DEFAULT_CONTEXT_WINDOW stays the single source of truth.
|
|
109
|
+
windowTokens = resolveExecutorContextWindow(undefined, undefined, sessionContextWindow);
|
|
105
110
|
}
|
|
106
111
|
const budgets = computeBudgets(windowTokens);
|
|
107
112
|
const { min, max } = budgets.taskCountRange;
|
|
@@ -1288,8 +1293,13 @@ async function renderSlicePrompt(options: {
|
|
|
1288
1293
|
promptTemplate: "plan-slice" | "refine-slice";
|
|
1289
1294
|
prependBlocks?: string[];
|
|
1290
1295
|
extraVars?: Record<string, string>;
|
|
1296
|
+
sessionContextWindow?: number;
|
|
1297
|
+
modelRegistry?: MinimalModelRegistry;
|
|
1291
1298
|
}): Promise<string> {
|
|
1292
|
-
const {
|
|
1299
|
+
const {
|
|
1300
|
+
mid, sid, sTitle, base, level, promptTemplate, prependBlocks = [], extraVars = {},
|
|
1301
|
+
sessionContextWindow, modelRegistry,
|
|
1302
|
+
} = options;
|
|
1293
1303
|
|
|
1294
1304
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
1295
1305
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
@@ -1341,7 +1351,7 @@ async function renderSlicePrompt(options: {
|
|
|
1341
1351
|
if (overridesInline) inlined.unshift(overridesInline);
|
|
1342
1352
|
|
|
1343
1353
|
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1344
|
-
const executorContextConstraints = formatExecutorConstraints();
|
|
1354
|
+
const executorContextConstraints = formatExecutorConstraints(sessionContextWindow, modelRegistry);
|
|
1345
1355
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
1346
1356
|
const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
1347
1357
|
|
|
@@ -1370,7 +1380,7 @@ async function renderSlicePrompt(options: {
|
|
|
1370
1380
|
|
|
1371
1381
|
export async function buildPlanSlicePrompt(
|
|
1372
1382
|
mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
|
|
1373
|
-
options?: { softScopeHint?: string },
|
|
1383
|
+
options?: { softScopeHint?: string; sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry },
|
|
1374
1384
|
): Promise<string> {
|
|
1375
1385
|
const prependBlocks: string[] = [];
|
|
1376
1386
|
// ADR-011: when the refining-phase dispatch rule gracefully downgrades to
|
|
@@ -1388,6 +1398,8 @@ export async function buildPlanSlicePrompt(
|
|
|
1388
1398
|
level: level ?? resolveInlineLevel(),
|
|
1389
1399
|
promptTemplate: "plan-slice",
|
|
1390
1400
|
prependBlocks,
|
|
1401
|
+
sessionContextWindow: options?.sessionContextWindow,
|
|
1402
|
+
modelRegistry: options?.modelRegistry,
|
|
1391
1403
|
});
|
|
1392
1404
|
}
|
|
1393
1405
|
|
|
@@ -1400,6 +1412,7 @@ export async function buildPlanSlicePrompt(
|
|
|
1400
1412
|
*/
|
|
1401
1413
|
export async function buildRefineSlicePrompt(
|
|
1402
1414
|
mid: string, _midTitle: string, sid: string, sTitle: string, base: string, level?: InlineLevel,
|
|
1415
|
+
options?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry },
|
|
1403
1416
|
): Promise<string> {
|
|
1404
1417
|
// Pull the stored sketch scope from the DB — the hard constraint we plan within.
|
|
1405
1418
|
let sketchScope = "";
|
|
@@ -1426,6 +1439,8 @@ export async function buildRefineSlicePrompt(
|
|
|
1426
1439
|
promptTemplate: "refine-slice",
|
|
1427
1440
|
prependBlocks,
|
|
1428
1441
|
extraVars: { sketchScope },
|
|
1442
|
+
sessionContextWindow: options?.sessionContextWindow,
|
|
1443
|
+
modelRegistry: options?.modelRegistry,
|
|
1429
1444
|
});
|
|
1430
1445
|
}
|
|
1431
1446
|
|
|
@@ -1434,6 +1449,10 @@ export interface ExecuteTaskPromptOptions {
|
|
|
1434
1449
|
level?: InlineLevel;
|
|
1435
1450
|
/** Override carry-forward paths (dependency-based instead of order-based). */
|
|
1436
1451
|
carryForwardPaths?: string[];
|
|
1452
|
+
/** Session model context window in tokens, forwarded to the budget engine. */
|
|
1453
|
+
sessionContextWindow?: number;
|
|
1454
|
+
/** Model registry forwarded to the budget engine for executor-model lookup. */
|
|
1455
|
+
modelRegistry?: MinimalModelRegistry;
|
|
1437
1456
|
}
|
|
1438
1457
|
|
|
1439
1458
|
export async function buildExecuteTaskPrompt(
|
|
@@ -1525,7 +1544,7 @@ export async function buildExecuteTaskPrompt(
|
|
|
1525
1544
|
|
|
1526
1545
|
// Compute verification budget for the executor's context window (issue #707)
|
|
1527
1546
|
const prefs = loadEffectiveGSDPreferences();
|
|
1528
|
-
const contextWindow = resolveExecutorContextWindow(
|
|
1547
|
+
const contextWindow = resolveExecutorContextWindow(opts.modelRegistry, prefs?.preferences, opts.sessionContextWindow);
|
|
1529
1548
|
const budgets = computeBudgets(contextWindow);
|
|
1530
1549
|
const verificationBudget = `~${Math.round(budgets.verificationBudgetChars / 1000)}K chars`;
|
|
1531
1550
|
|
|
@@ -2101,6 +2120,7 @@ export async function buildReactiveExecutePrompt(
|
|
|
2101
2120
|
mid: string, midTitle: string, sid: string, sTitle: string,
|
|
2102
2121
|
readyTaskIds: string[], base: string,
|
|
2103
2122
|
subagentModel?: string,
|
|
2123
|
+
opts?: { sessionContextWindow?: number; modelRegistry?: MinimalModelRegistry },
|
|
2104
2124
|
): Promise<string> {
|
|
2105
2125
|
const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
|
|
2106
2126
|
|
|
@@ -2142,7 +2162,11 @@ export async function buildReactiveExecutePrompt(
|
|
|
2142
2162
|
// Build a full execute-task prompt with dependency-based carry-forward
|
|
2143
2163
|
const taskPrompt = await buildExecuteTaskPrompt(
|
|
2144
2164
|
mid, sid, sTitle, tid, tTitle, base,
|
|
2145
|
-
{
|
|
2165
|
+
{
|
|
2166
|
+
carryForwardPaths: depPaths,
|
|
2167
|
+
sessionContextWindow: opts?.sessionContextWindow,
|
|
2168
|
+
modelRegistry: opts?.modelRegistry,
|
|
2169
|
+
},
|
|
2146
2170
|
);
|
|
2147
2171
|
|
|
2148
2172
|
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
@@ -238,6 +238,14 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
|
|
|
238
238
|
}
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
function isProjectGsdSymlink(basePath: string): boolean {
|
|
242
|
+
try {
|
|
243
|
+
return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
|
|
244
|
+
} catch {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
241
249
|
// ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
|
|
242
250
|
|
|
243
251
|
/** Patterns for machine-generated build artifacts that can be safely
|
|
@@ -1675,12 +1683,19 @@ export function mergeMilestoneToMain(
|
|
|
1675
1683
|
// CONTEXT files into the stash. If stash pop later fails, those files
|
|
1676
1684
|
// are permanently trapped in the stash entry and lost on the next
|
|
1677
1685
|
// stash push or drop.
|
|
1686
|
+
//
|
|
1687
|
+
// When `.gsd` itself is a symlink, Git rejects pathspecs below it
|
|
1688
|
+
// ("beyond a symbolic link"). In that layout, exclude the whole symlink
|
|
1689
|
+
// and keep stashing real project files that could block the merge.
|
|
1690
|
+
const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
|
|
1691
|
+
? [".", ":(exclude).gsd"]
|
|
1692
|
+
: [":(exclude).gsd/milestones"];
|
|
1678
1693
|
execFileSync(
|
|
1679
1694
|
"git",
|
|
1680
1695
|
[
|
|
1681
1696
|
"stash", "push", "--include-untracked",
|
|
1682
1697
|
"-m", `gsd: pre-merge stash for ${milestoneId}`,
|
|
1683
|
-
"--",
|
|
1698
|
+
"--", ...stashPathspecs,
|
|
1684
1699
|
],
|
|
1685
1700
|
{ cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" },
|
|
1686
1701
|
);
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// GSD2 — Memory tool registration
|
|
2
|
+
//
|
|
3
|
+
// Exposes the memory-layer tools (capture_thought, memory_query, gsd_graph)
|
|
4
|
+
// to the LLM over MCP. All three degrade gracefully when the GSD database
|
|
5
|
+
// is unavailable.
|
|
6
|
+
|
|
7
|
+
import { Type } from "@sinclair/typebox";
|
|
8
|
+
import type { ExtensionAPI } from "@gsd/pi-coding-agent";
|
|
9
|
+
|
|
10
|
+
import { ensureDbOpen } from "./dynamic-tools.js";
|
|
11
|
+
import {
|
|
12
|
+
executeGsdGraph,
|
|
13
|
+
executeMemoryCapture,
|
|
14
|
+
executeMemoryQuery,
|
|
15
|
+
} from "../tools/memory-tools.js";
|
|
16
|
+
|
|
17
|
+
export function registerMemoryTools(pi: ExtensionAPI): void {
|
|
18
|
+
// ─── capture_thought ────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
pi.registerTool({
|
|
21
|
+
name: "capture_thought",
|
|
22
|
+
label: "Capture Thought",
|
|
23
|
+
description:
|
|
24
|
+
"Record a durable piece of project knowledge (decision, convention, gotcha, pattern, " +
|
|
25
|
+
"preference, or environment detail) into the GSD memory store. Use sparingly — one memory " +
|
|
26
|
+
"per genuinely reusable insight, not per task.",
|
|
27
|
+
promptSnippet:
|
|
28
|
+
"Capture a durable project insight into the GSD memory store (categories: architecture, convention, gotcha, pattern, preference, environment)",
|
|
29
|
+
promptGuidelines: [
|
|
30
|
+
"Use capture_thought for insights that will remain useful across future sessions.",
|
|
31
|
+
"Do NOT capture one-off bug fixes, temporary state, secrets, or task-specific details.",
|
|
32
|
+
"Keep content to 1–3 sentences.",
|
|
33
|
+
"Set confidence: 0.6 tentative, 0.8 solid, 0.95 well-confirmed (default 0.8).",
|
|
34
|
+
],
|
|
35
|
+
parameters: Type.Object({
|
|
36
|
+
category: Type.Union(
|
|
37
|
+
[
|
|
38
|
+
Type.Literal("architecture"),
|
|
39
|
+
Type.Literal("convention"),
|
|
40
|
+
Type.Literal("gotcha"),
|
|
41
|
+
Type.Literal("preference"),
|
|
42
|
+
Type.Literal("environment"),
|
|
43
|
+
Type.Literal("pattern"),
|
|
44
|
+
],
|
|
45
|
+
{ description: "Memory category" },
|
|
46
|
+
),
|
|
47
|
+
content: Type.String({ description: "The memory text (1–3 sentences, no secrets)" }),
|
|
48
|
+
confidence: Type.Optional(
|
|
49
|
+
Type.Number({ description: "0.1–0.99, default 0.8", minimum: 0.1, maximum: 0.99 }),
|
|
50
|
+
),
|
|
51
|
+
tags: Type.Optional(Type.Array(Type.String(), { description: "Free-form tags (reserved for future use)" })),
|
|
52
|
+
scope: Type.Optional(Type.String({ description: "Scope name (reserved for future use; defaults to project)" })),
|
|
53
|
+
}),
|
|
54
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
55
|
+
const ok = await ensureDbOpen();
|
|
56
|
+
if (!ok) {
|
|
57
|
+
return {
|
|
58
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot capture memory." }],
|
|
59
|
+
details: { operation: "memory_capture", error: "db_unavailable" },
|
|
60
|
+
isError: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return executeMemoryCapture(params as Parameters<typeof executeMemoryCapture>[0]);
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ─── memory_query ───────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
pi.registerTool({
|
|
70
|
+
name: "memory_query",
|
|
71
|
+
label: "Query Memory",
|
|
72
|
+
description:
|
|
73
|
+
"Search the GSD memory store for relevant memories. Phase 1 uses keyword matching ranked " +
|
|
74
|
+
"by confidence and reinforcement; future phases add semantic (embedding) retrieval.",
|
|
75
|
+
promptSnippet:
|
|
76
|
+
"Search the GSD memory store by keyword; returns ranked memories with id, category, and content",
|
|
77
|
+
promptGuidelines: [
|
|
78
|
+
"Use memory_query when you need durable project context that may not be in the current prompt.",
|
|
79
|
+
"Provide a short keyword-style query — not a full question.",
|
|
80
|
+
"Use category to narrow results to gotchas, conventions, architecture notes, etc.",
|
|
81
|
+
],
|
|
82
|
+
parameters: Type.Object({
|
|
83
|
+
query: Type.String({ description: "Keyword query (2+ char terms)" }),
|
|
84
|
+
k: Type.Optional(Type.Number({ description: "Max results (default 10, max 50)", minimum: 1, maximum: 50 })),
|
|
85
|
+
category: Type.Optional(
|
|
86
|
+
Type.Union(
|
|
87
|
+
[
|
|
88
|
+
Type.Literal("architecture"),
|
|
89
|
+
Type.Literal("convention"),
|
|
90
|
+
Type.Literal("gotcha"),
|
|
91
|
+
Type.Literal("preference"),
|
|
92
|
+
Type.Literal("environment"),
|
|
93
|
+
Type.Literal("pattern"),
|
|
94
|
+
],
|
|
95
|
+
{ description: "Restrict results to a single category" },
|
|
96
|
+
),
|
|
97
|
+
),
|
|
98
|
+
scope: Type.Optional(Type.String({ description: "Only include memories with this scope (e.g. 'project', 'global')" })),
|
|
99
|
+
tag: Type.Optional(Type.String({ description: "Only include memories tagged with this value" })),
|
|
100
|
+
include_superseded: Type.Optional(Type.Boolean({ description: "Include superseded memories (default false)" })),
|
|
101
|
+
reinforce_hits: Type.Optional(
|
|
102
|
+
Type.Boolean({ description: "Increment hit_count on returned memories (default false)" }),
|
|
103
|
+
),
|
|
104
|
+
}),
|
|
105
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
106
|
+
const ok = await ensureDbOpen();
|
|
107
|
+
if (!ok) {
|
|
108
|
+
return {
|
|
109
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available. Cannot query memory." }],
|
|
110
|
+
details: { operation: "memory_query", error: "db_unavailable" },
|
|
111
|
+
isError: true,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return executeMemoryQuery(params as Parameters<typeof executeMemoryQuery>[0]);
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ─── gsd_graph ──────────────────────────────────────────────────────────
|
|
119
|
+
|
|
120
|
+
pi.registerTool({
|
|
121
|
+
name: "gsd_graph",
|
|
122
|
+
label: "GSD Knowledge Graph",
|
|
123
|
+
description:
|
|
124
|
+
"Inspect the relationship graph between memories. mode=query walks supersedes edges from a " +
|
|
125
|
+
"given memoryId; mode=build is a placeholder that future phases will use to rebuild graph " +
|
|
126
|
+
"edges from milestone LEARNINGS artifacts.",
|
|
127
|
+
promptSnippet: "Query the memory relationship graph or trigger a rebuild",
|
|
128
|
+
promptGuidelines: [
|
|
129
|
+
"Use mode=query with a memoryId when you want to see how a memory relates to others.",
|
|
130
|
+
"Phase 1 only exposes supersedes edges; additional relation types arrive in later phases.",
|
|
131
|
+
],
|
|
132
|
+
parameters: Type.Object({
|
|
133
|
+
mode: Type.Union([Type.Literal("build"), Type.Literal("query")], {
|
|
134
|
+
description: "build = recompute graph (placeholder), query = inspect edges",
|
|
135
|
+
}),
|
|
136
|
+
memoryId: Type.Optional(Type.String({ description: "Memory ID (required when mode=query)" })),
|
|
137
|
+
depth: Type.Optional(Type.Number({ description: "Hops to traverse (0–5, default 1)", minimum: 0, maximum: 5 })),
|
|
138
|
+
rel: Type.Optional(Type.Union([
|
|
139
|
+
Type.Literal("related_to"),
|
|
140
|
+
Type.Literal("depends_on"),
|
|
141
|
+
Type.Literal("contradicts"),
|
|
142
|
+
Type.Literal("elaborates"),
|
|
143
|
+
Type.Literal("supersedes"),
|
|
144
|
+
], { description: "Only include edges with this relation type" })),
|
|
145
|
+
}),
|
|
146
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
147
|
+
const ok = await ensureDbOpen();
|
|
148
|
+
if (!ok) {
|
|
149
|
+
return {
|
|
150
|
+
content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
|
|
151
|
+
details: { operation: "gsd_graph", error: "db_unavailable" },
|
|
152
|
+
isError: true,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return executeGsdGraph(params as Parameters<typeof executeGsdGraph>[0]);
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -9,6 +9,7 @@ import { loadEcosystemExtensions } from "../ecosystem/loader.js";
|
|
|
9
9
|
import { registerDbTools } from "./db-tools.js";
|
|
10
10
|
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
11
11
|
import { registerJournalTools } from "./journal-tools.js";
|
|
12
|
+
import { registerMemoryTools } from "./memory-tools.js";
|
|
12
13
|
import { registerQueryTools } from "./query-tools.js";
|
|
13
14
|
import { registerHooks } from "./register-hooks.js";
|
|
14
15
|
import { registerShortcuts } from "./register-shortcuts.js";
|
|
@@ -85,6 +86,7 @@ export function registerGsdExtension(pi: ExtensionAPI): void {
|
|
|
85
86
|
["db-tools", () => registerDbTools(pi)],
|
|
86
87
|
["journal-tools", () => registerJournalTools(pi)],
|
|
87
88
|
["query-tools", () => registerQueryTools(pi)],
|
|
89
|
+
["memory-tools", () => registerMemoryTools(pi)],
|
|
88
90
|
["shortcuts", () => registerShortcuts(pi)],
|
|
89
91
|
["hooks", () => registerHooks(pi, ecosystemHandlers)],
|
|
90
92
|
["ecosystem", () => {
|
|
@@ -113,10 +113,26 @@ export async function buildBeforeAgentStartResult(
|
|
|
113
113
|
|
|
114
114
|
let memoryBlock = "";
|
|
115
115
|
try {
|
|
116
|
-
const { formatMemoriesForPrompt, getActiveMemoriesRanked } = await import("../memory-store.js");
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const { formatMemoriesForPrompt, getActiveMemoriesRanked, queryMemoriesRanked } = await import("../memory-store.js");
|
|
117
|
+
|
|
118
|
+
// Always-on "critical" set — small, stable memories that belong in every
|
|
119
|
+
// turn (gotchas, environment, conventions). Ranked by the existing score.
|
|
120
|
+
const CRITICAL_CATEGORIES = new Set(["gotcha", "environment", "convention"]);
|
|
121
|
+
const allRanked = getActiveMemoriesRanked(60);
|
|
122
|
+
const critical = allRanked.filter((m) => CRITICAL_CATEGORIES.has(m.category)).slice(0, 5);
|
|
123
|
+
const criticalIds = new Set(critical.map((m) => m.id));
|
|
124
|
+
|
|
125
|
+
// Prompt-relevance set — hybrid FTS5 + (future) semantic retrieval.
|
|
126
|
+
let relevant: typeof allRanked = [];
|
|
127
|
+
const userPrompt = (event.prompt ?? "").trim();
|
|
128
|
+
if (userPrompt) {
|
|
129
|
+
const hits = queryMemoriesRanked({ query: userPrompt, k: 10 });
|
|
130
|
+
relevant = hits.map((h) => h.memory).filter((m) => !criticalIds.has(m.id));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const merged = [...critical, ...relevant];
|
|
134
|
+
if (merged.length > 0) {
|
|
135
|
+
const formatted = formatMemoriesForPrompt(merged, 2000);
|
|
120
136
|
if (formatted) {
|
|
121
137
|
memoryBlock = `\n\n${formatted}`;
|
|
122
138
|
}
|