chat-recall 0.7.23 → 0.7.25
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 +5 -5
- package/dist/cli.js +266 -69
- package/dist/mcp-relay.js +1 -1
- package/dist/mcp.js +339 -140
- package/dist/message-hook-core.d.ts.map +1 -1
- package/dist/message-hook-core.js +4 -1
- package/dist/message-hook-core.js.map +1 -1
- package/dist/message-hook.js +15 -6
- package/dist/message-hook.js.map +1 -1
- package/dist/scan-worker.js +138 -14
- package/dist/watch.js +181 -57
- package/package.json +1 -1
- package/skills/chat-recall-sessions/SKILL.md +7 -3
package/README.md
CHANGED
|
@@ -248,11 +248,11 @@ receive messages. The installer writes it only for a tool that is installed:
|
|
|
248
248
|
| Codex | `~/.codex/hooks.json` | At the next prompt. Approve the hook once with `/hooks` in Codex. |
|
|
249
249
|
| Antigravity | `~/.gemini/config/hooks.json` | At the next prompt |
|
|
250
250
|
| Cursor | `~/.cursor/hooks.json` | When the agent stops, as a follow-up prompt |
|
|
251
|
-
| opencode | `~/.config/opencode/plugins/chat-recall-messages.js` |
|
|
252
|
-
| pi | `~/.pi/agent/extensions/chat-recall-messages.js` |
|
|
251
|
+
| opencode | `~/.config/opencode/plugins/chat-recall-messages.js` | Within a few seconds, also when the session is idle |
|
|
252
|
+
| pi | `~/.pi/agent/extensions/chat-recall-messages.js` | Within a few seconds, also when the session is idle |
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
reads its messages when its user types next. `chat-recall doctor` shows the
|
|
254
|
+
Claude Code, opencode and pi wake an idle session. In Codex, Antigravity and Cursor, an
|
|
255
|
+
idle session reads its messages when its user types next. `chat-recall doctor` shows the
|
|
256
256
|
hook of each tool.
|
|
257
257
|
|
|
258
258
|
## Sessions that work together
|
|
@@ -306,7 +306,7 @@ skips any tool that is not on the machine (`--no-guard` to skip all of them):
|
|
|
306
306
|
|---|---|---|
|
|
307
307
|
| Claude Code | `~/.claude/settings.json`, every profile | `PreToolUse` |
|
|
308
308
|
| Codex | `~/.codex/hooks.json` | `PreToolUse` |
|
|
309
|
-
| Antigravity | `~/.gemini/
|
|
309
|
+
| Antigravity | `~/.gemini/config/hooks.json` | `PreToolUse` — the one tool with a real `ask` verdict, so it can prompt |
|
|
310
310
|
| Cursor | `~/.cursor/hooks.json` | `beforeShellExecution` + `afterFileEdit` |
|
|
311
311
|
| OpenCode | `~/.config/opencode/plugins/chat-recall-guard.js` | `tool.execute.before` |
|
|
312
312
|
|
package/dist/cli.js
CHANGED
|
@@ -9502,19 +9502,19 @@ function mapJsonStrings(line, fn, escapedOnly) {
|
|
|
9502
9502
|
const q = line.indexOf('"', i);
|
|
9503
9503
|
if (q < 0)
|
|
9504
9504
|
return out + line.slice(i);
|
|
9505
|
-
let
|
|
9506
|
-
while (
|
|
9507
|
-
const c = line.charCodeAt(
|
|
9505
|
+
let j2 = q + 1;
|
|
9506
|
+
while (j2 < line.length) {
|
|
9507
|
+
const c = line.charCodeAt(j2);
|
|
9508
9508
|
if (c === 92)
|
|
9509
|
-
|
|
9509
|
+
j2 += 2;
|
|
9510
9510
|
else if (c === 34)
|
|
9511
9511
|
break;
|
|
9512
9512
|
else
|
|
9513
|
-
|
|
9513
|
+
j2++;
|
|
9514
9514
|
}
|
|
9515
|
-
const token = line.slice(q,
|
|
9515
|
+
const token = line.slice(q, j2 + 1);
|
|
9516
9516
|
out += line.slice(i, q);
|
|
9517
|
-
i =
|
|
9517
|
+
i = j2 + 1;
|
|
9518
9518
|
const body2 = token.slice(1, -1);
|
|
9519
9519
|
const escaped = body2.includes("\\");
|
|
9520
9520
|
if (escapedOnly && !escaped) {
|
|
@@ -9785,6 +9785,130 @@ var init_sqlite_reader = __esm({
|
|
|
9785
9785
|
}
|
|
9786
9786
|
});
|
|
9787
9787
|
|
|
9788
|
+
// ../engine/dist/core/backends/opencode-v2.js
|
|
9789
|
+
function hasTable(db, name) {
|
|
9790
|
+
return !!db.prepare("SELECT 1 FROM main.sqlite_master WHERE type = 'table' AND name = ?").get(name);
|
|
9791
|
+
}
|
|
9792
|
+
function columnsOf(db, table) {
|
|
9793
|
+
return new Set(db.prepare(`SELECT name FROM pragma_table_info('${table}', 'main')`).all().map((r) => r.name));
|
|
9794
|
+
}
|
|
9795
|
+
function installOpencodeV2Views(db) {
|
|
9796
|
+
if (!hasTable(db, "session_v2") || !hasTable(db, "session_message"))
|
|
9797
|
+
return false;
|
|
9798
|
+
const v2Cols = columnsOf(db, "session_v2");
|
|
9799
|
+
const sessionCols = SESSION_COLUMNS.map((c) => v2Cols.has(c) ? c : `NULL AS ${c}`).join(", ");
|
|
9800
|
+
const hasV1 = hasTable(db, "message") && hasTable(db, "part");
|
|
9801
|
+
const v1Messages = hasV1 ? `SELECT id, session_id, time_created, time_updated, data FROM main.message WHERE session_id NOT IN (${V2_SESSIONS}) UNION ALL ` : "";
|
|
9802
|
+
const v1Parts = hasV1 ? `SELECT id, message_id, session_id, time_created, time_updated, data FROM main.part WHERE session_id NOT IN (${V2_SESSIONS}) UNION ALL ` : "";
|
|
9803
|
+
db.exec(`
|
|
9804
|
+
CREATE TEMP VIEW IF NOT EXISTS session AS SELECT ${sessionCols} FROM main.session_v2;
|
|
9805
|
+
|
|
9806
|
+
CREATE TEMP VIEW IF NOT EXISTS message AS
|
|
9807
|
+
${v1Messages}
|
|
9808
|
+
SELECT m.id, m.session_id, m.time_created, m.time_updated,
|
|
9809
|
+
CASE m.type
|
|
9810
|
+
WHEN 'user' THEN json_object('role', 'user', 'time', ${j("$.time")})
|
|
9811
|
+
ELSE json_object(
|
|
9812
|
+
'role', 'assistant',
|
|
9813
|
+
'agent', json_extract(m.data, '$.agent'),
|
|
9814
|
+
'modelID', json_extract(m.data, '$.model.id'),
|
|
9815
|
+
'providerID', json_extract(m.data, '$.model.providerID'),
|
|
9816
|
+
'cost', coalesce(json_extract(m.data, '$.cost'), 0),
|
|
9817
|
+
'tokens', ${j("$.tokens")},
|
|
9818
|
+
'time', ${j("$.time")})
|
|
9819
|
+
END AS data
|
|
9820
|
+
FROM main.session_message m WHERE m.type IN ('user', 'assistant');
|
|
9821
|
+
|
|
9822
|
+
CREATE TEMP VIEW IF NOT EXISTS part AS
|
|
9823
|
+
${v1Parts}
|
|
9824
|
+
SELECT m.id || ':text', m.id, m.session_id, m.time_created, m.time_updated,
|
|
9825
|
+
json_object('type', 'text', 'text', json_extract(m.data, '$.text'))
|
|
9826
|
+
FROM main.session_message m
|
|
9827
|
+
WHERE m.type = 'user' AND json_extract(m.data, '$.text') IS NOT NULL
|
|
9828
|
+
UNION ALL
|
|
9829
|
+
SELECT m.id || ':' || c.key, m.id, m.session_id, m.time_created + c.key, m.time_updated,
|
|
9830
|
+
CASE json_extract(c.value, '$.type')
|
|
9831
|
+
WHEN 'tool' THEN json_object(
|
|
9832
|
+
'type', 'tool',
|
|
9833
|
+
'tool', json_extract(c.value, '$.name'),
|
|
9834
|
+
'callID', json_extract(c.value, '$.id'),
|
|
9835
|
+
'state', json_object(
|
|
9836
|
+
'status', json_extract(c.value, '$.state.status'),
|
|
9837
|
+
'input', json(coalesce(json_extract(c.value, '$.state.input'), '{}')),
|
|
9838
|
+
'output', (SELECT group_concat(json_extract(o.value, '$.text'), char(10))
|
|
9839
|
+
FROM json_each(c.value, '$.state.content') o
|
|
9840
|
+
WHERE json_extract(o.value, '$.type') = 'text')))
|
|
9841
|
+
ELSE json_object('type', json_extract(c.value, '$.type'), 'text', json_extract(c.value, '$.text'))
|
|
9842
|
+
END
|
|
9843
|
+
FROM main.session_message m, json_each(m.data, '$.content') c
|
|
9844
|
+
WHERE m.type = 'assistant'
|
|
9845
|
+
UNION ALL
|
|
9846
|
+
SELECT m.id || ':step', m.id, m.session_id,
|
|
9847
|
+
coalesce(json_extract(m.data, '$.time.completed'), m.time_created), m.time_updated,
|
|
9848
|
+
json_object('type', 'step-finish', 'cost', coalesce(json_extract(m.data, '$.cost'), 0), 'tokens', ${j("$.tokens")})
|
|
9849
|
+
FROM main.session_message m WHERE m.type = 'assistant';
|
|
9850
|
+
`);
|
|
9851
|
+
return true;
|
|
9852
|
+
}
|
|
9853
|
+
function openOpencodeDb(path) {
|
|
9854
|
+
const db = openSqliteReadonly(path);
|
|
9855
|
+
if (!db)
|
|
9856
|
+
return null;
|
|
9857
|
+
try {
|
|
9858
|
+
installOpencodeV2Views(db);
|
|
9859
|
+
} catch {
|
|
9860
|
+
}
|
|
9861
|
+
return db;
|
|
9862
|
+
}
|
|
9863
|
+
function openOpencodeDbOrThrow(path) {
|
|
9864
|
+
const db = openSqliteReadonlyOrThrow(path);
|
|
9865
|
+
try {
|
|
9866
|
+
installOpencodeV2Views(db);
|
|
9867
|
+
} catch {
|
|
9868
|
+
}
|
|
9869
|
+
return db;
|
|
9870
|
+
}
|
|
9871
|
+
var SESSION_COLUMNS, j, V2_SESSIONS;
|
|
9872
|
+
var init_opencode_v2 = __esm({
|
|
9873
|
+
"../engine/dist/core/backends/opencode-v2.js"() {
|
|
9874
|
+
"use strict";
|
|
9875
|
+
init_sqlite_reader();
|
|
9876
|
+
SESSION_COLUMNS = [
|
|
9877
|
+
"id",
|
|
9878
|
+
"project_id",
|
|
9879
|
+
"parent_id",
|
|
9880
|
+
"slug",
|
|
9881
|
+
"directory",
|
|
9882
|
+
"title",
|
|
9883
|
+
"version",
|
|
9884
|
+
"share_url",
|
|
9885
|
+
"summary_additions",
|
|
9886
|
+
"summary_deletions",
|
|
9887
|
+
"summary_files",
|
|
9888
|
+
"summary_diffs",
|
|
9889
|
+
"revert",
|
|
9890
|
+
"permission",
|
|
9891
|
+
"time_created",
|
|
9892
|
+
"time_updated",
|
|
9893
|
+
"time_compacting",
|
|
9894
|
+
"time_archived",
|
|
9895
|
+
"workspace_id",
|
|
9896
|
+
"path",
|
|
9897
|
+
"agent",
|
|
9898
|
+
"model",
|
|
9899
|
+
"cost",
|
|
9900
|
+
"tokens_input",
|
|
9901
|
+
"tokens_output",
|
|
9902
|
+
"tokens_reasoning",
|
|
9903
|
+
"tokens_cache_read",
|
|
9904
|
+
"tokens_cache_write",
|
|
9905
|
+
"metadata"
|
|
9906
|
+
];
|
|
9907
|
+
j = (path, fallback = "'{}'") => `json(coalesce(json_extract(m.data, '${path}'), ${fallback}))`;
|
|
9908
|
+
V2_SESSIONS = "SELECT session_id FROM main.session_message";
|
|
9909
|
+
}
|
|
9910
|
+
});
|
|
9911
|
+
|
|
9788
9912
|
// ../engine/dist/core/backends/opencode.js
|
|
9789
9913
|
var opencode_exports = {};
|
|
9790
9914
|
__export(opencode_exports, {
|
|
@@ -9795,13 +9919,13 @@ import { existsSync as existsSync11 } from "fs";
|
|
|
9795
9919
|
import { homedir as homedir5 } from "os";
|
|
9796
9920
|
import { dirname as dirname9, join as join12 } from "path";
|
|
9797
9921
|
function openReadonly(path) {
|
|
9798
|
-
return
|
|
9922
|
+
return openOpencodeDb(path);
|
|
9799
9923
|
}
|
|
9800
9924
|
var PREFIX, OpencodeBackend, opencodeBackend;
|
|
9801
9925
|
var init_opencode = __esm({
|
|
9802
9926
|
"../engine/dist/core/backends/opencode.js"() {
|
|
9803
9927
|
"use strict";
|
|
9804
|
-
|
|
9928
|
+
init_opencode_v2();
|
|
9805
9929
|
init_tool_paths();
|
|
9806
9930
|
init_session_outcome();
|
|
9807
9931
|
init_session_git();
|
|
@@ -10092,9 +10216,9 @@ var init_opencode = __esm({
|
|
|
10092
10216
|
return [];
|
|
10093
10217
|
const parts = db.prepare(`
|
|
10094
10218
|
SELECT p.id, p.message_id, p.time_created, p.data, m.data AS msg_data
|
|
10095
|
-
FROM part p JOIN message m ON m.id = p.message_id
|
|
10219
|
+
FROM part p JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON m.id = p.message_id
|
|
10096
10220
|
WHERE p.session_id = ? ORDER BY p.time_created ASC
|
|
10097
|
-
`).all(ocId);
|
|
10221
|
+
`).all(ocId, ocId);
|
|
10098
10222
|
const events = [];
|
|
10099
10223
|
let lineNum = 0;
|
|
10100
10224
|
for (const p of parts) {
|
|
@@ -10310,17 +10434,17 @@ var init_opencode = __esm({
|
|
|
10310
10434
|
if (!existsSync11(dbPath))
|
|
10311
10435
|
return null;
|
|
10312
10436
|
const realId = this.toRawId(id);
|
|
10313
|
-
const db =
|
|
10437
|
+
const db = openOpencodeDbOrThrow(dbPath);
|
|
10314
10438
|
try {
|
|
10315
10439
|
const session = db.prepare(`SELECT * FROM session WHERE id = ?`).get(realId);
|
|
10316
10440
|
if (!session)
|
|
10317
10441
|
return null;
|
|
10318
10442
|
const rows = db.prepare(`
|
|
10319
10443
|
SELECT m.id AS message_id, m.data AS message_data, p.id AS part_id, p.data AS part_data, p.time_created
|
|
10320
|
-
FROM part p JOIN message m ON p.message_id = m.id
|
|
10444
|
+
FROM part p JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON p.message_id = m.id
|
|
10321
10445
|
WHERE p.session_id = ?
|
|
10322
10446
|
ORDER BY p.time_created ASC, p.id ASC
|
|
10323
|
-
`).all(realId);
|
|
10447
|
+
`).all(realId, realId);
|
|
10324
10448
|
const lines = [JSON.stringify({ kind: "session", row: session })];
|
|
10325
10449
|
for (const r of rows)
|
|
10326
10450
|
lines.push(JSON.stringify({ kind: "part", row: r }));
|
|
@@ -15492,19 +15616,19 @@ function runTrufflehog(binary, filePath, opts = {}) {
|
|
|
15492
15616
|
if (!line.trim())
|
|
15493
15617
|
continue;
|
|
15494
15618
|
try {
|
|
15495
|
-
const
|
|
15496
|
-
const raw =
|
|
15619
|
+
const j2 = JSON.parse(line);
|
|
15620
|
+
const raw = j2.Raw || "";
|
|
15497
15621
|
if (looksLikeUuid(raw))
|
|
15498
15622
|
continue;
|
|
15499
15623
|
out.push({
|
|
15500
|
-
rule:
|
|
15501
|
-
line:
|
|
15624
|
+
rule: j2.DetectorName || "unknown",
|
|
15625
|
+
line: j2.SourceMetadata?.Data?.Filesystem?.line || 0,
|
|
15502
15626
|
preview: mask(raw),
|
|
15503
15627
|
// trufflehog reports `Verified: true` only when --verification
|
|
15504
15628
|
// ran AND the call succeeded. With --only-verified set, every
|
|
15505
15629
|
// returned finding is verified live by definition, so we tag
|
|
15506
15630
|
// them all true; with --no-verification, leave undefined.
|
|
15507
|
-
verified: opts.verifyOnly ? true :
|
|
15631
|
+
verified: opts.verifyOnly ? true : j2.Verified === true ? true : void 0
|
|
15508
15632
|
});
|
|
15509
15633
|
} catch {
|
|
15510
15634
|
}
|
|
@@ -15557,16 +15681,16 @@ function runTrufflehogDir(binary, dir, opts = {}) {
|
|
|
15557
15681
|
if (!line.trim())
|
|
15558
15682
|
continue;
|
|
15559
15683
|
try {
|
|
15560
|
-
const
|
|
15561
|
-
const raw =
|
|
15684
|
+
const j2 = JSON.parse(line);
|
|
15685
|
+
const raw = j2.Raw || "";
|
|
15562
15686
|
if (looksLikeUuid(raw))
|
|
15563
15687
|
continue;
|
|
15564
15688
|
out.push({
|
|
15565
|
-
rule:
|
|
15566
|
-
line:
|
|
15689
|
+
rule: j2.DetectorName || "unknown",
|
|
15690
|
+
line: j2.SourceMetadata?.Data?.Filesystem?.line || 0,
|
|
15567
15691
|
preview: mask(raw),
|
|
15568
|
-
verified: opts.verifyOnly ? true :
|
|
15569
|
-
file:
|
|
15692
|
+
verified: opts.verifyOnly ? true : j2.Verified === true ? true : void 0,
|
|
15693
|
+
file: j2.SourceMetadata?.Data?.Filesystem?.file || ""
|
|
15570
15694
|
});
|
|
15571
15695
|
} catch {
|
|
15572
15696
|
}
|
|
@@ -15795,7 +15919,7 @@ async function parseOpenCodeSubagents(parentSessionId) {
|
|
|
15795
15919
|
if (!existsSync61(dbPath))
|
|
15796
15920
|
return [];
|
|
15797
15921
|
const realParent = parentSessionId.replace("opencode_", "");
|
|
15798
|
-
const db =
|
|
15922
|
+
const db = openOpencodeDbOrThrow(dbPath);
|
|
15799
15923
|
try {
|
|
15800
15924
|
const children = db.prepare(`
|
|
15801
15925
|
SELECT id, title, time_created
|
|
@@ -15839,16 +15963,16 @@ async function parseOpenCodeTranscript(sessionId) {
|
|
|
15839
15963
|
if (!existsSync61(dbPath))
|
|
15840
15964
|
return [];
|
|
15841
15965
|
const realId = sessionId.replace("opencode_", "");
|
|
15842
|
-
const db =
|
|
15966
|
+
const db = openOpencodeDbOrThrow(dbPath);
|
|
15843
15967
|
const messages = [];
|
|
15844
15968
|
try {
|
|
15845
15969
|
const parts = db.prepare(`
|
|
15846
15970
|
SELECT p.data, p.time_created, m.data as msg_data
|
|
15847
15971
|
FROM part p
|
|
15848
|
-
JOIN message m ON p.message_id = m.id
|
|
15972
|
+
JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON p.message_id = m.id
|
|
15849
15973
|
WHERE p.session_id = ?
|
|
15850
15974
|
ORDER BY p.time_created ASC
|
|
15851
|
-
`).all(realId);
|
|
15975
|
+
`).all(realId, realId);
|
|
15852
15976
|
let lineNum = 0;
|
|
15853
15977
|
for (const part of parts) {
|
|
15854
15978
|
try {
|
|
@@ -15879,7 +16003,7 @@ var log5;
|
|
|
15879
16003
|
var init_opencode2 = __esm({
|
|
15880
16004
|
"../engine/dist/transcript/opencode.js"() {
|
|
15881
16005
|
"use strict";
|
|
15882
|
-
|
|
16006
|
+
init_opencode_v2();
|
|
15883
16007
|
init_logger();
|
|
15884
16008
|
log5 = createLogger("transcript-opencode");
|
|
15885
16009
|
}
|
|
@@ -18209,7 +18333,7 @@ var MAX_CHUNK_CHARS8, OpenCodeSource;
|
|
|
18209
18333
|
var init_opencode_source = __esm({
|
|
18210
18334
|
"../engine/dist/parsers/opencode-source.js"() {
|
|
18211
18335
|
"use strict";
|
|
18212
|
-
|
|
18336
|
+
init_opencode_v2();
|
|
18213
18337
|
init_opencode();
|
|
18214
18338
|
init_settings();
|
|
18215
18339
|
MAX_CHUNK_CHARS8 = 2e3;
|
|
@@ -18226,7 +18350,7 @@ var init_opencode_source = __esm({
|
|
|
18226
18350
|
return;
|
|
18227
18351
|
let db;
|
|
18228
18352
|
try {
|
|
18229
|
-
db =
|
|
18353
|
+
db = openOpencodeDbOrThrow(this.dbPath);
|
|
18230
18354
|
} catch {
|
|
18231
18355
|
return;
|
|
18232
18356
|
}
|
|
@@ -18323,17 +18447,17 @@ var init_opencode_source = __esm({
|
|
|
18323
18447
|
if (!existsSync30(this.dbPath))
|
|
18324
18448
|
return [];
|
|
18325
18449
|
const sessionId = item.id.replace("opencode_", "");
|
|
18326
|
-
const db =
|
|
18450
|
+
const db = openOpencodeDbOrThrow(this.dbPath);
|
|
18327
18451
|
const chunks = [];
|
|
18328
18452
|
try {
|
|
18329
18453
|
const parts = db.prepare(`
|
|
18330
18454
|
SELECT p.data, m.data as msg_data
|
|
18331
18455
|
FROM part p
|
|
18332
|
-
JOIN message m ON p.message_id = m.id
|
|
18456
|
+
JOIN (SELECT id, data FROM message WHERE session_id = ?) m ON p.message_id = m.id
|
|
18333
18457
|
WHERE p.session_id = ?
|
|
18334
18458
|
AND (p.data LIKE '%"type":"text"%' OR p.data LIKE '%"type":"reasoning"%')
|
|
18335
18459
|
ORDER BY p.time_created ASC
|
|
18336
|
-
`).all(sessionId);
|
|
18460
|
+
`).all(sessionId, sessionId);
|
|
18337
18461
|
const userTexts = [];
|
|
18338
18462
|
const assistantTexts = [];
|
|
18339
18463
|
for (const part of parts) {
|
|
@@ -18397,7 +18521,7 @@ var OpenCodeTodoSource;
|
|
|
18397
18521
|
var init_opencode_todo_source = __esm({
|
|
18398
18522
|
"../engine/dist/parsers/opencode-todo-source.js"() {
|
|
18399
18523
|
"use strict";
|
|
18400
|
-
|
|
18524
|
+
init_opencode_v2();
|
|
18401
18525
|
init_opencode();
|
|
18402
18526
|
init_settings();
|
|
18403
18527
|
OpenCodeTodoSource = class {
|
|
@@ -18413,7 +18537,7 @@ var init_opencode_todo_source = __esm({
|
|
|
18413
18537
|
return;
|
|
18414
18538
|
let db;
|
|
18415
18539
|
try {
|
|
18416
|
-
db =
|
|
18540
|
+
db = openOpencodeDbOrThrow(this.dbPath);
|
|
18417
18541
|
} catch {
|
|
18418
18542
|
return;
|
|
18419
18543
|
}
|
|
@@ -18461,7 +18585,7 @@ var init_opencode_todo_source = __esm({
|
|
|
18461
18585
|
if (!existsSync31(this.dbPath))
|
|
18462
18586
|
return [];
|
|
18463
18587
|
const sessionId = item.extra?.sessionId || item.id.replace(`${opencodeBackend.idPrefix}todo_`, "");
|
|
18464
|
-
const db =
|
|
18588
|
+
const db = openOpencodeDbOrThrow(this.dbPath);
|
|
18465
18589
|
const chunks = [];
|
|
18466
18590
|
try {
|
|
18467
18591
|
const todos = db.prepare(`
|
|
@@ -20145,12 +20269,12 @@ var require_foldFlowLines = __commonJS({
|
|
|
20145
20269
|
ch = text[i += 1];
|
|
20146
20270
|
overflow = true;
|
|
20147
20271
|
}
|
|
20148
|
-
const
|
|
20149
|
-
if (escapedFolds[
|
|
20272
|
+
const j2 = i > escEnd + 1 ? i - 2 : escStart - 1;
|
|
20273
|
+
if (escapedFolds[j2])
|
|
20150
20274
|
return text;
|
|
20151
|
-
folds.push(
|
|
20152
|
-
escapedFolds[
|
|
20153
|
-
end =
|
|
20275
|
+
folds.push(j2);
|
|
20276
|
+
escapedFolds[j2] = true;
|
|
20277
|
+
end = j2 + endStep;
|
|
20154
20278
|
split = void 0;
|
|
20155
20279
|
} else {
|
|
20156
20280
|
overflow = true;
|
|
@@ -28040,7 +28164,7 @@ var init_http = __esm({
|
|
|
28040
28164
|
CLI_VERSION = JSON.parse(readFileSync34(new URL("../package.json", import.meta.url), "utf-8")).version || CLI_VERSION;
|
|
28041
28165
|
} catch {
|
|
28042
28166
|
try {
|
|
28043
|
-
if (true) CLI_VERSION = "0.7.
|
|
28167
|
+
if (true) CLI_VERSION = "0.7.25";
|
|
28044
28168
|
} catch {
|
|
28045
28169
|
}
|
|
28046
28170
|
}
|
|
@@ -28538,7 +28662,7 @@ var init_telemetry = __esm({
|
|
|
28538
28662
|
init_sync_client();
|
|
28539
28663
|
cliVersion2 = () => {
|
|
28540
28664
|
try {
|
|
28541
|
-
return true ? "0.7.
|
|
28665
|
+
return true ? "0.7.25" : "dev";
|
|
28542
28666
|
} catch {
|
|
28543
28667
|
return "dev";
|
|
28544
28668
|
}
|
|
@@ -31151,7 +31275,7 @@ var init_sync_client = __esm({
|
|
|
31151
31275
|
ownVersion = JSON.parse(readFileSync39(join46(dir, "../package.json"), "utf-8")).version || ownVersion;
|
|
31152
31276
|
} catch {
|
|
31153
31277
|
try {
|
|
31154
|
-
if (true) ownVersion = "0.7.
|
|
31278
|
+
if (true) ownVersion = "0.7.25";
|
|
31155
31279
|
} catch {
|
|
31156
31280
|
}
|
|
31157
31281
|
}
|
|
@@ -33547,6 +33671,22 @@ var init_guard_registration = __esm({
|
|
|
33547
33671
|
}
|
|
33548
33672
|
});
|
|
33549
33673
|
|
|
33674
|
+
// ../engine/dist/core/session-messages.js
|
|
33675
|
+
function selfIntroduction(sessionId) {
|
|
33676
|
+
return [
|
|
33677
|
+
MESSAGES_MARKER,
|
|
33678
|
+
`chat-recall session id: ${sessionId}`,
|
|
33679
|
+
"Other sessions of this account can send messages here. Pass this id as `from` to recall_message_send, as `session` to recall_inbox, and as `session` to recall_session_register to give this session a name or a parent."
|
|
33680
|
+
].join("\n");
|
|
33681
|
+
}
|
|
33682
|
+
var MESSAGES_MARKER;
|
|
33683
|
+
var init_session_messages = __esm({
|
|
33684
|
+
"../engine/dist/core/session-messages.js"() {
|
|
33685
|
+
"use strict";
|
|
33686
|
+
MESSAGES_MARKER = "<!-- chat-recall-messages -->";
|
|
33687
|
+
}
|
|
33688
|
+
});
|
|
33689
|
+
|
|
33550
33690
|
// ../engine/dist/core/message-registration.js
|
|
33551
33691
|
var message_registration_exports = {};
|
|
33552
33692
|
__export(message_registration_exports, {
|
|
@@ -33738,6 +33878,40 @@ function runHook(sessionId, event, cwd) {
|
|
|
33738
33878
|
child.stdin.end(JSON.stringify({ session_id: sessionId, hook_event_name: event, cwd }));
|
|
33739
33879
|
});
|
|
33740
33880
|
}
|
|
33881
|
+
|
|
33882
|
+
/**
|
|
33883
|
+
* Wait for a message to an idle session. The hook long-polls the server and
|
|
33884
|
+
* prints the messages when one arrives; onText gets them. One waiter per
|
|
33885
|
+
* session; stopWaiter ends it when the session gets a prompt.
|
|
33886
|
+
*/
|
|
33887
|
+
const waiters = new Map();
|
|
33888
|
+
|
|
33889
|
+
function startWaiter(sessionId, cwd, onText) {
|
|
33890
|
+
if (waiters.has(sessionId)) return;
|
|
33891
|
+
let out = "";
|
|
33892
|
+
let child;
|
|
33893
|
+
try {
|
|
33894
|
+
child = spawn(HOOK, ["--wait"], { stdio: ["pipe", "pipe", "ignore"] });
|
|
33895
|
+
} catch {
|
|
33896
|
+
return;
|
|
33897
|
+
}
|
|
33898
|
+
waiters.set(sessionId, child);
|
|
33899
|
+
child.stdout.on("data", (c) => { out += c; });
|
|
33900
|
+
child.on("error", () => { waiters.delete(sessionId); });
|
|
33901
|
+
child.on("close", () => {
|
|
33902
|
+
if (waiters.get(sessionId) === child) waiters.delete(sessionId);
|
|
33903
|
+
const text = out.trim();
|
|
33904
|
+
if (text) Promise.resolve().then(() => onText(text)).catch(() => {});
|
|
33905
|
+
});
|
|
33906
|
+
child.stdin.end(JSON.stringify({ session_id: sessionId, hook_event_name: "Stop", cwd }));
|
|
33907
|
+
}
|
|
33908
|
+
|
|
33909
|
+
function stopWaiter(sessionId) {
|
|
33910
|
+
const child = waiters.get(sessionId);
|
|
33911
|
+
if (!child) return;
|
|
33912
|
+
waiters.delete(sessionId);
|
|
33913
|
+
try { child.kill(); } catch { /* gone */ }
|
|
33914
|
+
}
|
|
33741
33915
|
`;
|
|
33742
33916
|
}
|
|
33743
33917
|
function opencodeMessagesSource(wrapperPath) {
|
|
@@ -33747,10 +33921,11 @@ function opencodeMessagesSource(wrapperPath) {
|
|
|
33747
33921
|
// other sessions sent it to the next prompt.
|
|
33748
33922
|
${runnerSource(wrapperPath)}
|
|
33749
33923
|
const RUN_END = new Set(["session.execution.succeeded", "session.execution.failed", "session.execution.interrupted", "session.idle"]);
|
|
33924
|
+
const INTRO = ${JSON.stringify(selfIntroduction("\0SESSION\0"))};
|
|
33925
|
+
const intro = (id) => INTRO.replace("\0SESSION\0", "opencode_" + id);
|
|
33750
33926
|
|
|
33751
33927
|
async function setup(ctx) {
|
|
33752
33928
|
const dir = ctx?.location?.directory;
|
|
33753
|
-
const started = new Set();
|
|
33754
33929
|
const children = new Set();
|
|
33755
33930
|
const isChild = async (id) => {
|
|
33756
33931
|
if (children.has(id)) return true;
|
|
@@ -33763,30 +33938,42 @@ async function setup(ctx) {
|
|
|
33763
33938
|
await ctx.session.hook("prompt", async (event) => {
|
|
33764
33939
|
const id = event?.sessionID;
|
|
33765
33940
|
if (!id || !event.prompt || (await isChild(id))) return;
|
|
33766
|
-
|
|
33767
|
-
|
|
33768
|
-
|
|
33769
|
-
parts.push(await runHook("opencode_" + id, "SessionStart", dir));
|
|
33770
|
-
}
|
|
33771
|
-
parts.push(await runHook("opencode_" + id, "UserPromptSubmit", dir));
|
|
33772
|
-
const text = parts.filter(Boolean).join("\\n\\n");
|
|
33941
|
+
stopWaiter("opencode_" + id);
|
|
33942
|
+
// The first prompt registers the session; the hook adds only waiting messages.
|
|
33943
|
+
const text = await runHook("opencode_" + id, "UserPromptSubmit", dir);
|
|
33773
33944
|
if (text) event.prompt.text = [event.prompt.text, text].filter(Boolean).join("\\n\\n");
|
|
33774
33945
|
});
|
|
33946
|
+
// The session id goes into the system context of each model call: the agent
|
|
33947
|
+
// always knows it, the user does not see it, and no prompt repeats it.
|
|
33948
|
+
await ctx.session.hook("context", async (event) => {
|
|
33949
|
+
const id = event?.sessionID;
|
|
33950
|
+
if (!id || !Array.isArray(event.system) || (await isChild(id))) return;
|
|
33951
|
+
event.system.push({ type: "text", text: intro(id) });
|
|
33952
|
+
});
|
|
33775
33953
|
const controller = new AbortController();
|
|
33776
33954
|
(async () => {
|
|
33777
33955
|
try {
|
|
33778
33956
|
for await (const ev of ctx.event.subscribe({ signal: controller.signal })) {
|
|
33779
33957
|
const id = ev?.data?.sessionID ?? ev?.properties?.sessionID;
|
|
33780
|
-
|
|
33781
|
-
|
|
33782
|
-
|
|
33783
|
-
|
|
33958
|
+
// A session that was open before this plugin loaded (a reload, an
|
|
33959
|
+
// update) is not in started, and still gets its waiter.
|
|
33960
|
+
if (!id || (await isChild(id))) continue;
|
|
33961
|
+
if (RUN_END.has(ev.type)) {
|
|
33962
|
+
await runHook("opencode_" + id, "Stop", dir);
|
|
33963
|
+
// An idle session acts on a message from another session by itself:
|
|
33964
|
+
// the text becomes its next prompt.
|
|
33965
|
+
startWaiter("opencode_" + id, dir, (text) => ctx.session.prompt({ sessionID: id, text }));
|
|
33966
|
+
} else if (ev.type === "session.deleted") {
|
|
33967
|
+
stopWaiter("opencode_" + id);
|
|
33784
33968
|
await runHook("opencode_" + id, "SessionEnd", dir);
|
|
33785
33969
|
}
|
|
33786
33970
|
}
|
|
33787
33971
|
} catch { /* the subscription ends with the plugin */ }
|
|
33788
33972
|
})();
|
|
33789
|
-
return () =>
|
|
33973
|
+
return () => {
|
|
33974
|
+
controller.abort();
|
|
33975
|
+
for (const id of [...waiters.keys()]) stopWaiter(id);
|
|
33976
|
+
};
|
|
33790
33977
|
}
|
|
33791
33978
|
|
|
33792
33979
|
const pending = new Map();
|
|
@@ -33814,6 +34001,11 @@ async function server({ directory }) {
|
|
|
33814
34001
|
await runHook("opencode_" + info.id, "SessionEnd", directory);
|
|
33815
34002
|
}
|
|
33816
34003
|
},
|
|
34004
|
+
"experimental.chat.system.transform": async (input, output) => {
|
|
34005
|
+
const id = input?.sessionID;
|
|
34006
|
+
if (!id || children.has(id) || !Array.isArray(output?.system)) return;
|
|
34007
|
+
output.system.push(intro(id));
|
|
34008
|
+
},
|
|
33817
34009
|
"chat.message": async (input, output) => {
|
|
33818
34010
|
const id = input?.sessionID;
|
|
33819
34011
|
if (!id || children.has(id) || !output?.message) return;
|
|
@@ -33845,6 +34037,7 @@ export default function (pi) {
|
|
|
33845
34037
|
pending = await runHook(idOf(ctx), "SessionStart", cwdOf(ctx));
|
|
33846
34038
|
});
|
|
33847
34039
|
pi.on("before_agent_start", async (event, ctx) => {
|
|
34040
|
+
stopWaiter(idOf(ctx));
|
|
33848
34041
|
if (event.messageType !== "user") return;
|
|
33849
34042
|
const now = await runHook(idOf(ctx), "UserPromptSubmit", cwdOf(ctx));
|
|
33850
34043
|
const text = [pending, now].filter(Boolean).join("\\n\\n");
|
|
@@ -33854,8 +34047,11 @@ export default function (pi) {
|
|
|
33854
34047
|
});
|
|
33855
34048
|
pi.on("agent_end", async (_event, ctx) => {
|
|
33856
34049
|
await runHook(idOf(ctx), "Stop", cwdOf(ctx));
|
|
34050
|
+
// An idle session acts on a message from another session by itself.
|
|
34051
|
+
startWaiter(idOf(ctx), cwdOf(ctx), (text) => pi.sendUserMessage(text));
|
|
33857
34052
|
});
|
|
33858
34053
|
pi.on("session_shutdown", async (_event, ctx) => {
|
|
34054
|
+
stopWaiter(idOf(ctx));
|
|
33859
34055
|
await runHook(idOf(ctx), "SessionEnd", cwdOf(ctx));
|
|
33860
34056
|
});
|
|
33861
34057
|
}
|
|
@@ -33867,6 +34063,7 @@ var init_message_registration = __esm({
|
|
|
33867
34063
|
"use strict";
|
|
33868
34064
|
init_backends();
|
|
33869
34065
|
init_guard_registration();
|
|
34066
|
+
init_session_messages();
|
|
33870
34067
|
MESSAGES_ID = "chat-recall-messages";
|
|
33871
34068
|
TIMEOUT_SECS2 = 10;
|
|
33872
34069
|
isOurs2 = (cmd) => typeof cmd === "string" && cmd.includes("chat_recall_messages_");
|
|
@@ -35456,16 +35653,16 @@ async function collectCode(opts) {
|
|
|
35456
35653
|
...blastTargets.map((f, i) => call(2e3 + i, "plan_change", { file: f, format: "json" }))
|
|
35457
35654
|
]);
|
|
35458
35655
|
blastTargets.forEach((f, i) => {
|
|
35459
|
-
const
|
|
35460
|
-
if (
|
|
35656
|
+
const j2 = parseJson(pc, 2e3 + i, null);
|
|
35657
|
+
if (j2 && typeof j2 === "object" && typeof j2.fan_in === "number") {
|
|
35461
35658
|
blast[f] = {
|
|
35462
|
-
fileRole:
|
|
35463
|
-
fanIn:
|
|
35464
|
-
fanOut:
|
|
35465
|
-
direct:
|
|
35466
|
-
transitive:
|
|
35467
|
-
maxDepth:
|
|
35468
|
-
directFiles: Array.isArray(
|
|
35659
|
+
fileRole: j2.file_role ?? "regular",
|
|
35660
|
+
fanIn: j2.fan_in ?? 0,
|
|
35661
|
+
fanOut: j2.fan_out ?? 0,
|
|
35662
|
+
direct: j2.direct ?? 0,
|
|
35663
|
+
transitive: j2.transitive ?? 0,
|
|
35664
|
+
maxDepth: j2.max_depth ?? 0,
|
|
35665
|
+
directFiles: Array.isArray(j2.direct_files) ? j2.direct_files.map(rel).slice(0, 20) : void 0
|
|
35469
35666
|
};
|
|
35470
35667
|
}
|
|
35471
35668
|
});
|
|
@@ -36209,7 +36406,7 @@ var init_intent_drain = __esm({
|
|
|
36209
36406
|
ownVersion2 = JSON.parse(readFileSync49(join62(dir, "../package.json"), "utf-8")).version || ownVersion2;
|
|
36210
36407
|
} catch {
|
|
36211
36408
|
try {
|
|
36212
|
-
if (true) ownVersion2 = "0.7.
|
|
36409
|
+
if (true) ownVersion2 = "0.7.25";
|
|
36213
36410
|
} catch {
|
|
36214
36411
|
}
|
|
36215
36412
|
}
|
package/dist/mcp-relay.js
CHANGED
|
@@ -299,7 +299,7 @@ function resilientBridge(input, output, first, reconnect, exit, opts = {}) {
|
|
|
299
299
|
}
|
|
300
300
|
|
|
301
301
|
// src/mcp-relay.ts
|
|
302
|
-
var VERSION = true ? "0.7.
|
|
302
|
+
var VERSION = true ? "0.7.25" : "0.0.0";
|
|
303
303
|
var SERVER_URL = new URL("./mcp.js", import.meta.url);
|
|
304
304
|
var SERVER_PATH = fileURLToPath(SERVER_URL);
|
|
305
305
|
var START_TIMEOUT_MS = 15e3;
|