muonroi-cli 1.8.3 → 1.8.4
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/LICENSE +9 -21
- package/README.md +133 -122
- package/dist/src/cli/cost-forensics.js +12 -12
- package/dist/src/council/prompts.js +20 -20
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/mcp/oauth-callback.js +2 -2
- package/dist/src/mcp/setup-guide-text.d.ts +1 -1
- package/dist/src/mcp/setup-guide-text.js +77 -76
- package/dist/src/ops/doctor.js +7 -7
- package/dist/src/orchestrator/prompts.js +159 -159
- package/dist/src/orchestrator/tool-engine.d.ts +4 -0
- package/dist/src/orchestrator/tool-engine.js +49 -0
- package/dist/src/pil/layer1-intent.js +37 -37
- package/dist/src/pil/layer2_5-ponytail.js +8 -8
- package/dist/src/product-loop/done-gate.js +3 -3
- package/dist/src/product-loop/loop-driver.js +18 -18
- package/dist/src/product-loop/progress-snapshot.js +4 -4
- package/dist/src/providers/mcp-vision-bridge.js +48 -48
- package/dist/src/reporter/index.js +1 -1
- package/dist/src/scaffold/bb-ecosystem-apply.js +47 -47
- package/dist/src/scaffold/bb-quality-gate.js +5 -5
- package/dist/src/scaffold/continuation-prompt.js +60 -60
- package/dist/src/scaffold/init-new.js +453 -453
- package/dist/src/self-qa/agentic-loop.js +19 -19
- package/dist/src/storage/interaction-log.js +5 -5
- package/dist/src/storage/migrations.js +125 -125
- package/dist/src/storage/session-experience-store.js +4 -4
- package/dist/src/storage/sessions.js +43 -43
- package/dist/src/storage/transcript.js +100 -100
- package/dist/src/storage/usage.js +14 -14
- package/dist/src/storage/workspaces.js +12 -12
- package/dist/src/ui/slash/council-inspect.js +4 -4
- package/dist/src/ui/use-app-logic.js +0 -0
- package/dist/src/utils/clipboard-image.js +23 -23
- package/dist/src/utils/install-manager.js +12 -10
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/utils/skills.js +3 -3
- package/package.json +2 -2
|
@@ -43,11 +43,11 @@ function isInternalCouncilMarker(content) {
|
|
|
43
43
|
}
|
|
44
44
|
function loadMessageRows(sessionId) {
|
|
45
45
|
const rows = getDatabase()
|
|
46
|
-
.prepare(`
|
|
47
|
-
SELECT session_id, seq, role, message_json, created_at
|
|
48
|
-
FROM messages
|
|
49
|
-
WHERE session_id = ?
|
|
50
|
-
ORDER BY seq ASC
|
|
46
|
+
.prepare(`
|
|
47
|
+
SELECT session_id, seq, role, message_json, created_at
|
|
48
|
+
FROM messages
|
|
49
|
+
WHERE session_id = ?
|
|
50
|
+
ORDER BY seq ASC
|
|
51
51
|
`)
|
|
52
52
|
.all(sessionId);
|
|
53
53
|
for (const row of rows) {
|
|
@@ -67,12 +67,12 @@ function toPersistedCompaction(row) {
|
|
|
67
67
|
}
|
|
68
68
|
export function loadLatestCompaction(sessionId) {
|
|
69
69
|
const row = getDatabase()
|
|
70
|
-
.prepare(`
|
|
71
|
-
SELECT session_id, first_kept_seq, summary, tokens_before, created_at
|
|
72
|
-
FROM compactions
|
|
73
|
-
WHERE session_id = ?
|
|
74
|
-
ORDER BY id DESC
|
|
75
|
-
LIMIT 1
|
|
70
|
+
.prepare(`
|
|
71
|
+
SELECT session_id, first_kept_seq, summary, tokens_before, created_at
|
|
72
|
+
FROM compactions
|
|
73
|
+
WHERE session_id = ?
|
|
74
|
+
ORDER BY id DESC
|
|
75
|
+
LIMIT 1
|
|
76
76
|
`)
|
|
77
77
|
.get(sessionId);
|
|
78
78
|
return toPersistedCompaction(row);
|
|
@@ -113,37 +113,37 @@ export function appendMessages(sessionId, messages) {
|
|
|
113
113
|
// with `status='completed'` and the full message_json. Pre-A5 callers
|
|
114
114
|
// hit this path identically: there is no pending row so the IGNORE
|
|
115
115
|
// branch is unused and the INSERT wins.
|
|
116
|
-
const insertMessage = db.prepare(`
|
|
117
|
-
INSERT INTO messages (session_id, seq, role, message_json, created_at, status)
|
|
118
|
-
VALUES (?, ?, ?, ?, ?, 'completed')
|
|
119
|
-
ON CONFLICT(session_id, seq) DO UPDATE SET
|
|
120
|
-
role = excluded.role,
|
|
121
|
-
message_json = excluded.message_json,
|
|
122
|
-
status = 'completed'
|
|
116
|
+
const insertMessage = db.prepare(`
|
|
117
|
+
INSERT INTO messages (session_id, seq, role, message_json, created_at, status)
|
|
118
|
+
VALUES (?, ?, ?, ?, ?, 'completed')
|
|
119
|
+
ON CONFLICT(session_id, seq) DO UPDATE SET
|
|
120
|
+
role = excluded.role,
|
|
121
|
+
message_json = excluded.message_json,
|
|
122
|
+
status = 'completed'
|
|
123
123
|
`);
|
|
124
|
-
const insertToolCall = db.prepare(`
|
|
125
|
-
INSERT OR IGNORE INTO tool_calls (
|
|
126
|
-
session_id, message_seq, tool_call_id, tool_name, args_json, status, started_at, completed_at
|
|
127
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
124
|
+
const insertToolCall = db.prepare(`
|
|
125
|
+
INSERT OR IGNORE INTO tool_calls (
|
|
126
|
+
session_id, message_seq, tool_call_id, tool_name, args_json, status, started_at, completed_at
|
|
127
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
128
128
|
`);
|
|
129
|
-
const updateToolCall = db.prepare(`
|
|
130
|
-
UPDATE tool_calls
|
|
131
|
-
SET tool_name = ?, args_json = ?, status = ?, completed_at = ?
|
|
132
|
-
WHERE session_id = ? AND tool_call_id = ?
|
|
129
|
+
const updateToolCall = db.prepare(`
|
|
130
|
+
UPDATE tool_calls
|
|
131
|
+
SET tool_name = ?, args_json = ?, status = ?, completed_at = ?
|
|
132
|
+
WHERE session_id = ? AND tool_call_id = ?
|
|
133
133
|
`);
|
|
134
|
-
const selectToolCall = db.prepare(`
|
|
135
|
-
SELECT id, tool_call_id, tool_name, args_json
|
|
136
|
-
FROM tool_calls
|
|
137
|
-
WHERE session_id = ? AND tool_call_id = ?
|
|
134
|
+
const selectToolCall = db.prepare(`
|
|
135
|
+
SELECT id, tool_call_id, tool_name, args_json
|
|
136
|
+
FROM tool_calls
|
|
137
|
+
WHERE session_id = ? AND tool_call_id = ?
|
|
138
138
|
`);
|
|
139
|
-
const insertToolResult = db.prepare(`
|
|
140
|
-
INSERT INTO tool_results (tool_call_row_id, output_kind, output_json, success, created_at)
|
|
141
|
-
VALUES (?, ?, ?, ?, ?)
|
|
139
|
+
const insertToolResult = db.prepare(`
|
|
140
|
+
INSERT INTO tool_results (tool_call_row_id, output_kind, output_json, success, created_at)
|
|
141
|
+
VALUES (?, ?, ?, ?, ?)
|
|
142
142
|
`);
|
|
143
|
-
const updateSession = db.prepare(`
|
|
144
|
-
UPDATE sessions
|
|
145
|
-
SET updated_at = ?
|
|
146
|
-
WHERE id = ?
|
|
143
|
+
const updateSession = db.prepare(`
|
|
144
|
+
UPDATE sessions
|
|
145
|
+
SET updated_at = ?
|
|
146
|
+
WHERE id = ?
|
|
147
147
|
`);
|
|
148
148
|
messages.forEach((message, index) => {
|
|
149
149
|
const seq = nextSeq + index;
|
|
@@ -205,10 +205,10 @@ export function persistToolCallWriteAhead(sessionId, messageSeq, toolCallId, too
|
|
|
205
205
|
const now = new Date().toISOString();
|
|
206
206
|
try {
|
|
207
207
|
getDatabase()
|
|
208
|
-
.prepare(`
|
|
209
|
-
INSERT OR IGNORE INTO tool_calls (
|
|
210
|
-
session_id, message_seq, tool_call_id, tool_name, args_json, status, started_at, completed_at
|
|
211
|
-
) VALUES (?, ?, ?, ?, ?, 'pending', ?, NULL)
|
|
208
|
+
.prepare(`
|
|
209
|
+
INSERT OR IGNORE INTO tool_calls (
|
|
210
|
+
session_id, message_seq, tool_call_id, tool_name, args_json, status, started_at, completed_at
|
|
211
|
+
) VALUES (?, ?, ?, ?, ?, 'pending', ?, NULL)
|
|
212
212
|
`)
|
|
213
213
|
.run(sessionId, messageSeq, toolCallId, toolName, argsJson, now);
|
|
214
214
|
}
|
|
@@ -254,10 +254,10 @@ export function persistMessageWriteAhead(sessionId, seq, role, messageJson) {
|
|
|
254
254
|
const now = new Date().toISOString();
|
|
255
255
|
try {
|
|
256
256
|
getDatabase()
|
|
257
|
-
.prepare(`
|
|
258
|
-
INSERT OR IGNORE INTO messages (
|
|
259
|
-
session_id, seq, role, message_json, created_at, status
|
|
260
|
-
) VALUES (?, ?, ?, ?, ?, 'pending')
|
|
257
|
+
.prepare(`
|
|
258
|
+
INSERT OR IGNORE INTO messages (
|
|
259
|
+
session_id, seq, role, message_json, created_at, status
|
|
260
|
+
) VALUES (?, ?, ?, ?, ?, 'pending')
|
|
261
261
|
`)
|
|
262
262
|
.run(sessionId, seq, role, messageJson, now);
|
|
263
263
|
}
|
|
@@ -275,10 +275,10 @@ export function persistMessageWriteAhead(sessionId, seq, role, messageJson) {
|
|
|
275
275
|
export function markMessageCompleted(sessionId, seq) {
|
|
276
276
|
try {
|
|
277
277
|
getDatabase()
|
|
278
|
-
.prepare(`
|
|
279
|
-
UPDATE messages
|
|
280
|
-
SET status = 'completed'
|
|
281
|
-
WHERE session_id = ? AND seq = ? AND status = 'pending'
|
|
278
|
+
.prepare(`
|
|
279
|
+
UPDATE messages
|
|
280
|
+
SET status = 'completed'
|
|
281
|
+
WHERE session_id = ? AND seq = ? AND status = 'pending'
|
|
282
282
|
`)
|
|
283
283
|
.run(sessionId, seq);
|
|
284
284
|
}
|
|
@@ -297,10 +297,10 @@ export function markMessageCompleted(sessionId, seq) {
|
|
|
297
297
|
export function markMessageErrored(sessionId, seq) {
|
|
298
298
|
try {
|
|
299
299
|
getDatabase()
|
|
300
|
-
.prepare(`
|
|
301
|
-
UPDATE messages
|
|
302
|
-
SET status = 'errored'
|
|
303
|
-
WHERE session_id = ? AND seq = ? AND status = 'pending'
|
|
300
|
+
.prepare(`
|
|
301
|
+
UPDATE messages
|
|
302
|
+
SET status = 'errored'
|
|
303
|
+
WHERE session_id = ? AND seq = ? AND status = 'pending'
|
|
304
304
|
`)
|
|
305
305
|
.run(sessionId, seq);
|
|
306
306
|
}
|
|
@@ -331,17 +331,17 @@ export function sweepStalePendingRows(staleAfterMs = 5 * 60 * 1000) {
|
|
|
331
331
|
try {
|
|
332
332
|
const db = getDatabase();
|
|
333
333
|
const toolCalls = db
|
|
334
|
-
.prepare(`
|
|
335
|
-
UPDATE tool_calls
|
|
336
|
-
SET status = 'aborted', completed_at = started_at
|
|
337
|
-
WHERE status = 'pending' AND started_at < ?
|
|
334
|
+
.prepare(`
|
|
335
|
+
UPDATE tool_calls
|
|
336
|
+
SET status = 'aborted', completed_at = started_at
|
|
337
|
+
WHERE status = 'pending' AND started_at < ?
|
|
338
338
|
`)
|
|
339
339
|
.run(cutoff);
|
|
340
340
|
const messages = db
|
|
341
|
-
.prepare(`
|
|
342
|
-
UPDATE messages
|
|
343
|
-
SET status = 'aborted'
|
|
344
|
-
WHERE status = 'pending' AND created_at < ?
|
|
341
|
+
.prepare(`
|
|
342
|
+
UPDATE messages
|
|
343
|
+
SET status = 'aborted'
|
|
344
|
+
WHERE status = 'pending' AND created_at < ?
|
|
345
345
|
`)
|
|
346
346
|
.run(cutoff);
|
|
347
347
|
return { toolCalls: toolCalls.changes, messages: messages.changes };
|
|
@@ -355,10 +355,10 @@ export function markToolCallErrored(sessionId, toolCallId, errorMessage) {
|
|
|
355
355
|
const now = new Date().toISOString();
|
|
356
356
|
try {
|
|
357
357
|
getDatabase()
|
|
358
|
-
.prepare(`
|
|
359
|
-
UPDATE tool_calls
|
|
360
|
-
SET status = 'errored', completed_at = ?, args_json = COALESCE(args_json, ?)
|
|
361
|
-
WHERE session_id = ? AND tool_call_id = ?
|
|
358
|
+
.prepare(`
|
|
359
|
+
UPDATE tool_calls
|
|
360
|
+
SET status = 'errored', completed_at = ?, args_json = COALESCE(args_json, ?)
|
|
361
|
+
WHERE session_id = ? AND tool_call_id = ?
|
|
362
362
|
`)
|
|
363
363
|
.run(now, JSON.stringify({ error: errorMessage.slice(0, 500) }), sessionId, toolCallId);
|
|
364
364
|
}
|
|
@@ -368,28 +368,28 @@ export function markToolCallErrored(sessionId, toolCallId, errorMessage) {
|
|
|
368
368
|
}
|
|
369
369
|
export function appendCompaction(sessionId, firstKeptSeq, summary, tokensBefore) {
|
|
370
370
|
withTransaction((db) => {
|
|
371
|
-
db.prepare(`
|
|
372
|
-
INSERT INTO compactions (session_id, first_kept_seq, summary, tokens_before, created_at)
|
|
373
|
-
VALUES (?, ?, ?, ?, ?)
|
|
371
|
+
db.prepare(`
|
|
372
|
+
INSERT INTO compactions (session_id, first_kept_seq, summary, tokens_before, created_at)
|
|
373
|
+
VALUES (?, ?, ?, ?, ?)
|
|
374
374
|
`).run(sessionId, firstKeptSeq, summary, tokensBefore, new Date().toISOString());
|
|
375
|
-
db.prepare(`
|
|
376
|
-
UPDATE sessions
|
|
377
|
-
SET updated_at = ?
|
|
378
|
-
WHERE id = ?
|
|
375
|
+
db.prepare(`
|
|
376
|
+
UPDATE sessions
|
|
377
|
+
SET updated_at = ?
|
|
378
|
+
WHERE id = ?
|
|
379
379
|
`).run(new Date().toISOString(), sessionId);
|
|
380
380
|
});
|
|
381
381
|
}
|
|
382
382
|
export function revertLatestCompaction(sessionId) {
|
|
383
383
|
withTransaction((db) => {
|
|
384
|
-
db.prepare(`
|
|
385
|
-
DELETE FROM compactions
|
|
386
|
-
WHERE session_id = ?
|
|
387
|
-
AND id = (SELECT MAX(id) FROM compactions WHERE session_id = ?)
|
|
384
|
+
db.prepare(`
|
|
385
|
+
DELETE FROM compactions
|
|
386
|
+
WHERE session_id = ?
|
|
387
|
+
AND id = (SELECT MAX(id) FROM compactions WHERE session_id = ?)
|
|
388
388
|
`).run(sessionId, sessionId);
|
|
389
|
-
db.prepare(`
|
|
390
|
-
UPDATE sessions
|
|
391
|
-
SET updated_at = ?
|
|
392
|
-
WHERE id = ?
|
|
389
|
+
db.prepare(`
|
|
390
|
+
UPDATE sessions
|
|
391
|
+
SET updated_at = ?
|
|
392
|
+
WHERE id = ?
|
|
393
393
|
`).run(new Date().toISOString(), sessionId);
|
|
394
394
|
});
|
|
395
395
|
}
|
|
@@ -439,10 +439,10 @@ export function getSessionChain(sessionId) {
|
|
|
439
439
|
.map(() => "?")
|
|
440
440
|
.join(",");
|
|
441
441
|
const sortedRows = db
|
|
442
|
-
.prepare(`
|
|
443
|
-
SELECT id FROM sessions
|
|
444
|
-
WHERE id IN (${placeholders})
|
|
445
|
-
ORDER BY created_at ASC
|
|
442
|
+
.prepare(`
|
|
443
|
+
SELECT id FROM sessions
|
|
444
|
+
WHERE id IN (${placeholders})
|
|
445
|
+
ORDER BY created_at ASC
|
|
446
446
|
`)
|
|
447
447
|
.all(...Array.from(allIds));
|
|
448
448
|
chain = sortedRows.map((r) => r.id);
|
|
@@ -624,10 +624,10 @@ export function buildChatEntries(sessionId) {
|
|
|
624
624
|
}
|
|
625
625
|
function getNextSequence(db, sessionId) {
|
|
626
626
|
const row = db
|
|
627
|
-
.prepare(`
|
|
628
|
-
SELECT COALESCE(MAX(seq), 0) AS max_seq
|
|
629
|
-
FROM messages
|
|
630
|
-
WHERE session_id = ?
|
|
627
|
+
.prepare(`
|
|
628
|
+
SELECT COALESCE(MAX(seq), 0) AS max_seq
|
|
629
|
+
FROM messages
|
|
630
|
+
WHERE session_id = ?
|
|
631
631
|
`)
|
|
632
632
|
.get(sessionId);
|
|
633
633
|
return (row?.max_seq ?? 0) + 1;
|
|
@@ -677,12 +677,12 @@ function renderAssistantContent(content, callMap) {
|
|
|
677
677
|
}
|
|
678
678
|
function loadStoredToolResults(sessionId) {
|
|
679
679
|
const rows = getDatabase()
|
|
680
|
-
.prepare(`
|
|
681
|
-
SELECT tc.tool_call_id, tr.output_json
|
|
682
|
-
FROM tool_results tr
|
|
683
|
-
JOIN tool_calls tc ON tc.id = tr.tool_call_row_id
|
|
684
|
-
WHERE tc.session_id = ?
|
|
685
|
-
ORDER BY tr.id ASC
|
|
680
|
+
.prepare(`
|
|
681
|
+
SELECT tc.tool_call_id, tr.output_json
|
|
682
|
+
FROM tool_results tr
|
|
683
|
+
JOIN tool_calls tc ON tc.id = tr.tool_call_row_id
|
|
684
|
+
WHERE tc.session_id = ?
|
|
685
|
+
ORDER BY tr.id ASC
|
|
686
686
|
`)
|
|
687
687
|
.all(sessionId);
|
|
688
688
|
return new Map(rows.map((row) => [row.tool_call_id, JSON.parse(row.output_json)]));
|
|
@@ -703,12 +703,12 @@ export function getLastTodoWriteArgs(sessionId) {
|
|
|
703
703
|
for (let i = chain.length - 1; i >= 0; i--) {
|
|
704
704
|
try {
|
|
705
705
|
const row = db
|
|
706
|
-
.prepare(`
|
|
707
|
-
SELECT args_json
|
|
708
|
-
FROM tool_calls
|
|
709
|
-
WHERE session_id = ? AND tool_name = 'todo_write'
|
|
710
|
-
ORDER BY id DESC
|
|
711
|
-
LIMIT 1
|
|
706
|
+
.prepare(`
|
|
707
|
+
SELECT args_json
|
|
708
|
+
FROM tool_calls
|
|
709
|
+
WHERE session_id = ? AND tool_name = 'todo_write'
|
|
710
|
+
ORDER BY id DESC
|
|
711
|
+
LIMIT 1
|
|
712
712
|
`)
|
|
713
713
|
.get(chain[i]);
|
|
714
714
|
if (row?.args_json) {
|
|
@@ -14,11 +14,11 @@ export function recordUsageEvent(sessionId, source, model, usage, messageSeq, pi
|
|
|
14
14
|
const cacheCreationTokens = usage.cacheCreationTokens ?? 0;
|
|
15
15
|
const costMicros = estimateCostMicros(model, inputTokens, outputTokens, cacheReadTokens);
|
|
16
16
|
getDatabase()
|
|
17
|
-
.prepare(`
|
|
18
|
-
INSERT INTO usage_events (
|
|
19
|
-
session_id, message_seq, source, model, input_tokens, output_tokens, total_tokens, cost_micros, created_at,
|
|
20
|
-
pil_active, enrichment_delta, cache_read_tokens, cache_creation_tokens, provider_options_shape
|
|
21
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
17
|
+
.prepare(`
|
|
18
|
+
INSERT INTO usage_events (
|
|
19
|
+
session_id, message_seq, source, model, input_tokens, output_tokens, total_tokens, cost_micros, created_at,
|
|
20
|
+
pil_active, enrichment_delta, cache_read_tokens, cache_creation_tokens, provider_options_shape
|
|
21
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
22
22
|
`)
|
|
23
23
|
.run(sessionId, messageSeq ?? null, source, model, inputTokens, outputTokens, totalTokens, costMicros, new Date().toISOString(), pilActive ? 1 : 0, enrichmentDelta, cacheReadTokens, cacheCreationTokens, providerOptionsShape);
|
|
24
24
|
}
|
|
@@ -26,10 +26,10 @@ export function getSessionTotalTokens(sessionId) {
|
|
|
26
26
|
const chain = getSessionChain(sessionId);
|
|
27
27
|
const placeholders = chain.map(() => "?").join(",");
|
|
28
28
|
const row = getDatabase()
|
|
29
|
-
.prepare(`
|
|
30
|
-
SELECT COALESCE(SUM(total_tokens), 0) AS total_tokens
|
|
31
|
-
FROM usage_events
|
|
32
|
-
WHERE session_id IN (${placeholders})
|
|
29
|
+
.prepare(`
|
|
30
|
+
SELECT COALESCE(SUM(total_tokens), 0) AS total_tokens
|
|
31
|
+
FROM usage_events
|
|
32
|
+
WHERE session_id IN (${placeholders})
|
|
33
33
|
`)
|
|
34
34
|
.get(...chain);
|
|
35
35
|
const total = row?.total_tokens ?? 0;
|
|
@@ -44,11 +44,11 @@ export function listSessionUsage(sessionId) {
|
|
|
44
44
|
const chain = getSessionChain(sessionId);
|
|
45
45
|
const placeholders = chain.map(() => "?").join(",");
|
|
46
46
|
const rows = getDatabase()
|
|
47
|
-
.prepare(`
|
|
48
|
-
SELECT id, session_id, message_seq, source, model, input_tokens, output_tokens, total_tokens, cost_micros, created_at, cache_read_tokens, cache_creation_tokens
|
|
49
|
-
FROM usage_events
|
|
50
|
-
WHERE session_id IN (${placeholders})
|
|
51
|
-
ORDER BY id ASC
|
|
47
|
+
.prepare(`
|
|
48
|
+
SELECT id, session_id, message_seq, source, model, input_tokens, output_tokens, total_tokens, cost_micros, created_at, cache_read_tokens, cache_creation_tokens
|
|
49
|
+
FROM usage_events
|
|
50
|
+
WHERE session_id IN (${placeholders})
|
|
51
|
+
ORDER BY id ASC
|
|
52
52
|
`)
|
|
53
53
|
.all(...chain);
|
|
54
54
|
logger.debug("storage", "Retrieved session usage events from chain", {
|
|
@@ -8,14 +8,14 @@ export function ensureWorkspace(cwd) {
|
|
|
8
8
|
const now = new Date().toISOString();
|
|
9
9
|
const id = createHash("sha1").update(resolved.scopeKey).digest("hex").slice(0, 16);
|
|
10
10
|
const db = getDatabase();
|
|
11
|
-
db.prepare(`
|
|
12
|
-
INSERT INTO workspaces (id, scope_key, canonical_path, git_root, display_name, last_seen_at)
|
|
13
|
-
VALUES (@id, @scope_key, @canonical_path, @git_root, @display_name, @last_seen_at)
|
|
14
|
-
ON CONFLICT(scope_key) DO UPDATE SET
|
|
15
|
-
canonical_path = excluded.canonical_path,
|
|
16
|
-
git_root = excluded.git_root,
|
|
17
|
-
display_name = excluded.display_name,
|
|
18
|
-
last_seen_at = excluded.last_seen_at
|
|
11
|
+
db.prepare(`
|
|
12
|
+
INSERT INTO workspaces (id, scope_key, canonical_path, git_root, display_name, last_seen_at)
|
|
13
|
+
VALUES (@id, @scope_key, @canonical_path, @git_root, @display_name, @last_seen_at)
|
|
14
|
+
ON CONFLICT(scope_key) DO UPDATE SET
|
|
15
|
+
canonical_path = excluded.canonical_path,
|
|
16
|
+
git_root = excluded.git_root,
|
|
17
|
+
display_name = excluded.display_name,
|
|
18
|
+
last_seen_at = excluded.last_seen_at
|
|
19
19
|
`).run({
|
|
20
20
|
id,
|
|
21
21
|
scope_key: resolved.scopeKey,
|
|
@@ -25,10 +25,10 @@ export function ensureWorkspace(cwd) {
|
|
|
25
25
|
last_seen_at: now,
|
|
26
26
|
});
|
|
27
27
|
const row = db
|
|
28
|
-
.prepare(`
|
|
29
|
-
SELECT id, scope_key, canonical_path, git_root, display_name, last_seen_at
|
|
30
|
-
FROM workspaces
|
|
31
|
-
WHERE scope_key = ?
|
|
28
|
+
.prepare(`
|
|
29
|
+
SELECT id, scope_key, canonical_path, git_root, display_name, last_seen_at
|
|
30
|
+
FROM workspaces
|
|
31
|
+
WHERE scope_key = ?
|
|
32
32
|
`)
|
|
33
33
|
.get(resolved.scopeKey);
|
|
34
34
|
if (!row) {
|
|
@@ -37,10 +37,10 @@ export const handleCouncilInspectSlash = async (args) => {
|
|
|
37
37
|
}
|
|
38
38
|
// Load all system messages for this session — parameterized query prevents SQL injection (T-17-04)
|
|
39
39
|
const rows = db
|
|
40
|
-
.prepare(`SELECT role, message_json, seq, created_at
|
|
41
|
-
FROM messages
|
|
42
|
-
WHERE session_id = ?
|
|
43
|
-
AND role = 'system'
|
|
40
|
+
.prepare(`SELECT role, message_json, seq, created_at
|
|
41
|
+
FROM messages
|
|
42
|
+
WHERE session_id = ?
|
|
43
|
+
AND role = 'system'
|
|
44
44
|
ORDER BY seq ASC`)
|
|
45
45
|
.all(sessionId);
|
|
46
46
|
if (rows.length === 0) {
|
|
Binary file
|
|
@@ -25,24 +25,24 @@ function readWin32() {
|
|
|
25
25
|
try {
|
|
26
26
|
const escaped = tmpFile.replace(/\\/g, "\\\\");
|
|
27
27
|
// Run in STA thread with retry — clipboard can be locked by other processes
|
|
28
|
-
const ps = `
|
|
29
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
30
|
-
Add-Type -AssemblyName System.Drawing
|
|
31
|
-
$maxRetries = 3
|
|
32
|
-
$img = $null
|
|
33
|
-
for ($i = 0; $i -lt $maxRetries; $i++) {
|
|
34
|
-
try {
|
|
35
|
-
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
36
|
-
if ($img -ne $null) { break }
|
|
37
|
-
} catch {
|
|
38
|
-
# clipboard locked — wait and retry
|
|
39
|
-
}
|
|
40
|
-
Start-Sleep -Milliseconds 100
|
|
41
|
-
}
|
|
42
|
-
if ($img -ne $null) {
|
|
43
|
-
$img.Save('${escaped}', [System.Drawing.Imaging.ImageFormat]::Png)
|
|
44
|
-
$img.Dispose()
|
|
45
|
-
}
|
|
28
|
+
const ps = `
|
|
29
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
30
|
+
Add-Type -AssemblyName System.Drawing
|
|
31
|
+
$maxRetries = 3
|
|
32
|
+
$img = $null
|
|
33
|
+
for ($i = 0; $i -lt $maxRetries; $i++) {
|
|
34
|
+
try {
|
|
35
|
+
$img = [System.Windows.Forms.Clipboard]::GetImage()
|
|
36
|
+
if ($img -ne $null) { break }
|
|
37
|
+
} catch {
|
|
38
|
+
# clipboard locked — wait and retry
|
|
39
|
+
}
|
|
40
|
+
Start-Sleep -Milliseconds 100
|
|
41
|
+
}
|
|
42
|
+
if ($img -ne $null) {
|
|
43
|
+
$img.Save('${escaped}', [System.Drawing.Imaging.ImageFormat]::Png)
|
|
44
|
+
$img.Dispose()
|
|
45
|
+
}
|
|
46
46
|
`;
|
|
47
47
|
const result = spawnSync("powershell", ["-NoProfile", "-STA", "-Command", ps], { timeout: 8000 });
|
|
48
48
|
if (result.error || result.status !== 0)
|
|
@@ -81,11 +81,11 @@ function readDarwin() {
|
|
|
81
81
|
const pngpaste = spawnSync("pngpaste", [tmpFile], { timeout: 5000 });
|
|
82
82
|
if (pngpaste.status !== 0) {
|
|
83
83
|
spawnSync("screencapture", ["-c", "-x"]);
|
|
84
|
-
const osascript = `
|
|
85
|
-
set imgData to the clipboard as «class PNGf»
|
|
86
|
-
set f to open for access POSIX file "${tmpFile}" with write permission
|
|
87
|
-
write imgData to f
|
|
88
|
-
close access f
|
|
84
|
+
const osascript = `
|
|
85
|
+
set imgData to the clipboard as «class PNGf»
|
|
86
|
+
set f to open for access POSIX file "${tmpFile}" with write permission
|
|
87
|
+
write imgData to f
|
|
88
|
+
close access f
|
|
89
89
|
`;
|
|
90
90
|
spawnSync("osascript", ["-e", osascript], { timeout: 5000 });
|
|
91
91
|
}
|
|
@@ -248,21 +248,23 @@ export async function runManagedUpdate(currentVersion) {
|
|
|
248
248
|
if (latestVersion && normalizedCurrent) {
|
|
249
249
|
hasUpdate = semverGt(latestVersion, normalizedCurrent);
|
|
250
250
|
if (hasUpdate) {
|
|
251
|
-
statusHeader =
|
|
251
|
+
statusHeader = `### 🔄 Update Available\n* **Current Version:** \`v${normalizedCurrent}\`\n* **Latest Version:** \`v${latestVersion}\`\n* **Status:** A new version of \`muonroi-cli\` is available!\n\n`;
|
|
252
|
+
}
|
|
253
|
+
else if (semverGt(normalizedCurrent, latestVersion)) {
|
|
254
|
+
statusHeader = `### 🚀 Ahead of Latest Release\n* **Current Version:** \`v${normalizedCurrent}\`\n* **Latest Version:** \`v${latestVersion}\`\n* **Status:** Your local installation is newer than the remote release tag.\n\n`;
|
|
252
255
|
}
|
|
253
256
|
else {
|
|
254
|
-
statusHeader =
|
|
257
|
+
statusHeader = `### ✅ Up to Date\n* **Current Version:** \`v${normalizedCurrent}\`\n* **Latest Version:** \`v${latestVersion}\`\n* **Status:** You are already up to date!\n\n`;
|
|
255
258
|
}
|
|
256
259
|
}
|
|
257
260
|
else if (normalizedCurrent) {
|
|
258
|
-
statusHeader =
|
|
261
|
+
statusHeader = `### ⚠️ Update Status\n* **Current Version:** \`v${normalizedCurrent}\`\n* **Status:** Unable to check the latest version from GitHub or NPM.\n\n`;
|
|
259
262
|
}
|
|
260
263
|
const cmd = getUpdateCommandForMethod(method);
|
|
261
264
|
if (cmd) {
|
|
262
|
-
const pm = method === "bun-global" ? "bun" : "npm";
|
|
263
265
|
const instruction = hasUpdate
|
|
264
|
-
? `To update, run this in a fresh terminal:\n\n
|
|
265
|
-
: `If you want to reinstall, run this in a fresh terminal:\n\n
|
|
266
|
+
? `To update, run this in a fresh terminal:\n\`\`\`bash\n${cmd}\n\`\`\`\nThen restart \`muonroi-cli\`.`
|
|
267
|
+
: `If you want to reinstall, run this in a fresh terminal:\n\`\`\`bash\n${cmd}\n\`\`\``;
|
|
266
268
|
return {
|
|
267
269
|
success: true,
|
|
268
270
|
output: `${statusHeader}${instruction}`,
|
|
@@ -272,8 +274,8 @@ export async function runManagedUpdate(currentVersion) {
|
|
|
272
274
|
const target = getReleaseTargetForPlatform();
|
|
273
275
|
const asset = target?.assetName ?? "the release asset for your platform";
|
|
274
276
|
const instruction = hasUpdate
|
|
275
|
-
? `Download the latest
|
|
276
|
-
: `If you want to reinstall, download the latest
|
|
277
|
+
? `Download the latest \`${asset}\` from [GitHub Releases](https://github.com/${GITHUB_REPO}/releases/latest) and replace the current binary, or rebuild from source.`
|
|
278
|
+
: `If you want to reinstall, download the latest \`${asset}\` from [GitHub Releases](https://github.com/${GITHUB_REPO}/releases/latest) and replace the current binary.`;
|
|
277
279
|
return {
|
|
278
280
|
success: true,
|
|
279
281
|
output: `${statusHeader}${instruction}`,
|
|
@@ -282,8 +284,8 @@ export async function runManagedUpdate(currentVersion) {
|
|
|
282
284
|
if (method === "dev-link") {
|
|
283
285
|
const target = root ?? "the muonroi-cli checkout";
|
|
284
286
|
const instruction = hasUpdate
|
|
285
|
-
? `To update, pull the latest changes and rebuild:\n\
|
|
286
|
-
: `To rebuild your local installation:\n\
|
|
287
|
+
? `To update, pull the latest changes and rebuild:\n\`\`\`bash\ngit -C "${target}" pull && bun install && bun run build\n\`\`\`\nThen restart \`muonroi-cli\`. (If you also use the compiled muonroi-cli-dev binary, rebuild that separately.)`
|
|
288
|
+
: `To rebuild your local installation:\n\`\`\`bash\ngit -C "${target}" pull && bun install && bun run build\n\`\`\`\nThen restart \`muonroi-cli\`.`;
|
|
287
289
|
return {
|
|
288
290
|
success: true,
|
|
289
291
|
output: `${statusHeader}${instruction}`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { generateText } from "ai";
|
|
2
2
|
import { resolveModelRuntime } from "../providers/runtime.js";
|
|
3
|
-
const SIDE_QUESTION_SYSTEM = `You are a helpful coding assistant answering a quick side question. The user is in the middle of a coding session and needs a fast, concise answer. Keep your response short and focused — this is a side question, not the main task.
|
|
4
|
-
|
|
3
|
+
const SIDE_QUESTION_SYSTEM = `You are a helpful coding assistant answering a quick side question. The user is in the middle of a coding session and needs a fast, concise answer. Keep your response short and focused — this is a side question, not the main task.
|
|
4
|
+
|
|
5
5
|
If conversation context is provided below, use it to give a more relevant answer.`;
|
|
6
6
|
export async function runSideQuestion(question, provider, modelId, conversationContext, signal) {
|
|
7
7
|
const runtime = resolveModelRuntime(provider, modelId);
|
package/dist/src/utils/skills.js
CHANGED
|
@@ -163,9 +163,9 @@ export function discoverSkills(projectRoot) {
|
|
|
163
163
|
_skillsCache = { skills, cachedAt: now, cwd: projectRoot };
|
|
164
164
|
return skills;
|
|
165
165
|
}
|
|
166
|
-
const SKILLS_INSTRUCTIONS = `AGENT SKILLS (optional):
|
|
167
|
-
The following <available_skills> list specialized workflows. Use them when they might help the user's request — not only on exact keyword matches.
|
|
168
|
-
If a skill's description fits the task or could improve consistency, read that skill's instructions first using read_file with the path from <location>, then follow the SKILL.md body.
|
|
166
|
+
const SKILLS_INSTRUCTIONS = `AGENT SKILLS (optional):
|
|
167
|
+
The following <available_skills> list specialized workflows. Use them when they might help the user's request — not only on exact keyword matches.
|
|
168
|
+
If a skill's description fits the task or could improve consistency, read that skill's instructions first using read_file with the path from <location>, then follow the SKILL.md body.
|
|
169
169
|
Paths inside a skill (scripts/, references/, assets/) are relative to the skill directory (the folder containing SKILL.md); prefer absolute paths in tool calls.`;
|
|
170
170
|
/** OpenCode-style XML catalog plus activation instructions for read_file. Returns null if no skills. */
|
|
171
171
|
export function formatSkillsForPrompt(skills) {
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"workspaces": [
|
|
4
4
|
"packages/*"
|
|
5
5
|
],
|
|
6
|
-
"version": "1.8.
|
|
6
|
+
"version": "1.8.4",
|
|
7
7
|
"description": "BYOK AI coding agent with multi-model council debate, role-based routing, and auto-compact.",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"opentui"
|
|
78
78
|
],
|
|
79
79
|
"author": "muonroi",
|
|
80
|
-
"license": "
|
|
80
|
+
"license": "UNLICENSED",
|
|
81
81
|
"dependencies": {
|
|
82
82
|
"@ai-sdk/anthropic": "3.0.72",
|
|
83
83
|
"@ai-sdk/google": "3.0.65",
|