gsd-pi 2.80.0-dev.2be7caf18 → 2.80.0-dev.3065f7bfa
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/README.md +1 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/phases.js +178 -108
- package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
- package/dist/resources/extensions/gsd/auto/run-unit.js +20 -30
- package/dist/resources/extensions/gsd/auto/session.js +8 -0
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
- package/dist/resources/extensions/gsd/auto-prompts.js +90 -15
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
- package/dist/resources/extensions/gsd/auto-start.js +197 -6
- package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
- package/dist/resources/extensions/gsd/auto.js +79 -24
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +42 -2
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
- package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +238 -28
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
- package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
- package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
- package/dist/resources/extensions/gsd/context-budget.js +37 -2
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
- package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
- package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
- package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
- package/dist/resources/extensions/gsd/git-service.js +36 -4
- package/dist/resources/extensions/gsd/gsd-db.js +46 -13
- package/dist/resources/extensions/gsd/guided-flow.js +66 -32
- package/dist/resources/extensions/gsd/memory-store.js +69 -12
- package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
- package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
- package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
- package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
- package/dist/resources/extensions/gsd/unit-runtime.js +11 -0
- package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
- 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 +15 -15
- 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 +15 -15
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +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/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +3 -3
- package/packages/mcp-server/src/workflow-tools.test.ts +9 -2
- package/packages/native/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/dist/agent.d.ts +5 -0
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/dist/index.d.ts +1 -0
- package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/index.js +2 -0
- package/packages/pi-agent-core/dist/index.js.map +1 -1
- package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
- package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
- package/packages/pi-agent-core/dist/token-audit.js +221 -0
- package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
- package/packages/pi-agent-core/dist/types.d.ts +9 -0
- package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/types.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-agent-core/src/agent.ts +8 -0
- package/packages/pi-agent-core/src/index.ts +2 -0
- package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
- package/packages/pi-agent-core/src/token-audit.ts +287 -0
- package/packages/pi-agent-core/src/types.ts +14 -0
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
- package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
- package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +41 -7
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -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 +3 -6
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +3 -3
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +32 -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/hooks-runner.test.js +2 -0
- package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
- package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
- package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +45 -7
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +3 -3
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +5 -5
- package/packages/pi-coding-agent/src/core/extensions/types.ts +35 -1
- package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
- package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
- package/src/resources/extensions/gsd/auto/phases.ts +251 -172
- package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
- package/src/resources/extensions/gsd/auto/run-unit.ts +25 -30
- package/src/resources/extensions/gsd/auto/session.ts +8 -0
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
- package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +95 -14
- package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
- package/src/resources/extensions/gsd/auto-start.ts +230 -9
- package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
- package/src/resources/extensions/gsd/auto.ts +87 -19
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +49 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
- package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +283 -28
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
- package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
- package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
- package/src/resources/extensions/gsd/context-budget.ts +44 -2
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
- package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
- package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
- package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
- package/src/resources/extensions/gsd/git-service.ts +46 -8
- package/src/resources/extensions/gsd/gsd-db.ts +50 -13
- package/src/resources/extensions/gsd/guided-flow.ts +84 -39
- package/src/resources/extensions/gsd/memory-store.ts +77 -12
- package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
- package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
- package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
- package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +417 -10
- package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
- package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
- package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
- package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
- package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
- package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
- package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
- package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
- package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
- package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
- package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
- package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
- package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
- package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
- package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
- package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
- package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
- package/src/resources/extensions/gsd/unit-runtime.ts +11 -0
- package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{3EDDd9ULaybSupPoA_vf- → O2WvnXROue6l7nAp_4dhC}/_ssgManifest.js +0 -0
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
// excluded from this invariant.
|
|
24
24
|
|
|
25
25
|
import { createRequire } from "node:module";
|
|
26
|
+
import { createHash } from "node:crypto";
|
|
26
27
|
import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
|
|
27
28
|
import { dirname } from "node:path";
|
|
28
29
|
import type { Decision, Requirement, GateRow, GateId, GateScope, GateStatus, GateVerdict } from "./types.js";
|
|
@@ -78,6 +79,8 @@ import {
|
|
|
78
79
|
applyMigrationV22QualityGateRepair,
|
|
79
80
|
applyMigrationV23MilestoneQueue,
|
|
80
81
|
applyMigrationV26MilestoneCommitAttributions,
|
|
82
|
+
applyMigrationV27ArtifactHash,
|
|
83
|
+
applyMigrationV28MemoryLastHitAt,
|
|
81
84
|
} from "./db-migration-steps.js";
|
|
82
85
|
import { isMemoriesFtsAvailableSchema, tryCreateMemoriesFtsSchema } from "./db-memory-fts-schema.js";
|
|
83
86
|
import { createDbOpenState, type DbOpenPhase } from "./db-open-state.js";
|
|
@@ -106,7 +109,7 @@ const providerLoader = createSqliteProviderLoader({
|
|
|
106
109
|
writeStderr: (message: string) => process.stderr.write(message),
|
|
107
110
|
});
|
|
108
111
|
|
|
109
|
-
export const SCHEMA_VERSION =
|
|
112
|
+
export const SCHEMA_VERSION = 28;
|
|
110
113
|
|
|
111
114
|
function initSchema(db: DbAdapter, fileBacked: boolean): void {
|
|
112
115
|
if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
|
|
@@ -335,6 +338,16 @@ function migrateSchema(db: DbAdapter): void {
|
|
|
335
338
|
recordSchemaVersion(db, 26);
|
|
336
339
|
}
|
|
337
340
|
|
|
341
|
+
if (currentVersion < 27) {
|
|
342
|
+
applyMigrationV27ArtifactHash(db);
|
|
343
|
+
recordSchemaVersion(db, 27);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (currentVersion < 28) {
|
|
347
|
+
applyMigrationV28MemoryLastHitAt(db);
|
|
348
|
+
recordSchemaVersion(db, 28);
|
|
349
|
+
}
|
|
350
|
+
|
|
338
351
|
db.exec("COMMIT");
|
|
339
352
|
} catch (err) {
|
|
340
353
|
db.exec("ROLLBACK");
|
|
@@ -913,9 +926,10 @@ export function insertArtifact(a: {
|
|
|
913
926
|
full_content: string;
|
|
914
927
|
}): void {
|
|
915
928
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
929
|
+
const contentHash = createHash("sha256").update(a.full_content).digest("hex");
|
|
916
930
|
currentDb.prepare(
|
|
917
|
-
`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
|
|
918
|
-
VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at)`,
|
|
931
|
+
`INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash)
|
|
932
|
+
VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at, :content_hash)`,
|
|
919
933
|
).run({
|
|
920
934
|
":path": a.path,
|
|
921
935
|
":artifact_type": a.artifact_type,
|
|
@@ -924,6 +938,7 @@ export function insertArtifact(a: {
|
|
|
924
938
|
":task_id": a.task_id,
|
|
925
939
|
":full_content": a.full_content,
|
|
926
940
|
":imported_at": new Date().toISOString(),
|
|
941
|
+
":content_hash": contentHash,
|
|
927
942
|
});
|
|
928
943
|
}
|
|
929
944
|
|
|
@@ -1795,6 +1810,13 @@ export function reconcileWorktreeDb(
|
|
|
1795
1810
|
const hasEscalationAwaiting = wtTaskInfo.some((col) => col["name"] === "escalation_awaiting_review");
|
|
1796
1811
|
const hasEscalationArtifact = wtTaskInfo.some((col) => col["name"] === "escalation_artifact_path");
|
|
1797
1812
|
const hasEscalationOverride = wtTaskInfo.some((col) => col["name"] === "escalation_override_applied_at");
|
|
1813
|
+
const wtArtifactInfo = adapter.prepare("PRAGMA wt.table_info('artifacts')").all();
|
|
1814
|
+
const hasArtifactContentHash = wtArtifactInfo.some((col) => col["name"] === "content_hash");
|
|
1815
|
+
const wtMemoryInfo = adapter.prepare("PRAGMA wt.table_info('memories')").all();
|
|
1816
|
+
const hasMemoryScope = wtMemoryInfo.some((col) => col["name"] === "scope");
|
|
1817
|
+
const hasMemoryTags = wtMemoryInfo.some((col) => col["name"] === "tags");
|
|
1818
|
+
const hasMemoryStructuredFields = wtMemoryInfo.some((col) => col["name"] === "structured_fields");
|
|
1819
|
+
const hasMemoryLastHitAt = wtMemoryInfo.some((col) => col["name"] === "last_hit_at");
|
|
1798
1820
|
|
|
1799
1821
|
const decConf = adapter.prepare(
|
|
1800
1822
|
`SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${
|
|
@@ -1842,12 +1864,17 @@ export function reconcileWorktreeDb(
|
|
|
1842
1864
|
FROM wt.requirements
|
|
1843
1865
|
`).run());
|
|
1844
1866
|
|
|
1867
|
+
// V27: preserve content_hash. If the worktree predates V27 (no column),
|
|
1868
|
+
// fall back to the main DB's existing hash so reconcile doesn't null
|
|
1869
|
+
// out integrity fingerprints on artifacts that were unchanged in wt.
|
|
1845
1870
|
merged.artifacts = countChanges(adapter.prepare(`
|
|
1846
1871
|
INSERT OR REPLACE INTO artifacts (
|
|
1847
|
-
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1872
|
+
path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at, content_hash
|
|
1848
1873
|
)
|
|
1849
|
-
SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
|
|
1850
|
-
|
|
1874
|
+
SELECT w.path, w.artifact_type, w.milestone_id, w.slice_id, w.task_id, w.full_content, w.imported_at,
|
|
1875
|
+
${hasArtifactContentHash ? "w.content_hash" : "m.content_hash"}
|
|
1876
|
+
FROM wt.artifacts w
|
|
1877
|
+
LEFT JOIN artifacts m ON m.path = w.path
|
|
1851
1878
|
`).run());
|
|
1852
1879
|
|
|
1853
1880
|
// Merge milestones — worktree may have updated status/planning fields.
|
|
@@ -1949,15 +1976,25 @@ export function reconcileWorktreeDb(
|
|
|
1949
1976
|
LEFT JOIN tasks m ON m.milestone_id = w.milestone_id AND m.slice_id = w.slice_id AND m.id = w.id
|
|
1950
1977
|
`).run());
|
|
1951
1978
|
|
|
1952
|
-
// Merge memories — keep worktree-learned insights
|
|
1979
|
+
// Merge memories — keep worktree-learned insights.
|
|
1980
|
+
// V18 (scope, tags), V21 (structured_fields), V28 (last_hit_at): for each
|
|
1981
|
+
// column the wt may not yet have (older worktree DB), fall back to the
|
|
1982
|
+
// main DB's existing value via LEFT JOIN so reconcile never silently
|
|
1983
|
+
// resets these fields to defaults on rows that already had them.
|
|
1953
1984
|
merged.memories = countChanges(adapter.prepare(`
|
|
1954
1985
|
INSERT OR REPLACE INTO memories (
|
|
1955
1986
|
seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1956
|
-
created_at, updated_at, superseded_by, hit_count
|
|
1987
|
+
created_at, updated_at, superseded_by, hit_count,
|
|
1988
|
+
scope, tags, structured_fields, last_hit_at
|
|
1957
1989
|
)
|
|
1958
|
-
SELECT seq, id, category, content, confidence, source_unit_type, source_unit_id,
|
|
1959
|
-
created_at, updated_at, superseded_by, hit_count
|
|
1960
|
-
|
|
1990
|
+
SELECT w.seq, w.id, w.category, w.content, w.confidence, w.source_unit_type, w.source_unit_id,
|
|
1991
|
+
w.created_at, w.updated_at, w.superseded_by, w.hit_count,
|
|
1992
|
+
${hasMemoryScope ? "w.scope" : "COALESCE(m.scope, 'project')"},
|
|
1993
|
+
${hasMemoryTags ? "w.tags" : "COALESCE(m.tags, '[]')"},
|
|
1994
|
+
${hasMemoryStructuredFields ? "w.structured_fields" : "m.structured_fields"},
|
|
1995
|
+
${hasMemoryLastHitAt ? "w.last_hit_at" : "m.last_hit_at"}
|
|
1996
|
+
FROM wt.memories w
|
|
1997
|
+
LEFT JOIN memories m ON m.id = w.id
|
|
1961
1998
|
`).run());
|
|
1962
1999
|
|
|
1963
2000
|
// Merge verification evidence — append-only, use INSERT OR IGNORE to avoid duplicates
|
|
@@ -3062,8 +3099,8 @@ export function updateMemoryContentRow(
|
|
|
3062
3099
|
export function incrementMemoryHitCount(id: string, updatedAt: string): void {
|
|
3063
3100
|
if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
|
|
3064
3101
|
currentDb.prepare(
|
|
3065
|
-
"UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at WHERE id = :id",
|
|
3066
|
-
).run({ ":updated_at": updatedAt, ":id": id });
|
|
3102
|
+
"UPDATE memories SET hit_count = hit_count + 1, updated_at = :updated_at, last_hit_at = :last_hit_at WHERE id = :id",
|
|
3103
|
+
).run({ ":updated_at": updatedAt, ":last_hit_at": updatedAt, ":id": id });
|
|
3067
3104
|
}
|
|
3068
3105
|
|
|
3069
3106
|
export function supersedeMemoryRow(oldId: string, newId: string, updatedAt: string): void {
|
|
@@ -86,6 +86,36 @@ export {
|
|
|
86
86
|
import { logWarning } from "./workflow-logger.js";
|
|
87
87
|
import { deleteRuntimeKv } from "./db/runtime-kv.js";
|
|
88
88
|
import { PAUSED_SESSION_KV_KEY } from "./interrupted-session.js";
|
|
89
|
+
import { buildWorkflowDispatchContent } from "./workflow-protocol.js";
|
|
90
|
+
import { isFullGsdToolSurfaceRequested, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "./bootstrap/register-hooks.js";
|
|
91
|
+
|
|
92
|
+
type AutoStartOptions = Parameters<typeof startAutoDetached>[4];
|
|
93
|
+
type AutoStartLauncher = typeof startAutoDetached;
|
|
94
|
+
|
|
95
|
+
function scheduleAutoStartAfterIdle(
|
|
96
|
+
ctx: ExtensionCommandContext,
|
|
97
|
+
pi: ExtensionAPI,
|
|
98
|
+
basePath: string,
|
|
99
|
+
verboseMode: boolean,
|
|
100
|
+
options?: AutoStartOptions,
|
|
101
|
+
launch: AutoStartLauncher = startAutoDetached,
|
|
102
|
+
): void {
|
|
103
|
+
const waitForIdle =
|
|
104
|
+
typeof (ctx as { waitForIdle?: unknown }).waitForIdle === "function"
|
|
105
|
+
? ctx.waitForIdle.bind(ctx)
|
|
106
|
+
: async () => {};
|
|
107
|
+
void waitForIdle()
|
|
108
|
+
.then(() => {
|
|
109
|
+
setTimeout(() => launch(ctx, pi, basePath, verboseMode, options), 0);
|
|
110
|
+
})
|
|
111
|
+
.catch((err) => {
|
|
112
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
113
|
+
ctx.ui.notify(`Auto-start failed while waiting for the prior turn to settle: ${message}`, "error");
|
|
114
|
+
logWarning("guided", `auto-start idle wait failed: ${message}`);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const _scheduleAutoStartAfterIdleForTest = scheduleAutoStartAfterIdle;
|
|
89
119
|
|
|
90
120
|
// ─── Scope-based validator wrappers ──────────────────────────────────────────
|
|
91
121
|
// These thin wrappers accept a MilestoneScope so callers that already hold a
|
|
@@ -446,7 +476,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
446
476
|
|
|
447
477
|
if (!hasPendingDeepStage(prefs, entry.basePath)) {
|
|
448
478
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
449
|
-
|
|
479
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
450
480
|
return true;
|
|
451
481
|
}
|
|
452
482
|
|
|
@@ -479,7 +509,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
479
509
|
entry.ctx.ui.notify(result.reason, result.level);
|
|
480
510
|
} else if (hasPendingDeepStage(prefs, entry.basePath)) {
|
|
481
511
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
482
|
-
|
|
512
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
483
513
|
return true;
|
|
484
514
|
}
|
|
485
515
|
return false;
|
|
@@ -487,7 +517,7 @@ async function dispatchNextDeepProjectSetupStage(entry: PendingDeepProjectSetupE
|
|
|
487
517
|
|
|
488
518
|
if (!USER_DRIVEN_DEEP_SETUP_UNITS.has(result.unitType)) {
|
|
489
519
|
pendingDeepProjectSetupMap.delete(entry.basePath);
|
|
490
|
-
|
|
520
|
+
scheduleAutoStartAfterIdle(entry.ctx, entry.pi, entry.basePath, false, { step: entry.step });
|
|
491
521
|
return true;
|
|
492
522
|
}
|
|
493
523
|
|
|
@@ -693,7 +723,7 @@ export function checkAutoStartAfterDiscuss(): boolean {
|
|
|
693
723
|
|
|
694
724
|
pendingAutoStartMap.delete(basePath);
|
|
695
725
|
ctx.ui.notify(`Milestone ${milestoneId} ready.`, "success");
|
|
696
|
-
|
|
726
|
+
scheduleAutoStartAfterIdle(ctx, pi, basePath, false, { step });
|
|
697
727
|
return true;
|
|
698
728
|
}
|
|
699
729
|
|
|
@@ -1034,46 +1064,61 @@ async function dispatchWorkflow(
|
|
|
1034
1064
|
}
|
|
1035
1065
|
}
|
|
1036
1066
|
|
|
1037
|
-
// Scope tools for
|
|
1067
|
+
// Scope tools for guided workflow turns (#2949, token-consumption savings).
|
|
1038
1068
|
// Providers with grammar-based constrained decoding (xAI/Grok) return
|
|
1039
1069
|
// "Grammar is too complex" when the combined tool schema is too large.
|
|
1040
|
-
//
|
|
1041
|
-
//
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
const currentTools = pi.getActiveTools();
|
|
1045
|
-
savedTools = currentTools;
|
|
1046
|
-
// Keep all non-GSD tools (builtins, other extensions) and only the
|
|
1047
|
-
// GSD tools on the discuss allowlist.
|
|
1048
|
-
const scopedTools = currentTools.filter(
|
|
1049
|
-
(t) => !t.startsWith("gsd_") || DISCUSS_TOOLS_ALLOWLIST.includes(t),
|
|
1050
|
-
);
|
|
1051
|
-
pi.setActiveTools(scopedTools);
|
|
1052
|
-
debugLog("discuss-tool-scoping", {
|
|
1053
|
-
unitType,
|
|
1054
|
-
before: currentTools.length,
|
|
1055
|
-
after: scopedTools.length,
|
|
1056
|
-
removed: currentTools.length - scopedTools.length,
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
1070
|
+
// Guided workflow turns only need the active unit's tool surface; strip
|
|
1071
|
+
// unrelated GSD tools and broad non-GSD tools for this queued turn, then
|
|
1072
|
+
// restore so the narrowed surface does not leak into future dispatches.
|
|
1073
|
+
let savedTools: ReturnType<typeof scopeGsdWorkflowToolsForDispatch> = null;
|
|
1059
1074
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1075
|
+
try {
|
|
1076
|
+
const currentTools = pi.getActiveTools();
|
|
1077
|
+
savedTools = {
|
|
1078
|
+
tools: currentTools,
|
|
1079
|
+
visibleSkills: typeof pi.getVisibleSkills === "function" ? pi.getVisibleSkills() : undefined,
|
|
1080
|
+
restoreVisibleSkills: typeof pi.setVisibleSkills === "function",
|
|
1081
|
+
};
|
|
1082
|
+
if (unitType?.startsWith("discuss-") && !isFullGsdToolSurfaceRequested()) {
|
|
1083
|
+
// Keep all non-GSD tools (builtins, other extensions) and only the
|
|
1084
|
+
// GSD tools on the discuss allowlist.
|
|
1085
|
+
const scopedTools = currentTools.filter(
|
|
1086
|
+
(t) => !t.startsWith("gsd_") || DISCUSS_TOOLS_ALLOWLIST.includes(t),
|
|
1087
|
+
);
|
|
1088
|
+
pi.setActiveTools(scopedTools);
|
|
1089
|
+
const scopedState = scopeGsdWorkflowToolsForDispatch(pi, unitType);
|
|
1090
|
+
savedTools = {
|
|
1091
|
+
tools: currentTools,
|
|
1092
|
+
visibleSkills: scopedState?.visibleSkills ?? savedTools.visibleSkills,
|
|
1093
|
+
restoreVisibleSkills: scopedState?.restoreVisibleSkills ?? savedTools.restoreVisibleSkills,
|
|
1094
|
+
};
|
|
1095
|
+
debugLog("discuss-tool-scoping", {
|
|
1096
|
+
unitType,
|
|
1097
|
+
before: currentTools.length,
|
|
1098
|
+
after: pi.getActiveTools().length,
|
|
1099
|
+
removed: currentTools.length - pi.getActiveTools().length,
|
|
1100
|
+
});
|
|
1101
|
+
} else {
|
|
1102
|
+
savedTools = scopeGsdWorkflowToolsForDispatch(pi, unitType) ?? savedTools;
|
|
1103
|
+
}
|
|
1062
1104
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
customType,
|
|
1066
|
-
content: `Read the following GSD workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${note}`,
|
|
1067
|
-
display: false,
|
|
1068
|
-
},
|
|
1069
|
-
{ triggerTurn: true },
|
|
1070
|
-
);
|
|
1105
|
+
const workflowPath = process.env.GSD_WORKFLOW_PATH ?? join(gsdHome(), "agent", "GSD-WORKFLOW.md");
|
|
1106
|
+
const workflow = readFileSync(workflowPath, "utf-8");
|
|
1071
1107
|
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1108
|
+
pi.sendMessage(
|
|
1109
|
+
{
|
|
1110
|
+
customType,
|
|
1111
|
+
content: buildWorkflowDispatchContent({ workflow, workflowPath, task: note }),
|
|
1112
|
+
display: false,
|
|
1113
|
+
},
|
|
1114
|
+
{ triggerTurn: true },
|
|
1115
|
+
);
|
|
1116
|
+
} finally {
|
|
1117
|
+
// Restore full tool set after the message is queued. The LLM turn has
|
|
1118
|
+
// already captured the scoped set — restoring prevents the narrowed
|
|
1119
|
+
// tools from leaking into subsequent dispatches (#3628). The finally
|
|
1120
|
+
// block ensures restoration even if sendMessage throws.
|
|
1121
|
+
restoreGsdWorkflowTools(pi, savedTools);
|
|
1077
1122
|
}
|
|
1078
1123
|
}
|
|
1079
1124
|
|
|
@@ -44,6 +44,8 @@ export interface Memory {
|
|
|
44
44
|
* decisions table (Step 5) with the original scope/decision/choice/etc.
|
|
45
45
|
*/
|
|
46
46
|
structured_fields: Record<string, unknown> | null;
|
|
47
|
+
/** ISO timestamp of the most recent memory_query hit. NULL until first hit. */
|
|
48
|
+
last_hit_at: string | null;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
export type MemoryActionCreate = {
|
|
@@ -100,6 +102,27 @@ const CATEGORY_PRIORITY: Record<string, number> = {
|
|
|
100
102
|
preference: 5,
|
|
101
103
|
};
|
|
102
104
|
|
|
105
|
+
// ─── Scoring Helpers ─────────────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Time-decay factor for memory relevance scoring.
|
|
109
|
+
* Returns 1.0 for never-hit or recently-hit memories, decaying linearly to
|
|
110
|
+
* 0.7 for memories not accessed in 90+ days. Floor at 0.7 keeps old-but-valid
|
|
111
|
+
* knowledge from being fully suppressed.
|
|
112
|
+
*
|
|
113
|
+
* Defensive parsing: invalid timestamp strings (NaN from Date.parse) are
|
|
114
|
+
* treated as "no decay" rather than propagating NaN into score arithmetic.
|
|
115
|
+
* Future timestamps (clock skew, manual DB edits) clamp to daysAgo=0 so the
|
|
116
|
+
* factor stays in the documented [0.7, 1.0] contract.
|
|
117
|
+
*/
|
|
118
|
+
export function memoryDecayFactor(lastHitAt: string | null): number {
|
|
119
|
+
if (!lastHitAt) return 1.0;
|
|
120
|
+
const ts = Date.parse(lastHitAt);
|
|
121
|
+
if (!Number.isFinite(ts)) return 1.0;
|
|
122
|
+
const daysAgo = Math.max(0, (Date.now() - ts) / 86_400_000);
|
|
123
|
+
return Math.max(0.7, 1.0 - 0.3 * Math.min(1.0, daysAgo / 90));
|
|
124
|
+
}
|
|
125
|
+
|
|
103
126
|
// ─── Row Mapping ────────────────────────────────────────────────────────────
|
|
104
127
|
|
|
105
128
|
function rowToMemory(row: Record<string, unknown>): Memory {
|
|
@@ -118,6 +141,7 @@ function rowToMemory(row: Record<string, unknown>): Memory {
|
|
|
118
141
|
scope: (row['scope'] as string) ?? 'project',
|
|
119
142
|
tags: parseTags(row['tags']),
|
|
120
143
|
structured_fields: parseStructuredFields(row['structured_fields']),
|
|
144
|
+
last_hit_at: (row['last_hit_at'] as string | null) ?? null,
|
|
121
145
|
};
|
|
122
146
|
}
|
|
123
147
|
|
|
@@ -233,15 +257,39 @@ export function queryMemoriesRanked(opts: QueryMemoriesOptions): RankedMemory[]
|
|
|
233
257
|
: [];
|
|
234
258
|
|
|
235
259
|
if (keywordHits.length === 0 && semanticHits.length === 0 && !trimmedQuery) {
|
|
236
|
-
// No query at all —
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
260
|
+
// No query at all — return top-k by decay-aware ranked score.
|
|
261
|
+
//
|
|
262
|
+
// Build the candidate pool from a direct SQL query that honors the
|
|
263
|
+
// request's activeClause (i.e. include_superseded). Using
|
|
264
|
+
// getActiveMemoriesRanked here would silently drop superseded rows even
|
|
265
|
+
// when the caller explicitly opted in, and would slice by raw score
|
|
266
|
+
// before decay/filters had a chance to reorder.
|
|
267
|
+
const candidatePool = Math.min(Math.max(k * 5, 50), 500);
|
|
268
|
+
const rows = adapter
|
|
269
|
+
.prepare(
|
|
270
|
+
`SELECT * FROM memories ${activeClause}
|
|
271
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
|
|
272
|
+
LIMIT :limit`,
|
|
273
|
+
)
|
|
274
|
+
.all({ ':limit': candidatePool });
|
|
275
|
+
|
|
276
|
+
const ranked: RankedMemory[] = [];
|
|
277
|
+
for (const row of rows) {
|
|
278
|
+
const memory = rowToMemory(row);
|
|
279
|
+
if (!passesFilters(memory, opts)) continue;
|
|
280
|
+
const decay = memoryDecayFactor(memory.last_hit_at);
|
|
281
|
+
const score = memory.confidence * (1 + memory.hit_count * 0.1) * decay;
|
|
282
|
+
ranked.push({
|
|
283
|
+
memory,
|
|
284
|
+
score,
|
|
285
|
+
keywordRank: null,
|
|
286
|
+
semanticRank: null,
|
|
287
|
+
confidenceBoost: score,
|
|
288
|
+
reason: 'ranked' as const,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
ranked.sort((a, b) => b.score - a.score);
|
|
292
|
+
return ranked.slice(0, k);
|
|
245
293
|
}
|
|
246
294
|
|
|
247
295
|
// 3) Reciprocal rank fusion — each hit contributes 1/(rrfK + rank).
|
|
@@ -275,7 +323,7 @@ export function queryMemoriesRanked(opts: QueryMemoriesOptions): RankedMemory[]
|
|
|
275
323
|
const ranked: RankedMemory[] = [];
|
|
276
324
|
for (const entry of fused.values()) {
|
|
277
325
|
if (!passesFilters(entry.memory, opts)) continue;
|
|
278
|
-
const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1);
|
|
326
|
+
const boost = entry.memory.confidence * (1 + entry.memory.hit_count * 0.1) * memoryDecayFactor(entry.memory.last_hit_at);
|
|
279
327
|
const reason: RankedMemory['reason'] =
|
|
280
328
|
entry.kwRank != null && entry.semRank != null
|
|
281
329
|
? 'both'
|
|
@@ -313,6 +361,8 @@ function passesFilters(memory: Memory, filters: QueryMemoriesFilters): boolean {
|
|
|
313
361
|
return true;
|
|
314
362
|
}
|
|
315
363
|
|
|
364
|
+
let ftsWarningEmitted = false;
|
|
365
|
+
|
|
316
366
|
function keywordSearch(
|
|
317
367
|
adapter: NonNullable<ReturnType<typeof _getAdapter>>,
|
|
318
368
|
rawQuery: string,
|
|
@@ -340,14 +390,29 @@ function keywordSearch(
|
|
|
340
390
|
}
|
|
341
391
|
}
|
|
342
392
|
|
|
343
|
-
// LIKE fallback — scans
|
|
393
|
+
// LIKE fallback — scans a capped candidate pool.
|
|
394
|
+
if (!ftsWarningEmitted) {
|
|
395
|
+
ftsWarningEmitted = true;
|
|
396
|
+
logWarning('memory-store', 'FTS5 unavailable — using LIKE fallback scan (consider enabling FTS5)');
|
|
397
|
+
}
|
|
398
|
+
|
|
344
399
|
const terms = rawQuery
|
|
345
400
|
.toLowerCase()
|
|
346
401
|
.split(/[^a-z0-9_]+/)
|
|
347
402
|
.filter((t) => t.length >= 2);
|
|
348
403
|
if (terms.length === 0) return [];
|
|
349
404
|
|
|
350
|
-
const
|
|
405
|
+
const preScanCap = Math.min(limit * 20, 2000);
|
|
406
|
+
// ORDER BY confidence-weighted hit_count DESC so the cap keeps the most
|
|
407
|
+
// valuable candidates instead of the oldest-by-rowid (which would silently
|
|
408
|
+
// exclude recently-stored memories on tables larger than preScanCap).
|
|
409
|
+
const rows = adapter
|
|
410
|
+
.prepare(
|
|
411
|
+
`SELECT * FROM memories ${activeClause}
|
|
412
|
+
ORDER BY (confidence * (1.0 + hit_count * 0.1)) DESC
|
|
413
|
+
LIMIT :preScanCap`,
|
|
414
|
+
)
|
|
415
|
+
.all({ ':preScanCap': preScanCap });
|
|
351
416
|
const scored: Array<{ memory: Memory; score: number }> = [];
|
|
352
417
|
for (const row of rows) {
|
|
353
418
|
const memory = rowToMemory(row);
|
|
@@ -1183,6 +1183,33 @@ export function nativeRmForce(basePath: string, paths: string[]): void {
|
|
|
1183
1183
|
}
|
|
1184
1184
|
}
|
|
1185
1185
|
|
|
1186
|
+
function runGitWorktreeAdd(
|
|
1187
|
+
basePath: string,
|
|
1188
|
+
wtPath: string,
|
|
1189
|
+
branch: string,
|
|
1190
|
+
createBranch?: boolean,
|
|
1191
|
+
startPoint?: string,
|
|
1192
|
+
): void {
|
|
1193
|
+
if (createBranch) {
|
|
1194
|
+
const branchRef = gitExec(basePath, ["show-ref", "--verify", `refs/heads/${branch}`], true);
|
|
1195
|
+
if (branchRef) {
|
|
1196
|
+
gitExec(basePath, ["worktree", "add", wtPath, branch]);
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
gitExec(basePath, ["worktree", "add", "-b", branch, wtPath, startPoint ?? "HEAD"]);
|
|
1200
|
+
} else {
|
|
1201
|
+
gitExec(basePath, ["worktree", "add", wtPath, branch]);
|
|
1202
|
+
}
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
export function assertWorktreeMaterialized(wtPath: string): void {
|
|
1206
|
+
if (existsSync(join(wtPath, ".git"))) return;
|
|
1207
|
+
throw new GSDError(
|
|
1208
|
+
GSD_GIT_ERROR,
|
|
1209
|
+
`git worktree add did not materialize a valid worktree at ${wtPath}: missing .git file`,
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1186
1213
|
/**
|
|
1187
1214
|
* Add a new git worktree.
|
|
1188
1215
|
* Native: libgit2 worktree API.
|
|
@@ -1198,14 +1225,20 @@ export function nativeWorktreeAdd(
|
|
|
1198
1225
|
const native = loadNative();
|
|
1199
1226
|
if (native) {
|
|
1200
1227
|
native.gitWorktreeAdd(basePath, wtPath, branch, createBranch, startPoint);
|
|
1201
|
-
|
|
1228
|
+
try {
|
|
1229
|
+
assertWorktreeMaterialized(wtPath);
|
|
1230
|
+
return;
|
|
1231
|
+
} catch {
|
|
1232
|
+
rmSync(wtPath, { recursive: true, force: true });
|
|
1233
|
+
gitExec(basePath, ["worktree", "prune"], true);
|
|
1234
|
+
runGitWorktreeAdd(basePath, wtPath, branch, createBranch, startPoint);
|
|
1235
|
+
assertWorktreeMaterialized(wtPath);
|
|
1236
|
+
return;
|
|
1237
|
+
}
|
|
1202
1238
|
}
|
|
1203
1239
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
} else {
|
|
1207
|
-
gitExec(basePath, ["worktree", "add", wtPath, branch]);
|
|
1208
|
-
}
|
|
1240
|
+
runGitWorktreeAdd(basePath, wtPath, branch, createBranch, startPoint);
|
|
1241
|
+
assertWorktreeMaterialized(wtPath);
|
|
1209
1242
|
}
|
|
1210
1243
|
|
|
1211
1244
|
/**
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/orphan-stash-audit.ts
|
|
2
|
+
// Startup sweep for orphaned gsd-preflight-stash entries left behind by
|
|
3
|
+
// interrupted milestone merges (#5538-followup).
|
|
4
|
+
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
7
|
+
|
|
8
|
+
export interface OrphanPreflightStashAuditResult {
|
|
9
|
+
applied: Array<{ milestoneId: string; stashRef: string }>;
|
|
10
|
+
warnings: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Recognize the "already restored" failure mode of `git stash apply`.
|
|
15
|
+
*
|
|
16
|
+
* When a preflight stash captured untracked files via `--include-untracked`
|
|
17
|
+
* and those files are now present in the working tree (e.g. a prior audit
|
|
18
|
+
* run already applied this stash), `git stash apply` aborts with
|
|
19
|
+
* `<path> already exists, no checkout` and exits non-zero. That is the
|
|
20
|
+
* idempotent steady state for this audit, not a recovery failure — treat
|
|
21
|
+
* it as a no-op so repeated GSD startups stop spamming the user with
|
|
22
|
+
* warnings about stashes that have already been restored (#5538-followup
|
|
23
|
+
* peer-review feedback).
|
|
24
|
+
*/
|
|
25
|
+
function _isAlreadyRestoredApplyError(err: unknown): boolean {
|
|
26
|
+
if (!err || typeof err !== "object") return false;
|
|
27
|
+
const stderr = (err as { stderr?: unknown }).stderr;
|
|
28
|
+
const stderrText = typeof stderr === "string" ? stderr : stderr instanceof Uint8Array ? Buffer.from(stderr).toString("utf-8") : "";
|
|
29
|
+
if (stderrText && /already exists, no checkout/i.test(stderrText)) return true;
|
|
30
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
31
|
+
return /already exists, no checkout/i.test(message);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { _isAlreadyRestoredApplyError };
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Audit `git stash list` for orphaned `gsd-preflight-stash:M00x:*` entries.
|
|
38
|
+
*
|
|
39
|
+
* The matching merge code in `phases.ts` previously skipped the postflight
|
|
40
|
+
* pop whenever `mergeAndExit` threw, leaking the user's pre-merge working
|
|
41
|
+
* tree into the stash list. For every preflight-stash entry whose milestone
|
|
42
|
+
* is now complete (per the supplied callback), `git stash apply` is invoked —
|
|
43
|
+
* NOT `pop`. The stash entry stays in the list so the user retains a backup
|
|
44
|
+
* if the apply produces unexpected merge results. Idempotent across repeated
|
|
45
|
+
* startup runs.
|
|
46
|
+
*
|
|
47
|
+
* Failures are best-effort: a list error (no repo, git unavailable) returns
|
|
48
|
+
* an empty result. An apply error becomes a warning the user sees alongside
|
|
49
|
+
* the existing orphan-branch audit messages — startup continues.
|
|
50
|
+
*/
|
|
51
|
+
export function auditOrphanedPreflightStashes(
|
|
52
|
+
basePath: string,
|
|
53
|
+
isMilestoneComplete: (milestoneId: string) => boolean,
|
|
54
|
+
): OrphanPreflightStashAuditResult {
|
|
55
|
+
const result: OrphanPreflightStashAuditResult = { applied: [], warnings: [] };
|
|
56
|
+
|
|
57
|
+
let listOutput: string;
|
|
58
|
+
try {
|
|
59
|
+
listOutput = execFileSync(
|
|
60
|
+
"git",
|
|
61
|
+
["stash", "list", "--format=%gd%x00%s"],
|
|
62
|
+
{
|
|
63
|
+
cwd: basePath,
|
|
64
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
65
|
+
encoding: "utf-8",
|
|
66
|
+
env: GIT_NO_PROMPT_ENV,
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
} catch {
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const MARKER_RE = /\bgsd-preflight-stash:([A-Za-z0-9_-]+):/;
|
|
74
|
+
for (const line of listOutput.split("\n")) {
|
|
75
|
+
const sep = line.indexOf("\x00");
|
|
76
|
+
if (sep < 0) continue;
|
|
77
|
+
const ref = line.slice(0, sep);
|
|
78
|
+
const subject = line.slice(sep + 1);
|
|
79
|
+
if (!ref || !subject) continue;
|
|
80
|
+
|
|
81
|
+
const match = MARKER_RE.exec(subject);
|
|
82
|
+
if (!match) continue;
|
|
83
|
+
const milestoneId = match[1];
|
|
84
|
+
|
|
85
|
+
let complete = false;
|
|
86
|
+
try {
|
|
87
|
+
complete = isMilestoneComplete(milestoneId);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
result.warnings.push(
|
|
90
|
+
`Could not determine completion status for ${milestoneId} during preflight-stash audit: ${err instanceof Error ? err.message : String(err)}.`,
|
|
91
|
+
);
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (!complete) continue;
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
execFileSync("git", ["stash", "apply", "--quiet", ref], {
|
|
98
|
+
cwd: basePath,
|
|
99
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
100
|
+
encoding: "utf-8",
|
|
101
|
+
env: GIT_NO_PROMPT_ENV,
|
|
102
|
+
});
|
|
103
|
+
result.applied.push({ milestoneId, stashRef: ref });
|
|
104
|
+
} catch (err) {
|
|
105
|
+
// Idempotent steady state: stash was already applied in a prior audit
|
|
106
|
+
// run; the files exist and `git stash apply` refuses to overwrite.
|
|
107
|
+
// Skip silently so repeat runs are no-ops.
|
|
108
|
+
if (_isAlreadyRestoredApplyError(err)) continue;
|
|
109
|
+
result.warnings.push(
|
|
110
|
+
`Could not apply orphaned preflight stash ${ref} (milestone ${milestoneId}): ${err instanceof Error ? err.message : String(err)}. ` +
|
|
111
|
+
`Run \`git stash apply ${ref}\` manually to restore your pre-merge changes.`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
@@ -21,7 +21,7 @@ import { join, dirname } from "node:path";
|
|
|
21
21
|
import { fileURLToPath } from "node:url";
|
|
22
22
|
import { gsdRoot } from "./paths.js";
|
|
23
23
|
import { createWorktree, worktreePath } from "./worktree-manager.js";
|
|
24
|
-
import { autoWorktreeBranch, runWorktreePostCreateHook, syncGsdStateToWorktree } from "./auto-worktree.js";
|
|
24
|
+
import { autoWorktreeBranch, fastForwardReusedMilestoneBranchIfSafe, runWorktreePostCreateHook, syncGsdStateToWorktree } from "./auto-worktree.js";
|
|
25
25
|
import { nativeBranchExists } from "./native-git-bridge.js";
|
|
26
26
|
import { readIntegrationBranch } from "./git-service.js";
|
|
27
27
|
import { resolveParallelConfig } from "./preferences.js";
|
|
@@ -471,7 +471,7 @@ export async function startParallel(
|
|
|
471
471
|
// Create the worktree (without chdir — coordinator stays in project root)
|
|
472
472
|
let wtPath: string;
|
|
473
473
|
try {
|
|
474
|
-
wtPath =
|
|
474
|
+
wtPath = _createMilestoneWorktree(basePath, mid);
|
|
475
475
|
} catch (e) {
|
|
476
476
|
logWarning("parallel", `createMilestoneWorktree fallback for ${mid}: ${(e as Error).message}`);
|
|
477
477
|
wtPath = worktreePath(basePath, mid);
|
|
@@ -532,13 +532,23 @@ export async function startParallel(
|
|
|
532
532
|
/**
|
|
533
533
|
* Create a git worktree for a milestone without changing the coordinator's cwd.
|
|
534
534
|
* Uses milestone/<MID> branch naming (same as auto-worktree.ts).
|
|
535
|
+
*
|
|
536
|
+
* Exported with the `_` prefix purely for tests — production callers stay on
|
|
537
|
+
* the closure-private name `createMilestoneWorktree` below.
|
|
535
538
|
*/
|
|
536
|
-
function
|
|
539
|
+
export function _createMilestoneWorktree(basePath: string, milestoneId: string): string {
|
|
537
540
|
const branch = autoWorktreeBranch(milestoneId);
|
|
538
541
|
const branchExists = nativeBranchExists(basePath, branch);
|
|
539
542
|
|
|
540
543
|
let info: { name: string; path: string; branch: string; exists: boolean };
|
|
541
544
|
if (branchExists) {
|
|
545
|
+
// #5549 post-merge audit (R3): match the fast-forward behavior added to
|
|
546
|
+
// `createAutoWorktree` in commit 8996cb68e. When a worker reuses an
|
|
547
|
+
// existing milestone branch, fast-forward it onto the integration branch
|
|
548
|
+
// when safe so per-worker worktrees don't fork from a stale base after a
|
|
549
|
+
// sibling milestone has merged. Same `nativeIsAncestor` + worktree-list
|
|
550
|
+
// safety guards apply.
|
|
551
|
+
fastForwardReusedMilestoneBranchIfSafe(basePath, milestoneId, branch);
|
|
542
552
|
info = createWorktree(basePath, milestoneId, { branch, reuseExistingBranch: true });
|
|
543
553
|
} else {
|
|
544
554
|
const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
|