memorix 1.4.1 → 1.4.2
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/dist/cli/index.js +1165 -464
- package/dist/index.js +933 -316
- package/dist/maintenance-runner.js +514 -159
- package/dist/media-video-runner.js +514 -159
- package/dist/memcode-runtime/CHANGELOG.md +19 -0
- package/dist/sdk.js +932 -315
- package/dist/vector-backfill-runner.js +514 -159
- package/docs/1.4.2-EVIDENCE-GOVERNED-MEMORY-SPEC.md +434 -0
- package/package.json +1 -1
- package/plugins/claude/memorix/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/copilot/memorix/plugin.json +1 -1
- package/plugins/gemini/memorix/gemini-extension.json +1 -1
- package/plugins/omp/memorix/package.json +1 -1
- package/plugins/openclaw/memorix/.codex-plugin/plugin.json +1 -1
- package/plugins/pi/memorix/package.json +1 -1
- package/server.json +2 -2
- package/src/cli/commands/codegraph.ts +83 -2
- package/src/codegraph/auto-context.ts +20 -0
- package/src/codegraph/external-provider.ts +1 -0
- package/src/codegraph/store.ts +216 -5
- package/src/codegraph/types.ts +42 -0
- package/src/knowledge/context-assembly.ts +25 -0
- package/src/knowledge/governor.ts +156 -0
- package/src/knowledge/outcome-store.ts +102 -0
- package/src/knowledge/outcome-types.ts +49 -0
- package/src/knowledge/workflows.ts +32 -1
- package/src/knowledge/workset.ts +203 -13
- package/src/store/sqlite-db.ts +42 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append-only evidence about whether a context artifact helped or was
|
|
3
|
+
* contradicted. Signals never replace the artifact they describe.
|
|
4
|
+
*/
|
|
5
|
+
export const MEMORY_OUTCOME_CANDIDATE_KINDS = ['claim', 'durable-memory', 'workflow'] as const;
|
|
6
|
+
export type MemoryOutcomeCandidateKind = typeof MEMORY_OUTCOME_CANDIDATE_KINDS[number];
|
|
7
|
+
|
|
8
|
+
export const MEMORY_OUTCOME_SIGNAL_KINDS = [
|
|
9
|
+
'verification-passed',
|
|
10
|
+
'verification-failed',
|
|
11
|
+
'verified-reuse',
|
|
12
|
+
'user-pin',
|
|
13
|
+
'user-correction',
|
|
14
|
+
'source-changed',
|
|
15
|
+
'conflict-confirmed',
|
|
16
|
+
'manual-review',
|
|
17
|
+
] as const;
|
|
18
|
+
export type MemoryOutcomeSignalKind = typeof MEMORY_OUTCOME_SIGNAL_KINDS[number];
|
|
19
|
+
|
|
20
|
+
export interface MemoryOutcomeSignal {
|
|
21
|
+
id: string;
|
|
22
|
+
projectId: string;
|
|
23
|
+
candidateKind: MemoryOutcomeCandidateKind;
|
|
24
|
+
candidateId: string;
|
|
25
|
+
kind: MemoryOutcomeSignalKind;
|
|
26
|
+
sourceRef: string;
|
|
27
|
+
snapshotId?: string;
|
|
28
|
+
detail?: string;
|
|
29
|
+
observedAt: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type OutcomeQuality = 'verified' | 'probationary' | 'degraded';
|
|
33
|
+
|
|
34
|
+
/** The newest auditable signal wins; missing signals leave lifecycle state in charge. */
|
|
35
|
+
export function qualityFromOutcome(signal?: Pick<MemoryOutcomeSignal, 'kind'>): OutcomeQuality | undefined {
|
|
36
|
+
if (!signal) return undefined;
|
|
37
|
+
if (signal.kind === 'verification-failed'
|
|
38
|
+
|| signal.kind === 'user-correction'
|
|
39
|
+
|| signal.kind === 'source-changed'
|
|
40
|
+
|| signal.kind === 'conflict-confirmed') {
|
|
41
|
+
return 'degraded';
|
|
42
|
+
}
|
|
43
|
+
if (signal.kind === 'verification-passed'
|
|
44
|
+
|| signal.kind === 'verified-reuse'
|
|
45
|
+
|| signal.kind === 'user-pin') {
|
|
46
|
+
return 'verified';
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
@@ -7,6 +7,8 @@ import { atomicWriteFile, withFileLock } from '../store/file-lock.js';
|
|
|
7
7
|
import { WorkflowSyncer } from '../workspace/workflow-sync.js';
|
|
8
8
|
import { getKnowledgeWorkspacePaths, resolveKnowledgeWorkspaceFile } from './workspace.js';
|
|
9
9
|
import { WorkflowStore } from './workflow-store.js';
|
|
10
|
+
import { OutcomeStore } from './outcome-store.js';
|
|
11
|
+
import type { MemoryOutcomeCandidateKind, MemoryOutcomeSignalKind } from './outcome-types.js';
|
|
10
12
|
import { containsTaskKeyword } from '../codegraph/task-lens.js';
|
|
11
13
|
import type {
|
|
12
14
|
WorkflowAdapterPreview,
|
|
@@ -751,7 +753,36 @@ export async function recordWorkflowRun(input: {
|
|
|
751
753
|
throw new Error('Workflow was not found for this workspace');
|
|
752
754
|
}
|
|
753
755
|
if (!input.run.task.trim()) throw new Error('Workflow run task is required');
|
|
754
|
-
|
|
756
|
+
const run = store.recordRun(input.run);
|
|
757
|
+
const kind: MemoryOutcomeSignalKind | undefined = run.outcome === 'passed' && run.verificationVerdict === 'passed'
|
|
758
|
+
? 'verification-passed'
|
|
759
|
+
: run.outcome === 'failed' || run.verificationVerdict === 'failed'
|
|
760
|
+
? 'verification-failed'
|
|
761
|
+
: undefined;
|
|
762
|
+
if (!kind) return run;
|
|
763
|
+
|
|
764
|
+
const evidence = new Map<string, MemoryOutcomeCandidateKind>();
|
|
765
|
+
evidence.set(workflow.id, 'workflow');
|
|
766
|
+
for (const reference of run.selectedEvidence) {
|
|
767
|
+
const match = /^(claim|durable):(.+)$/.exec(reference);
|
|
768
|
+
if (!match) continue;
|
|
769
|
+
evidence.set(match[2], match[1] === 'claim' ? 'claim' : 'durable-memory');
|
|
770
|
+
}
|
|
771
|
+
const outcomes = new OutcomeStore();
|
|
772
|
+
await outcomes.init(workflowStoreDataDir(input.workspace));
|
|
773
|
+
for (const [candidateId, candidateKind] of evidence) {
|
|
774
|
+
outcomes.record({
|
|
775
|
+
projectId: run.projectId,
|
|
776
|
+
candidateKind,
|
|
777
|
+
candidateId,
|
|
778
|
+
kind,
|
|
779
|
+
sourceRef: 'workflow-run:' + run.id,
|
|
780
|
+
...(run.startingSnapshotId ? { snapshotId: run.startingSnapshotId } : {}),
|
|
781
|
+
...(run.failureReason ? { detail: run.failureReason } : {}),
|
|
782
|
+
observedAt: run.completedAt ?? run.startedAt,
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
return run;
|
|
755
786
|
}
|
|
756
787
|
|
|
757
788
|
export async function selectWorkspaceWorkflows(input: {
|
package/src/knowledge/workset.ts
CHANGED
|
@@ -5,8 +5,11 @@ import { selectClaimsForTask } from './claims.js';
|
|
|
5
5
|
import { KnowledgeWorkspaceStore } from './workspace-store.js';
|
|
6
6
|
import { loadKnowledgeWorkspace } from './workspace.js';
|
|
7
7
|
import { WorkflowStore } from './workflow-store.js';
|
|
8
|
+
import { OutcomeStore } from './outcome-store.js';
|
|
9
|
+
import { qualityFromOutcome, type OutcomeQuality } from './outcome-types.js';
|
|
8
10
|
import { selectWorkflows } from './workflows.js';
|
|
9
11
|
import { renderLongTermMemorySummary, selectLongTermMemoriesForTask } from '../memory/long-term.js';
|
|
12
|
+
import { governContextCandidates, type GovernancePlan } from './governor.js';
|
|
10
13
|
import type {
|
|
11
14
|
ContextCandidateFreshness,
|
|
12
15
|
ContextCandidateKind,
|
|
@@ -86,6 +89,7 @@ export interface WorksetLongTermMemory {
|
|
|
86
89
|
kind: LongTermMemory['kind'];
|
|
87
90
|
scope: LongTermMemory['scope'];
|
|
88
91
|
state: LongTermMemory['state'];
|
|
92
|
+
quality: OutcomeQuality;
|
|
89
93
|
title: string;
|
|
90
94
|
summary: string;
|
|
91
95
|
evidenceRefs: string[];
|
|
@@ -117,6 +121,17 @@ export interface WorksetContinuation {
|
|
|
117
121
|
};
|
|
118
122
|
}
|
|
119
123
|
|
|
124
|
+
/** A small, exact file-level delta between the two latest complete Code State snapshots. */
|
|
125
|
+
export interface WorksetCodeEvolution {
|
|
126
|
+
fromSnapshotId: string;
|
|
127
|
+
toSnapshotId: string;
|
|
128
|
+
changes: Array<{ path: string; kind: 'added' | 'modified' | 'removed' }>;
|
|
129
|
+
directlyConnectedPaths: string[];
|
|
130
|
+
/** Current stale memory bindings directly tied to the changed file paths. */
|
|
131
|
+
affectedMemoryCount?: number;
|
|
132
|
+
truncated: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
120
135
|
export interface TaskWorkset {
|
|
121
136
|
version: '1.3';
|
|
122
137
|
task: string;
|
|
@@ -124,6 +139,7 @@ export interface TaskWorkset {
|
|
|
124
139
|
currentFacts: string[];
|
|
125
140
|
continuation?: WorksetContinuation;
|
|
126
141
|
codeState?: string;
|
|
142
|
+
codeEvolution?: WorksetCodeEvolution;
|
|
127
143
|
startHere: string[];
|
|
128
144
|
/** Bounded task-specific relations from a validated local semantic graph. */
|
|
129
145
|
semanticCode?: ExternalCodeGraphOutline;
|
|
@@ -161,6 +177,7 @@ export interface BuildTaskWorksetInput {
|
|
|
161
177
|
currentFacts?: string[];
|
|
162
178
|
continuation?: WorksetContinuation;
|
|
163
179
|
codeState?: string;
|
|
180
|
+
codeEvolution?: WorksetCodeEvolution;
|
|
164
181
|
startHere: string[];
|
|
165
182
|
semanticCode?: ExternalCodeGraphOutline;
|
|
166
183
|
providerQuality?: CodeGraphProviderQuality;
|
|
@@ -258,7 +275,7 @@ function pageMatchesClaim(page: KnowledgePageRecord, claimIds: Set<string>): boo
|
|
|
258
275
|
&& page.claimIds.some(claimId => claimIds.has(claimId));
|
|
259
276
|
}
|
|
260
277
|
|
|
261
|
-
function evidenceIdsForClaim(claim: KnowledgeClaim, evidence: ClaimEvidenceRef[]): string[] {
|
|
278
|
+
function evidenceIdsForClaim(claim: Pick<KnowledgeClaim, 'id'>, evidence: ClaimEvidenceRef[]): string[] {
|
|
262
279
|
return unique([
|
|
263
280
|
'claim:' + claim.id,
|
|
264
281
|
...evidence.map(item => item.evidenceKind + ':' + item.evidenceId),
|
|
@@ -353,6 +370,87 @@ function scheduledActions(cautions: WorksetCaution[]): string[] {
|
|
|
353
370
|
.map(caution => short(caution.message, 28));
|
|
354
371
|
}
|
|
355
372
|
|
|
373
|
+
function isGovernedDelivery(disposition: GovernancePlan['decisions'][number]['disposition']): boolean {
|
|
374
|
+
return disposition === 'include' || disposition === 'compact';
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Optional knowledge is where stale or over-confident context does the most
|
|
379
|
+
* harm. Qualify it before prompt rendering; current project facts and cautions
|
|
380
|
+
* remain visible so the agent can still inspect live source.
|
|
381
|
+
*/
|
|
382
|
+
function governOptionalArtifacts(input: {
|
|
383
|
+
reliableMemory: WorksetMemorySource[];
|
|
384
|
+
claims: WorksetClaim[];
|
|
385
|
+
durableMemory: WorksetLongTermMemory[];
|
|
386
|
+
workflows: WorksetWorkflow[];
|
|
387
|
+
}): {
|
|
388
|
+
plan: GovernancePlan;
|
|
389
|
+
reliableMemory: WorksetMemorySource[];
|
|
390
|
+
claims: WorksetClaim[];
|
|
391
|
+
durableMemory: WorksetLongTermMemory[];
|
|
392
|
+
workflows: WorksetWorkflow[];
|
|
393
|
+
} {
|
|
394
|
+
const candidates = [
|
|
395
|
+
...input.reliableMemory.map((memory, index) => ({
|
|
396
|
+
kind: 'memory' as const,
|
|
397
|
+
id: 'memory:' + memory.id,
|
|
398
|
+
estimatedTokens: 10,
|
|
399
|
+
relevance: Math.max(0.2, 0.9 - index * 0.1),
|
|
400
|
+
scopeAllowed: true,
|
|
401
|
+
freshness: freshnessForMemory(memory.status),
|
|
402
|
+
trust: 'historical' as const,
|
|
403
|
+
quality: memory.status === 'current' ? 'verified' as const : 'degraded' as const,
|
|
404
|
+
})),
|
|
405
|
+
...input.claims.map((claim, index) => ({
|
|
406
|
+
kind: 'claim' as const,
|
|
407
|
+
id: 'claim:' + claim.id,
|
|
408
|
+
estimatedTokens: 12,
|
|
409
|
+
relevance: Math.max(0.2, 0.95 - index * 0.1),
|
|
410
|
+
scopeAllowed: true,
|
|
411
|
+
freshness: claim.status === 'active' ? 'current' as const : 'unknown' as const,
|
|
412
|
+
trust: 'source-backed' as const,
|
|
413
|
+
quality: claim.reviewState === 'approved' ? 'verified' as const : 'probationary' as const,
|
|
414
|
+
conflict: claim.status === 'disputed' ? 'confirmed' as const : 'none' as const,
|
|
415
|
+
evidenceCount: claim.evidenceRefs.length,
|
|
416
|
+
})),
|
|
417
|
+
...input.durableMemory.map((memory, index) => ({
|
|
418
|
+
kind: 'durable-memory' as const,
|
|
419
|
+
id: 'durable:' + memory.id,
|
|
420
|
+
estimatedTokens: 14,
|
|
421
|
+
relevance: Math.max(0.2, 0.8 - index * 0.1),
|
|
422
|
+
scopeAllowed: true,
|
|
423
|
+
freshness: memory.state === 'approved' ? 'current' as const : 'unknown' as const,
|
|
424
|
+
trust: 'source-backed' as const,
|
|
425
|
+
quality: memory.quality,
|
|
426
|
+
evidenceCount: memory.evidenceRefs.length,
|
|
427
|
+
})),
|
|
428
|
+
...input.workflows.map((workflow, index) => ({
|
|
429
|
+
kind: 'workflow' as const,
|
|
430
|
+
id: 'workflow:' + workflow.id,
|
|
431
|
+
estimatedTokens: 14,
|
|
432
|
+
relevance: Math.max(0.2, 0.85 - index * 0.1),
|
|
433
|
+
scopeAllowed: true,
|
|
434
|
+
freshness: 'current' as const,
|
|
435
|
+
trust: 'source-backed' as const,
|
|
436
|
+
quality: 'verified' as const,
|
|
437
|
+
})),
|
|
438
|
+
];
|
|
439
|
+
// Prompt rendering owns the final whole-item token budget. This preflight
|
|
440
|
+
// only decides whether optional evidence is safe enough to render at all.
|
|
441
|
+
const plan = governContextCandidates(candidates, Number.MAX_SAFE_INTEGER);
|
|
442
|
+
const permitted = new Set(plan.decisions
|
|
443
|
+
.filter(decision => isGovernedDelivery(decision.disposition))
|
|
444
|
+
.map(decision => decision.candidate.id));
|
|
445
|
+
return {
|
|
446
|
+
plan,
|
|
447
|
+
reliableMemory: input.reliableMemory.filter(memory => permitted.has('memory:' + memory.id)),
|
|
448
|
+
claims: input.claims.filter(claim => permitted.has('claim:' + claim.id)),
|
|
449
|
+
durableMemory: input.durableMemory.filter(memory => permitted.has('durable:' + memory.id)),
|
|
450
|
+
workflows: input.workflows.filter(workflow => permitted.has('workflow:' + workflow.id)),
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
356
454
|
/**
|
|
357
455
|
* Render a bounded prompt from whole evidence items. It never cuts a page,
|
|
358
456
|
* workflow, or claim body into an untraceable partial fragment.
|
|
@@ -505,6 +603,57 @@ export function renderTaskWorksetPrompt(input: Omit<TaskWorkset, 'prompt' | 'bud
|
|
|
505
603
|
});
|
|
506
604
|
}
|
|
507
605
|
|
|
606
|
+
if (input.codeEvolution && input.codeEvolution.changes.length > 0) {
|
|
607
|
+
appendLine(lines, '', maxTokens, omitted, 'code-evolution-heading');
|
|
608
|
+
appendLine(lines, 'Code changes since prior scan', maxTokens, omitted, 'code-evolution-heading');
|
|
609
|
+
for (const change of input.codeEvolution.changes.slice(0, 3)) {
|
|
610
|
+
appendLine(
|
|
611
|
+
lines,
|
|
612
|
+
'- ' + change.kind + ': ' + change.path,
|
|
613
|
+
maxTokens,
|
|
614
|
+
omitted,
|
|
615
|
+
'code-evolution-change',
|
|
616
|
+
selected,
|
|
617
|
+
{
|
|
618
|
+
kind: 'code-state',
|
|
619
|
+
id: 'snapshot:' + input.codeEvolution.toSnapshotId,
|
|
620
|
+
reason: 'exact file hash change between complete CodeGraph snapshots',
|
|
621
|
+
freshness: 'current',
|
|
622
|
+
trust: 'source-backed',
|
|
623
|
+
},
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
for (const path of input.codeEvolution.directlyConnectedPaths.slice(0, 2)) {
|
|
627
|
+
appendLine(
|
|
628
|
+
lines,
|
|
629
|
+
'- connected now: ' + path,
|
|
630
|
+
maxTokens,
|
|
631
|
+
omitted,
|
|
632
|
+
'code-evolution-impact',
|
|
633
|
+
selected,
|
|
634
|
+
{
|
|
635
|
+
kind: 'code-state',
|
|
636
|
+
id: 'snapshot:' + input.codeEvolution.toSnapshotId,
|
|
637
|
+
reason: 'bounded current CodeGraph impact relation',
|
|
638
|
+
freshness: 'current',
|
|
639
|
+
trust: 'derived',
|
|
640
|
+
},
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
if ((input.codeEvolution.affectedMemoryCount ?? 0) > 0) {
|
|
644
|
+
appendLine(
|
|
645
|
+
lines,
|
|
646
|
+
'- ' + input.codeEvolution.affectedMemoryCount + ' stored memory link(s) reference this changed code; reread source before relying on their conclusions.',
|
|
647
|
+
maxTokens,
|
|
648
|
+
omitted,
|
|
649
|
+
'code-evolution-stale-memory',
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
if (input.codeEvolution.truncated) {
|
|
653
|
+
appendLine(lines, '- More connected files exist; inspect CodeGraph before assuming complete impact.', maxTokens, omitted, 'code-evolution-truncated');
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
508
657
|
if (input.semanticCode && (input.semanticCode.entryPoints.length > 0 || input.semanticCode.relations.length > 0)) {
|
|
509
658
|
appendLine(lines, '', maxTokens, omitted, 'semantic-code-heading');
|
|
510
659
|
appendLine(lines, 'Semantic code outline', maxTokens, omitted, 'semantic-code-heading');
|
|
@@ -716,6 +865,21 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
|
|
|
716
865
|
|
|
717
866
|
let durableMemory: WorksetLongTermMemory[] = [];
|
|
718
867
|
let durableEvidence: LongTermMemoryEvidence[] = [];
|
|
868
|
+
let outcomeStore: OutcomeStore | undefined;
|
|
869
|
+
try {
|
|
870
|
+
outcomeStore = new OutcomeStore();
|
|
871
|
+
await outcomeStore.init(input.dataDir);
|
|
872
|
+
} catch {
|
|
873
|
+
// Outcome evidence enriches the decision; normal current-source context
|
|
874
|
+
// remains usable if an older local database cannot expose it yet.
|
|
875
|
+
}
|
|
876
|
+
const claimQuality = outcomeStore
|
|
877
|
+
? outcomeStore.latestForCandidates(input.projectId, 'claim', claims.map(claim => claim.id))
|
|
878
|
+
: new Map();
|
|
879
|
+
for (const claim of claims) {
|
|
880
|
+
const outcomeQuality = qualityFromOutcome(claimQuality.get(claim.id));
|
|
881
|
+
if (outcomeQuality === 'degraded') claim.reviewState = 'needs-review';
|
|
882
|
+
}
|
|
719
883
|
if (task) {
|
|
720
884
|
try {
|
|
721
885
|
const selected = await selectLongTermMemoriesForTask({
|
|
@@ -726,11 +890,16 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
|
|
|
726
890
|
...(input.reader?.isTeamMember ? { isTeamMember: true } : {}),
|
|
727
891
|
limit: 3,
|
|
728
892
|
});
|
|
893
|
+
const durableQuality = outcomeStore
|
|
894
|
+
? outcomeStore.latestForCandidates(input.projectId, 'durable-memory', selected.map(item => item.memory.id))
|
|
895
|
+
: new Map();
|
|
729
896
|
durableMemory = selected.map(item => ({
|
|
730
897
|
id: item.memory.id,
|
|
731
898
|
kind: item.memory.kind,
|
|
732
899
|
scope: item.memory.scope,
|
|
733
900
|
state: item.memory.state,
|
|
901
|
+
quality: qualityFromOutcome(durableQuality.get(item.memory.id))
|
|
902
|
+
?? (item.memory.state === 'approved' ? 'verified' : 'probationary'),
|
|
734
903
|
title: item.memory.title,
|
|
735
904
|
summary: renderLongTermMemorySummary(item.memory, 20),
|
|
736
905
|
evidenceRefs: item.evidence.map(evidence => evidence.id),
|
|
@@ -785,13 +954,6 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
|
|
|
785
954
|
// Knowledge is optional. Existing Code Memory remains usable without it.
|
|
786
955
|
}
|
|
787
956
|
|
|
788
|
-
const evidenceIds = unique(selection.claims.flatMap(claim => evidenceIdsForClaim(
|
|
789
|
-
claim,
|
|
790
|
-
evidenceByClaim.get(claim.id) ?? [],
|
|
791
|
-
)).concat(
|
|
792
|
-
durableMemory.map(memory => 'durable:' + memory.id),
|
|
793
|
-
durableEvidence.map(evidence => 'durable-evidence:' + evidence.id),
|
|
794
|
-
));
|
|
795
957
|
const verification = unique([
|
|
796
958
|
...workflows.flatMap(workflow => workflow.verificationGates),
|
|
797
959
|
...input.verificationHints,
|
|
@@ -799,6 +961,22 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
|
|
|
799
961
|
const normalizedCautions = unique(cautions.map(caution => caution.kind))
|
|
800
962
|
.map(kind => cautions.find(caution => caution.kind === kind)!)
|
|
801
963
|
.slice(0, 6);
|
|
964
|
+
const governed = governOptionalArtifacts({
|
|
965
|
+
reliableMemory: input.reliableMemory?.slice(0, 3) ?? [],
|
|
966
|
+
claims,
|
|
967
|
+
durableMemory,
|
|
968
|
+
workflows,
|
|
969
|
+
});
|
|
970
|
+
const governedDurableIds = new Set(governed.durableMemory.map(memory => memory.id));
|
|
971
|
+
const evidenceIds = unique(governed.claims.flatMap(claim => evidenceIdsForClaim(
|
|
972
|
+
claim,
|
|
973
|
+
evidenceByClaim.get(claim.id) ?? [],
|
|
974
|
+
)).concat(
|
|
975
|
+
governed.durableMemory.map(memory => 'durable:' + memory.id),
|
|
976
|
+
durableEvidence
|
|
977
|
+
.filter(evidence => governedDurableIds.has(evidence.memoryId))
|
|
978
|
+
.map(evidence => 'durable-evidence:' + evidence.id),
|
|
979
|
+
));
|
|
802
980
|
const continuation = input.continuation
|
|
803
981
|
&& (
|
|
804
982
|
input.continuation.previousSession
|
|
@@ -838,15 +1016,16 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
|
|
|
838
1016
|
: short(fact, 28)).slice(0, 4) ?? [],
|
|
839
1017
|
...(continuation ? { continuation } : {}),
|
|
840
1018
|
...(input.codeState ? { codeState: short(input.codeState, 28) } : {}),
|
|
1019
|
+
...(input.codeEvolution ? { codeEvolution: input.codeEvolution } : {}),
|
|
841
1020
|
startHere: unique(input.startHere).slice(0, 5),
|
|
842
1021
|
...(input.semanticCode ? { semanticCode: input.semanticCode } : {}),
|
|
843
|
-
reliableMemory:
|
|
1022
|
+
reliableMemory: governed.reliableMemory,
|
|
844
1023
|
cautionMemory: input.cautionMemory?.slice(0, 3) ?? [],
|
|
845
1024
|
hiddenCautionMemoryCount: input.hiddenCautionMemoryCount ?? 0,
|
|
846
|
-
claims,
|
|
847
|
-
pages,
|
|
848
|
-
durableMemory,
|
|
849
|
-
workflows,
|
|
1025
|
+
claims: governed.claims,
|
|
1026
|
+
pages: pages.filter(page => page.claimIds.some(claimId => governed.claims.some(claim => claim.id === claimId))),
|
|
1027
|
+
durableMemory: governed.durableMemory,
|
|
1028
|
+
workflows: governed.workflows,
|
|
850
1029
|
cautions: normalizedCautions,
|
|
851
1030
|
verification,
|
|
852
1031
|
evidenceIds,
|
|
@@ -871,6 +1050,17 @@ export async function buildTaskWorkset(input: BuildTaskWorksetInput): Promise<Ta
|
|
|
871
1050
|
},
|
|
872
1051
|
selected: rendered.selected,
|
|
873
1052
|
omitted: receiptOmissions(rendered.omittedItems, base.hiddenCautionMemoryCount),
|
|
1053
|
+
governance: {
|
|
1054
|
+
scope: 'optional-evidence',
|
|
1055
|
+
mode: governed.plan.mode,
|
|
1056
|
+
decisions: governed.plan.decisions.map(decision => ({
|
|
1057
|
+
kind: decision.candidate.kind,
|
|
1058
|
+
...(decision.candidate.id ? { id: decision.candidate.id } : {}),
|
|
1059
|
+
disposition: decision.disposition,
|
|
1060
|
+
reasons: decision.reasons,
|
|
1061
|
+
})),
|
|
1062
|
+
cautions: governed.plan.cautions,
|
|
1063
|
+
},
|
|
874
1064
|
scheduledActions: scheduledActions(normalizedCautions),
|
|
875
1065
|
};
|
|
876
1066
|
return {
|
package/src/store/sqlite-db.ts
CHANGED
|
@@ -321,6 +321,18 @@ const CREATE_CODE_STATE_SNAPSHOTS_TABLE = [
|
|
|
321
321
|
');',
|
|
322
322
|
].join('\n');
|
|
323
323
|
|
|
324
|
+
const CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE = [
|
|
325
|
+
'CREATE TABLE IF NOT EXISTS code_state_snapshot_files (',
|
|
326
|
+
' snapshotId TEXT NOT NULL,',
|
|
327
|
+
' projectId TEXT NOT NULL,',
|
|
328
|
+
' fileId TEXT NOT NULL,',
|
|
329
|
+
' path TEXT NOT NULL,',
|
|
330
|
+
' contentHash TEXT NOT NULL,',
|
|
331
|
+
' PRIMARY KEY (snapshotId, path),',
|
|
332
|
+
' FOREIGN KEY (snapshotId) REFERENCES code_state_snapshots(id) ON DELETE CASCADE',
|
|
333
|
+
');',
|
|
334
|
+
].join('\n');
|
|
335
|
+
|
|
324
336
|
// ── 1.2 Knowledge Claim Ledger ──────────────────────────────────────
|
|
325
337
|
|
|
326
338
|
const CREATE_KNOWLEDGE_CLAIMS_TABLE = `
|
|
@@ -553,6 +565,22 @@ CREATE TABLE IF NOT EXISTS long_term_memory_events (
|
|
|
553
565
|
);
|
|
554
566
|
`;
|
|
555
567
|
|
|
568
|
+
// ── 1.4 Evidence-governed memory ───────────────────────────────────
|
|
569
|
+
|
|
570
|
+
const CREATE_MEMORY_OUTCOME_SIGNALS_TABLE = `
|
|
571
|
+
CREATE TABLE IF NOT EXISTS memory_outcome_signals (
|
|
572
|
+
id TEXT PRIMARY KEY,
|
|
573
|
+
projectId TEXT NOT NULL,
|
|
574
|
+
candidateKind TEXT NOT NULL,
|
|
575
|
+
candidateId TEXT NOT NULL,
|
|
576
|
+
kind TEXT NOT NULL,
|
|
577
|
+
sourceRef TEXT NOT NULL,
|
|
578
|
+
snapshotId TEXT,
|
|
579
|
+
detail TEXT,
|
|
580
|
+
observedAt TEXT NOT NULL
|
|
581
|
+
);
|
|
582
|
+
`;
|
|
583
|
+
|
|
556
584
|
// ── 1.3.3 Controlled media assets ─────────────────────────────────
|
|
557
585
|
|
|
558
586
|
const CREATE_MEDIA_ASSETS_TABLE = `
|
|
@@ -826,6 +854,20 @@ const SCHEMA_MIGRATIONS: SchemaMigration[] = [
|
|
|
826
854
|
db.exec('CREATE INDEX IF NOT EXISTS idx_code_edges_snapshot ON code_edges(projectId, snapshotId)');
|
|
827
855
|
},
|
|
828
856
|
},
|
|
857
|
+
{
|
|
858
|
+
id: '1.4-codegraph-snapshot-manifest',
|
|
859
|
+
apply: (db) => {
|
|
860
|
+
db.exec(CREATE_CODE_STATE_SNAPSHOT_FILES_TABLE);
|
|
861
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_code_snapshot_files_project_path ON code_state_snapshot_files(projectId, path)');
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
id: '1.4-memory-outcome-signals',
|
|
866
|
+
apply: (db) => {
|
|
867
|
+
db.exec(CREATE_MEMORY_OUTCOME_SIGNALS_TABLE);
|
|
868
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_outcomes_candidate ON memory_outcome_signals(projectId, candidateKind, candidateId, observedAt DESC)');
|
|
869
|
+
},
|
|
870
|
+
},
|
|
829
871
|
{
|
|
830
872
|
id: '1.2-knowledge-claim-ledger',
|
|
831
873
|
apply: (db) => {
|