@hadooppei/hwcode 1.0.8 → 1.0.10
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/.pi/dist/lib/knowledge/extractor.js +32 -0
- package/.pi/dist/lib/knowledge/review-worker.js +292 -0
- package/.pi/dist/lib/knowledge/sanitize.js +24 -0
- package/.pi/dist/lib/knowledge/session-scanner.js +165 -0
- package/.pi/dist/lib/knowledge/store.js +370 -0
- package/.pi/dist/lib/knowledge/types.js +1 -0
- package/.pi/dist/lib/knowledge/worker-protocol.js +1 -0
- package/.pi/dist/lib/runtime/defaults.js +50 -0
- package/.pi/dist/lib/runtime/paths.js +55 -0
- package/.pi/extensions/knowledge.ts +186 -190
- package/.pi/lib/knowledge/review-worker.ts +240 -42
- package/.pi/lib/knowledge/session-scanner.ts +155 -0
- package/.pi/lib/knowledge/store.ts +237 -123
- package/.pi/lib/knowledge/types.ts +37 -5
- package/.pi/lib/knowledge/worker-protocol.ts +16 -7
- package/.pi/lib/runtime/defaults.ts +5 -2
- package/.pi/lib/runtime/paths.ts +16 -11
- package/README.md +19 -16
- package/package.json +5 -2
|
@@ -1,212 +1,199 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
2
5
|
import { Worker } from "node:worker_threads";
|
|
3
6
|
|
|
4
7
|
import { Type } from "@earendil-works/pi-ai";
|
|
5
8
|
import { defineTool, type ExtensionAPI, type ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
9
|
|
|
7
10
|
import {
|
|
8
|
-
buildKnowledgeExtractionPrompt,
|
|
11
|
+
buildKnowledgeExtractionPrompt, KNOWLEDGE_EXTRACTION_SYSTEM_PROMPT,
|
|
9
12
|
} from "../lib/knowledge/extractor.ts";
|
|
10
13
|
import { matchKnowledge } from "../lib/knowledge/matcher.ts";
|
|
11
|
-
import {
|
|
12
|
-
loadKnowledgeById, loadKnowledgeSnapshot, projectKnowledgeKey,
|
|
13
|
-
} from "../lib/knowledge/store.ts";
|
|
14
|
-
import type { KnowledgeReviewState, KnowledgeSnapshot } from "../lib/knowledge/types.ts";
|
|
14
|
+
import { loadKnowledgeById, loadKnowledgeSnapshot, projectKnowledgeKey } from "../lib/knowledge/store.ts";
|
|
15
15
|
import type { KnowledgeWorkerInput, KnowledgeWorkerOutput } from "../lib/knowledge/worker-protocol.ts";
|
|
16
16
|
import { KNOWLEDGE_RUNTIME_DEFAULTS } from "../lib/runtime/defaults.ts";
|
|
17
17
|
import { getWorkingDirectory } from "../lib/working-directory.ts";
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
interface PendingReview {
|
|
19
|
+
interface ActiveReview {
|
|
22
20
|
requestId: string;
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
leaderToken: string;
|
|
22
|
+
controller: AbortController;
|
|
25
23
|
}
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
25
|
+
interface ProcessKnowledgeRuntime {
|
|
26
|
+
worker?: Worker;
|
|
27
|
+
context?: ExtensionContext;
|
|
28
|
+
sessionsRoot?: string;
|
|
29
|
+
modelAvailable?: boolean;
|
|
30
|
+
activeReview?: ActiveReview;
|
|
31
|
+
capabilityTimer?: NodeJS.Timeout;
|
|
32
|
+
workerRetryTimer?: NodeJS.Timeout;
|
|
33
|
+
workerRetryAttempt: number;
|
|
34
|
+
lastWorkerIssue?: { message: string; reportedAt: number };
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if (data.type !== "message" || !data.message || typeof data.message !== "object") return "";
|
|
44
|
-
const message = data.message as Record<string, unknown>;
|
|
45
|
-
const role = typeof message.role === "string" ? message.role : "message";
|
|
46
|
-
const content = textContent(message.content);
|
|
47
|
-
return content ? `[${role}]\n${content}` : "";
|
|
48
|
-
}
|
|
37
|
+
const RUNTIME_SYMBOL = Symbol.for("hwcode.knowledge.runtime.v3");
|
|
38
|
+
const processGlobals = globalThis as unknown as Record<PropertyKey, unknown>;
|
|
39
|
+
const runtime = (processGlobals[RUNTIME_SYMBOL] ??= { workerRetryAttempt: 0 }) as ProcessKnowledgeRuntime;
|
|
40
|
+
runtime.workerRetryAttempt ??= 0;
|
|
41
|
+
|
|
42
|
+
const WORKER_RETRY_DELAYS_MS = [1_000, 5_000, 30_000, 60_000] as const;
|
|
49
43
|
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
const id = (entry as Record<string, unknown>).id;
|
|
53
|
-
return typeof id === "string" ? id : undefined;
|
|
44
|
+
function post(message: KnowledgeWorkerInput): void {
|
|
45
|
+
runtime.worker?.postMessage(message);
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
function
|
|
57
|
-
return ctx.
|
|
48
|
+
function modelAvailable(ctx: ExtensionContext | undefined): boolean {
|
|
49
|
+
return Boolean(ctx?.model && ctx.modelRegistry.hasConfiguredAuth(ctx.model));
|
|
58
50
|
}
|
|
59
51
|
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
function updateCapability(): void {
|
|
53
|
+
if (!runtime.worker && runtime.context) ensureWorker();
|
|
54
|
+
if (!runtime.worker || !runtime.sessionsRoot) return;
|
|
55
|
+
const available = modelAvailable(runtime.context);
|
|
56
|
+
if (runtime.modelAvailable === available) return;
|
|
57
|
+
runtime.modelAvailable = available;
|
|
58
|
+
post({ type: "configure", modelAvailable: available, sessionsRoot: runtime.sessionsRoot });
|
|
63
59
|
}
|
|
64
60
|
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
const lastIndex = lastReviewedEntryId ? entries.findIndex((entry) => entryId(entry) === lastReviewedEntryId) : -1;
|
|
68
|
-
const pending = entries.slice(lastIndex + 1);
|
|
69
|
-
const lastEntryId = pending.length > 0 ? entryId(pending[pending.length - 1]) : undefined;
|
|
70
|
-
const parts: string[] = [];
|
|
71
|
-
let chars = 0;
|
|
72
|
-
for (const entry of pending.slice().reverse()) {
|
|
73
|
-
const part = entryMessageText(entry);
|
|
74
|
-
if (!part) continue;
|
|
75
|
-
const remaining = KNOWLEDGE_RUNTIME_DEFAULTS.review.maxDeltaChars - chars;
|
|
76
|
-
if (remaining <= 0) break;
|
|
77
|
-
const clipped = part.slice(0, remaining);
|
|
78
|
-
parts.unshift(clipped);
|
|
79
|
-
chars += clipped.length;
|
|
80
|
-
if (clipped.length < part.length) break;
|
|
81
|
-
}
|
|
82
|
-
return { text: parts.join("\n\n"), lastEntryId };
|
|
61
|
+
function cancelActiveReview(): void {
|
|
62
|
+
runtime.activeReview?.controller.abort();
|
|
83
63
|
}
|
|
84
64
|
|
|
85
|
-
function
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
65
|
+
function reportWorkerIssue(error: unknown): void {
|
|
66
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
67
|
+
const now = Date.now();
|
|
68
|
+
const prior = runtime.lastWorkerIssue;
|
|
69
|
+
if (prior?.message === message && now - prior.reportedAt < 60_000) return;
|
|
70
|
+
runtime.lastWorkerIssue = { message, reportedAt: now };
|
|
71
|
+
const rendered = `HWCode knowledge worker failed: ${message}`;
|
|
72
|
+
if (runtime.context?.hasUI) runtime.context.ui.notify(rendered, "error");
|
|
73
|
+
else console.error(rendered);
|
|
93
74
|
}
|
|
94
75
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
76
|
+
function scheduleWorkerRestart(): void {
|
|
77
|
+
if (!runtime.context || runtime.workerRetryTimer) return;
|
|
78
|
+
const index = Math.min(runtime.workerRetryAttempt, WORKER_RETRY_DELAYS_MS.length - 1);
|
|
79
|
+
const delay = WORKER_RETRY_DELAYS_MS[index];
|
|
80
|
+
runtime.workerRetryAttempt += 1;
|
|
81
|
+
runtime.workerRetryTimer = setTimeout(() => {
|
|
82
|
+
runtime.workerRetryTimer = undefined;
|
|
83
|
+
if (!runtime.context) return;
|
|
84
|
+
ensureWorker();
|
|
85
|
+
updateCapability();
|
|
86
|
+
}, delay);
|
|
87
|
+
runtime.workerRetryTimer.unref();
|
|
88
|
+
}
|
|
104
89
|
|
|
105
|
-
|
|
90
|
+
function workerEntryUrl(): URL {
|
|
91
|
+
const compiled = new URL("../dist/lib/knowledge/review-worker.js", import.meta.url);
|
|
92
|
+
if (existsSync(fileURLToPath(compiled))) return compiled;
|
|
93
|
+
return new URL("../lib/knowledge/review-worker.ts", import.meta.url);
|
|
94
|
+
}
|
|
106
95
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
96
|
+
async function runReview(message: Extract<KnowledgeWorkerOutput, { type: "review_request" }>): Promise<void> {
|
|
97
|
+
const ctx = runtime.context;
|
|
98
|
+
if (!ctx?.model || !ctx.modelRegistry.hasConfiguredAuth(ctx.model)) {
|
|
99
|
+
post({ type: "review_result", leaderToken: message.leaderToken, requestId: message.requestId, error: "no-configured-model" });
|
|
100
|
+
updateCapability();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!ctx.isIdle() || ctx.hasPendingMessages()) {
|
|
104
|
+
post({ type: "review_result", leaderToken: message.leaderToken, requestId: message.requestId, error: "model-executor-is-busy" });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const controller = new AbortController();
|
|
108
|
+
runtime.activeReview = { requestId: message.requestId, leaderToken: message.leaderToken, controller };
|
|
109
|
+
const timeout = setTimeout(() => controller.abort(), KNOWLEDGE_RUNTIME_DEFAULTS.review.modelTimeoutMs);
|
|
110
|
+
timeout.unref();
|
|
111
|
+
try {
|
|
112
|
+
const response = await ctx.modelRegistry.complete(
|
|
113
|
+
ctx.model,
|
|
114
|
+
{
|
|
115
|
+
systemPrompt: KNOWLEDGE_EXTRACTION_SYSTEM_PROMPT,
|
|
116
|
+
messages: [{
|
|
117
|
+
role: "user",
|
|
118
|
+
content: [{ type: "text", text: buildKnowledgeExtractionPrompt(message.task.projectRoot, message.task.delta) }],
|
|
119
|
+
timestamp: Date.now(),
|
|
120
|
+
}],
|
|
121
|
+
},
|
|
122
|
+
{ signal: controller.signal, reasoningEffort: "low", cacheRetention: "none", sessionId: randomUUID() },
|
|
123
|
+
);
|
|
124
|
+
if (controller.signal.aborted) throw new Error("knowledge-review-aborted-or-timed-out");
|
|
125
|
+
const raw = response.content.filter((item): item is { type: "text"; text: string } => item.type === "text")
|
|
126
|
+
.map((item) => item.text).join("\n");
|
|
127
|
+
post({ type: "review_result", leaderToken: message.leaderToken, requestId: message.requestId, raw });
|
|
128
|
+
} catch (error) {
|
|
129
|
+
post({
|
|
130
|
+
type: "review_result", leaderToken: message.leaderToken, requestId: message.requestId,
|
|
131
|
+
error: error instanceof Error ? error.message : String(error),
|
|
132
|
+
});
|
|
133
|
+
} finally {
|
|
134
|
+
clearTimeout(timeout);
|
|
135
|
+
if (runtime.activeReview?.requestId === message.requestId) runtime.activeReview = undefined;
|
|
114
136
|
}
|
|
137
|
+
}
|
|
115
138
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
pendingReview = { requestId, lastEntryId: delta.lastEntryId, deltaDigest: knowledgeDeltaDigest(delta.text) };
|
|
128
|
-
if (!ctx.model || !ctx.modelRegistry.hasConfiguredAuth(ctx.model)) {
|
|
129
|
-
post({ type: "review_result", requestId, error: "no-configured-model" });
|
|
130
|
-
pendingReview = undefined;
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const controller = new AbortController();
|
|
134
|
-
reviewController = controller;
|
|
135
|
-
try {
|
|
136
|
-
const response = await ctx.modelRegistry.complete(
|
|
137
|
-
ctx.model,
|
|
138
|
-
{
|
|
139
|
-
systemPrompt: KNOWLEDGE_EXTRACTION_SYSTEM_PROMPT,
|
|
140
|
-
messages: [{
|
|
141
|
-
role: "user",
|
|
142
|
-
content: [{ type: "text", text: buildKnowledgeExtractionPrompt(activeProjectRoot, delta.text) }],
|
|
143
|
-
timestamp: Date.now(),
|
|
144
|
-
}],
|
|
145
|
-
},
|
|
146
|
-
{ signal: controller.signal, reasoningEffort: "low", cacheRetention: "none", sessionId: randomUUID() },
|
|
147
|
-
);
|
|
148
|
-
if (controller.signal.aborted) return;
|
|
149
|
-
const raw = response.content.filter((item): item is { type: "text"; text: string } => item.type === "text")
|
|
150
|
-
.map((item) => item.text).join("\n");
|
|
151
|
-
post({ type: "review_result", requestId, raw });
|
|
152
|
-
} catch (error) {
|
|
153
|
-
if (!controller.signal.aborted) {
|
|
154
|
-
post({ type: "review_result", requestId, error: error instanceof Error ? error.message : String(error) });
|
|
155
|
-
pendingReview = undefined;
|
|
156
|
-
}
|
|
157
|
-
} finally {
|
|
158
|
-
if (reviewController === controller) reviewController = undefined;
|
|
159
|
-
}
|
|
139
|
+
function handleWorkerMessage(message: KnowledgeWorkerOutput): void {
|
|
140
|
+
if (message.type === "ready") {
|
|
141
|
+
runtime.workerRetryAttempt = 0;
|
|
142
|
+
runtime.lastWorkerIssue = undefined;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (message.type === "review_request") { void runReview(message); return; }
|
|
146
|
+
if (message.type === "review_cancel" && runtime.activeReview?.requestId === message.requestId) {
|
|
147
|
+
runtime.activeReview.controller.abort();
|
|
148
|
+
return;
|
|
160
149
|
}
|
|
150
|
+
if (message.type === "review_failed") reportWorkerIssue(message.error);
|
|
151
|
+
}
|
|
161
152
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
pi.appendEntry<KnowledgeReviewState>(KNOWLEDGE_REVIEW_STATE_TYPE, reviewState);
|
|
177
|
-
snapshot = loadKnowledgeSnapshot(activeProjectKey);
|
|
178
|
-
pendingReview = undefined;
|
|
179
|
-
return;
|
|
153
|
+
function ensureWorker(): Worker | undefined {
|
|
154
|
+
if (runtime.worker) return runtime.worker;
|
|
155
|
+
if (runtime.workerRetryTimer) return undefined;
|
|
156
|
+
const worker = new Worker(workerEntryUrl());
|
|
157
|
+
worker.unref();
|
|
158
|
+
runtime.worker = worker;
|
|
159
|
+
worker.on("message", (message: KnowledgeWorkerOutput) => handleWorkerMessage(message));
|
|
160
|
+
worker.on("error", (error) => {
|
|
161
|
+
reportWorkerIssue(error);
|
|
162
|
+
cancelActiveReview();
|
|
163
|
+
if (runtime.worker === worker) {
|
|
164
|
+
runtime.worker = undefined;
|
|
165
|
+
runtime.modelAvailable = undefined;
|
|
166
|
+
scheduleWorkerRestart();
|
|
180
167
|
}
|
|
181
|
-
|
|
182
|
-
|
|
168
|
+
});
|
|
169
|
+
worker.on("exit", (code) => {
|
|
170
|
+
if (runtime.worker === worker) {
|
|
171
|
+
runtime.worker = undefined;
|
|
172
|
+
runtime.modelAvailable = undefined;
|
|
173
|
+
if (code !== 0) reportWorkerIssue(`worker exited with code ${code}`);
|
|
174
|
+
scheduleWorkerRestart();
|
|
183
175
|
}
|
|
184
|
-
}
|
|
176
|
+
});
|
|
177
|
+
runtime.capabilityTimer ??= setInterval(updateCapability, KNOWLEDGE_RUNTIME_DEFAULTS.review.capabilityPollMs);
|
|
178
|
+
runtime.capabilityTimer.unref();
|
|
179
|
+
return worker;
|
|
180
|
+
}
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
activeProjectKey = projectKnowledgeKey(activeProjectRoot);
|
|
195
|
-
reviewState = loadReviewState(ctx);
|
|
196
|
-
snapshot = loadKnowledgeSnapshot(activeProjectKey);
|
|
197
|
-
const startedWorker = new Worker(new URL("../lib/knowledge/review-worker.ts", import.meta.url));
|
|
198
|
-
worker = startedWorker;
|
|
199
|
-
startedWorker.on("message", (message: KnowledgeWorkerOutput) => handleWorkerMessage(message));
|
|
200
|
-
startedWorker.on("error", () => { if (worker === startedWorker) worker = undefined; });
|
|
201
|
-
const latest = lastReviewableEntryId(ctx);
|
|
202
|
-
post({
|
|
203
|
-
type: "configure",
|
|
204
|
-
sessionId: ctx.sessionManager.getSessionId(),
|
|
205
|
-
projectKey: activeProjectKey,
|
|
206
|
-
dirty: Boolean(latest && latest !== reviewState.lastReviewedEntryId),
|
|
207
|
-
});
|
|
208
|
-
}
|
|
182
|
+
function configureForContext(ctx: ExtensionContext): void {
|
|
183
|
+
runtime.context = ctx;
|
|
184
|
+
runtime.sessionsRoot = dirname(ctx.sessionManager.getSessionDir());
|
|
185
|
+
ensureWorker();
|
|
186
|
+
const available = modelAvailable(ctx);
|
|
187
|
+
runtime.modelAvailable = available;
|
|
188
|
+
post({ type: "configure", modelAvailable: available, sessionsRoot: runtime.sessionsRoot });
|
|
189
|
+
}
|
|
209
190
|
|
|
191
|
+
function currentProject(ctx: ExtensionContext): { root: string; key: string } {
|
|
192
|
+
const root = getWorkingDirectory(ctx.sessionManager);
|
|
193
|
+
return { root, key: projectKnowledgeKey(root) };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export default function registerKnowledgeExtension(pi: ExtensionAPI): void {
|
|
210
197
|
pi.registerTool(defineTool({
|
|
211
198
|
name: "hwcode_knowledge_lookup",
|
|
212
199
|
label: "HWCode Knowledge Lookup",
|
|
@@ -218,14 +205,18 @@ export default function registerKnowledgeExtension(pi: ExtensionAPI): void {
|
|
|
218
205
|
}),
|
|
219
206
|
executionMode: "sequential",
|
|
220
207
|
async execute(_id, params) {
|
|
208
|
+
const ctx = runtime.context;
|
|
209
|
+
if (!ctx) return { content: [{ type: "text", text: "Knowledge context is not available." }], isError: true, details: {} };
|
|
210
|
+
const { key } = currentProject(ctx);
|
|
221
211
|
if (params.id) {
|
|
222
|
-
const found = loadKnowledgeById(params.id,
|
|
212
|
+
const found = loadKnowledgeById(params.id, key);
|
|
223
213
|
if (!found) return { content: [{ type: "text", text: `No applicable knowledge found with ID: ${params.id}` }], isError: true, details: {} };
|
|
224
214
|
return { content: [{ type: "text", text: found.content }], details: {} };
|
|
225
215
|
}
|
|
226
216
|
if (params.query) {
|
|
217
|
+
const snapshot = loadKnowledgeSnapshot(key);
|
|
227
218
|
const matches = matchKnowledge(params.query, snapshot.catalog)
|
|
228
|
-
.filter((item) => item.scope === "global" || item.scope === `project:${
|
|
219
|
+
.filter((item) => item.scope === "global" || item.scope === `project:${key}`);
|
|
229
220
|
if (matches.length === 0) return { content: [{ type: "text", text: `No relevant knowledge found for: ${params.query}` }], details: {} };
|
|
230
221
|
return {
|
|
231
222
|
content: [{ type: "text", text: matches.map((item) => `- [${item.id}] ${item.title}: ${item.summary}`).join("\n") }],
|
|
@@ -236,10 +227,18 @@ export default function registerKnowledgeExtension(pi: ExtensionAPI): void {
|
|
|
236
227
|
},
|
|
237
228
|
}));
|
|
238
229
|
|
|
239
|
-
pi.on("session_start", (_event, ctx) =>
|
|
230
|
+
pi.on("session_start", (_event, ctx) => configureForContext(ctx));
|
|
231
|
+
|
|
232
|
+
pi.on("model_select", (_event, ctx) => {
|
|
233
|
+
runtime.context = ctx;
|
|
234
|
+
updateCapability();
|
|
235
|
+
});
|
|
240
236
|
|
|
241
237
|
pi.on("before_agent_start", (event, ctx) => {
|
|
242
|
-
|
|
238
|
+
runtime.context = ctx;
|
|
239
|
+
updateCapability();
|
|
240
|
+
const { key } = currentProject(ctx);
|
|
241
|
+
const snapshot = loadKnowledgeSnapshot(key);
|
|
243
242
|
const sections = [
|
|
244
243
|
snapshot.rulesPrompt ? `<hwcode_rules>\n${snapshot.rulesPrompt}\n</hwcode_rules>` : "",
|
|
245
244
|
snapshot.memoryPrompt ? `<hwcode_knowledge_index>\n${snapshot.memoryPrompt}\n</hwcode_knowledge_index>` : "",
|
|
@@ -249,31 +248,28 @@ export default function registerKnowledgeExtension(pi: ExtensionAPI): void {
|
|
|
249
248
|
});
|
|
250
249
|
|
|
251
250
|
pi.on("input", (_event, ctx) => {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
251
|
+
runtime.context = ctx;
|
|
252
|
+
cancelActiveReview();
|
|
253
|
+
updateCapability();
|
|
255
254
|
return undefined;
|
|
256
255
|
});
|
|
257
256
|
|
|
258
257
|
pi.on("agent_start", (_event, ctx) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
post({ type: "activity", state: "busy", dirty: true });
|
|
258
|
+
runtime.context = ctx;
|
|
259
|
+
cancelActiveReview();
|
|
262
260
|
});
|
|
263
261
|
|
|
264
262
|
pi.on("agent_settled", (_event, ctx) => {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
post({ type: "
|
|
263
|
+
runtime.context = ctx;
|
|
264
|
+
updateCapability();
|
|
265
|
+
post({ type: "scan_now" });
|
|
268
266
|
});
|
|
269
267
|
|
|
270
|
-
pi.on("session_shutdown",
|
|
271
|
-
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
268
|
+
pi.on("session_shutdown", (event) => {
|
|
269
|
+
cancelActiveReview();
|
|
270
|
+
if (event.reason === "quit") {
|
|
271
|
+
runtime.context = undefined;
|
|
272
|
+
updateCapability();
|
|
275
273
|
}
|
|
276
|
-
worker = undefined;
|
|
277
|
-
activeContext = undefined;
|
|
278
274
|
});
|
|
279
275
|
}
|