memorix 1.1.13 → 1.2.0
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/CHANGELOG.md +19 -0
- package/README.md +3 -2
- package/README.zh-CN.md +3 -2
- package/dist/cli/index.js +36313 -31189
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/static/app.js +50 -30
- package/dist/index.js +5348 -674
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +3661 -293
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +19 -0
- package/dist/sdk.d.ts +1 -1
- package/dist/sdk.js +5346 -672
- package/dist/sdk.js.map +1 -1
- package/docs/1.2.0-CLAIM-LEDGER.md +72 -0
- package/docs/1.2.0-CODE-STATE.md +61 -0
- package/docs/1.2.0-DEVELOPMENT-CHARTER.md +255 -0
- package/docs/1.2.0-DYNAMIC-LIFECYCLE.md +71 -0
- package/docs/1.2.0-EVALUATION-HARNESS.md +51 -0
- package/docs/1.2.0-IMPLEMENTATION-PLAN.md +554 -0
- package/docs/1.2.0-KNOWLEDGE-WORKFLOW-RESEARCH.md +205 -0
- package/docs/1.2.0-KNOWLEDGE-WORKSPACE.md +68 -0
- package/docs/1.2.0-PRODUCT-STORY.md +234 -0
- package/docs/1.2.0-PROVIDER-QUALITY.md +189 -0
- package/docs/1.2.0-WORKFLOW-INHERITANCE.md +101 -0
- package/docs/1.2.0-WORKSET-RETRIEVAL.md +80 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +6 -3
- package/docs/API_REFERENCE.md +25 -6
- package/docs/CONFIGURATION.md +21 -3
- package/docs/README.md +17 -2
- package/docs/dev-log/progress.txt +120 -40
- package/llms-full.txt +16 -2
- package/llms.txt +9 -4
- package/package.json +3 -2
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/cli/capability-map.ts +1 -0
- package/src/cli/commands/codegraph.ts +112 -9
- package/src/cli/commands/context.ts +2 -0
- package/src/cli/commands/doctor.ts +73 -4
- package/src/cli/commands/knowledge.ts +282 -0
- package/src/cli/commands/serve-http.ts +12 -1
- package/src/cli/index.ts +3 -1
- package/src/cli/tui/App.tsx +1 -1
- package/src/cli/tui/Panels.tsx +8 -8
- package/src/cli/tui/theme.ts +1 -1
- package/src/cli/tui/views/GraphView.tsx +8 -7
- package/src/cli/tui/views/KnowledgeView.tsx +9 -9
- package/src/codegraph/auto-context.ts +171 -9
- package/src/codegraph/code-state.ts +95 -0
- package/src/codegraph/context-pack.ts +82 -1
- package/src/codegraph/external-provider.ts +581 -0
- package/src/codegraph/lite-provider.ts +64 -19
- package/src/codegraph/project-context.ts +9 -1
- package/src/codegraph/store.ts +154 -6
- package/src/codegraph/types.ts +117 -0
- package/src/config/resolved-config.ts +28 -0
- package/src/config/toml-loader.ts +3 -0
- package/src/config/yaml-loader.ts +6 -0
- package/src/dashboard/server.ts +15 -1
- package/src/evaluation/workset-evaluation.ts +120 -0
- package/src/hooks/handler.ts +48 -6
- package/src/knowledge/claim-store.ts +267 -0
- package/src/knowledge/claims.ts +537 -0
- package/src/knowledge/markdown.ts +129 -0
- package/src/knowledge/types.ts +157 -0
- package/src/knowledge/wiki.ts +524 -0
- package/src/knowledge/workflow-store.ts +168 -0
- package/src/knowledge/workflow-types.ts +95 -0
- package/src/knowledge/workflows.ts +743 -0
- package/src/knowledge/workset.ts +515 -0
- package/src/knowledge/workspace-store.ts +220 -0
- package/src/knowledge/workspace-types.ts +106 -0
- package/src/knowledge/workspace.ts +220 -0
- package/src/memory/observations.ts +19 -0
- package/src/runtime/control-plane-maintenance.ts +5 -0
- package/src/runtime/isolated-maintenance.ts +5 -0
- package/src/runtime/lifecycle-status.ts +102 -0
- package/src/runtime/lifecycle.ts +107 -0
- package/src/runtime/maintenance-jobs.ts +5 -0
- package/src/runtime/project-maintenance.ts +190 -0
- package/src/server/tool-profile.ts +3 -2
- package/src/server.ts +354 -14
- package/src/store/file-lock.ts +24 -4
- package/src/store/sqlite-db.ts +307 -0
- package/src/wiki/generator.ts +4 -2
- package/src/wiki/knowledge-graph.ts +7 -4
- package/src/wiki/types.ts +16 -4
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import { countTextTokens, truncateToTokenBudget } from '../compact/token-budget.js';
|
|
2
|
+
import { sanitizeCredentials } from '../memory/secret-filter.js';
|
|
3
|
+
import { ClaimStore } from './claim-store.js';
|
|
4
|
+
import { selectClaimsForTask } from './claims.js';
|
|
5
|
+
import { KnowledgeWorkspaceStore } from './workspace-store.js';
|
|
6
|
+
import { loadKnowledgeWorkspace } from './workspace.js';
|
|
7
|
+
import { WorkflowStore } from './workflow-store.js';
|
|
8
|
+
import { selectWorkflows } from './workflows.js';
|
|
9
|
+
import type { CodeGraphProviderQuality, ExternalCodeGraphOutline } from '../codegraph/types.js';
|
|
10
|
+
import type { KnowledgeClaim, ClaimEvidenceRef } from './types.js';
|
|
11
|
+
import type { KnowledgePageRecord, KnowledgeWorkspace } from './workspace-types.js';
|
|
12
|
+
import type { WorkflowSelection } from './workflow-types.js';
|
|
13
|
+
|
|
14
|
+
export type WorksetCautionKind =
|
|
15
|
+
| 'dirty-worktree'
|
|
16
|
+
| 'incomplete-scan'
|
|
17
|
+
| 'suspect-code-memory'
|
|
18
|
+
| 'stale-code-memory'
|
|
19
|
+
| 'claim-conflict'
|
|
20
|
+
| 'claim-needs-review'
|
|
21
|
+
| 'workflow-failed-verification'
|
|
22
|
+
| 'codegraph-refresh-queued'
|
|
23
|
+
| 'codegraph-refresh-failed'
|
|
24
|
+
| 'external-codegraph-fallback';
|
|
25
|
+
|
|
26
|
+
export interface WorksetCaution {
|
|
27
|
+
kind: WorksetCautionKind;
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface WorksetClaim {
|
|
32
|
+
id: string;
|
|
33
|
+
assertion: string;
|
|
34
|
+
status: KnowledgeClaim['status'];
|
|
35
|
+
reviewState: KnowledgeClaim['reviewState'];
|
|
36
|
+
confidence: number;
|
|
37
|
+
evidenceRefs: string[];
|
|
38
|
+
reason: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface WorksetPage {
|
|
42
|
+
id: string;
|
|
43
|
+
title: string;
|
|
44
|
+
relativePath: string;
|
|
45
|
+
claimIds: string[];
|
|
46
|
+
reason: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface WorksetWorkflow {
|
|
50
|
+
id: string;
|
|
51
|
+
title: string;
|
|
52
|
+
reason: string[];
|
|
53
|
+
firstPhase: {
|
|
54
|
+
id: string;
|
|
55
|
+
title: string;
|
|
56
|
+
instructions: string;
|
|
57
|
+
};
|
|
58
|
+
verificationGates: string[];
|
|
59
|
+
cautions: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface WorksetMemorySource {
|
|
63
|
+
id: number;
|
|
64
|
+
title: string;
|
|
65
|
+
type: string;
|
|
66
|
+
status: 'current' | 'suspect' | 'stale' | 'unbound';
|
|
67
|
+
path?: string;
|
|
68
|
+
symbol?: string;
|
|
69
|
+
reason?: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface TaskWorkset {
|
|
73
|
+
version: '1.2';
|
|
74
|
+
task: string;
|
|
75
|
+
lens: string;
|
|
76
|
+
currentFacts: string[];
|
|
77
|
+
codeState?: string;
|
|
78
|
+
startHere: string[];
|
|
79
|
+
/** Bounded task-specific relations from a validated local semantic graph. */
|
|
80
|
+
semanticCode?: ExternalCodeGraphOutline;
|
|
81
|
+
reliableMemory: WorksetMemorySource[];
|
|
82
|
+
cautionMemory: WorksetMemorySource[];
|
|
83
|
+
hiddenCautionMemoryCount: number;
|
|
84
|
+
claims: WorksetClaim[];
|
|
85
|
+
pages: WorksetPage[];
|
|
86
|
+
workflows: WorksetWorkflow[];
|
|
87
|
+
cautions: WorksetCaution[];
|
|
88
|
+
verification: string[];
|
|
89
|
+
evidenceIds: string[];
|
|
90
|
+
provenance: {
|
|
91
|
+
snapshotId?: string;
|
|
92
|
+
sourceEpoch?: number;
|
|
93
|
+
workspaceId?: string;
|
|
94
|
+
codeProvider?: CodeGraphProviderQuality;
|
|
95
|
+
};
|
|
96
|
+
budget: {
|
|
97
|
+
maxTokens: number;
|
|
98
|
+
tokenCount: number;
|
|
99
|
+
omitted: string[];
|
|
100
|
+
};
|
|
101
|
+
prompt: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface BuildTaskWorksetInput {
|
|
105
|
+
projectId: string;
|
|
106
|
+
dataDir: string;
|
|
107
|
+
task?: string;
|
|
108
|
+
lens: string;
|
|
109
|
+
currentFacts?: string[];
|
|
110
|
+
codeState?: string;
|
|
111
|
+
startHere: string[];
|
|
112
|
+
semanticCode?: ExternalCodeGraphOutline;
|
|
113
|
+
providerQuality?: CodeGraphProviderQuality;
|
|
114
|
+
reliableMemory?: WorksetMemorySource[];
|
|
115
|
+
cautionMemory?: WorksetMemorySource[];
|
|
116
|
+
hiddenCautionMemoryCount?: number;
|
|
117
|
+
verificationHints: string[];
|
|
118
|
+
worktreeDirty: boolean;
|
|
119
|
+
snapshot?: {
|
|
120
|
+
id?: string;
|
|
121
|
+
sourceEpoch?: number;
|
|
122
|
+
worktreeState?: 'clean' | 'dirty' | 'unavailable';
|
|
123
|
+
incomplete?: boolean;
|
|
124
|
+
};
|
|
125
|
+
freshness?: {
|
|
126
|
+
suspect: number;
|
|
127
|
+
stale: number;
|
|
128
|
+
};
|
|
129
|
+
runtimeCautions?: WorksetCaution[];
|
|
130
|
+
maxTokens?: number;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function unique(values: string[]): string[] {
|
|
134
|
+
return [...new Set(values.filter(Boolean))];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function short(text: string, budget = 28): string {
|
|
138
|
+
const safe = sanitizeCredentials(text).replace(/\s+/g, ' ').trim();
|
|
139
|
+
return countTextTokens(safe) <= budget ? safe : truncateToTokenBudget(safe, budget);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function claimAssertion(claim: KnowledgeClaim): string {
|
|
143
|
+
return short([claim.subject, claim.predicate, claim.objectValue].join(' '));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function preferredWorkspace(projectId: string, dataDir: string): Promise<KnowledgeWorkspace | undefined> {
|
|
147
|
+
const [versioned, local] = await Promise.all([
|
|
148
|
+
loadKnowledgeWorkspace({ projectId, dataDir, mode: 'versioned' }),
|
|
149
|
+
loadKnowledgeWorkspace({ projectId, dataDir, mode: 'local' }),
|
|
150
|
+
]);
|
|
151
|
+
return versioned ?? local;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function mapClaimCaution(kind: string): WorksetCaution | undefined {
|
|
155
|
+
if (kind === 'claim-conflict') {
|
|
156
|
+
return { kind, message: 'A task-matching claim conflicts with another active assertion.' };
|
|
157
|
+
}
|
|
158
|
+
if (kind === 'claim-needs-review') {
|
|
159
|
+
return { kind, message: 'A task-matching claim needs review before it is treated as current.' };
|
|
160
|
+
}
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function snapshotCautions(input: BuildTaskWorksetInput): WorksetCaution[] {
|
|
165
|
+
const cautions: WorksetCaution[] = [];
|
|
166
|
+
if (input.worktreeDirty || input.snapshot?.worktreeState === 'dirty') {
|
|
167
|
+
cautions.push({
|
|
168
|
+
kind: 'dirty-worktree',
|
|
169
|
+
message: 'The Git worktree has uncommitted changes; current files outrank stored knowledge.',
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (input.snapshot?.incomplete) {
|
|
173
|
+
cautions.push({
|
|
174
|
+
kind: 'incomplete-scan',
|
|
175
|
+
message: 'The latest Code Memory scan is incomplete; inspect skipped or changed code directly.',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if ((input.freshness?.suspect ?? 0) > 0) {
|
|
179
|
+
cautions.push({
|
|
180
|
+
kind: 'suspect-code-memory',
|
|
181
|
+
message: String(input.freshness!.suspect) + ' suspect code-memory link(s) need current-source verification.',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
if ((input.freshness?.stale ?? 0) > 0) {
|
|
185
|
+
cautions.push({
|
|
186
|
+
kind: 'stale-code-memory',
|
|
187
|
+
message: String(input.freshness!.stale) + ' code-memory link(s) are stale and should not guide edits without rereading code.',
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return cautions;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function pageMatchesClaim(page: KnowledgePageRecord, claimIds: Set<string>): boolean {
|
|
194
|
+
return page.status === 'active'
|
|
195
|
+
&& page.reviewState === 'approved'
|
|
196
|
+
&& page.claimIds.some(claimId => claimIds.has(claimId));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function evidenceIdsForClaim(claim: KnowledgeClaim, evidence: ClaimEvidenceRef[]): string[] {
|
|
200
|
+
return unique([
|
|
201
|
+
'claim:' + claim.id,
|
|
202
|
+
...evidence.map(item => item.evidenceKind + ':' + item.evidenceId),
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function workflowOutput(selection: WorkflowSelection): WorksetWorkflow {
|
|
207
|
+
const gates = unique([
|
|
208
|
+
...selection.workflow.verificationGates,
|
|
209
|
+
...selection.firstPhase.verificationGates,
|
|
210
|
+
]).slice(0, 3);
|
|
211
|
+
return {
|
|
212
|
+
id: selection.workflow.id,
|
|
213
|
+
title: selection.workflow.title,
|
|
214
|
+
reason: selection.reasons,
|
|
215
|
+
firstPhase: {
|
|
216
|
+
id: selection.firstPhase.id,
|
|
217
|
+
title: selection.firstPhase.title,
|
|
218
|
+
instructions: short(selection.firstPhase.instructions || selection.firstPhase.title, 28),
|
|
219
|
+
},
|
|
220
|
+
verificationGates: gates.map(gate => short(gate, 20)),
|
|
221
|
+
cautions: selection.cautions.map(caution => short(caution, 22)),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function appendLine(
|
|
226
|
+
lines: string[],
|
|
227
|
+
candidate: string,
|
|
228
|
+
maxTokens: number,
|
|
229
|
+
omitted: string[],
|
|
230
|
+
omittedKind: string,
|
|
231
|
+
): boolean {
|
|
232
|
+
const next = lines.length ? lines.join('\n') + '\n' + candidate : candidate;
|
|
233
|
+
if (countTextTokens(next) <= maxTokens) {
|
|
234
|
+
lines.push(candidate);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
omitted.push(omittedKind);
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Render a bounded prompt from whole evidence items. It never cuts a page,
|
|
243
|
+
* workflow, or claim body into an untraceable partial fragment.
|
|
244
|
+
*/
|
|
245
|
+
export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'budget'> & {
|
|
246
|
+
budget?: Partial<TaskWorkset['budget']>;
|
|
247
|
+
}): { prompt: string; tokenCount: number; omitted: string[] } {
|
|
248
|
+
const maxTokens = input.budget?.maxTokens ?? 180;
|
|
249
|
+
const omitted: string[] = [];
|
|
250
|
+
const lines: string[] = ['Memorix Autopilot Brief'];
|
|
251
|
+
const task = short(input.task || 'Continue the current task.', 34);
|
|
252
|
+
appendLine(lines, 'Task: ' + task, maxTokens, omitted, 'task-detail');
|
|
253
|
+
appendLine(lines, 'Task lens: ' + input.lens, maxTokens, omitted, 'lens');
|
|
254
|
+
|
|
255
|
+
if (input.cautions.length > 0 || input.cautionMemory.length > 0) {
|
|
256
|
+
appendLine(lines, '', maxTokens, omitted, 'caution-heading');
|
|
257
|
+
appendLine(lines, 'Cautions', maxTokens, omitted, 'caution-heading');
|
|
258
|
+
for (const caution of input.cautions.slice(0, 6)) {
|
|
259
|
+
appendLine(lines, '- ' + short(caution.message, 22), maxTokens, omitted, 'caution');
|
|
260
|
+
}
|
|
261
|
+
for (const memory of input.cautionMemory.slice(0, 3)) {
|
|
262
|
+
const location = memory.path
|
|
263
|
+
? memory.path + (memory.symbol ? '#' + memory.symbol : '')
|
|
264
|
+
: 'no current code location';
|
|
265
|
+
const reason = memory.reason ? '; ' + short(memory.reason, 14) : '';
|
|
266
|
+
appendLine(
|
|
267
|
+
lines,
|
|
268
|
+
'- #' + memory.id + ' ' + memory.status + ': ' + short(memory.title, 18) + ' (' + location + reason + ')',
|
|
269
|
+
maxTokens,
|
|
270
|
+
omitted,
|
|
271
|
+
'caution-memory',
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
if (input.hiddenCautionMemoryCount > 0) {
|
|
275
|
+
appendLine(lines, '- Other unrelated warning details are hidden for this task.', maxTokens, omitted, 'hidden-caution-count');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (input.currentFacts.length > 0) {
|
|
280
|
+
appendLine(lines, '', maxTokens, omitted, 'facts-heading');
|
|
281
|
+
appendLine(lines, 'Current project facts', maxTokens, omitted, 'facts-heading');
|
|
282
|
+
for (const fact of input.currentFacts.slice(0, 4)) {
|
|
283
|
+
appendLine(lines, '- ' + short(fact, 40), maxTokens, omitted, 'current-fact');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (input.codeState) {
|
|
288
|
+
appendLine(lines, '', maxTokens, omitted, 'state-heading');
|
|
289
|
+
appendLine(lines, 'Project state', maxTokens, omitted, 'state-heading');
|
|
290
|
+
appendLine(lines, input.codeState, maxTokens, omitted, 'code-state');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
294
|
+
appendLine(lines, '', maxTokens, omitted, 'semantic-code-heading');
|
|
295
|
+
appendLine(lines, 'Semantic code outline', maxTokens, omitted, 'semantic-code-heading');
|
|
296
|
+
for (const relation of input.semanticCode.relations.slice(0, 2)) {
|
|
297
|
+
const location = relation.from.path + (relation.line ? ':' + relation.line : '');
|
|
298
|
+
appendLine(
|
|
299
|
+
lines,
|
|
300
|
+
'- ' + location + ': ' + short(relation.from.name, 12) + ' ' + short(relation.kind, 8) + ' ' + short(relation.to.name, 12),
|
|
301
|
+
maxTokens,
|
|
302
|
+
omitted,
|
|
303
|
+
'semantic-relation',
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
if (input.semanticCode.relations.length === 0) {
|
|
307
|
+
for (const entry of input.semanticCode.entryPoints.slice(0, 2)) {
|
|
308
|
+
const location = entry.path + (entry.startLine ? ':' + entry.startLine : '');
|
|
309
|
+
appendLine(
|
|
310
|
+
lines,
|
|
311
|
+
'- ' + location + ': ' + short(entry.name, 16) + ' (' + short(entry.kind, 8) + ')',
|
|
312
|
+
maxTokens,
|
|
313
|
+
omitted,
|
|
314
|
+
'semantic-entry',
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (input.startHere.length > 0) {
|
|
321
|
+
appendLine(lines, '', maxTokens, omitted, 'start-heading');
|
|
322
|
+
appendLine(lines, 'Start here', maxTokens, omitted, 'start-heading');
|
|
323
|
+
for (const source of input.startHere.slice(0, 5)) {
|
|
324
|
+
appendLine(lines, '- ' + source, maxTokens, omitted, 'start-here');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (input.reliableMemory.length > 0) {
|
|
329
|
+
appendLine(lines, '', maxTokens, omitted, 'memory-heading');
|
|
330
|
+
appendLine(lines, 'Reliable memory', maxTokens, omitted, 'memory-heading');
|
|
331
|
+
for (const memory of input.reliableMemory.slice(0, 3)) {
|
|
332
|
+
const location = memory.path
|
|
333
|
+
? memory.path + (memory.symbol ? '#' + memory.symbol : '')
|
|
334
|
+
: 'no current code location';
|
|
335
|
+
appendLine(
|
|
336
|
+
lines,
|
|
337
|
+
'- #' + memory.id + ' ' + memory.type + ': ' + short(memory.title, 18) + ' (' + location + ')',
|
|
338
|
+
maxTokens,
|
|
339
|
+
omitted,
|
|
340
|
+
'reliable-memory',
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (input.claims.length > 0 || input.pages.length > 0) {
|
|
346
|
+
appendLine(lines, '', maxTokens, omitted, 'knowledge-heading');
|
|
347
|
+
appendLine(lines, 'Project knowledge', maxTokens, omitted, 'knowledge-heading');
|
|
348
|
+
for (const claim of input.claims.slice(0, 3)) {
|
|
349
|
+
appendLine(lines, '- ' + claim.assertion + ' [' + claim.id + ']', maxTokens, omitted, 'claim');
|
|
350
|
+
}
|
|
351
|
+
for (const page of input.pages.slice(0, 2)) {
|
|
352
|
+
appendLine(lines, '- page: ' + page.relativePath, maxTokens, omitted, 'knowledge-page');
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (input.workflows.length > 0) {
|
|
357
|
+
appendLine(lines, '', maxTokens, omitted, 'workflow-heading');
|
|
358
|
+
appendLine(lines, 'Project workflow', maxTokens, omitted, 'workflow-heading');
|
|
359
|
+
for (const workflow of input.workflows.slice(0, 2)) {
|
|
360
|
+
appendLine(
|
|
361
|
+
lines,
|
|
362
|
+
'- ' + workflow.title + ': ' + workflow.firstPhase.title + ' - ' + workflow.firstPhase.instructions,
|
|
363
|
+
maxTokens,
|
|
364
|
+
omitted,
|
|
365
|
+
'workflow',
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (input.verification.length > 0) {
|
|
371
|
+
appendLine(lines, '', maxTokens, omitted, 'verification-heading');
|
|
372
|
+
appendLine(lines, 'Verify', maxTokens, omitted, 'verification-heading');
|
|
373
|
+
for (const check of input.verification.slice(0, 4)) {
|
|
374
|
+
appendLine(lines, '- ' + short(check, 20), maxTokens, omitted, 'verification');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
return {
|
|
379
|
+
prompt: lines.join('\n'),
|
|
380
|
+
tokenCount: countTextTokens(lines.join('\n')),
|
|
381
|
+
omitted: unique(omitted),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Build a small, source-aware task Workset. Optional knowledge artifacts are
|
|
387
|
+
* treated as enrichment: absent or invalid artifacts never prevent a code
|
|
388
|
+
* context response.
|
|
389
|
+
*/
|
|
390
|
+
export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<TaskWorkset> {
|
|
391
|
+
const task = input.task?.trim() ?? '';
|
|
392
|
+
const maxTokens = Math.max(96, Math.min(Math.floor(input.maxTokens ?? 180), 320));
|
|
393
|
+
const cautions = [...(input.runtimeCautions ?? []), ...snapshotCautions(input)];
|
|
394
|
+
const claimStore = new ClaimStore();
|
|
395
|
+
await claimStore.init(input.dataDir);
|
|
396
|
+
const selection = task
|
|
397
|
+
? selectClaimsForTask(claimStore, {
|
|
398
|
+
projectId: input.projectId,
|
|
399
|
+
task,
|
|
400
|
+
limit: 3,
|
|
401
|
+
maxTokens: 68,
|
|
402
|
+
})
|
|
403
|
+
: { claims: [], cautions: [], tokenCount: 0, reasons: {} };
|
|
404
|
+
for (const caution of selection.cautions) {
|
|
405
|
+
const mapped = mapClaimCaution(caution);
|
|
406
|
+
if (mapped) cautions.push(mapped);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
const evidenceByClaim = new Map<string, ClaimEvidenceRef[]>();
|
|
410
|
+
for (const claim of selection.claims) {
|
|
411
|
+
evidenceByClaim.set(claim.id, claimStore.listEvidence(claim.id));
|
|
412
|
+
}
|
|
413
|
+
const claims: WorksetClaim[] = selection.claims.map(claim => ({
|
|
414
|
+
id: claim.id,
|
|
415
|
+
assertion: claimAssertion(claim),
|
|
416
|
+
status: claim.status,
|
|
417
|
+
reviewState: claim.reviewState,
|
|
418
|
+
confidence: claim.confidence,
|
|
419
|
+
evidenceRefs: evidenceByClaim.get(claim.id)!.map(item => item.id),
|
|
420
|
+
reason: selection.reasons[claim.id] ?? 'source-qualified task match',
|
|
421
|
+
}));
|
|
422
|
+
|
|
423
|
+
let workspace: KnowledgeWorkspace | undefined;
|
|
424
|
+
let pages: WorksetPage[] = [];
|
|
425
|
+
let workflows: WorksetWorkflow[] = [];
|
|
426
|
+
try {
|
|
427
|
+
workspace = await preferredWorkspace(input.projectId, input.dataDir);
|
|
428
|
+
if (workspace) {
|
|
429
|
+
const workspaceStore = new KnowledgeWorkspaceStore();
|
|
430
|
+
await workspaceStore.init(input.dataDir);
|
|
431
|
+
const selectedClaimIds = new Set(selection.claims.map(claim => claim.id));
|
|
432
|
+
pages = workspaceStore.listPages(workspace.id)
|
|
433
|
+
.filter(page => pageMatchesClaim(page, selectedClaimIds))
|
|
434
|
+
.slice(0, 2)
|
|
435
|
+
.map(page => ({
|
|
436
|
+
id: page.id,
|
|
437
|
+
title: page.title,
|
|
438
|
+
relativePath: page.relativePath,
|
|
439
|
+
claimIds: page.claimIds.filter(claimId => selectedClaimIds.has(claimId)),
|
|
440
|
+
reason: 'published page links to a selected claim',
|
|
441
|
+
}));
|
|
442
|
+
|
|
443
|
+
if (task) {
|
|
444
|
+
const workflowStore = new WorkflowStore();
|
|
445
|
+
await workflowStore.init(input.dataDir);
|
|
446
|
+
const selected = selectWorkflows({
|
|
447
|
+
workflows: workflowStore.listWorkflows(workspace.id, 'active'),
|
|
448
|
+
task,
|
|
449
|
+
projectId: input.projectId,
|
|
450
|
+
store: workflowStore,
|
|
451
|
+
limit: 2,
|
|
452
|
+
});
|
|
453
|
+
workflows = selected.map(workflowOutput);
|
|
454
|
+
for (const workflow of workflows) {
|
|
455
|
+
for (const caution of workflow.cautions) {
|
|
456
|
+
cautions.push({ kind: 'workflow-failed-verification', message: caution });
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
} catch {
|
|
462
|
+
// Knowledge is optional. Existing Code Memory remains usable without it.
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const evidenceIds = unique(selection.claims.flatMap(claim => evidenceIdsForClaim(
|
|
466
|
+
claim,
|
|
467
|
+
evidenceByClaim.get(claim.id) ?? [],
|
|
468
|
+
)));
|
|
469
|
+
const verification = unique([
|
|
470
|
+
...workflows.flatMap(workflow => workflow.verificationGates),
|
|
471
|
+
...input.verificationHints,
|
|
472
|
+
]).slice(0, 4);
|
|
473
|
+
const normalizedCautions = unique(cautions.map(caution => caution.kind))
|
|
474
|
+
.map(kind => cautions.find(caution => caution.kind === kind)!)
|
|
475
|
+
.slice(0, 6);
|
|
476
|
+
const base = {
|
|
477
|
+
version: '1.2' as const,
|
|
478
|
+
task,
|
|
479
|
+
lens: input.lens,
|
|
480
|
+
currentFacts: input.currentFacts?.map(fact => fact.startsWith('Historical note:')
|
|
481
|
+
? short(fact, 48)
|
|
482
|
+
: short(fact, 28)).slice(0, 4) ?? [],
|
|
483
|
+
...(input.codeState ? { codeState: short(input.codeState, 28) } : {}),
|
|
484
|
+
startHere: unique(input.startHere).slice(0, 5),
|
|
485
|
+
...(input.semanticCode ? { semanticCode: input.semanticCode } : {}),
|
|
486
|
+
reliableMemory: input.reliableMemory?.slice(0, 3) ?? [],
|
|
487
|
+
cautionMemory: input.cautionMemory?.slice(0, 3) ?? [],
|
|
488
|
+
hiddenCautionMemoryCount: input.hiddenCautionMemoryCount ?? 0,
|
|
489
|
+
claims,
|
|
490
|
+
pages,
|
|
491
|
+
workflows,
|
|
492
|
+
cautions: normalizedCautions,
|
|
493
|
+
verification,
|
|
494
|
+
evidenceIds,
|
|
495
|
+
provenance: {
|
|
496
|
+
...(input.snapshot?.id ? { snapshotId: input.snapshot.id } : {}),
|
|
497
|
+
...(input.snapshot?.sourceEpoch !== undefined ? { sourceEpoch: input.snapshot.sourceEpoch } : {}),
|
|
498
|
+
...(workspace ? { workspaceId: workspace.id } : {}),
|
|
499
|
+
...(input.providerQuality ? { codeProvider: input.providerQuality } : {}),
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
const rendered = renderTaskWorksetPrompt({
|
|
503
|
+
...base,
|
|
504
|
+
budget: { maxTokens },
|
|
505
|
+
});
|
|
506
|
+
return {
|
|
507
|
+
...base,
|
|
508
|
+
budget: {
|
|
509
|
+
maxTokens,
|
|
510
|
+
tokenCount: rendered.tokenCount,
|
|
511
|
+
omitted: rendered.omitted,
|
|
512
|
+
},
|
|
513
|
+
prompt: rendered.prompt,
|
|
514
|
+
};
|
|
515
|
+
}
|