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
|
@@ -607,6 +607,8 @@ export async function runDispatch(ic, preData, loopState) {
|
|
|
607
607
|
prefs,
|
|
608
608
|
session: s,
|
|
609
609
|
structuredQuestionsAvailable,
|
|
610
|
+
sessionContextWindow: ctx.model?.contextWindow,
|
|
611
|
+
modelRegistry: ctx.modelRegistry,
|
|
610
612
|
});
|
|
611
613
|
if (dispatchResult.action === "stop") {
|
|
612
614
|
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
|
|
@@ -9,6 +9,7 @@ import { getCurrentBranch } from "./worktree.js";
|
|
|
9
9
|
import { getActiveHook } from "./post-unit-hooks.js";
|
|
10
10
|
import { getLedger, getProjectTotals } from "./metrics.js";
|
|
11
11
|
import { getErrorMessage } from "./error-utils.js";
|
|
12
|
+
import { nativeIsRepo } from "./native-git-bridge.js";
|
|
12
13
|
import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
|
|
13
14
|
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
14
15
|
import { execFileSync } from "node:child_process";
|
|
@@ -256,6 +257,10 @@ let cachedLastCommit = null;
|
|
|
256
257
|
let lastCommitFetchedAt = 0;
|
|
257
258
|
function refreshLastCommit(basePath) {
|
|
258
259
|
try {
|
|
260
|
+
if (!nativeIsRepo(basePath)) {
|
|
261
|
+
cachedLastCommit = null;
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
259
264
|
const raw = execFileSync("git", ["log", "-1", "--format=%cr|%s"], {
|
|
260
265
|
cwd: basePath,
|
|
261
266
|
encoding: "utf-8",
|
|
@@ -269,12 +274,15 @@ function refreshLastCommit(basePath) {
|
|
|
269
274
|
message: raw.slice(sep + 1),
|
|
270
275
|
};
|
|
271
276
|
}
|
|
272
|
-
lastCommitFetchedAt = Date.now();
|
|
273
277
|
}
|
|
274
278
|
catch (err) {
|
|
275
279
|
// Non-fatal — just skip last commit display
|
|
280
|
+
cachedLastCommit = null;
|
|
276
281
|
logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
277
282
|
}
|
|
283
|
+
finally {
|
|
284
|
+
lastCommitFetchedAt = Date.now();
|
|
285
|
+
}
|
|
278
286
|
}
|
|
279
287
|
function getLastCommit(basePath) {
|
|
280
288
|
// Refresh at most every 15 seconds
|
|
@@ -283,6 +291,19 @@ function getLastCommit(basePath) {
|
|
|
283
291
|
}
|
|
284
292
|
return cachedLastCommit;
|
|
285
293
|
}
|
|
294
|
+
export function _resetLastCommitCacheForTests() {
|
|
295
|
+
cachedLastCommit = null;
|
|
296
|
+
lastCommitFetchedAt = 0;
|
|
297
|
+
}
|
|
298
|
+
export function _refreshLastCommitForTests(basePath) {
|
|
299
|
+
refreshLastCommit(basePath);
|
|
300
|
+
}
|
|
301
|
+
export function _getLastCommitForTests(basePath) {
|
|
302
|
+
return getLastCommit(basePath);
|
|
303
|
+
}
|
|
304
|
+
export function _getLastCommitFetchedAtForTests() {
|
|
305
|
+
return lastCommitFetchedAt;
|
|
306
|
+
}
|
|
286
307
|
// ─── Footer Factory ───────────────────────────────────────────────────────────
|
|
287
308
|
/**
|
|
288
309
|
* Footer factory used by auto-mode.
|
|
@@ -68,7 +68,10 @@ export async function dispatchDirectPhase(ctx, pi, phase, base) {
|
|
|
68
68
|
}
|
|
69
69
|
unitType = "plan-slice";
|
|
70
70
|
unitId = `${mid}/${sid}`;
|
|
71
|
-
prompt = await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, base
|
|
71
|
+
prompt = await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, base, undefined, {
|
|
72
|
+
sessionContextWindow: ctx.model?.contextWindow,
|
|
73
|
+
modelRegistry: ctx.modelRegistry,
|
|
74
|
+
});
|
|
72
75
|
}
|
|
73
76
|
else {
|
|
74
77
|
unitType = "plan-milestone";
|
|
@@ -93,7 +96,10 @@ export async function dispatchDirectPhase(ctx, pi, phase, base) {
|
|
|
93
96
|
}
|
|
94
97
|
unitType = "execute-task";
|
|
95
98
|
unitId = `${mid}/${sid}/${tid}`;
|
|
96
|
-
prompt = await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
|
|
99
|
+
prompt = await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base, {
|
|
100
|
+
sessionContextWindow: ctx.model?.contextWindow,
|
|
101
|
+
modelRegistry: ctx.modelRegistry,
|
|
102
|
+
});
|
|
97
103
|
break;
|
|
98
104
|
}
|
|
99
105
|
case "complete":
|
|
@@ -412,7 +412,7 @@ export const DISPATCH_RULES = [
|
|
|
412
412
|
// auto-heal without either adding an explicit `setSliceSketchFlag(..., false)`
|
|
413
413
|
// call here or doing so inside the plan-slice tool handler.
|
|
414
414
|
name: "refining → refine-slice",
|
|
415
|
-
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
415
|
+
match: async ({ state, mid, midTitle, basePath, prefs, sessionContextWindow, modelRegistry }) => {
|
|
416
416
|
if (state.phase !== "refining")
|
|
417
417
|
return null;
|
|
418
418
|
if (!state.activeSlice)
|
|
@@ -438,20 +438,20 @@ export const DISPATCH_RULES = [
|
|
|
438
438
|
action: "dispatch",
|
|
439
439
|
unitType: "plan-slice",
|
|
440
440
|
unitId: `${mid}/${sid}`,
|
|
441
|
-
prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, softScopeHint ? { softScopeHint } :
|
|
441
|
+
prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, { ...(softScopeHint ? { softScopeHint } : {}), sessionContextWindow, modelRegistry }),
|
|
442
442
|
};
|
|
443
443
|
}
|
|
444
444
|
return {
|
|
445
445
|
action: "dispatch",
|
|
446
446
|
unitType: "refine-slice",
|
|
447
447
|
unitId: `${mid}/${sid}`,
|
|
448
|
-
prompt: await buildRefineSlicePrompt(mid, midTitle, sid, sTitle, basePath),
|
|
448
|
+
prompt: await buildRefineSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, { sessionContextWindow, modelRegistry }),
|
|
449
449
|
};
|
|
450
450
|
},
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
name: "planning → plan-slice",
|
|
454
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
454
|
+
match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry }) => {
|
|
455
455
|
if (state.phase !== "planning")
|
|
456
456
|
return null;
|
|
457
457
|
if (!state.activeSlice)
|
|
@@ -462,7 +462,7 @@ export const DISPATCH_RULES = [
|
|
|
462
462
|
action: "dispatch",
|
|
463
463
|
unitType: "plan-slice",
|
|
464
464
|
unitId: `${mid}/${sid}`,
|
|
465
|
-
prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath),
|
|
465
|
+
prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, { sessionContextWindow, modelRegistry }),
|
|
466
466
|
};
|
|
467
467
|
},
|
|
468
468
|
},
|
|
@@ -511,7 +511,7 @@ export const DISPATCH_RULES = [
|
|
|
511
511
|
},
|
|
512
512
|
{
|
|
513
513
|
name: "executing → reactive-execute (parallel dispatch)",
|
|
514
|
-
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
514
|
+
match: async ({ state, mid, midTitle, basePath, prefs, sessionContextWindow, modelRegistry }) => {
|
|
515
515
|
if (state.phase !== "executing" || !state.activeTask)
|
|
516
516
|
return null;
|
|
517
517
|
if (!state.activeSlice)
|
|
@@ -574,7 +574,7 @@ export const DISPATCH_RULES = [
|
|
|
574
574
|
action: "dispatch",
|
|
575
575
|
unitType: "reactive-execute",
|
|
576
576
|
unitId: `${mid}/${sid}/reactive+${batchSuffix}`,
|
|
577
|
-
prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel),
|
|
577
|
+
prompt: await buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, selected, basePath, subagentModel, { sessionContextWindow, modelRegistry }),
|
|
578
578
|
};
|
|
579
579
|
}
|
|
580
580
|
catch (err) {
|
|
@@ -586,7 +586,7 @@ export const DISPATCH_RULES = [
|
|
|
586
586
|
},
|
|
587
587
|
{
|
|
588
588
|
name: "executing → execute-task (recover missing task plan → plan-slice)",
|
|
589
|
-
match: async ({ state, mid, midTitle, basePath }) => {
|
|
589
|
+
match: async ({ state, mid, midTitle, basePath, sessionContextWindow, modelRegistry }) => {
|
|
590
590
|
if (state.phase !== "executing" || !state.activeTask)
|
|
591
591
|
return null;
|
|
592
592
|
if (!state.activeSlice)
|
|
@@ -605,7 +605,7 @@ export const DISPATCH_RULES = [
|
|
|
605
605
|
action: "dispatch",
|
|
606
606
|
unitType: "plan-slice",
|
|
607
607
|
unitId: `${mid}/${sid}`,
|
|
608
|
-
prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath),
|
|
608
|
+
prompt: await buildPlanSlicePrompt(mid, midTitle, sid, sTitle, basePath, undefined, { sessionContextWindow, modelRegistry }),
|
|
609
609
|
};
|
|
610
610
|
}
|
|
611
611
|
return null;
|
|
@@ -613,7 +613,7 @@ export const DISPATCH_RULES = [
|
|
|
613
613
|
},
|
|
614
614
|
{
|
|
615
615
|
name: "executing → execute-task",
|
|
616
|
-
match: async ({ state, mid, basePath }) => {
|
|
616
|
+
match: async ({ state, mid, basePath, sessionContextWindow, modelRegistry }) => {
|
|
617
617
|
if (state.phase !== "executing" || !state.activeTask)
|
|
618
618
|
return null;
|
|
619
619
|
if (!state.activeSlice)
|
|
@@ -626,7 +626,7 @@ export const DISPATCH_RULES = [
|
|
|
626
626
|
action: "dispatch",
|
|
627
627
|
unitType: "execute-task",
|
|
628
628
|
unitId: `${mid}/${sid}/${tid}`,
|
|
629
|
-
prompt: await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, basePath),
|
|
629
|
+
prompt: await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, basePath, { sessionContextWindow, modelRegistry }),
|
|
630
630
|
};
|
|
631
631
|
},
|
|
632
632
|
},
|
|
@@ -382,7 +382,9 @@ export function resolveModelId(modelId, availableModels, currentProvider) {
|
|
|
382
382
|
if (providerMatch)
|
|
383
383
|
return providerMatch;
|
|
384
384
|
}
|
|
385
|
-
// Prefer "anthropic" as the canonical provider for Anthropic models
|
|
385
|
+
// Prefer "anthropic" as the canonical provider for Anthropic models.
|
|
386
|
+
// Transport-specific tiebreaker (ADR-012): intentionally keys on provider,
|
|
387
|
+
// not api — we want the plain Anthropic transport when multiple are available.
|
|
386
388
|
const anthropicMatch = candidates.find(m => m.provider === "anthropic");
|
|
387
389
|
if (anthropicMatch)
|
|
388
390
|
return anthropicMatch;
|
|
@@ -75,15 +75,17 @@ function capPreamble(preamble) {
|
|
|
75
75
|
* Uses the budget engine to compute task count ranges and inline context budgets
|
|
76
76
|
* based on the configured executor model's context window.
|
|
77
77
|
*/
|
|
78
|
-
function formatExecutorConstraints() {
|
|
78
|
+
function formatExecutorConstraints(sessionContextWindow, modelRegistry) {
|
|
79
79
|
let windowTokens;
|
|
80
80
|
try {
|
|
81
81
|
const prefs = loadEffectiveGSDPreferences();
|
|
82
|
-
windowTokens = resolveExecutorContextWindow(
|
|
82
|
+
windowTokens = resolveExecutorContextWindow(modelRegistry, prefs?.preferences, sessionContextWindow);
|
|
83
83
|
}
|
|
84
84
|
catch (e) {
|
|
85
85
|
logWarning("prompt", `resolveExecutorContextWindow failed: ${e.message}`);
|
|
86
|
-
|
|
86
|
+
// Delegate to the budget engine without prefs (the path that just threw)
|
|
87
|
+
// so DEFAULT_CONTEXT_WINDOW stays the single source of truth.
|
|
88
|
+
windowTokens = resolveExecutorContextWindow(undefined, undefined, sessionContextWindow);
|
|
87
89
|
}
|
|
88
90
|
const budgets = computeBudgets(windowTokens);
|
|
89
91
|
const { min, max } = budgets.taskCountRange;
|
|
@@ -1136,7 +1138,7 @@ export async function buildResearchSlicePrompt(mid, _midTitle, sid, sTitle, base
|
|
|
1136
1138
|
* sketch-scope constraint).
|
|
1137
1139
|
*/
|
|
1138
1140
|
async function renderSlicePrompt(options) {
|
|
1139
|
-
const { mid, sid, sTitle, base, level, promptTemplate, prependBlocks = [], extraVars = {} } = options;
|
|
1141
|
+
const { mid, sid, sTitle, base, level, promptTemplate, prependBlocks = [], extraVars = {}, sessionContextWindow, modelRegistry, } = options;
|
|
1140
1142
|
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
1141
1143
|
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
1142
1144
|
const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
|
|
@@ -1186,7 +1188,7 @@ async function renderSlicePrompt(options) {
|
|
|
1186
1188
|
if (overridesInline)
|
|
1187
1189
|
inlined.unshift(overridesInline);
|
|
1188
1190
|
const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
|
|
1189
|
-
const executorContextConstraints = formatExecutorConstraints();
|
|
1191
|
+
const executorContextConstraints = formatExecutorConstraints(sessionContextWindow, modelRegistry);
|
|
1190
1192
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
1191
1193
|
const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
|
|
1192
1194
|
return loadPrompt(promptTemplate, {
|
|
@@ -1226,6 +1228,8 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
|
|
|
1226
1228
|
level: level ?? resolveInlineLevel(),
|
|
1227
1229
|
promptTemplate: "plan-slice",
|
|
1228
1230
|
prependBlocks,
|
|
1231
|
+
sessionContextWindow: options?.sessionContextWindow,
|
|
1232
|
+
modelRegistry: options?.modelRegistry,
|
|
1229
1233
|
});
|
|
1230
1234
|
}
|
|
1231
1235
|
/**
|
|
@@ -1235,7 +1239,7 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
|
|
|
1235
1239
|
* blank-sheet planning pass. Reuses inlineDependencySummaries for prior
|
|
1236
1240
|
* slice SUMMARY and inlines the stored sketch_scope as a hard constraint.
|
|
1237
1241
|
*/
|
|
1238
|
-
export async function buildRefineSlicePrompt(mid, _midTitle, sid, sTitle, base, level) {
|
|
1242
|
+
export async function buildRefineSlicePrompt(mid, _midTitle, sid, sTitle, base, level, options) {
|
|
1239
1243
|
// Pull the stored sketch scope from the DB — the hard constraint we plan within.
|
|
1240
1244
|
let sketchScope = "";
|
|
1241
1245
|
try {
|
|
@@ -1258,6 +1262,8 @@ export async function buildRefineSlicePrompt(mid, _midTitle, sid, sTitle, base,
|
|
|
1258
1262
|
promptTemplate: "refine-slice",
|
|
1259
1263
|
prependBlocks,
|
|
1260
1264
|
extraVars: { sketchScope },
|
|
1265
|
+
sessionContextWindow: options?.sessionContextWindow,
|
|
1266
|
+
modelRegistry: options?.modelRegistry,
|
|
1261
1267
|
});
|
|
1262
1268
|
}
|
|
1263
1269
|
export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base, level) {
|
|
@@ -1323,7 +1329,7 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
|
|
|
1323
1329
|
const overridesSection = formatOverridesSection(activeOverrides);
|
|
1324
1330
|
// Compute verification budget for the executor's context window (issue #707)
|
|
1325
1331
|
const prefs = loadEffectiveGSDPreferences();
|
|
1326
|
-
const contextWindow = resolveExecutorContextWindow(
|
|
1332
|
+
const contextWindow = resolveExecutorContextWindow(opts.modelRegistry, prefs?.preferences, opts.sessionContextWindow);
|
|
1327
1333
|
const budgets = computeBudgets(contextWindow);
|
|
1328
1334
|
const verificationBudget = `~${Math.round(budgets.verificationBudgetChars / 1000)}K chars`;
|
|
1329
1335
|
// Truncate carry-forward section when it exceeds 40% of inline context budget.
|
|
@@ -1858,7 +1864,7 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
|
|
|
1858
1864
|
});
|
|
1859
1865
|
}
|
|
1860
1866
|
// ─── Reactive Execute Prompt ──────────────────────────────────────────────
|
|
1861
|
-
export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, readyTaskIds, base, subagentModel) {
|
|
1867
|
+
export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, readyTaskIds, base, subagentModel, opts) {
|
|
1862
1868
|
const { loadSliceTaskIO, deriveTaskGraph, graphMetrics } = await import("./reactive-graph.js");
|
|
1863
1869
|
// Build graph for context
|
|
1864
1870
|
const taskIO = await loadSliceTaskIO(base, mid, sid);
|
|
@@ -1889,7 +1895,11 @@ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, rea
|
|
|
1889
1895
|
// Build dependency-scoped carry-forward paths for this task
|
|
1890
1896
|
const depPaths = await getDependencyTaskSummaryPaths(mid, sid, tid, node?.dependsOn ?? [], base);
|
|
1891
1897
|
// Build a full execute-task prompt with dependency-based carry-forward
|
|
1892
|
-
const taskPrompt = await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base, {
|
|
1898
|
+
const taskPrompt = await buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base, {
|
|
1899
|
+
carryForwardPaths: depPaths,
|
|
1900
|
+
sessionContextWindow: opts?.sessionContextWindow,
|
|
1901
|
+
modelRegistry: opts?.modelRegistry,
|
|
1902
|
+
});
|
|
1893
1903
|
const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
|
|
1894
1904
|
subagentSections.push([
|
|
1895
1905
|
`### ${tid}: ${tTitle}`,
|
|
@@ -180,6 +180,14 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
|
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
+
function isProjectGsdSymlink(basePath) {
|
|
184
|
+
try {
|
|
185
|
+
return lstatSyncFn(join(basePath, ".gsd")).isSymbolicLink();
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
183
191
|
// ─── Build Artifact Auto-Resolve ─────────────────────────────────────────────
|
|
184
192
|
/** Patterns for machine-generated build artifacts that can be safely
|
|
185
193
|
* auto-resolved by accepting --theirs during merge. These files are
|
|
@@ -1440,10 +1448,17 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
|
|
|
1440
1448
|
// CONTEXT files into the stash. If stash pop later fails, those files
|
|
1441
1449
|
// are permanently trapped in the stash entry and lost on the next
|
|
1442
1450
|
// stash push or drop.
|
|
1451
|
+
//
|
|
1452
|
+
// When `.gsd` itself is a symlink, Git rejects pathspecs below it
|
|
1453
|
+
// ("beyond a symbolic link"). In that layout, exclude the whole symlink
|
|
1454
|
+
// and keep stashing real project files that could block the merge.
|
|
1455
|
+
const stashPathspecs = isProjectGsdSymlink(originalBasePath_)
|
|
1456
|
+
? [".", ":(exclude).gsd"]
|
|
1457
|
+
: [":(exclude).gsd/milestones"];
|
|
1443
1458
|
execFileSync("git", [
|
|
1444
1459
|
"stash", "push", "--include-untracked",
|
|
1445
1460
|
"-m", `gsd: pre-merge stash for ${milestoneId}`,
|
|
1446
|
-
"--",
|
|
1461
|
+
"--", ...stashPathspecs,
|
|
1447
1462
|
], { cwd: originalBasePath_, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" });
|
|
1448
1463
|
stashed = true;
|
|
1449
1464
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
import { Type } from "@sinclair/typebox";
|
|
7
|
+
import { ensureDbOpen } from "./dynamic-tools.js";
|
|
8
|
+
import { executeGsdGraph, executeMemoryCapture, executeMemoryQuery, } from "../tools/memory-tools.js";
|
|
9
|
+
export function registerMemoryTools(pi) {
|
|
10
|
+
// ─── capture_thought ────────────────────────────────────────────────────
|
|
11
|
+
pi.registerTool({
|
|
12
|
+
name: "capture_thought",
|
|
13
|
+
label: "Capture Thought",
|
|
14
|
+
description: "Record a durable piece of project knowledge (decision, convention, gotcha, pattern, " +
|
|
15
|
+
"preference, or environment detail) into the GSD memory store. Use sparingly — one memory " +
|
|
16
|
+
"per genuinely reusable insight, not per task.",
|
|
17
|
+
promptSnippet: "Capture a durable project insight into the GSD memory store (categories: architecture, convention, gotcha, pattern, preference, environment)",
|
|
18
|
+
promptGuidelines: [
|
|
19
|
+
"Use capture_thought for insights that will remain useful across future sessions.",
|
|
20
|
+
"Do NOT capture one-off bug fixes, temporary state, secrets, or task-specific details.",
|
|
21
|
+
"Keep content to 1–3 sentences.",
|
|
22
|
+
"Set confidence: 0.6 tentative, 0.8 solid, 0.95 well-confirmed (default 0.8).",
|
|
23
|
+
],
|
|
24
|
+
parameters: Type.Object({
|
|
25
|
+
category: Type.Union([
|
|
26
|
+
Type.Literal("architecture"),
|
|
27
|
+
Type.Literal("convention"),
|
|
28
|
+
Type.Literal("gotcha"),
|
|
29
|
+
Type.Literal("preference"),
|
|
30
|
+
Type.Literal("environment"),
|
|
31
|
+
Type.Literal("pattern"),
|
|
32
|
+
], { description: "Memory category" }),
|
|
33
|
+
content: Type.String({ description: "The memory text (1–3 sentences, no secrets)" }),
|
|
34
|
+
confidence: Type.Optional(Type.Number({ description: "0.1–0.99, default 0.8", minimum: 0.1, maximum: 0.99 })),
|
|
35
|
+
tags: Type.Optional(Type.Array(Type.String(), { description: "Free-form tags (reserved for future use)" })),
|
|
36
|
+
scope: Type.Optional(Type.String({ description: "Scope name (reserved for future use; defaults to project)" })),
|
|
37
|
+
}),
|
|
38
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
39
|
+
const ok = await ensureDbOpen();
|
|
40
|
+
if (!ok) {
|
|
41
|
+
return {
|
|
42
|
+
content: [{ type: "text", text: "Error: GSD database is not available. Cannot capture memory." }],
|
|
43
|
+
details: { operation: "memory_capture", error: "db_unavailable" },
|
|
44
|
+
isError: true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return executeMemoryCapture(params);
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
// ─── memory_query ───────────────────────────────────────────────────────
|
|
51
|
+
pi.registerTool({
|
|
52
|
+
name: "memory_query",
|
|
53
|
+
label: "Query Memory",
|
|
54
|
+
description: "Search the GSD memory store for relevant memories. Phase 1 uses keyword matching ranked " +
|
|
55
|
+
"by confidence and reinforcement; future phases add semantic (embedding) retrieval.",
|
|
56
|
+
promptSnippet: "Search the GSD memory store by keyword; returns ranked memories with id, category, and content",
|
|
57
|
+
promptGuidelines: [
|
|
58
|
+
"Use memory_query when you need durable project context that may not be in the current prompt.",
|
|
59
|
+
"Provide a short keyword-style query — not a full question.",
|
|
60
|
+
"Use category to narrow results to gotchas, conventions, architecture notes, etc.",
|
|
61
|
+
],
|
|
62
|
+
parameters: Type.Object({
|
|
63
|
+
query: Type.String({ description: "Keyword query (2+ char terms)" }),
|
|
64
|
+
k: Type.Optional(Type.Number({ description: "Max results (default 10, max 50)", minimum: 1, maximum: 50 })),
|
|
65
|
+
category: Type.Optional(Type.Union([
|
|
66
|
+
Type.Literal("architecture"),
|
|
67
|
+
Type.Literal("convention"),
|
|
68
|
+
Type.Literal("gotcha"),
|
|
69
|
+
Type.Literal("preference"),
|
|
70
|
+
Type.Literal("environment"),
|
|
71
|
+
Type.Literal("pattern"),
|
|
72
|
+
], { description: "Restrict results to a single category" })),
|
|
73
|
+
scope: Type.Optional(Type.String({ description: "Only include memories with this scope (e.g. 'project', 'global')" })),
|
|
74
|
+
tag: Type.Optional(Type.String({ description: "Only include memories tagged with this value" })),
|
|
75
|
+
include_superseded: Type.Optional(Type.Boolean({ description: "Include superseded memories (default false)" })),
|
|
76
|
+
reinforce_hits: Type.Optional(Type.Boolean({ description: "Increment hit_count on returned memories (default false)" })),
|
|
77
|
+
}),
|
|
78
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
79
|
+
const ok = await ensureDbOpen();
|
|
80
|
+
if (!ok) {
|
|
81
|
+
return {
|
|
82
|
+
content: [{ type: "text", text: "Error: GSD database is not available. Cannot query memory." }],
|
|
83
|
+
details: { operation: "memory_query", error: "db_unavailable" },
|
|
84
|
+
isError: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return executeMemoryQuery(params);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
// ─── gsd_graph ──────────────────────────────────────────────────────────
|
|
91
|
+
pi.registerTool({
|
|
92
|
+
name: "gsd_graph",
|
|
93
|
+
label: "GSD Knowledge Graph",
|
|
94
|
+
description: "Inspect the relationship graph between memories. mode=query walks supersedes edges from a " +
|
|
95
|
+
"given memoryId; mode=build is a placeholder that future phases will use to rebuild graph " +
|
|
96
|
+
"edges from milestone LEARNINGS artifacts.",
|
|
97
|
+
promptSnippet: "Query the memory relationship graph or trigger a rebuild",
|
|
98
|
+
promptGuidelines: [
|
|
99
|
+
"Use mode=query with a memoryId when you want to see how a memory relates to others.",
|
|
100
|
+
"Phase 1 only exposes supersedes edges; additional relation types arrive in later phases.",
|
|
101
|
+
],
|
|
102
|
+
parameters: Type.Object({
|
|
103
|
+
mode: Type.Union([Type.Literal("build"), Type.Literal("query")], {
|
|
104
|
+
description: "build = recompute graph (placeholder), query = inspect edges",
|
|
105
|
+
}),
|
|
106
|
+
memoryId: Type.Optional(Type.String({ description: "Memory ID (required when mode=query)" })),
|
|
107
|
+
depth: Type.Optional(Type.Number({ description: "Hops to traverse (0–5, default 1)", minimum: 0, maximum: 5 })),
|
|
108
|
+
rel: Type.Optional(Type.Union([
|
|
109
|
+
Type.Literal("related_to"),
|
|
110
|
+
Type.Literal("depends_on"),
|
|
111
|
+
Type.Literal("contradicts"),
|
|
112
|
+
Type.Literal("elaborates"),
|
|
113
|
+
Type.Literal("supersedes"),
|
|
114
|
+
], { description: "Only include edges with this relation type" })),
|
|
115
|
+
}),
|
|
116
|
+
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
117
|
+
const ok = await ensureDbOpen();
|
|
118
|
+
if (!ok) {
|
|
119
|
+
return {
|
|
120
|
+
content: [{ type: "text", text: "Error: GSD database is not available." }],
|
|
121
|
+
details: { operation: "gsd_graph", error: "db_unavailable" },
|
|
122
|
+
isError: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return executeGsdGraph(params);
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
}
|
|
@@ -5,6 +5,7 @@ import { loadEcosystemExtensions } from "../ecosystem/loader.js";
|
|
|
5
5
|
import { registerDbTools } from "./db-tools.js";
|
|
6
6
|
import { registerDynamicTools } from "./dynamic-tools.js";
|
|
7
7
|
import { registerJournalTools } from "./journal-tools.js";
|
|
8
|
+
import { registerMemoryTools } from "./memory-tools.js";
|
|
8
9
|
import { registerQueryTools } from "./query-tools.js";
|
|
9
10
|
import { registerHooks } from "./register-hooks.js";
|
|
10
11
|
import { registerShortcuts } from "./register-shortcuts.js";
|
|
@@ -74,6 +75,7 @@ export function registerGsdExtension(pi) {
|
|
|
74
75
|
["db-tools", () => registerDbTools(pi)],
|
|
75
76
|
["journal-tools", () => registerJournalTools(pi)],
|
|
76
77
|
["query-tools", () => registerQueryTools(pi)],
|
|
78
|
+
["memory-tools", () => registerMemoryTools(pi)],
|
|
77
79
|
["shortcuts", () => registerShortcuts(pi)],
|
|
78
80
|
["hooks", () => registerHooks(pi, ecosystemHandlers)],
|
|
79
81
|
["ecosystem", () => {
|
|
@@ -89,10 +89,23 @@ export async function buildBeforeAgentStartResult(event, ctx) {
|
|
|
89
89
|
}
|
|
90
90
|
let memoryBlock = "";
|
|
91
91
|
try {
|
|
92
|
-
const { formatMemoriesForPrompt, getActiveMemoriesRanked } = await import("../memory-store.js");
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
92
|
+
const { formatMemoriesForPrompt, getActiveMemoriesRanked, queryMemoriesRanked } = await import("../memory-store.js");
|
|
93
|
+
// Always-on "critical" set — small, stable memories that belong in every
|
|
94
|
+
// turn (gotchas, environment, conventions). Ranked by the existing score.
|
|
95
|
+
const CRITICAL_CATEGORIES = new Set(["gotcha", "environment", "convention"]);
|
|
96
|
+
const allRanked = getActiveMemoriesRanked(60);
|
|
97
|
+
const critical = allRanked.filter((m) => CRITICAL_CATEGORIES.has(m.category)).slice(0, 5);
|
|
98
|
+
const criticalIds = new Set(critical.map((m) => m.id));
|
|
99
|
+
// Prompt-relevance set — hybrid FTS5 + (future) semantic retrieval.
|
|
100
|
+
let relevant = [];
|
|
101
|
+
const userPrompt = (event.prompt ?? "").trim();
|
|
102
|
+
if (userPrompt) {
|
|
103
|
+
const hits = queryMemoriesRanked({ query: userPrompt, k: 10 });
|
|
104
|
+
relevant = hits.map((h) => h.memory).filter((m) => !criticalIds.has(m.id));
|
|
105
|
+
}
|
|
106
|
+
const merged = [...critical, ...relevant];
|
|
107
|
+
if (merged.length > 0) {
|
|
108
|
+
const formatted = formatMemoriesForPrompt(merged, 2000);
|
|
96
109
|
if (formatted) {
|
|
97
110
|
memoryBlock = `\n\n${formatted}`;
|
|
98
111
|
}
|