openclaw-node-harness 2.0.4 → 2.1.1
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/README.md +646 -3
- package/bin/hyperagent.mjs +419 -0
- package/bin/lane-watchdog.js +23 -2
- package/bin/mesh-agent.js +439 -28
- package/bin/mesh-bridge.js +69 -3
- package/bin/mesh-health-publisher.js +41 -1
- package/bin/mesh-task-daemon.js +821 -26
- package/bin/mesh.js +411 -20
- package/config/claude-settings.json +95 -0
- package/config/daemon.json.template +2 -1
- package/config/git-hooks/pre-commit +13 -0
- package/config/git-hooks/pre-push +12 -0
- package/config/harness-rules.json +174 -0
- package/config/plan-templates/team-bugfix.yaml +52 -0
- package/config/plan-templates/team-deploy.yaml +50 -0
- package/config/plan-templates/team-feature.yaml +71 -0
- package/config/roles/qa-engineer.yaml +36 -0
- package/config/roles/solidity-dev.yaml +51 -0
- package/config/roles/tech-architect.yaml +36 -0
- package/config/rules/framework/solidity.md +22 -0
- package/config/rules/framework/typescript.md +21 -0
- package/config/rules/framework/unity.md +21 -0
- package/config/rules/universal/design-docs.md +18 -0
- package/config/rules/universal/git-hygiene.md +18 -0
- package/config/rules/universal/security.md +19 -0
- package/config/rules/universal/test-standards.md +19 -0
- package/identity/DELEGATION.md +6 -6
- package/install.sh +296 -10
- package/lib/agent-activity.js +2 -2
- package/lib/circling-parser.js +119 -0
- package/lib/exec-safety.js +105 -0
- package/lib/hyperagent-store.mjs +652 -0
- package/lib/kanban-io.js +24 -31
- package/lib/llm-providers.js +16 -0
- package/lib/mcp-knowledge/bench.mjs +118 -0
- package/lib/mcp-knowledge/core.mjs +530 -0
- package/lib/mcp-knowledge/package.json +25 -0
- package/lib/mcp-knowledge/server.mjs +252 -0
- package/lib/mcp-knowledge/test.mjs +802 -0
- package/lib/memory-budget.mjs +261 -0
- package/lib/mesh-collab.js +483 -165
- package/lib/mesh-harness.js +427 -0
- package/lib/mesh-plans.js +79 -50
- package/lib/mesh-tasks.js +132 -49
- package/lib/nats-resolve.js +4 -4
- package/lib/plan-templates.js +226 -0
- package/lib/pre-compression-flush.mjs +322 -0
- package/lib/role-loader.js +292 -0
- package/lib/rule-loader.js +358 -0
- package/lib/session-store.mjs +461 -0
- package/lib/transcript-parser.mjs +292 -0
- package/mission-control/drizzle/soul_schema_update.sql +29 -0
- package/mission-control/drizzle.config.ts +1 -4
- package/mission-control/package-lock.json +1571 -83
- package/mission-control/package.json +6 -2
- package/mission-control/scripts/gen-chronology.js +3 -3
- package/mission-control/scripts/import-pipeline-v2.js +0 -16
- package/mission-control/scripts/import-pipeline.js +0 -15
- package/mission-control/src/app/api/cowork/clusters/[id]/members/route.ts +117 -0
- package/mission-control/src/app/api/cowork/clusters/[id]/route.ts +84 -0
- package/mission-control/src/app/api/cowork/clusters/route.ts +141 -0
- package/mission-control/src/app/api/cowork/dispatch/route.ts +128 -0
- package/mission-control/src/app/api/cowork/events/route.ts +65 -0
- package/mission-control/src/app/api/cowork/intervene/route.ts +259 -0
- package/mission-control/src/app/api/cowork/sessions/[id]/route.ts +37 -0
- package/mission-control/src/app/api/cowork/sessions/route.ts +64 -0
- package/mission-control/src/app/api/diagnostics/route.ts +97 -0
- package/mission-control/src/app/api/diagnostics/test-runner/route.ts +990 -0
- package/mission-control/src/app/api/memory/search/route.ts +6 -3
- package/mission-control/src/app/api/mesh/events/route.ts +95 -19
- package/mission-control/src/app/api/mesh/identity/route.ts +11 -0
- package/mission-control/src/app/api/mesh/tasks/[id]/route.ts +92 -0
- package/mission-control/src/app/api/mesh/tasks/route.ts +91 -0
- package/mission-control/src/app/api/souls/[id]/evolution/route.ts +21 -5
- package/mission-control/src/app/api/souls/[id]/prompt/route.ts +7 -1
- package/mission-control/src/app/api/souls/[id]/propagate/route.ts +14 -2
- package/mission-control/src/app/api/tasks/[id]/handoff/route.ts +8 -2
- package/mission-control/src/app/api/tasks/[id]/route.ts +90 -4
- package/mission-control/src/app/api/tasks/route.ts +21 -30
- package/mission-control/src/app/api/workspace/read/route.ts +11 -0
- package/mission-control/src/app/cowork/page.tsx +261 -0
- package/mission-control/src/app/diagnostics/page.tsx +385 -0
- package/mission-control/src/app/graph/page.tsx +26 -0
- package/mission-control/src/app/memory/page.tsx +1 -1
- package/mission-control/src/app/obsidian/page.tsx +36 -6
- package/mission-control/src/app/roadmap/page.tsx +24 -0
- package/mission-control/src/app/souls/page.tsx +2 -2
- package/mission-control/src/components/board/execution-config.tsx +431 -0
- package/mission-control/src/components/board/kanban-board.tsx +75 -9
- package/mission-control/src/components/board/kanban-column.tsx +135 -19
- package/mission-control/src/components/board/task-card.tsx +55 -2
- package/mission-control/src/components/board/unified-task-dialog.tsx +82 -4
- package/mission-control/src/components/cowork/cluster-card.tsx +176 -0
- package/mission-control/src/components/cowork/create-cluster-dialog.tsx +251 -0
- package/mission-control/src/components/cowork/dispatch-form.tsx +423 -0
- package/mission-control/src/components/cowork/role-picker.tsx +102 -0
- package/mission-control/src/components/cowork/session-card.tsx +284 -0
- package/mission-control/src/components/layout/sidebar.tsx +39 -2
- package/mission-control/src/lib/__tests__/daily-log.test.ts +82 -0
- package/mission-control/src/lib/__tests__/memory-md.test.ts +87 -0
- package/mission-control/src/lib/__tests__/mesh-kv-sync.test.ts +465 -0
- package/mission-control/src/lib/__tests__/mocks/mock-kv.ts +131 -0
- package/mission-control/src/lib/__tests__/status-kanban.test.ts +46 -0
- package/mission-control/src/lib/__tests__/task-markdown.test.ts +188 -0
- package/mission-control/src/lib/__tests__/wikilinks.test.ts +175 -0
- package/mission-control/src/lib/config.ts +67 -0
- package/mission-control/src/lib/db/index.ts +85 -1
- package/mission-control/src/lib/db/schema.ts +61 -3
- package/mission-control/src/lib/hooks.ts +309 -0
- package/mission-control/src/lib/memory/entities.ts +3 -2
- package/mission-control/src/lib/memory/extract.ts +2 -1
- package/mission-control/src/lib/memory/retrieval.ts +3 -2
- package/mission-control/src/lib/nats.ts +66 -1
- package/mission-control/src/lib/parsers/task-markdown.ts +52 -2
- package/mission-control/src/lib/parsers/transcript.ts +4 -4
- package/mission-control/src/lib/scheduler.ts +12 -11
- package/mission-control/src/lib/sync/mesh-kv.ts +279 -0
- package/mission-control/src/lib/sync/tasks.ts +23 -1
- package/mission-control/src/lib/task-id.ts +32 -0
- package/mission-control/src/lib/tts/index.ts +33 -9
- package/mission-control/src/middleware.ts +82 -0
- package/mission-control/tsconfig.json +2 -1
- package/mission-control/vitest.config.ts +14 -0
- package/package.json +15 -2
- package/services/launchd/ai.openclaw.log-rotate.plist +11 -0
- package/services/launchd/ai.openclaw.mesh-deploy-listener.plist +4 -0
- package/services/launchd/ai.openclaw.mesh-health-publisher.plist +4 -0
- package/services/launchd/ai.openclaw.mission-control.plist +1 -1
- package/services/service-manifest.json +1 -1
- package/skills/cc-godmode/references/agents.md +8 -8
- package/uninstall.sh +37 -9
- package/workspace-bin/memory-daemon.mjs +199 -5
- package/workspace-bin/session-search.mjs +204 -0
- package/workspace-bin/web-fetch.mjs +65 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* transcript-parser.mjs — Format-agnostic JSONL transcript parser
|
|
3
|
+
*
|
|
4
|
+
* Abstracts away the differences between JSONL formats produced by
|
|
5
|
+
* different OpenClaw frontends:
|
|
6
|
+
*
|
|
7
|
+
* - claude-code: {type: "user"|"assistant", message: {role, content}, timestamp, usage}
|
|
8
|
+
* - openclaw-gateway: {type: "message", message: {role, content}, timestamp}
|
|
9
|
+
* (plus metadata types: session, model_change, etc.)
|
|
10
|
+
* - mesh-agent: Raw text stdout/stderr (non-JSONL, handled separately)
|
|
11
|
+
*
|
|
12
|
+
* Provides a unified stream of { role, content, timestamp } messages
|
|
13
|
+
* regardless of source format.
|
|
14
|
+
*
|
|
15
|
+
* Usage:
|
|
16
|
+
* import { parseJsonl, parseJsonlFile, detectFormat } from './transcript-parser.mjs';
|
|
17
|
+
*
|
|
18
|
+
* // Parse a JSONL file with auto-detected format
|
|
19
|
+
* const messages = await parseJsonlFile('/path/to/session.jsonl');
|
|
20
|
+
*
|
|
21
|
+
* // Parse with explicit format
|
|
22
|
+
* const messages = await parseJsonlFile(path, { format: 'openclaw-gateway' });
|
|
23
|
+
*
|
|
24
|
+
* // Parse a single JSONL line
|
|
25
|
+
* const msg = parseLine(jsonString, { format: 'claude-code' });
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
import { createReadStream } from 'fs';
|
|
30
|
+
import { createInterface } from 'readline';
|
|
31
|
+
|
|
32
|
+
// ── Format Registry ────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Adapters indexed by format name.
|
|
36
|
+
* Each adapter has:
|
|
37
|
+
* - isMessage(entry): boolean — does this JSONL entry represent a conversation message?
|
|
38
|
+
* - extractMessage(entry): { role, content, timestamp, metadata? } | null
|
|
39
|
+
*/
|
|
40
|
+
const ADAPTERS = {};
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Register a new format adapter.
|
|
44
|
+
* @param {string} name - Format identifier (e.g. 'claude-code', 'openclaw-gateway')
|
|
45
|
+
* @param {Object} adapter - { isMessage(entry), extractMessage(entry) }
|
|
46
|
+
*/
|
|
47
|
+
export function registerFormat(name, adapter) {
|
|
48
|
+
ADAPTERS[name] = adapter;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ── Built-in Adapters ────────────────────────────────────
|
|
52
|
+
|
|
53
|
+
// Claude Code format: {type: "user"|"assistant", message: {role, content}, timestamp}
|
|
54
|
+
registerFormat('claude-code', {
|
|
55
|
+
isMessage(entry) {
|
|
56
|
+
return (entry.type === 'user' || entry.type === 'assistant') && entry.message;
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
extractMessage(entry) {
|
|
60
|
+
const content = extractContent(entry.message);
|
|
61
|
+
if (!content) return null;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
role: entry.type, // 'user' or 'assistant'
|
|
65
|
+
content,
|
|
66
|
+
timestamp: entry.timestamp || null,
|
|
67
|
+
metadata: {
|
|
68
|
+
usage: entry.usage || null,
|
|
69
|
+
model: entry.model || null,
|
|
70
|
+
costUSD: entry.costUSD || null,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// OpenClaw Gateway format: {type: "message", message: {role, content}, timestamp}
|
|
77
|
+
// Metadata types to skip: session, model_change, thinking_level_change, queue-operation, tool_result, custom
|
|
78
|
+
const GATEWAY_SKIP_TYPES = new Set([
|
|
79
|
+
'session', 'model_change', 'thinking_level_change',
|
|
80
|
+
'custom', 'queue-operation', 'tool_result',
|
|
81
|
+
]);
|
|
82
|
+
|
|
83
|
+
registerFormat('openclaw-gateway', {
|
|
84
|
+
isMessage(entry) {
|
|
85
|
+
if (GATEWAY_SKIP_TYPES.has(entry.type)) return false;
|
|
86
|
+
return entry.type === 'message' && entry.message && entry.message.role;
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
extractMessage(entry) {
|
|
90
|
+
let content = extractContent(entry.message);
|
|
91
|
+
if (!content) return null;
|
|
92
|
+
|
|
93
|
+
// Strip gateway header noise: "[Mon 2026-03-22 14:30 GMT-5] actual message"
|
|
94
|
+
content = content.replace(/^\[(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+GMT[+-]\d+\]\s*/i, '');
|
|
95
|
+
|
|
96
|
+
// Strip metadata noise: "Conversation info (untrusted metadata): ```json {...} ```"
|
|
97
|
+
content = content.replace(/^Conversation info \(untrusted metadata\):[\s\S]*?```\s*/i, '');
|
|
98
|
+
content = content.trim();
|
|
99
|
+
|
|
100
|
+
if (!content) return null;
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
role: entry.message.role, // 'user' or 'assistant'
|
|
104
|
+
content,
|
|
105
|
+
timestamp: entry.timestamp || null,
|
|
106
|
+
metadata: {},
|
|
107
|
+
};
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Generic fallback: tries both formats
|
|
112
|
+
registerFormat('auto', {
|
|
113
|
+
isMessage(entry) {
|
|
114
|
+
// Role-as-type format (type='user'|'assistant')
|
|
115
|
+
if ((entry.type === 'user' || entry.type === 'assistant') && entry.message) return true;
|
|
116
|
+
// Unified-message format (type='message' with role field)
|
|
117
|
+
if (entry.type === 'message' && entry.message && entry.message.role) return true;
|
|
118
|
+
return false;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
extractMessage(entry) {
|
|
122
|
+
// Try role-as-type format first
|
|
123
|
+
if ((entry.type === 'user' || entry.type === 'assistant') && entry.message) {
|
|
124
|
+
return ADAPTERS['claude-code'].extractMessage(entry);
|
|
125
|
+
}
|
|
126
|
+
// Try unified-message format
|
|
127
|
+
if (entry.type === 'message' && entry.message && entry.message.role) {
|
|
128
|
+
return ADAPTERS['openclaw-gateway'].extractMessage(entry);
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── Content Extraction ────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Extract text content from a message object.
|
|
138
|
+
* Handles both string content and array-of-blocks content.
|
|
139
|
+
*
|
|
140
|
+
* @param {Object} message - { content: string | Array<{type, text}> }
|
|
141
|
+
* @returns {string}
|
|
142
|
+
*/
|
|
143
|
+
export function extractContent(message) {
|
|
144
|
+
if (!message) return '';
|
|
145
|
+
|
|
146
|
+
const { content } = message;
|
|
147
|
+
if (typeof content === 'string') return content;
|
|
148
|
+
|
|
149
|
+
if (Array.isArray(content)) {
|
|
150
|
+
return content
|
|
151
|
+
.filter(block => block.type === 'text')
|
|
152
|
+
.map(block => block.text || '')
|
|
153
|
+
.join('\n');
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return '';
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Format Detection ────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Auto-detect JSONL format by sampling the first few entries.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} jsonlPath - Path to the JSONL file
|
|
165
|
+
* @param {number} sampleLines - Number of lines to sample (default: 10)
|
|
166
|
+
* @returns {Promise<string>} Format name: 'claude-code' | 'openclaw-gateway' | 'auto'
|
|
167
|
+
*/
|
|
168
|
+
export async function detectFormat(jsonlPath, sampleLines = 10) {
|
|
169
|
+
if (!fs.existsSync(jsonlPath)) return 'auto';
|
|
170
|
+
|
|
171
|
+
const rl = createInterface({ input: createReadStream(jsonlPath), crlfDelay: Infinity });
|
|
172
|
+
let roleTypeHits = 0; // role-as-type format (type='user'|'assistant')
|
|
173
|
+
let unifiedMsgHits = 0; // unified-message format (type='message' + role field)
|
|
174
|
+
let lineCount = 0;
|
|
175
|
+
|
|
176
|
+
for await (const line of rl) {
|
|
177
|
+
if (lineCount >= sampleLines) break;
|
|
178
|
+
if (!line.trim()) continue;
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const entry = JSON.parse(line);
|
|
182
|
+
|
|
183
|
+
if (entry.type === 'user' || entry.type === 'assistant') roleTypeHits++;
|
|
184
|
+
if (entry.type === 'message' && entry.message?.role) unifiedMsgHits++;
|
|
185
|
+
if (GATEWAY_SKIP_TYPES.has(entry.type)) unifiedMsgHits++; // metadata entries = gateway signal
|
|
186
|
+
if (entry.usage || entry.costUSD) roleTypeHits++; // token tracking = role-type format signal
|
|
187
|
+
} catch { /* skip */ }
|
|
188
|
+
|
|
189
|
+
lineCount++;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (roleTypeHits > unifiedMsgHits) return 'claude-code';
|
|
193
|
+
if (unifiedMsgHits > roleTypeHits) return 'openclaw-gateway';
|
|
194
|
+
return 'auto';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ── Parsing ────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Parse a single JSONL line into a message.
|
|
201
|
+
*
|
|
202
|
+
* @param {string} line - Raw JSONL string
|
|
203
|
+
* @param {Object} opts
|
|
204
|
+
* @param {string} opts.format - Format name (default: 'auto')
|
|
205
|
+
* @returns {{ role: string, content: string, timestamp: string|null, metadata?: object } | null}
|
|
206
|
+
*/
|
|
207
|
+
export function parseLine(line, opts = {}) {
|
|
208
|
+
const { format = 'auto' } = opts;
|
|
209
|
+
const adapter = ADAPTERS[format] || ADAPTERS['auto'];
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const entry = JSON.parse(line);
|
|
213
|
+
if (!adapter.isMessage(entry)) return null;
|
|
214
|
+
return adapter.extractMessage(entry);
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Parse an entire JSONL file into an array of messages.
|
|
222
|
+
* Optionally returns only the tail (last N messages).
|
|
223
|
+
*
|
|
224
|
+
* @param {string} jsonlPath - Path to the JSONL file
|
|
225
|
+
* @param {Object} opts
|
|
226
|
+
* @param {string} opts.format - Format name (default: auto-detected)
|
|
227
|
+
* @param {number} opts.tail - If set, return only the last N messages
|
|
228
|
+
* @returns {Promise<Array<{ role, content, timestamp, metadata? }>>}
|
|
229
|
+
*/
|
|
230
|
+
export async function parseJsonlFile(jsonlPath, opts = {}) {
|
|
231
|
+
let { format, tail } = opts;
|
|
232
|
+
|
|
233
|
+
if (!fs.existsSync(jsonlPath)) return [];
|
|
234
|
+
|
|
235
|
+
// Auto-detect format if not specified
|
|
236
|
+
if (!format) {
|
|
237
|
+
format = await detectFormat(jsonlPath);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const adapter = ADAPTERS[format] || ADAPTERS['auto'];
|
|
241
|
+
const messages = [];
|
|
242
|
+
|
|
243
|
+
const rl = createInterface({ input: createReadStream(jsonlPath), crlfDelay: Infinity });
|
|
244
|
+
|
|
245
|
+
for await (const line of rl) {
|
|
246
|
+
if (!line.trim()) continue;
|
|
247
|
+
try {
|
|
248
|
+
const entry = JSON.parse(line);
|
|
249
|
+
if (!adapter.isMessage(entry)) continue;
|
|
250
|
+
const msg = adapter.extractMessage(entry);
|
|
251
|
+
if (msg) messages.push(msg);
|
|
252
|
+
} catch { /* skip malformed */ }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (tail && tail > 0) {
|
|
256
|
+
return messages.slice(-tail);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return messages;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Count messages and estimate tokens from a JSONL file.
|
|
264
|
+
*
|
|
265
|
+
* @param {string} jsonlPath
|
|
266
|
+
* @param {Object} opts
|
|
267
|
+
* @param {string} opts.format - Format name (default: auto-detected)
|
|
268
|
+
* @param {number} opts.charsPerToken - Chars per token estimate (default: 4)
|
|
269
|
+
* @returns {Promise<{ messageCount, totalChars, estimatedTokens }>}
|
|
270
|
+
*/
|
|
271
|
+
export async function estimateFileTokens(jsonlPath, opts = {}) {
|
|
272
|
+
const { charsPerToken = 4 } = opts;
|
|
273
|
+
const messages = await parseJsonlFile(jsonlPath, opts);
|
|
274
|
+
|
|
275
|
+
let totalChars = 0;
|
|
276
|
+
for (const msg of messages) {
|
|
277
|
+
totalChars += msg.content.length;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
messageCount: messages.length,
|
|
282
|
+
totalChars,
|
|
283
|
+
estimatedTokens: Math.ceil(totalChars / charsPerToken),
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Get list of registered format names.
|
|
289
|
+
*/
|
|
290
|
+
export function listFormats() {
|
|
291
|
+
return Object.keys(ADAPTERS);
|
|
292
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Add soul tracking columns to tasks table
|
|
2
|
+
ALTER TABLE tasks ADD COLUMN soul_id TEXT;
|
|
3
|
+
ALTER TABLE tasks ADD COLUMN handoff_source TEXT;
|
|
4
|
+
ALTER TABLE tasks ADD COLUMN handoff_reason TEXT;
|
|
5
|
+
|
|
6
|
+
-- Create soul_handoffs table
|
|
7
|
+
CREATE TABLE IF NOT EXISTS soul_handoffs (
|
|
8
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
9
|
+
task_id TEXT NOT NULL,
|
|
10
|
+
from_soul TEXT NOT NULL,
|
|
11
|
+
to_soul TEXT NOT NULL,
|
|
12
|
+
reason TEXT,
|
|
13
|
+
context_path TEXT,
|
|
14
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
-- Create soul_evolution_log table
|
|
18
|
+
CREATE TABLE IF NOT EXISTS soul_evolution_log (
|
|
19
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
|
20
|
+
soul_id TEXT NOT NULL,
|
|
21
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
22
|
+
event_type TEXT NOT NULL,
|
|
23
|
+
description TEXT NOT NULL,
|
|
24
|
+
review_status TEXT NOT NULL DEFAULT 'pending',
|
|
25
|
+
commit_hash TEXT,
|
|
26
|
+
reviewed_by TEXT,
|
|
27
|
+
reviewed_at TEXT,
|
|
28
|
+
timestamp TEXT NOT NULL DEFAULT (datetime('now'))
|
|
29
|
+
);
|
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { defineConfig } from "drizzle-kit";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
const dbPath = process.env.DB_PATH || path.resolve(__dirname, "data", "mission-control.db");
|
|
5
2
|
|
|
6
3
|
export default defineConfig({
|
|
7
4
|
schema: "./src/lib/db/schema.ts",
|
|
8
5
|
out: "./drizzle",
|
|
9
6
|
dialect: "sqlite",
|
|
10
7
|
dbCredentials: {
|
|
11
|
-
url:
|
|
8
|
+
url: "./data/mission-control.db",
|
|
12
9
|
},
|
|
13
10
|
});
|