memorix 1.2.1 → 1.2.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 +16 -0
- package/README.md +14 -2
- package/README.zh-CN.md +14 -2
- package/TEAM.md +86 -86
- package/dist/cli/index.js +15407 -13779
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +1321 -529
- package/dist/index.js.map +1 -1
- package/dist/maintenance-runner.d.ts +1 -1
- package/dist/maintenance-runner.js +8458 -8087
- package/dist/maintenance-runner.js.map +1 -1
- package/dist/memcode-runtime/CHANGELOG.md +16 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +1349 -535
- package/dist/sdk.js.map +1 -1
- package/dist/types.d.ts +49 -1
- package/dist/types.js.map +1 -1
- package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
- package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
- package/docs/API_REFERENCE.md +24 -4
- package/docs/DESIGN_DECISIONS.md +357 -357
- package/docs/README.md +1 -1
- package/docs/dev-log/progress.txt +91 -11
- package/package.json +1 -1
- package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
- package/src/audit/index.ts +156 -156
- package/src/cli/command-guide.ts +192 -0
- package/src/cli/commands/audit-list.ts +89 -89
- package/src/cli/commands/audit.ts +9 -4
- package/src/cli/commands/background.ts +659 -659
- package/src/cli/commands/cleanup.ts +5 -1
- package/src/cli/commands/codegraph.ts +15 -5
- package/src/cli/commands/context.ts +3 -2
- package/src/cli/commands/doctor.ts +4 -2
- package/src/cli/commands/explain.ts +9 -3
- package/src/cli/commands/formation.ts +48 -48
- package/src/cli/commands/git-hook-install.ts +111 -111
- package/src/cli/commands/handoff.ts +75 -61
- package/src/cli/commands/hooks-status.ts +63 -63
- package/src/cli/commands/identity.ts +116 -0
- package/src/cli/commands/ingest-commit.ts +153 -153
- package/src/cli/commands/ingest-image.ts +71 -69
- package/src/cli/commands/ingest-log.ts +180 -180
- package/src/cli/commands/ingest.ts +44 -44
- package/src/cli/commands/integrate-shared.ts +15 -15
- package/src/cli/commands/lock.ts +93 -92
- package/src/cli/commands/memory.ts +58 -21
- package/src/cli/commands/message.ts +123 -118
- package/src/cli/commands/operator-shared.ts +98 -3
- package/src/cli/commands/poll.ts +74 -64
- package/src/cli/commands/purge-all-memory.ts +85 -85
- package/src/cli/commands/purge-project-memory.ts +83 -83
- package/src/cli/commands/reasoning.ts +135 -121
- package/src/cli/commands/retention.ts +9 -4
- package/src/cli/commands/serve-http.ts +8 -2
- package/src/cli/commands/serve-shared.ts +118 -118
- package/src/cli/commands/session.ts +29 -3
- package/src/cli/commands/skills.ts +124 -119
- package/src/cli/commands/status.ts +4 -3
- package/src/cli/commands/task.ts +193 -184
- package/src/cli/commands/team.ts +14 -10
- package/src/cli/commands/transfer.ts +108 -55
- package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
- package/src/cli/identity.ts +89 -0
- package/src/cli/index.ts +96 -19
- package/src/cli/invocation.ts +115 -0
- package/src/cli/tui/ChatView.tsx +234 -234
- package/src/cli/tui/CommandBar.tsx +312 -312
- package/src/cli/tui/ContextRail.tsx +118 -118
- package/src/cli/tui/HeaderBar.tsx +72 -72
- package/src/cli/tui/LogoBanner.tsx +51 -51
- package/src/cli/tui/Sidebar.tsx +179 -179
- package/src/cli/tui/chat-service.ts +41 -18
- package/src/cli/tui/data.ts +23 -44
- package/src/cli/tui/index.ts +41 -41
- package/src/cli/tui/markdown-render.tsx +371 -371
- package/src/cli/tui/operator-context.ts +60 -0
- package/src/cli/tui/use-mouse.ts +157 -157
- package/src/cli/tui/useNavigation.ts +56 -56
- package/src/cli/tui/views/MemoryView.tsx +10 -8
- package/src/cli/update-checker.ts +211 -211
- package/src/cli/version.ts +7 -7
- package/src/cli/workbench.ts +1 -1
- package/src/codegraph/auto-context.ts +31 -2
- package/src/codegraph/context-pack.ts +1 -0
- package/src/codegraph/project-context.ts +2 -0
- package/src/compact/engine.ts +26 -10
- package/src/compact/index-format.ts +25 -2
- package/src/compact/token-budget.ts +74 -74
- package/src/dashboard/project-classification.ts +64 -64
- package/src/dashboard/server.ts +46 -9
- package/src/embedding/fastembed-provider.ts +142 -142
- package/src/embedding/transformers-provider.ts +111 -111
- package/src/git/extractor.ts +209 -209
- package/src/git/hooks-path.ts +85 -85
- package/src/hooks/admission.ts +117 -0
- package/src/hooks/handler.ts +98 -91
- package/src/hooks/pattern-detector.ts +173 -173
- package/src/hooks/significance-filter.ts +250 -250
- package/src/knowledge/context-assembly.ts +97 -0
- package/src/knowledge/workset.ts +179 -10
- package/src/llm/memory-manager.ts +328 -328
- package/src/llm/provider.ts +885 -885
- package/src/llm/quality.ts +248 -248
- package/src/memory/admission.ts +57 -0
- package/src/memory/attribution-guard.ts +249 -249
- package/src/memory/consolidation.ts +13 -2
- package/src/memory/disclosure-policy.ts +140 -135
- package/src/memory/entity-extractor.ts +197 -197
- package/src/memory/export-import.ts +11 -3
- package/src/memory/formation/evaluate.ts +217 -217
- package/src/memory/formation/extract.ts +361 -361
- package/src/memory/formation/index.ts +417 -417
- package/src/memory/formation/resolve.ts +344 -344
- package/src/memory/formation/types.ts +315 -315
- package/src/memory/freshness.ts +122 -122
- package/src/memory/graph-context.ts +8 -2
- package/src/memory/graph.ts +197 -197
- package/src/memory/observations.ts +162 -4
- package/src/memory/quality-audit.ts +2 -0
- package/src/memory/refs.ts +94 -94
- package/src/memory/retention.ts +22 -2
- package/src/memory/secret-filter.ts +79 -79
- package/src/memory/session.ts +5 -2
- package/src/memory/visibility.ts +80 -0
- package/src/multimodal/image-loader.ts +143 -143
- package/src/orchestrate/adapters/claude-stream.ts +192 -192
- package/src/orchestrate/adapters/claude.ts +111 -111
- package/src/orchestrate/adapters/codex-stream.ts +134 -134
- package/src/orchestrate/adapters/codex.ts +41 -41
- package/src/orchestrate/adapters/gemini-stream.ts +166 -166
- package/src/orchestrate/adapters/gemini.ts +42 -42
- package/src/orchestrate/adapters/index.ts +73 -73
- package/src/orchestrate/adapters/opencode-stream.ts +143 -143
- package/src/orchestrate/adapters/opencode.ts +47 -47
- package/src/orchestrate/adapters/spawn-helper.ts +286 -286
- package/src/orchestrate/adapters/types.ts +77 -77
- package/src/orchestrate/capability-router.ts +284 -284
- package/src/orchestrate/context-compact.ts +188 -188
- package/src/orchestrate/cost-tracker.ts +219 -219
- package/src/orchestrate/error-recovery.ts +191 -191
- package/src/orchestrate/evidence.ts +140 -140
- package/src/orchestrate/ledger.ts +110 -110
- package/src/orchestrate/memorix-bridge.ts +378 -340
- package/src/orchestrate/output-budget.ts +80 -80
- package/src/orchestrate/permission.ts +152 -152
- package/src/orchestrate/pipeline-trace.ts +131 -131
- package/src/orchestrate/prompt-builder.ts +155 -155
- package/src/orchestrate/ring-buffer.ts +37 -37
- package/src/orchestrate/task-graph.ts +389 -389
- package/src/orchestrate/worktree.ts +232 -232
- package/src/project/aliases.ts +374 -374
- package/src/project/detector.ts +268 -268
- package/src/rules/adapters/claude-code.ts +99 -99
- package/src/rules/adapters/codex.ts +97 -97
- package/src/rules/adapters/copilot.ts +124 -124
- package/src/rules/adapters/cursor.ts +114 -114
- package/src/rules/adapters/kiro.ts +126 -126
- package/src/rules/adapters/trae.ts +56 -56
- package/src/rules/adapters/windsurf.ts +83 -83
- package/src/rules/syncer.ts +235 -235
- package/src/runtime/control-plane-maintenance.ts +1 -0
- package/src/runtime/isolated-maintenance.ts +1 -0
- package/src/runtime/lifecycle.ts +18 -0
- package/src/runtime/maintenance-jobs.ts +1 -0
- package/src/runtime/maintenance-runner.ts +2 -0
- package/src/runtime/project-maintenance.ts +89 -0
- package/src/sdk.ts +334 -304
- package/src/search/intent-detector.ts +289 -289
- package/src/search/query-expansion.ts +52 -52
- package/src/server/formation-timeout.ts +27 -27
- package/src/server.ts +260 -81
- package/src/skills/mini-skills.ts +386 -386
- package/src/store/chat-store.ts +119 -119
- package/src/store/graph-store.ts +249 -249
- package/src/store/mini-skill-store.ts +349 -349
- package/src/store/orama-store.ts +61 -6
- package/src/store/persistence-json.ts +212 -212
- package/src/store/persistence.ts +291 -291
- package/src/store/project-affinity.ts +195 -195
- package/src/store/sqlite-db.ts +23 -1
- package/src/store/sqlite-store.ts +12 -2
- package/src/team/event-bus.ts +76 -76
- package/src/team/file-locks.ts +173 -173
- package/src/team/handoff.ts +168 -161
- package/src/team/messages.ts +203 -203
- package/src/team/poll.ts +132 -132
- package/src/team/tasks.ts +211 -211
- package/src/types.ts +51 -0
- package/src/wiki/generator.ts +2 -0
- package/src/workspace/mcp-adapters/codex.ts +191 -191
- package/src/workspace/mcp-adapters/copilot.ts +105 -105
- package/src/workspace/mcp-adapters/cursor.ts +53 -53
- package/src/workspace/mcp-adapters/kiro.ts +64 -64
- package/src/workspace/mcp-adapters/opencode.ts +123 -123
- package/src/workspace/mcp-adapters/trae.ts +134 -134
- package/src/workspace/mcp-adapters/windsurf.ts +91 -91
- package/src/workspace/sanitizer.ts +60 -60
- package/src/workspace/workflow-sync.ts +131 -131
package/src/team/tasks.ts
CHANGED
|
@@ -1,211 +1,211 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Task Manager — Simple task DAG with dependencies
|
|
3
|
-
*
|
|
4
|
-
* Agents can create tasks, claim them, complete them, and query available work.
|
|
5
|
-
* Dependencies are validated: a task can only be claimed when all deps are completed.
|
|
6
|
-
* Simple JSON format — no complex graph structures.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { randomUUID } from 'node:crypto';
|
|
10
|
-
|
|
11
|
-
// ─── Types ───────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
14
|
-
|
|
15
|
-
export interface TaskCreateInput {
|
|
16
|
-
description: string;
|
|
17
|
-
deps?: string[];
|
|
18
|
-
metadata?: Record<string, unknown>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface Task {
|
|
22
|
-
id: string;
|
|
23
|
-
description: string;
|
|
24
|
-
status: TaskStatus;
|
|
25
|
-
deps: string[];
|
|
26
|
-
assignee?: string;
|
|
27
|
-
result?: string;
|
|
28
|
-
metadata?: Record<string, unknown>;
|
|
29
|
-
createdAt: Date;
|
|
30
|
-
updatedAt: Date;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface TaskListFilter {
|
|
34
|
-
status?: TaskStatus;
|
|
35
|
-
assignee?: string;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// ─── Manager ─────────────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
export class TaskManager {
|
|
41
|
-
private tasks = new Map<string, Task>();
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Create a new task. Validates that all dependency IDs exist.
|
|
45
|
-
*/
|
|
46
|
-
create(input: TaskCreateInput): Task {
|
|
47
|
-
const deps = input.deps ?? [];
|
|
48
|
-
|
|
49
|
-
// Validate deps exist
|
|
50
|
-
for (const dep of deps) {
|
|
51
|
-
if (!this.tasks.has(dep)) {
|
|
52
|
-
throw new Error(`Unknown dependency task: ${dep}`);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const now = new Date();
|
|
57
|
-
const task: Task = {
|
|
58
|
-
id: randomUUID(),
|
|
59
|
-
description: input.description,
|
|
60
|
-
status: 'pending',
|
|
61
|
-
deps,
|
|
62
|
-
metadata: input.metadata,
|
|
63
|
-
createdAt: now,
|
|
64
|
-
updatedAt: now,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
this.tasks.set(task.id, task);
|
|
68
|
-
return { ...task };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Claim a pending task for an agent.
|
|
73
|
-
* Validates: task exists, not claimed by another, all deps completed.
|
|
74
|
-
*/
|
|
75
|
-
claim(taskId: string, agentId: string): Task {
|
|
76
|
-
const task = this.tasks.get(taskId);
|
|
77
|
-
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
78
|
-
|
|
79
|
-
// Allow same agent to re-claim
|
|
80
|
-
if (task.assignee && task.assignee !== agentId) {
|
|
81
|
-
throw new Error(`Task already claimed by ${task.assignee}`);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Check all dependencies are completed
|
|
85
|
-
for (const depId of task.deps) {
|
|
86
|
-
const dep = this.tasks.get(depId);
|
|
87
|
-
if (!dep || dep.status !== 'completed') {
|
|
88
|
-
throw new Error(`Cannot claim: dependencies not completed (${depId})`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
task.assignee = agentId;
|
|
93
|
-
task.status = 'in_progress';
|
|
94
|
-
task.updatedAt = new Date();
|
|
95
|
-
return { ...task };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Complete a task with a result. Only the assignee can complete,
|
|
100
|
-
* unless allowRescue is true (used when original assignee left).
|
|
101
|
-
*/
|
|
102
|
-
complete(taskId: string, agentId: string, result: string, allowRescue = false): Task {
|
|
103
|
-
const task = this.tasks.get(taskId);
|
|
104
|
-
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
105
|
-
if (!task.assignee) throw new Error(`Task not claimed: ${taskId}`);
|
|
106
|
-
if (task.assignee !== agentId && !allowRescue) {
|
|
107
|
-
throw new Error(`Only assignee ${task.assignee} can complete this task`);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
task.assignee = agentId;
|
|
111
|
-
task.status = 'completed';
|
|
112
|
-
task.result = result;
|
|
113
|
-
task.updatedAt = new Date();
|
|
114
|
-
return { ...task };
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Release all in_progress tasks assigned to a specific agent.
|
|
119
|
-
* Returns them to 'pending' so other agents can claim them.
|
|
120
|
-
*/
|
|
121
|
-
releaseByAgent(agentId: string): number {
|
|
122
|
-
let count = 0;
|
|
123
|
-
for (const task of this.tasks.values()) {
|
|
124
|
-
if (task.assignee === agentId && task.status === 'in_progress') {
|
|
125
|
-
task.status = 'pending';
|
|
126
|
-
task.assignee = undefined;
|
|
127
|
-
task.updatedAt = new Date();
|
|
128
|
-
count++;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return count;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* List tasks with optional filters.
|
|
136
|
-
*/
|
|
137
|
-
list(filter?: TaskListFilter): Task[] {
|
|
138
|
-
let all = [...this.tasks.values()];
|
|
139
|
-
if (filter?.status) {
|
|
140
|
-
all = all.filter(t => t.status === filter.status);
|
|
141
|
-
}
|
|
142
|
-
if (filter?.assignee) {
|
|
143
|
-
all = all.filter(t => t.assignee === filter.assignee);
|
|
144
|
-
}
|
|
145
|
-
return all.map(t => ({ ...t }));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Get tasks that are available to be claimed:
|
|
150
|
-
* - status is 'pending'
|
|
151
|
-
* - all dependencies are 'completed'
|
|
152
|
-
*/
|
|
153
|
-
getAvailable(): Task[] {
|
|
154
|
-
const result: Task[] = [];
|
|
155
|
-
for (const task of this.tasks.values()) {
|
|
156
|
-
if (task.status !== 'pending') continue;
|
|
157
|
-
|
|
158
|
-
const depsReady = task.deps.every(depId => {
|
|
159
|
-
const dep = this.tasks.get(depId);
|
|
160
|
-
return dep && dep.status === 'completed';
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
if (depsReady) {
|
|
164
|
-
result.push({ ...task });
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Get a single task by ID.
|
|
172
|
-
*/
|
|
173
|
-
getTask(taskId: string): Task | null {
|
|
174
|
-
const task = this.tasks.get(taskId);
|
|
175
|
-
return task ? { ...task } : null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Check if a task's assignee matches the given agentId.
|
|
180
|
-
*/
|
|
181
|
-
isAssignee(taskId: string, agentId: string): boolean {
|
|
182
|
-
const task = this.tasks.get(taskId);
|
|
183
|
-
return task?.assignee === agentId;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/** Serialize state for file persistence */
|
|
187
|
-
serialize(): { tasks: Record<string, unknown> } {
|
|
188
|
-
const tasks: Record<string, unknown> = {};
|
|
189
|
-
for (const [id, t] of this.tasks) {
|
|
190
|
-
tasks[id] = {
|
|
191
|
-
...t,
|
|
192
|
-
createdAt: t.createdAt.toISOString(),
|
|
193
|
-
updatedAt: t.updatedAt.toISOString(),
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
return { tasks };
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/** Hydrate state from file persistence */
|
|
200
|
-
hydrate(data: { tasks?: Record<string, any> }): void {
|
|
201
|
-
this.tasks.clear();
|
|
202
|
-
if (!data?.tasks) return;
|
|
203
|
-
for (const [id, raw] of Object.entries(data.tasks)) {
|
|
204
|
-
this.tasks.set(id, {
|
|
205
|
-
...(raw as any),
|
|
206
|
-
createdAt: new Date((raw as any).createdAt),
|
|
207
|
-
updatedAt: new Date((raw as any).updatedAt),
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Task Manager — Simple task DAG with dependencies
|
|
3
|
+
*
|
|
4
|
+
* Agents can create tasks, claim them, complete them, and query available work.
|
|
5
|
+
* Dependencies are validated: a task can only be claimed when all deps are completed.
|
|
6
|
+
* Simple JSON format — no complex graph structures.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { randomUUID } from 'node:crypto';
|
|
10
|
+
|
|
11
|
+
// ─── Types ───────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
14
|
+
|
|
15
|
+
export interface TaskCreateInput {
|
|
16
|
+
description: string;
|
|
17
|
+
deps?: string[];
|
|
18
|
+
metadata?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Task {
|
|
22
|
+
id: string;
|
|
23
|
+
description: string;
|
|
24
|
+
status: TaskStatus;
|
|
25
|
+
deps: string[];
|
|
26
|
+
assignee?: string;
|
|
27
|
+
result?: string;
|
|
28
|
+
metadata?: Record<string, unknown>;
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
updatedAt: Date;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface TaskListFilter {
|
|
34
|
+
status?: TaskStatus;
|
|
35
|
+
assignee?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ─── Manager ─────────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
export class TaskManager {
|
|
41
|
+
private tasks = new Map<string, Task>();
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a new task. Validates that all dependency IDs exist.
|
|
45
|
+
*/
|
|
46
|
+
create(input: TaskCreateInput): Task {
|
|
47
|
+
const deps = input.deps ?? [];
|
|
48
|
+
|
|
49
|
+
// Validate deps exist
|
|
50
|
+
for (const dep of deps) {
|
|
51
|
+
if (!this.tasks.has(dep)) {
|
|
52
|
+
throw new Error(`Unknown dependency task: ${dep}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const now = new Date();
|
|
57
|
+
const task: Task = {
|
|
58
|
+
id: randomUUID(),
|
|
59
|
+
description: input.description,
|
|
60
|
+
status: 'pending',
|
|
61
|
+
deps,
|
|
62
|
+
metadata: input.metadata,
|
|
63
|
+
createdAt: now,
|
|
64
|
+
updatedAt: now,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
this.tasks.set(task.id, task);
|
|
68
|
+
return { ...task };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Claim a pending task for an agent.
|
|
73
|
+
* Validates: task exists, not claimed by another, all deps completed.
|
|
74
|
+
*/
|
|
75
|
+
claim(taskId: string, agentId: string): Task {
|
|
76
|
+
const task = this.tasks.get(taskId);
|
|
77
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
78
|
+
|
|
79
|
+
// Allow same agent to re-claim
|
|
80
|
+
if (task.assignee && task.assignee !== agentId) {
|
|
81
|
+
throw new Error(`Task already claimed by ${task.assignee}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Check all dependencies are completed
|
|
85
|
+
for (const depId of task.deps) {
|
|
86
|
+
const dep = this.tasks.get(depId);
|
|
87
|
+
if (!dep || dep.status !== 'completed') {
|
|
88
|
+
throw new Error(`Cannot claim: dependencies not completed (${depId})`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
task.assignee = agentId;
|
|
93
|
+
task.status = 'in_progress';
|
|
94
|
+
task.updatedAt = new Date();
|
|
95
|
+
return { ...task };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Complete a task with a result. Only the assignee can complete,
|
|
100
|
+
* unless allowRescue is true (used when original assignee left).
|
|
101
|
+
*/
|
|
102
|
+
complete(taskId: string, agentId: string, result: string, allowRescue = false): Task {
|
|
103
|
+
const task = this.tasks.get(taskId);
|
|
104
|
+
if (!task) throw new Error(`Task not found: ${taskId}`);
|
|
105
|
+
if (!task.assignee) throw new Error(`Task not claimed: ${taskId}`);
|
|
106
|
+
if (task.assignee !== agentId && !allowRescue) {
|
|
107
|
+
throw new Error(`Only assignee ${task.assignee} can complete this task`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
task.assignee = agentId;
|
|
111
|
+
task.status = 'completed';
|
|
112
|
+
task.result = result;
|
|
113
|
+
task.updatedAt = new Date();
|
|
114
|
+
return { ...task };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Release all in_progress tasks assigned to a specific agent.
|
|
119
|
+
* Returns them to 'pending' so other agents can claim them.
|
|
120
|
+
*/
|
|
121
|
+
releaseByAgent(agentId: string): number {
|
|
122
|
+
let count = 0;
|
|
123
|
+
for (const task of this.tasks.values()) {
|
|
124
|
+
if (task.assignee === agentId && task.status === 'in_progress') {
|
|
125
|
+
task.status = 'pending';
|
|
126
|
+
task.assignee = undefined;
|
|
127
|
+
task.updatedAt = new Date();
|
|
128
|
+
count++;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return count;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* List tasks with optional filters.
|
|
136
|
+
*/
|
|
137
|
+
list(filter?: TaskListFilter): Task[] {
|
|
138
|
+
let all = [...this.tasks.values()];
|
|
139
|
+
if (filter?.status) {
|
|
140
|
+
all = all.filter(t => t.status === filter.status);
|
|
141
|
+
}
|
|
142
|
+
if (filter?.assignee) {
|
|
143
|
+
all = all.filter(t => t.assignee === filter.assignee);
|
|
144
|
+
}
|
|
145
|
+
return all.map(t => ({ ...t }));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Get tasks that are available to be claimed:
|
|
150
|
+
* - status is 'pending'
|
|
151
|
+
* - all dependencies are 'completed'
|
|
152
|
+
*/
|
|
153
|
+
getAvailable(): Task[] {
|
|
154
|
+
const result: Task[] = [];
|
|
155
|
+
for (const task of this.tasks.values()) {
|
|
156
|
+
if (task.status !== 'pending') continue;
|
|
157
|
+
|
|
158
|
+
const depsReady = task.deps.every(depId => {
|
|
159
|
+
const dep = this.tasks.get(depId);
|
|
160
|
+
return dep && dep.status === 'completed';
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
if (depsReady) {
|
|
164
|
+
result.push({ ...task });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get a single task by ID.
|
|
172
|
+
*/
|
|
173
|
+
getTask(taskId: string): Task | null {
|
|
174
|
+
const task = this.tasks.get(taskId);
|
|
175
|
+
return task ? { ...task } : null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Check if a task's assignee matches the given agentId.
|
|
180
|
+
*/
|
|
181
|
+
isAssignee(taskId: string, agentId: string): boolean {
|
|
182
|
+
const task = this.tasks.get(taskId);
|
|
183
|
+
return task?.assignee === agentId;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Serialize state for file persistence */
|
|
187
|
+
serialize(): { tasks: Record<string, unknown> } {
|
|
188
|
+
const tasks: Record<string, unknown> = {};
|
|
189
|
+
for (const [id, t] of this.tasks) {
|
|
190
|
+
tasks[id] = {
|
|
191
|
+
...t,
|
|
192
|
+
createdAt: t.createdAt.toISOString(),
|
|
193
|
+
updatedAt: t.updatedAt.toISOString(),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { tasks };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Hydrate state from file persistence */
|
|
200
|
+
hydrate(data: { tasks?: Record<string, any> }): void {
|
|
201
|
+
this.tasks.clear();
|
|
202
|
+
if (!data?.tasks) return;
|
|
203
|
+
for (const [id, raw] of Object.entries(data.tasks)) {
|
|
204
|
+
this.tasks.set(id, {
|
|
205
|
+
...(raw as any),
|
|
206
|
+
createdAt: new Date((raw as any).createdAt),
|
|
207
|
+
updatedAt: new Date((raw as any).updatedAt),
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -85,6 +85,33 @@ export const OBSERVATION_ICONS: Record<ObservationType, string> = {
|
|
|
85
85
|
/** Observation lifecycle status */
|
|
86
86
|
export type ObservationStatus = 'active' | 'resolved' | 'archived';
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Control-plane admission state for automatically captured observations.
|
|
90
|
+
* Candidate and ephemeral records remain inspectable, but only qualified
|
|
91
|
+
* records are eligible for automatic context delivery. Missing state preserves
|
|
92
|
+
* legacy behavior for observations written before this policy existed.
|
|
93
|
+
*/
|
|
94
|
+
export type ObservationAdmissionState = 'ephemeral' | 'candidate' | 'qualified';
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Who may retrieve an observation through agent-facing memory surfaces.
|
|
98
|
+
* Missing visibility is a legacy record and resolves to `project`.
|
|
99
|
+
*/
|
|
100
|
+
export type ObservationVisibility = 'personal' | 'project' | 'team';
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Identity available to an agent-facing retrieval call. Internal maintenance
|
|
104
|
+
* deliberately omits this and may inspect all project records for lifecycle work.
|
|
105
|
+
*/
|
|
106
|
+
export interface ObservationReader {
|
|
107
|
+
/** Bound project for a normal project-scoped call. Omit only for explicit global search. */
|
|
108
|
+
projectId?: string;
|
|
109
|
+
/** Stable coordination identity, available after an explicit team join. */
|
|
110
|
+
agentId?: string;
|
|
111
|
+
/** True only when agentId is an active member of the bound project team. */
|
|
112
|
+
isTeamMember?: boolean;
|
|
113
|
+
}
|
|
114
|
+
|
|
88
115
|
/** Progress tracking for task/feature observations */
|
|
89
116
|
export interface ProgressInfo {
|
|
90
117
|
feature: string;
|
|
@@ -132,6 +159,14 @@ export interface Observation {
|
|
|
132
159
|
sourceDetail?: 'explicit' | 'hook' | 'git-ingest';
|
|
133
160
|
/** Value category from formation pipeline evaluation */
|
|
134
161
|
valueCategory?: 'core' | 'contextual' | 'ephemeral';
|
|
162
|
+
/** Control-plane admission state for automatic capture and delivery. */
|
|
163
|
+
admissionState?: ObservationAdmissionState;
|
|
164
|
+
/** Short, sanitized explanation for the latest admission decision. */
|
|
165
|
+
admissionReason?: string;
|
|
166
|
+
/** Retrieval scope. Legacy records without a value remain project-visible. */
|
|
167
|
+
visibility?: ObservationVisibility;
|
|
168
|
+
/** Explicit additional readers for a personal record, used by targeted handoffs. */
|
|
169
|
+
sharedWithAgentIds?: string[];
|
|
135
170
|
/** Phase 4a: Agent ID that created this observation (team attribution) */
|
|
136
171
|
createdByAgentId?: string;
|
|
137
172
|
/** Phase 4a: Monotonic write generation — snapshot of storage_generation at write time (watermark coherence) */
|
|
@@ -176,6 +211,10 @@ export interface IndexEntry {
|
|
|
176
211
|
sourceDetail?: 'explicit' | 'hook' | 'git-ingest';
|
|
177
212
|
/** Value category for source-aware ranking */
|
|
178
213
|
valueCategory?: 'core' | 'contextual' | 'ephemeral';
|
|
214
|
+
/** Control-plane admission state when known. */
|
|
215
|
+
admissionState?: ObservationAdmissionState;
|
|
216
|
+
/** Retrieval scope when known. */
|
|
217
|
+
visibility?: ObservationVisibility;
|
|
179
218
|
/** Explainable recall: why this result matched. */
|
|
180
219
|
matchedFields?: string[];
|
|
181
220
|
/** Entity name — used for entity-affinity scoring and workstream deduplication. */
|
|
@@ -216,6 +255,8 @@ export interface SearchOptions {
|
|
|
216
255
|
source?: 'agent' | 'git' | 'manual';
|
|
217
256
|
/** Internal observability probes can disable access tracking without affecting normal search behavior. */
|
|
218
257
|
trackAccess?: boolean;
|
|
258
|
+
/** Internal reader context for agent-facing visibility filtering. */
|
|
259
|
+
reader?: ObservationReader;
|
|
219
260
|
}
|
|
220
261
|
|
|
221
262
|
/** Topic key family heuristics for suggesting stable topic keys */
|
|
@@ -258,6 +299,16 @@ export interface MemorixDocument {
|
|
|
258
299
|
sourceDetail?: string;
|
|
259
300
|
/** Value category from formation evaluation */
|
|
260
301
|
valueCategory?: string;
|
|
302
|
+
/** Control-plane admission state for automatic capture and delivery. */
|
|
303
|
+
admissionState?: string;
|
|
304
|
+
/** Short, sanitized explanation for the latest admission decision. */
|
|
305
|
+
admissionReason?: string;
|
|
306
|
+
/** Retrieval scope for agent-facing filtering. */
|
|
307
|
+
visibility?: string;
|
|
308
|
+
/** Agent that owns a personal record, or authored a shared record. */
|
|
309
|
+
createdByAgentId?: string;
|
|
310
|
+
/** Explicit readers for a targeted personal record. */
|
|
311
|
+
sharedWithAgentIds?: string;
|
|
261
312
|
/** Optional vector embedding for semantic/hybrid retrieval */
|
|
262
313
|
embedding?: number[];
|
|
263
314
|
/** Document type: observation or mini-skill (Phase 3a) */
|
package/src/wiki/generator.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Observation, MiniSkill } from '../types.js';
|
|
2
|
+
import { isEligibleForKnowledgePromotion } from '../memory/admission.js';
|
|
2
3
|
import type { KnowledgeSourceRef, KnowledgeItem, KnowledgeSection, ProjectKnowledgeOverview } from './types.js';
|
|
3
4
|
|
|
4
5
|
const COMMAND_LOG_TITLE = /^(Ran:|Command:|Executed:)\s/i;
|
|
@@ -63,6 +64,7 @@ function isEligible(o: Observation, projectId: string): boolean {
|
|
|
63
64
|
if (isCommandLog(o)) return false;
|
|
64
65
|
if (isInactive(o)) return false;
|
|
65
66
|
if (isOtherProject(o, projectId)) return false;
|
|
67
|
+
if (!isEligibleForKnowledgePromotion(o)) return false;
|
|
66
68
|
if (o.valueCategory === 'ephemeral') return false;
|
|
67
69
|
if (!contextualHasSubstance(o)) return false;
|
|
68
70
|
return true;
|