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.
Files changed (181) hide show
  1. package/dist/resources/extensions/gsd/auto/phases.js +2 -0
  2. package/dist/resources/extensions/gsd/auto-dashboard.js +22 -1
  3. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +8 -2
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -11
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +3 -1
  6. package/dist/resources/extensions/gsd/auto-prompts.js +19 -9
  7. package/dist/resources/extensions/gsd/auto-worktree.js +16 -1
  8. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +128 -0
  9. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  10. package/dist/resources/extensions/gsd/bootstrap/system-context.js +17 -4
  11. package/dist/resources/extensions/gsd/commands/handlers/onboarding.js +52 -92
  12. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  13. package/dist/resources/extensions/gsd/commands-memory.js +462 -0
  14. package/dist/resources/extensions/gsd/doctor-git-checks.js +22 -2
  15. package/dist/resources/extensions/gsd/gsd-db.js +237 -4
  16. package/dist/resources/extensions/gsd/memory-embeddings.js +219 -0
  17. package/dist/resources/extensions/gsd/memory-extractor.js +78 -27
  18. package/dist/resources/extensions/gsd/memory-ingest.js +218 -0
  19. package/dist/resources/extensions/gsd/memory-relations.js +189 -0
  20. package/dist/resources/extensions/gsd/memory-source-store.js +113 -0
  21. package/dist/resources/extensions/gsd/memory-store.js +299 -6
  22. package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -8
  23. package/dist/resources/extensions/gsd/prompts/add-tests.md +1 -0
  24. package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
  25. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  26. package/dist/resources/extensions/gsd/tools/memory-tools.js +306 -0
  27. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +14 -0
  28. package/dist/resources/extensions/search-the-web/command-search-provider.js +4 -1
  29. package/dist/resources/extensions/search-the-web/native-search.js +13 -2
  30. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  31. package/dist/web/standalone/.next/BUILD_ID +1 -1
  32. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  33. package/dist/web/standalone/.next/build-manifest.json +2 -2
  34. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  35. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/index.html +1 -1
  52. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  59. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  61. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  62. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  63. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  64. package/package.json +1 -1
  65. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  66. package/packages/mcp-server/dist/server.js +12 -10
  67. package/packages/mcp-server/dist/server.js.map +1 -1
  68. package/packages/mcp-server/dist/session-manager.d.ts.map +1 -1
  69. package/packages/mcp-server/dist/session-manager.js +8 -1
  70. package/packages/mcp-server/dist/session-manager.js.map +1 -1
  71. package/packages/mcp-server/dist/workflow-tools.d.ts +1 -0
  72. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  73. package/packages/mcp-server/dist/workflow-tools.js +207 -71
  74. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  75. package/packages/mcp-server/src/mcp-server.test.ts +40 -4
  76. package/packages/mcp-server/src/server.ts +12 -10
  77. package/packages/mcp-server/src/session-manager.ts +10 -3
  78. package/packages/mcp-server/src/workflow-tools.test.ts +346 -1
  79. package/packages/mcp-server/src/workflow-tools.ts +228 -75
  80. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  81. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  82. package/packages/pi-ai/dist/index.d.ts +1 -0
  83. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  84. package/packages/pi-ai/dist/index.js +1 -0
  85. package/packages/pi-ai/dist/index.js.map +1 -1
  86. package/packages/pi-ai/dist/providers/api-family.d.ts +27 -0
  87. package/packages/pi-ai/dist/providers/api-family.d.ts.map +1 -0
  88. package/packages/pi-ai/dist/providers/api-family.js +47 -0
  89. package/packages/pi-ai/dist/providers/api-family.js.map +1 -0
  90. package/packages/pi-ai/dist/providers/api-family.test.d.ts +2 -0
  91. package/packages/pi-ai/dist/providers/api-family.test.d.ts.map +1 -0
  92. package/packages/pi-ai/dist/providers/api-family.test.js +101 -0
  93. package/packages/pi-ai/dist/providers/api-family.test.js.map +1 -0
  94. package/packages/pi-ai/src/index.ts +1 -0
  95. package/packages/pi-ai/src/providers/api-family.test.ts +129 -0
  96. package/packages/pi-ai/src/providers/api-family.ts +57 -0
  97. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  98. package/packages/pi-coding-agent/dist/cli/args.d.ts +6 -0
  99. package/packages/pi-coding-agent/dist/cli/args.d.ts.map +1 -1
  100. package/packages/pi-coding-agent/dist/cli/args.js +14 -4
  101. package/packages/pi-coding-agent/dist/cli/args.js.map +1 -1
  102. package/packages/pi-coding-agent/dist/cli/args.test.d.ts +2 -0
  103. package/packages/pi-coding-agent/dist/cli/args.test.d.ts.map +1 -0
  104. package/packages/pi-coding-agent/dist/cli/args.test.js +38 -0
  105. package/packages/pi-coding-agent/dist/cli/args.test.js.map +1 -0
  106. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -0
  107. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  108. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  109. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -1
  110. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  112. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/retry-handler.js +4 -1
  114. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -0
  116. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/sdk.js +7 -1
  118. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/main.js +3 -0
  121. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  122. package/packages/pi-coding-agent/src/cli/args.test.ts +44 -0
  123. package/packages/pi-coding-agent/src/cli/args.ts +21 -6
  124. package/packages/pi-coding-agent/src/core/extensions/runner.ts +4 -1
  125. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -2
  126. package/packages/pi-coding-agent/src/core/retry-handler.ts +4 -1
  127. package/packages/pi-coding-agent/src/core/sdk.ts +17 -1
  128. package/packages/pi-coding-agent/src/main.ts +4 -0
  129. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  130. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -10
  131. package/src/resources/extensions/gsd/auto/phases.ts +3 -0
  132. package/src/resources/extensions/gsd/auto-dashboard.ts +25 -1
  133. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +15 -2
  134. package/src/resources/extensions/gsd/auto-dispatch.ts +21 -7
  135. package/src/resources/extensions/gsd/auto-model-selection.ts +3 -1
  136. package/src/resources/extensions/gsd/auto-prompts.ts +33 -9
  137. package/src/resources/extensions/gsd/auto-worktree.ts +16 -1
  138. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +158 -0
  139. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  140. package/src/resources/extensions/gsd/bootstrap/system-context.ts +20 -4
  141. package/src/resources/extensions/gsd/commands/handlers/onboarding.ts +65 -131
  142. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  143. package/src/resources/extensions/gsd/commands-memory.ts +551 -0
  144. package/src/resources/extensions/gsd/doctor-git-checks.ts +23 -2
  145. package/src/resources/extensions/gsd/gsd-db.ts +273 -4
  146. package/src/resources/extensions/gsd/memory-embeddings.ts +235 -0
  147. package/src/resources/extensions/gsd/memory-extractor.ts +100 -34
  148. package/src/resources/extensions/gsd/memory-ingest.ts +286 -0
  149. package/src/resources/extensions/gsd/memory-relations.ts +240 -0
  150. package/src/resources/extensions/gsd/memory-source-store.ts +138 -0
  151. package/src/resources/extensions/gsd/memory-store.ts +351 -7
  152. package/src/resources/extensions/gsd/pre-execution-checks.ts +12 -8
  153. package/src/resources/extensions/gsd/prompts/add-tests.md +1 -0
  154. package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
  155. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  156. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +49 -0
  157. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  158. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  159. package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -2
  160. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  161. package/src/resources/extensions/gsd/tests/integration/doctor-git-symlink-cwd.test.ts +79 -0
  162. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  163. package/src/resources/extensions/gsd/tests/memory-embeddings.test.ts +213 -0
  164. package/src/resources/extensions/gsd/tests/memory-ingest.test.ts +153 -0
  165. package/src/resources/extensions/gsd/tests/memory-maintenance.test.ts +107 -0
  166. package/src/resources/extensions/gsd/tests/memory-relations.test.ts +175 -0
  167. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  168. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +295 -0
  169. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +66 -0
  170. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +132 -8
  171. package/src/resources/extensions/gsd/tests/prompts-no-gitignored-test-refs.test.ts +56 -0
  172. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +54 -0
  173. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +97 -0
  174. package/src/resources/extensions/gsd/tools/memory-tools.ts +380 -0
  175. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +14 -0
  176. package/src/resources/extensions/gsd/workflow-logger.ts +3 -1
  177. package/src/resources/extensions/search-the-web/command-search-provider.ts +4 -1
  178. package/src/resources/extensions/search-the-web/native-search.ts +13 -3
  179. package/src/resources/extensions/gsd/tests/onboarding-handler-loader.test.ts +0 -41
  180. /package/dist/web/standalone/.next/static/{J2z3GMC9QtSLr7gyoM38c → By_yegSJ-AA1OP0QjYbSl}/_buildManifest.js +0 -0
  181. /package/dist/web/standalone/.next/static/{J2z3GMC9QtSLr7gyoM38c → By_yegSJ-AA1OP0QjYbSl}/_ssgManifest.js +0 -0
@@ -0,0 +1,380 @@
1
+ // GSD Memory Tools — Phase 1 executors for capture_thought, memory_query, gsd_graph
2
+ //
3
+ // These executors back the three memory-layer tools the LLM can call at any
4
+ // point in a session. They build on the existing `memory-store.ts` layer
5
+ // (SQLite memories table) and degrade gracefully when the DB is unavailable.
6
+ //
7
+ // Phase 1 scope:
8
+ // - capture_thought → create a memory with the caller-supplied category/content
9
+ // - memory_query → keyword-filtered, score-ranked listing of active memories
10
+ // - gsd_graph → returns a memory and its supersedes edges only (Phase 4 adds memory_relations)
11
+
12
+ import { _getAdapter, isDbAvailable } from "../gsd-db.js";
13
+ import {
14
+ createMemory,
15
+ getActiveMemoriesRanked,
16
+ queryMemoriesRanked,
17
+ reinforceMemory,
18
+ } from "../memory-store.js";
19
+ import type { Memory, RankedMemory } from "../memory-store.js";
20
+ import { traverseGraph } from "../memory-relations.js";
21
+
22
+ // ─── Shared result shape (matches tools/workflow-tool-executors.ts) ─────────
23
+
24
+ export interface ToolExecutionResult {
25
+ content: Array<{ type: "text"; text: string }>;
26
+ details: Record<string, unknown>;
27
+ isError?: boolean;
28
+ }
29
+
30
+ function dbUnavailable(operation: string): ToolExecutionResult {
31
+ return {
32
+ content: [
33
+ {
34
+ type: "text",
35
+ text: "Error: GSD database is not available. Memory tools require an initialized .gsd/ project.",
36
+ },
37
+ ],
38
+ details: { operation, error: "db_unavailable" },
39
+ isError: true,
40
+ };
41
+ }
42
+
43
+ // ─── capture_thought ────────────────────────────────────────────────────────
44
+
45
+ export interface MemoryCaptureParams {
46
+ category: string;
47
+ content: string;
48
+ confidence?: number;
49
+ tags?: string[];
50
+ scope?: string;
51
+ }
52
+
53
+ const VALID_CATEGORIES = new Set([
54
+ "architecture",
55
+ "convention",
56
+ "gotcha",
57
+ "preference",
58
+ "environment",
59
+ "pattern",
60
+ ]);
61
+
62
+ export function executeMemoryCapture(params: MemoryCaptureParams): ToolExecutionResult {
63
+ if (!isDbAvailable()) return dbUnavailable("memory_capture");
64
+
65
+ const category = (params.category ?? "").trim().toLowerCase();
66
+ const content = (params.content ?? "").trim();
67
+ if (!category || !content) {
68
+ return {
69
+ content: [{ type: "text", text: "Error: category and content are required." }],
70
+ details: { operation: "memory_capture", error: "missing_fields" },
71
+ isError: true,
72
+ };
73
+ }
74
+ if (!VALID_CATEGORIES.has(category)) {
75
+ return {
76
+ content: [
77
+ {
78
+ type: "text",
79
+ text: `Error: invalid category "${category}". Must be one of: ${[...VALID_CATEGORIES].join(", ")}.`,
80
+ },
81
+ ],
82
+ details: { operation: "memory_capture", error: "invalid_category" },
83
+ isError: true,
84
+ };
85
+ }
86
+ const confidence = clampConfidence(params.confidence);
87
+ const scope = normalizeScope(params.scope);
88
+ const tags = normalizeTags(params.tags);
89
+
90
+ const id = createMemory({ category, content, confidence, scope, tags });
91
+ if (!id) {
92
+ return {
93
+ content: [{ type: "text", text: "Error: failed to create memory." }],
94
+ details: { operation: "memory_capture", error: "create_failed" },
95
+ isError: true,
96
+ };
97
+ }
98
+
99
+ return {
100
+ content: [{ type: "text", text: `Captured ${id} (${category}): ${content}` }],
101
+ details: { operation: "memory_capture", id, category, confidence, scope, tags },
102
+ };
103
+ }
104
+
105
+ function normalizeScope(value: unknown): string {
106
+ if (typeof value !== "string") return "project";
107
+ const trimmed = value.trim();
108
+ return trimmed.length === 0 ? "project" : trimmed;
109
+ }
110
+
111
+ function normalizeTags(value: unknown): string[] {
112
+ if (!Array.isArray(value)) return [];
113
+ return value.filter((t): t is string => typeof t === "string" && t.trim().length > 0).slice(0, 10);
114
+ }
115
+
116
+ function clampConfidence(value: unknown): number {
117
+ if (typeof value !== "number" || !Number.isFinite(value)) return 0.8;
118
+ if (value < 0.1) return 0.1;
119
+ if (value > 0.99) return 0.99;
120
+ return value;
121
+ }
122
+
123
+ // ─── memory_query ───────────────────────────────────────────────────────────
124
+
125
+ export interface MemoryQueryParams {
126
+ query: string;
127
+ k?: number;
128
+ category?: string;
129
+ scope?: string;
130
+ tag?: string;
131
+ include_superseded?: boolean;
132
+ reinforce_hits?: boolean;
133
+ }
134
+
135
+ export interface MemoryQueryHit {
136
+ id: string;
137
+ category: string;
138
+ content: string;
139
+ confidence: number;
140
+ hit_count: number;
141
+ score: number;
142
+ reason: "keyword" | "semantic" | "both" | "ranked";
143
+ keyword_rank: number | null;
144
+ semantic_rank: number | null;
145
+ }
146
+
147
+ export function executeMemoryQuery(params: MemoryQueryParams): ToolExecutionResult {
148
+ if (!isDbAvailable()) return dbUnavailable("memory_query");
149
+
150
+ const query = (params.query ?? "").trim();
151
+ const k = clampTopK(params.k, 10);
152
+ const includeSuperseded = params.include_superseded === true;
153
+ const category = params.category?.trim().toLowerCase() || undefined;
154
+ const scopeFilter = params.scope?.trim() || undefined;
155
+ const tagFilter = params.tag?.trim().toLowerCase() || undefined;
156
+
157
+ try {
158
+ let ranked: RankedMemory[] = [];
159
+ if (query) {
160
+ ranked = queryMemoriesRanked({
161
+ query,
162
+ k,
163
+ category,
164
+ scope: scopeFilter,
165
+ tag: tagFilter,
166
+ include_superseded: includeSuperseded,
167
+ });
168
+ } else {
169
+ const candidates: Memory[] = includeSuperseded
170
+ ? includeSupersededMemories(getActiveMemoriesRanked(200))
171
+ : getActiveMemoriesRanked(200);
172
+ ranked = candidates
173
+ .filter((m) => {
174
+ if (category && m.category.toLowerCase() !== category) return false;
175
+ if (scopeFilter && m.scope !== scopeFilter) return false;
176
+ if (tagFilter && !m.tags.map((t) => t.toLowerCase()).includes(tagFilter)) return false;
177
+ return true;
178
+ })
179
+ .slice(0, k)
180
+ .map((memory) => ({
181
+ memory,
182
+ score: memory.confidence * (1 + memory.hit_count * 0.1),
183
+ keywordRank: null,
184
+ semanticRank: null,
185
+ confidenceBoost: memory.confidence * (1 + memory.hit_count * 0.1),
186
+ reason: "ranked" as const,
187
+ }));
188
+ }
189
+
190
+ const hits: MemoryQueryHit[] = ranked.map((r) => ({
191
+ id: r.memory.id,
192
+ category: r.memory.category,
193
+ content: r.memory.content,
194
+ confidence: r.memory.confidence,
195
+ hit_count: r.memory.hit_count,
196
+ score: r.score,
197
+ reason: r.reason,
198
+ keyword_rank: r.keywordRank,
199
+ semantic_rank: r.semanticRank,
200
+ }));
201
+
202
+ if (params.reinforce_hits) {
203
+ for (const h of hits) reinforceMemory(h.id);
204
+ }
205
+
206
+ const summary = hits.length === 0
207
+ ? "No matching memories."
208
+ : hits.map((h) => `- [${h.id}] (${h.category}) ${h.content}`).join("\n");
209
+
210
+ return {
211
+ content: [{ type: "text", text: summary }],
212
+ details: {
213
+ operation: "memory_query",
214
+ query,
215
+ k,
216
+ returned: hits.length,
217
+ hits,
218
+ },
219
+ };
220
+ } catch (err) {
221
+ return {
222
+ content: [{ type: "text", text: `Error: memory query failed: ${(err as Error).message}` }],
223
+ details: { operation: "memory_query", error: (err as Error).message },
224
+ isError: true,
225
+ };
226
+ }
227
+ }
228
+
229
+ function clampTopK(value: unknown, fallback: number): number {
230
+ if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
231
+ if (value < 1) return 1;
232
+ if (value > 50) return 50;
233
+ return Math.floor(value);
234
+ }
235
+
236
+ function includeSupersededMemories(rankedActive: Memory[]): Memory[] {
237
+ const adapter = _getAdapter();
238
+ if (!adapter) return rankedActive;
239
+ try {
240
+ const rows = adapter.prepare("SELECT * FROM memories").all();
241
+ return rows.map((row) => {
242
+ let tags: string[] = [];
243
+ if (typeof row["tags"] === "string") {
244
+ try {
245
+ const parsed = JSON.parse(row["tags"] as string);
246
+ if (Array.isArray(parsed)) tags = parsed.filter((t): t is string => typeof t === "string");
247
+ } catch {
248
+ /* leave empty */
249
+ }
250
+ }
251
+ return {
252
+ seq: row["seq"] as number,
253
+ id: row["id"] as string,
254
+ category: row["category"] as string,
255
+ content: row["content"] as string,
256
+ confidence: row["confidence"] as number,
257
+ source_unit_type: (row["source_unit_type"] as string) ?? null,
258
+ source_unit_id: (row["source_unit_id"] as string) ?? null,
259
+ created_at: row["created_at"] as string,
260
+ updated_at: row["updated_at"] as string,
261
+ superseded_by: (row["superseded_by"] as string) ?? null,
262
+ hit_count: row["hit_count"] as number,
263
+ scope: (row["scope"] as string) ?? "project",
264
+ tags,
265
+ };
266
+ });
267
+ } catch {
268
+ return rankedActive;
269
+ }
270
+ }
271
+
272
+ // ─── gsd_graph ──────────────────────────────────────────────────────────────
273
+
274
+ export interface GsdGraphParams {
275
+ mode: "build" | "query";
276
+ memoryId?: string;
277
+ depth?: number;
278
+ rel?: string;
279
+ }
280
+
281
+ export interface GraphNode {
282
+ id: string;
283
+ category: string;
284
+ content: string;
285
+ confidence: number;
286
+ }
287
+
288
+ export interface GraphEdge {
289
+ from: string;
290
+ to: string;
291
+ rel: string;
292
+ confidence: number;
293
+ }
294
+
295
+ export function executeGsdGraph(params: GsdGraphParams): ToolExecutionResult {
296
+ if (!isDbAvailable()) return dbUnavailable("gsd_graph");
297
+
298
+ if (params.mode === "build") {
299
+ // The extractor emits LINK actions incrementally (Phase 4). There is no
300
+ // batch rebuild step to run today — ingest artifacts via `/gsd memory
301
+ // extract <SRC-...>` and the next extraction turn will add edges.
302
+ return {
303
+ content: [
304
+ {
305
+ type: "text",
306
+ text:
307
+ "gsd_graph build acknowledged. Graph edges are populated incrementally by memory " +
308
+ "extraction (including LINK actions). Use `/gsd memory extract <SRC-...>` to trigger " +
309
+ "extraction against a specific ingested source.",
310
+ },
311
+ ],
312
+ details: { operation: "gsd_graph", mode: "build", built: 0 },
313
+ };
314
+ }
315
+
316
+ if (params.mode !== "query") {
317
+ return {
318
+ content: [{ type: "text", text: `Error: unknown mode "${params.mode}". Must be "build" or "query".` }],
319
+ details: { operation: "gsd_graph", error: "invalid_mode" },
320
+ isError: true,
321
+ };
322
+ }
323
+
324
+ const memoryId = params.memoryId?.trim();
325
+ if (!memoryId) {
326
+ return {
327
+ content: [{ type: "text", text: "Error: memoryId is required for mode=query." }],
328
+ details: { operation: "gsd_graph", error: "missing_memory_id" },
329
+ isError: true,
330
+ };
331
+ }
332
+
333
+ try {
334
+ const graph = traverseGraph(memoryId, clampDepth(params.depth));
335
+ const rel = params.rel?.trim().toLowerCase() || null;
336
+ const edges = rel ? graph.edges.filter((e) => e.rel === rel) : graph.edges;
337
+ const relevantIds = new Set<string>([memoryId]);
338
+ for (const e of edges) {
339
+ relevantIds.add(e.from);
340
+ relevantIds.add(e.to);
341
+ }
342
+ const nodes = graph.nodes.filter((n) => relevantIds.has(n.id));
343
+
344
+ if (nodes.length === 0) {
345
+ return {
346
+ content: [{ type: "text", text: `No memory found with id ${memoryId}.` }],
347
+ details: { operation: "gsd_graph", mode: "query", memoryId, nodes: [], edges: [] },
348
+ };
349
+ }
350
+
351
+ const summary = [
352
+ `Memory ${memoryId} — ${nodes.length} node(s), ${edges.length} edge(s).`,
353
+ ...nodes.map((n) => ` [${n.id}] (${n.category}) ${n.content}`),
354
+ ...edges.map((e) => ` ${e.from} --${e.rel}-> ${e.to}`),
355
+ ].join("\n");
356
+ return {
357
+ content: [{ type: "text", text: summary }],
358
+ details: {
359
+ operation: "gsd_graph",
360
+ mode: "query",
361
+ memoryId,
362
+ nodes: nodes.map((n) => ({ id: n.id, category: n.category, content: n.content })),
363
+ edges: edges.map((e) => ({ from: e.from, to: e.to, rel: e.rel })),
364
+ },
365
+ };
366
+ } catch (err) {
367
+ return {
368
+ content: [{ type: "text", text: `Error: graph query failed: ${(err as Error).message}` }],
369
+ details: { operation: "gsd_graph", error: (err as Error).message },
370
+ isError: true,
371
+ };
372
+ }
373
+ }
374
+
375
+ function clampDepth(value: unknown): number {
376
+ if (typeof value !== "number" || !Number.isFinite(value)) return 1;
377
+ if (value < 0) return 0;
378
+ if (value > 5) return 5;
379
+ return Math.floor(value);
380
+ }
@@ -10,6 +10,8 @@ import {
10
10
  } from "../gsd-db.js";
11
11
  import { GATE_REGISTRY } from "../gate-registry.js";
12
12
  import { saveArtifactToDb } from "../db-writer.js";
13
+ import { resolveMilestoneFile, resolveSliceFile } from "../paths.js";
14
+ import { unlinkSync } from "node:fs";
13
15
  import type { CompleteMilestoneParams } from "./complete-milestone.js";
14
16
  import { handleCompleteMilestone } from "./complete-milestone.js";
15
17
  import { handleCompleteTask } from "./complete-task.js";
@@ -103,6 +105,18 @@ export async function executeSummarySave(
103
105
  },
104
106
  basePath,
105
107
  );
108
+
109
+ if (params.artifact_type === "CONTEXT" && !params.task_id) {
110
+ try {
111
+ const draftFile = params.slice_id
112
+ ? resolveSliceFile(basePath, params.milestone_id, params.slice_id, "CONTEXT-DRAFT")
113
+ : resolveMilestoneFile(basePath, params.milestone_id, "CONTEXT-DRAFT");
114
+ if (draftFile) unlinkSync(draftFile);
115
+ } catch (e) {
116
+ logWarning("tool", `CONTEXT-DRAFT.md unlink failed: ${(e as Error).message}`);
117
+ }
118
+ }
119
+
106
120
  return {
107
121
  content: [{ type: "text", text: `Saved ${params.artifact_type} artifact to ${relativePath}` }],
108
122
  details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type },
@@ -54,7 +54,9 @@ export type LogComponent =
54
54
  | "registry" // Rule registry hook state
55
55
  | "renderer" // Markdown renderer and projections
56
56
  | "safety" // LLM safety harness
57
- | "ecosystem"; // GSD ecosystem extension loader and dispatch
57
+ | "ecosystem" // GSD ecosystem extension loader and dispatch
58
+ | "memory-embeddings" // Memory layer embedding generation
59
+ | "memory-ingest"; // Memory layer ingestion pipeline
58
60
 
59
61
  export interface LogEntry {
60
62
  ts: string;
@@ -8,6 +8,7 @@
8
8
  * All provider logic lives in provider.ts (S01) — this is pure UI wiring.
9
9
  */
10
10
 
11
+ import { isAnthropicApi } from '@gsd/pi-ai'
11
12
  import type { ExtensionAPI } from '@gsd/pi-coding-agent'
12
13
  import type { AutocompleteItem } from '@gsd/pi-tui'
13
14
  import {
@@ -90,7 +91,9 @@ export function registerSearchProviderCommand(pi: ExtensionAPI): void {
90
91
 
91
92
  setSearchProviderPreference(chosen)
92
93
  const effective = resolveSearchProvider()
93
- const isAnthropic = ctx.model?.provider === 'anthropic'
94
+ // Gate on api (#4478 / ADR-012): covers claude-code, anthropic-vertex, and
95
+ // other Anthropic-fronting transports — not just the plain `anthropic` provider.
96
+ const isAnthropic = isAnthropicApi(ctx.model)
94
97
  const nativeNote = isAnthropic ? '\nNote: Native Anthropic web search is also active (automatic, no API key needed).' : ''
95
98
  ctx.ui.notify(
96
99
  `Search provider set to ${chosen}. Effective provider: ${effective ?? 'none (no API keys)'}${nativeNote}`,
@@ -5,6 +5,7 @@
5
5
  * the heavy tool-registration modules.
6
6
  */
7
7
 
8
+ import { isAnthropicApi } from "@gsd/pi-ai";
8
9
  import { resolveSearchProviderFromPreferences } from "../gsd/preferences.js";
9
10
 
10
11
  /** Tool names for the Brave-backed custom search tools */
@@ -94,7 +95,10 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
94
95
  pi.on("model_select", async (event: any, ctx: any) => {
95
96
  modelSelectFired = true;
96
97
  const wasAnthropic = isAnthropicProvider;
97
- isAnthropicProvider = event.model.provider === "anthropic";
98
+ // Gate on `api` not `provider` (#4478 / ADR-012): covers claude-code OAuth,
99
+ // anthropic-vertex, and Vercel-gateway-hosted Anthropic — all serve the
100
+ // Messages API and accept the native web_search tool.
101
+ isAnthropicProvider = isAnthropicApi(event.model);
98
102
 
99
103
  const hasBrave = !!process.env.BRAVE_API_KEY;
100
104
 
@@ -139,9 +143,15 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
139
143
  // the model_select flag, then to the model name heuristic (last resort).
140
144
  // The model name heuristic is needed for session restores where
141
145
  // modelsAreEqual suppresses model_select AND the SDK doesn't pass model.
142
- const eventModel = event.model as { provider: string } | undefined;
146
+ const eventModel = event.model as { provider?: string; api?: string } | undefined;
143
147
  let isAnthropic: boolean;
144
- if (eventModel?.provider) {
148
+ if (eventModel?.api) {
149
+ // Preferred path: gate on wire protocol (#4478 / ADR-012).
150
+ isAnthropic = isAnthropicApi(eventModel);
151
+ } else if (eventModel?.provider) {
152
+ // Fallback for event shapes that carry provider but not api — only plain
153
+ // `anthropic` maps unambiguously without the api field. Other Anthropic
154
+ // transports will arrive via the modelSelectFired or model-name branch.
145
155
  isAnthropic = eventModel.provider === "anthropic";
146
156
  } else if (modelSelectFired) {
147
157
  isAnthropic = isAnthropicProvider;
@@ -1,41 +0,0 @@
1
- import test from "node:test"
2
- import assert from "node:assert/strict"
3
- import { readFileSync } from "node:fs"
4
- import { join } from "node:path"
5
-
6
- test("onboarding handler resolves wizard module from deployed runtime paths", () => {
7
- const source = readFileSync(
8
- join(import.meta.dirname, "..", "commands", "handlers", "onboarding.ts"),
9
- "utf-8",
10
- )
11
-
12
- assert.match(
13
- source,
14
- /process\.env\.GSD_PKG_ROOT/,
15
- "handler should probe GSD_PKG_ROOT for deployed dist\/onboarding.js",
16
- )
17
-
18
- assert.match(
19
- source,
20
- /process\.argv\[1\]/,
21
- "handler should fall back to argv-derived package root when env is missing",
22
- )
23
-
24
- assert.match(
25
- source,
26
- /candidates\.push\("\.\.\/\.\.\/\.\.\/\.\.\/\.\.\/onboarding\.js"\)/,
27
- "handler must keep the relative source\/dist fallback",
28
- )
29
-
30
- assert.match(
31
- source,
32
- /for \(const specifier of candidates\)/,
33
- "handler should try candidates in order",
34
- )
35
-
36
- assert.match(
37
- source,
38
- /Failed to load onboarding wizard module/,
39
- "handler should throw a diagnostic error when no candidate resolves",
40
- )
41
- })