@oatelauser/crosschat 1.0.1 → 1.2.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/CHANGELOG.md +79 -0
- package/README.md +18 -9
- package/dist/cli.js +44 -9
- package/dist/cli.js.map +1 -1
- package/dist/codex/client.js +5 -0
- package/dist/codex/client.js.map +1 -1
- package/dist/codex/deliver.js +30 -13
- package/dist/codex/deliver.js.map +1 -1
- package/dist/codex/rollout-meta.js +6 -0
- package/dist/codex/rollout-meta.js.map +1 -1
- package/dist/codex/transport.js +23 -2
- package/dist/codex/transport.js.map +1 -1
- package/dist/commands/doctor.js +9 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/send.js +92 -18
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/status.js +66 -1
- package/dist/commands/status.js.map +1 -1
- package/dist/conversation-summary.js +123 -0
- package/dist/conversation-summary.js.map +1 -0
- package/dist/conversations.js +174 -0
- package/dist/conversations.js.map +1 -0
- package/dist/envelope.js +1 -1
- package/dist/envelope.js.map +1 -1
- package/dist/outbox.js +230 -52
- package/dist/outbox.js.map +1 -1
- package/dist/send-log.js +130 -0
- package/dist/send-log.js.map +1 -0
- package/dist/watchdog.js +104 -0
- package/dist/watchdog.js.map +1 -0
- package/package.json +2 -3
- package/skills/crosschat/SKILL.md +4 -2
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { mkdirSync, readFileSync, readdirSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import process from 'node:process';
|
|
6
|
+
import { decodeRef, encodeRef, newConversationRef, nextTurnRef, } from './ref.js';
|
|
7
|
+
/**
|
|
8
|
+
* Pair-keyed conversation continuity (B15): `--to` sends continue the pair's
|
|
9
|
+
* latest conversation instead of always starting a new one, so the turn
|
|
10
|
+
* counter is meaningful without either agent having to carry refs by hand
|
|
11
|
+
* (measured 2026-10-02: 30 distinct worker->codex messages all arrived
|
|
12
|
+
* labeled turn=1). Purely local, self-healing state; corrupt/missing data
|
|
13
|
+
* simply falls back to a fresh conversation. Role-free: works for any pair
|
|
14
|
+
* of endpoints in any direction.
|
|
15
|
+
*
|
|
16
|
+
* Storage (B20): one JSON shard per pair in a `conversations/` directory
|
|
17
|
+
* next to the legacy state file (the `rate/<sha256>.json` precedent), so
|
|
18
|
+
* concurrent sends from different pairs no longer read-modify-write one
|
|
19
|
+
* global file. Lazy migration: reads prefer the shard and fall back to the
|
|
20
|
+
* legacy global file (read-only — never rewritten, never deleted); writes
|
|
21
|
+
* only ever touch the shard, so a stale global snapshot is harmless.
|
|
22
|
+
*/
|
|
23
|
+
export function defaultConversationsFile() {
|
|
24
|
+
return join(process.env.LOCALAPPDATA ?? homedir(), 'crosschat', 'conversations.json');
|
|
25
|
+
}
|
|
26
|
+
/** Shard directory for a state-file path: its `conversations/` sibling. */
|
|
27
|
+
function shardDirOf(file) {
|
|
28
|
+
return join(dirname(file), 'conversations');
|
|
29
|
+
}
|
|
30
|
+
function shardFileOf(file, pairKey) {
|
|
31
|
+
const name = createHash('sha256').update(pairKey, 'utf8').digest('hex');
|
|
32
|
+
return join(shardDirOf(file), `${name}.json`);
|
|
33
|
+
}
|
|
34
|
+
function parseShard(text) {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(text);
|
|
37
|
+
if (parsed !== null &&
|
|
38
|
+
typeof parsed === 'object' &&
|
|
39
|
+
typeof parsed.pair === 'string' &&
|
|
40
|
+
typeof parsed.ref === 'string' &&
|
|
41
|
+
typeof parsed.updatedAt === 'number') {
|
|
42
|
+
return { pair: parsed.pair, ref: parsed.ref, updatedAt: parsed.updatedAt };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// corrupt shard: self-heal as absent (isolated to this pair)
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
function readShard(shardFile) {
|
|
51
|
+
try {
|
|
52
|
+
return parseShard(readFileSync(shardFile, 'utf8'));
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined; // missing/unreadable shard
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The legacy global file, read-only: `{pairs: Record<pairKey, record>}`. */
|
|
59
|
+
function loadLegacyPairs(file) {
|
|
60
|
+
try {
|
|
61
|
+
const parsed = JSON.parse(readFileSync(file, 'utf8'));
|
|
62
|
+
if (parsed !== null && typeof parsed === 'object' && parsed.pairs !== undefined)
|
|
63
|
+
return parsed.pairs;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// missing/corrupt: nothing to fall back to
|
|
67
|
+
}
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
/** One pair's record: shard first, else its entry in the legacy global file. */
|
|
71
|
+
function loadPair(file, pairKey) {
|
|
72
|
+
const shard = readShard(shardFileOf(file, pairKey));
|
|
73
|
+
if (shard !== undefined)
|
|
74
|
+
return shard;
|
|
75
|
+
return loadLegacyPairs(file)[pairKey];
|
|
76
|
+
}
|
|
77
|
+
function savePair(file, pairKey, record) {
|
|
78
|
+
try {
|
|
79
|
+
const dir = shardDirOf(file);
|
|
80
|
+
mkdirSync(dir, { recursive: true });
|
|
81
|
+
const shard = shardFileOf(file, pairKey);
|
|
82
|
+
// ponytail: no cross-process locking; same-pair millisecond races are
|
|
83
|
+
// last-write-wins — the pair's continuity lags one turn at worst, and
|
|
84
|
+
// refs are self-contained so in-flight replies are unaffected.
|
|
85
|
+
const tmp = `${shard}.${process.pid}.${Date.now()}.tmp`;
|
|
86
|
+
writeFileSync(tmp, JSON.stringify({ pair: pairKey, ...record }), 'utf8');
|
|
87
|
+
renameSync(tmp, shard);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// ponytail: continuity is best-effort; a lost record just starts a new thread.
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/** All pairs' records: every shard, plus legacy records not yet migrated (shard wins). */
|
|
94
|
+
export function loadAllPairs(file) {
|
|
95
|
+
const pairs = loadLegacyPairs(file);
|
|
96
|
+
let names = [];
|
|
97
|
+
try {
|
|
98
|
+
names = readdirSync(shardDirOf(file));
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return pairs; // no shard directory yet: legacy view only
|
|
102
|
+
}
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
if (!name.endsWith('.json'))
|
|
105
|
+
continue;
|
|
106
|
+
const shard = readShard(join(shardDirOf(file), name));
|
|
107
|
+
if (shard !== undefined)
|
|
108
|
+
pairs[shard.pair] = shard;
|
|
109
|
+
}
|
|
110
|
+
return pairs;
|
|
111
|
+
}
|
|
112
|
+
function endpointKey(endpoint) {
|
|
113
|
+
return endpoint.p === 'human' ? 'human' : `${endpoint.p}:${endpoint.id}`;
|
|
114
|
+
}
|
|
115
|
+
/** Unordered pair key: (A→B) and (B→A) map to the same conversation. */
|
|
116
|
+
export function pairKeyOf(a, b) {
|
|
117
|
+
return [endpointKey(a), endpointKey(b)].sort().join('\n');
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* The conversation a fresh `--to` send should join: the pair's stored ref
|
|
121
|
+
* advanced by one turn. Either endpoint may continue (f/t order is the
|
|
122
|
+
* initiator's and stays fixed). No usable record -> brand-new conversation.
|
|
123
|
+
*/
|
|
124
|
+
export function continueConversation(file, caller, target) {
|
|
125
|
+
const stored = loadPair(file, pairKeyOf(caller, target));
|
|
126
|
+
if (stored !== undefined) {
|
|
127
|
+
try {
|
|
128
|
+
const ref = decodeRef(stored.ref);
|
|
129
|
+
const sameEndpoints = matchesEndpoints(ref, caller, target);
|
|
130
|
+
if (sameEndpoints && ref.c >= 1) {
|
|
131
|
+
const next = nextTurnRef(ref);
|
|
132
|
+
return { ref: next, turn: next.c };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// stored ref no longer decodes: fall through to a fresh conversation
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { ref: newConversationRef(caller, target), turn: 1 };
|
|
140
|
+
}
|
|
141
|
+
/** Both endpoints present, in either f/t order (a record either side can continue). */
|
|
142
|
+
function matchesEndpoints(ref, a, b) {
|
|
143
|
+
return ((endpointKey(ref.f) === endpointKey(a) && endpointKey(ref.t) === endpointKey(b)) ||
|
|
144
|
+
(endpointKey(ref.f) === endpointKey(b) && endpointKey(ref.t) === endpointKey(a)));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* The conversation a `--conversation` reply should join (B22): the passed ref
|
|
148
|
+
* advanced by one turn — unless the pair's shard has already moved at or past
|
|
149
|
+
* it (late reply, or several senders working from the same stale base), in
|
|
150
|
+
* which case the shard line wins and turn collisions disappear. Shard behind
|
|
151
|
+
* the passed ref, absent, a different conversation (nonce differs), or
|
|
152
|
+
* endpoint-mismatched: unchanged pass-through continuation.
|
|
153
|
+
*/
|
|
154
|
+
export function continueFromRef(file, ref) {
|
|
155
|
+
const stored = loadPair(file, pairKeyOf(ref.f, ref.t));
|
|
156
|
+
if (stored !== undefined) {
|
|
157
|
+
try {
|
|
158
|
+
const shard = decodeRef(stored.ref);
|
|
159
|
+
if (matchesEndpoints(shard, ref.f, ref.t) && shard.n === ref.n && shard.c >= ref.c) {
|
|
160
|
+
const next = nextTurnRef(shard);
|
|
161
|
+
return { ref: next, turn: next.c };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// stored ref no longer decodes: continue from the passed ref
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return { ref: nextTurnRef(ref), turn: ref.c + 1 };
|
|
169
|
+
}
|
|
170
|
+
/** Record the pair's latest ref (issued with a message that got out: delivered, queued, or parked). */
|
|
171
|
+
export function recordConversation(file, ref, nowMs = Date.now()) {
|
|
172
|
+
savePair(file, pairKeyOf(ref.f, ref.t), { ref: encodeRef(ref), updatedAt: nowMs });
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=conversations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversations.js","sourceRoot":"","sources":["../src/conversations.ts"],"names":[],"mappings":"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"}
|
package/dist/envelope.js
CHANGED
|
@@ -27,7 +27,7 @@ export function composeEnvelope(input) {
|
|
|
27
27
|
return (`<cross-session-message from-name="${escapeAttr(fromName)}" turn="${input.turn}"${aliasAttr}>\n` +
|
|
28
28
|
`<crosschat-reply-hint conversation="${input.ref}" reply-as="${escapeAttr(input.toName)}">` +
|
|
29
29
|
`回复请运行: crosschat send --conversation ${input.ref} --body "<你的回复>"</crosschat-reply-hint>\n` +
|
|
30
|
-
|
|
30
|
+
`新话题或对话已推进时: crosschat send --to <名字> --body "..."(--to 自动接续该端对最近对话);超 16KiB 请写文件后只发路径\n` +
|
|
31
31
|
`${body}\n` +
|
|
32
32
|
`</cross-session-message>`);
|
|
33
33
|
}
|
package/dist/envelope.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,4FAA4F,CAAC;AAc1H,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,wBAAwB,CAAC;IAC/D,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;IACrG,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,MAAM,IAAI,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,CACL,qCAAqC,UAAU,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAC,IAAI,IAAI,SAAS,KAAK;QAChG,uCAAuC,KAAK,CAAC,GAAG,eAAe,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI;QAC3F,wCAAwC,KAAK,CAAC,GAAG,2CAA2C;QAC5F,
|
|
1
|
+
{"version":3,"file":"envelope.js","sourceRoot":"","sources":["../src/envelope.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,yEAAyE;AACzE,MAAM,CAAC,MAAM,wBAAwB,GAAG,EAAE,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,oBAAoB,GAAG,4FAA4F,CAAC;AAc1H,SAAS,UAAU,CAAC,KAAa;IAC/B,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,MAAM,UAAU,GAAG,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,SAAS,GAAG,UAAU,CAAC,MAAM,GAAG,wBAAwB,CAAC;IAC/D,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,wBAAwB,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC;IACrG,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,gBAAgB,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACjF,MAAM,IAAI,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChD,OAAO,CACL,qCAAqC,UAAU,CAAC,QAAQ,CAAC,WAAW,KAAK,CAAC,IAAI,IAAI,SAAS,KAAK;QAChG,uCAAuC,KAAK,CAAC,GAAG,eAAe,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI;QAC3F,wCAAwC,KAAK,CAAC,GAAG,2CAA2C;QAC5F,yFAAyF;QACzF,GAAG,IAAI,IAAI;QACX,0BAA0B,CAC3B,CAAC;AACJ,CAAC"}
|
package/dist/outbox.js
CHANGED
|
@@ -1,43 +1,81 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { appendFileSync, closeSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, writeSync, } from 'node:fs';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import process from 'node:process';
|
|
5
|
+
import { randomBytes } from 'node:crypto';
|
|
5
6
|
import { MultichatError } from './errors.js';
|
|
6
7
|
import { checkAndRecord, rateKey } from './rate-limit.js';
|
|
7
8
|
/**
|
|
8
|
-
* Opportunistic outbox (B10): when a codex delivery fails
|
|
9
|
-
* CODEX_THREAD_BUSY_TIMEOUT / CODEX_THREAD_LOCKED, the fully composed
|
|
10
|
-
* is parked on disk (one JSON file per thread under
|
|
9
|
+
* Opportunistic outbox (B10/B11, reworked B12): when a codex delivery fails
|
|
10
|
+
* with CODEX_THREAD_BUSY_TIMEOUT / CODEX_THREAD_LOCKED, the fully composed
|
|
11
|
+
* envelope is parked on disk (one JSON file per thread under
|
|
11
12
|
* %LOCALAPPDATA%/crosschat/outbox/) and re-delivered verbatim by the next
|
|
12
|
-
* send/status invocation
|
|
13
|
-
*
|
|
13
|
+
* send/status invocation or by the detached watchdog loop (src/watchdog.ts).
|
|
14
|
+
*
|
|
15
|
+
* B12 invariants, learned from the 2026-10-02 incident:
|
|
16
|
+
* - busy/locked is a THREAD-level state: one failing attempt aborts the whole
|
|
17
|
+
* thread for this round instead of burning the budget on guaranteed failures
|
|
18
|
+
* (the old loop starved queue tails to attempts=0 for 12h).
|
|
19
|
+
* - new sends join the queue instead of jumping it (enforced in send.ts).
|
|
20
|
+
* - every parked/delivered/dropped item is mirrored into a human-readable
|
|
21
|
+
* mailbox file so stuck content is always readable.
|
|
22
|
+
* - park/drain mutations take a per-thread lockfile so the watchdog and CLI
|
|
23
|
+
* drains cannot lose items by racing read-modify-write cycles.
|
|
14
24
|
*/
|
|
15
|
-
export const OUTBOX_MAX_PER_THREAD =
|
|
25
|
+
export const OUTBOX_MAX_PER_THREAD = 200;
|
|
16
26
|
/** Short wait for drains: opportunistic relief must not block the real command. */
|
|
17
27
|
const DEFAULT_DRAIN_BUSY_TIMEOUT_MS = 5_000;
|
|
18
28
|
/** B11: a drain round re-delivers at most this many items (rest waits for the next round). */
|
|
19
29
|
const DEFAULT_DRAIN_MAX_ITEMS = 5;
|
|
20
30
|
/** B11: wall-clock budget for one drain round, judged on the injected clock. */
|
|
21
31
|
const DEFAULT_DRAIN_BUDGET_MS = 15_000;
|
|
32
|
+
/** Per-thread lockfile staleness; a crashed holder is stolen after this. */
|
|
33
|
+
const LOCK_STALE_MS = 60_000;
|
|
22
34
|
export function defaultOutboxDir() {
|
|
23
35
|
return join(process.env.LOCALAPPDATA ?? homedir(), 'crosschat', 'outbox');
|
|
24
36
|
}
|
|
37
|
+
/** Mailbox mirror lives next to the outbox root (sibling "mailbox" directory). */
|
|
38
|
+
export function mailboxDirFor(outboxDir) {
|
|
39
|
+
return join(outboxDir, '..', 'mailbox');
|
|
40
|
+
}
|
|
41
|
+
export function mailboxFileFor(outboxDir, threadId) {
|
|
42
|
+
return join(mailboxDirFor(outboxDir), `${threadId}.md`);
|
|
43
|
+
}
|
|
44
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
45
|
+
function genId(nowMs) {
|
|
46
|
+
return `${nowMs.toString(36)}-${randomBytes(3).toString('hex')}`;
|
|
47
|
+
}
|
|
48
|
+
function isoOf(nowMs) {
|
|
49
|
+
return new Date(nowMs).toISOString();
|
|
50
|
+
}
|
|
25
51
|
/** Append one parked item (FIFO); refuses honestly past the per-thread cap. */
|
|
26
|
-
export function park(dir, threadId, item, nowMs = Date.now()) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
52
|
+
export function park(dir, threadId, item, nowMs = Date.now(), mailboxDir = mailboxDirFor(dir)) {
|
|
53
|
+
// Best-effort lock: a contended park falls through lockless (ms-scale window,
|
|
54
|
+
// same risk as pre-B12) rather than blocking the sending command.
|
|
55
|
+
const locked = acquireThreadLock(dir, threadId, Date.now());
|
|
56
|
+
try {
|
|
57
|
+
const items = readItems(dir, threadId) ?? []; // missing/corrupt: self-heal as empty
|
|
58
|
+
if (items.length >= OUTBOX_MAX_PER_THREAD) {
|
|
59
|
+
throw new MultichatError('OUTBOX_FULL', `Outbox full for codex thread ${threadId} (${OUTBOX_MAX_PER_THREAD} parked). ` +
|
|
60
|
+
`对方线程持续忙或已死:读 ${join(mailboxDir, `${threadId}.md`)} 取回内容,` +
|
|
61
|
+
`或确认线程仍存在后重发。`);
|
|
62
|
+
}
|
|
63
|
+
const parked = { ...item, id: genId(nowMs), parkedAt: nowMs, attempts: 0 };
|
|
64
|
+
writeItems(dir, threadId, [...items, parked]);
|
|
65
|
+
appendMailbox(mailboxDir, threadId, `\n## ${parked.id} 寄存 ${isoOf(nowMs)}(队列第 ${items.length + 1} 位)\n${parked.envelope}\n`);
|
|
66
|
+
return parked;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
if (locked)
|
|
70
|
+
releaseThreadLock(dir, threadId);
|
|
31
71
|
}
|
|
32
|
-
const parked = { ...item, parkedAt: nowMs, attempts: 0 };
|
|
33
|
-
writeItems(dir, threadId, [...items, parked]);
|
|
34
|
-
return parked;
|
|
35
72
|
}
|
|
36
73
|
/**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
74
|
+
* Deliver parked items head-first, one at a time: each delivery re-reads the
|
|
75
|
+
* file under the thread lock and removes exactly the delivered id, so a
|
|
76
|
+
* concurrent park is never lost. A busy/locked failure aborts the thread for
|
|
77
|
+
* this round (the state is thread-level); CODEX_THREAD_NOT_FOUND dead-letters
|
|
78
|
+
* the whole thread; any other error skips just that item for this round.
|
|
41
79
|
* Never throws (corrupt files are skipped, a missing dir is a no-op), so it
|
|
42
80
|
* can run before any command without failing it.
|
|
43
81
|
*/
|
|
@@ -45,6 +83,7 @@ export async function drain(dir, deliverFn, opts = {}) {
|
|
|
45
83
|
const busyTimeoutMs = opts.busyTimeoutMs ?? DEFAULT_DRAIN_BUSY_TIMEOUT_MS;
|
|
46
84
|
const maxItems = opts.maxItems ?? DEFAULT_DRAIN_MAX_ITEMS;
|
|
47
85
|
const budgetMs = opts.budgetMs ?? DEFAULT_DRAIN_BUDGET_MS;
|
|
86
|
+
const mailboxDir = opts.mailboxDir ?? mailboxDirFor(dir);
|
|
48
87
|
const now = opts.now ?? Date.now;
|
|
49
88
|
const deadline = now() + budgetMs;
|
|
50
89
|
let allowance = maxItems;
|
|
@@ -60,25 +99,32 @@ export async function drain(dir, deliverFn, opts = {}) {
|
|
|
60
99
|
if (!file.endsWith('.json'))
|
|
61
100
|
continue;
|
|
62
101
|
const threadId = file.slice(0, -'.json'.length);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const remaining = [];
|
|
102
|
+
// Items to ignore for the rest of this round (rate-window full, unknown
|
|
103
|
+
// delivery error): kept verbatim, retried by the next round.
|
|
104
|
+
const skipIds = new Set();
|
|
67
105
|
let delivered = 0;
|
|
68
106
|
let dropped = 0;
|
|
69
|
-
let
|
|
70
|
-
for (
|
|
71
|
-
if (allowance <= 0 || now() >= deadline)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
let toName = '';
|
|
108
|
+
for (;;) {
|
|
109
|
+
if (allowance <= 0 || now() >= deadline)
|
|
110
|
+
break;
|
|
111
|
+
const head = await withThreadLock(dir, threadId, 2_000, () => {
|
|
112
|
+
const items = readItems(dir, threadId);
|
|
113
|
+
if (items === undefined || items.length === 0)
|
|
114
|
+
return undefined;
|
|
115
|
+
const item = items.find((candidate) => !skipIds.has(candidate.id));
|
|
116
|
+
return item === undefined ? undefined : { item };
|
|
117
|
+
});
|
|
118
|
+
if (head === undefined)
|
|
119
|
+
break;
|
|
120
|
+
toName = head.item.toName;
|
|
75
121
|
if (opts.rateDir !== undefined) {
|
|
76
122
|
try {
|
|
77
|
-
checkAndRecord(opts.rateDir, rateKey(item.callerKey ?? 'human', `codex:${threadId}`), now());
|
|
123
|
+
checkAndRecord(opts.rateDir, rateKey(head.item.callerKey ?? 'human', `codex:${threadId}`), now());
|
|
78
124
|
}
|
|
79
125
|
catch (err) {
|
|
80
126
|
if (err instanceof MultichatError && err.code === 'RATE_LIMITED') {
|
|
81
|
-
|
|
127
|
+
skipIds.add(head.item.id); // window full: skip this round, retry next drain
|
|
82
128
|
continue;
|
|
83
129
|
}
|
|
84
130
|
throw err;
|
|
@@ -86,44 +132,51 @@ export async function drain(dir, deliverFn, opts = {}) {
|
|
|
86
132
|
}
|
|
87
133
|
allowance -= 1;
|
|
88
134
|
try {
|
|
89
|
-
await deliverFn(threadId, item.envelope, busyTimeoutMs);
|
|
135
|
+
await deliverFn(threadId, head.item.envelope, busyTimeoutMs);
|
|
90
136
|
delivered++;
|
|
91
|
-
|
|
137
|
+
appendMailbox(mailboxDir, threadId, `> ${head.item.id} 送达 ${isoOf(now())}\n`);
|
|
138
|
+
await withThreadLock(dir, threadId, 2_000, () => {
|
|
139
|
+
removeById(dir, threadId, head.item.id);
|
|
140
|
+
});
|
|
92
141
|
}
|
|
93
142
|
catch (err) {
|
|
94
143
|
if (err instanceof MultichatError && err.code === 'CODEX_THREAD_NOT_FOUND') {
|
|
95
144
|
// Dead letter: the thread is gone, so every parked item for it is
|
|
96
145
|
// unreachable — discard all of them, not just this one.
|
|
97
|
-
|
|
98
|
-
|
|
146
|
+
const removed = (await withThreadLock(dir, threadId, 2_000, () => {
|
|
147
|
+
const items = readItems(dir, threadId) ?? [];
|
|
148
|
+
writeItems(dir, threadId, []);
|
|
149
|
+
return items.length;
|
|
150
|
+
})) ?? 0;
|
|
151
|
+
dropped += removed;
|
|
152
|
+
appendMailbox(mailboxDir, threadId, `> 线程已不存在,丢弃 ${removed} 条(${isoOf(now())})\n`);
|
|
99
153
|
break;
|
|
100
154
|
}
|
|
101
155
|
if (err instanceof MultichatError &&
|
|
102
156
|
(err.code === 'CODEX_THREAD_BUSY_TIMEOUT' || err.code === 'CODEX_THREAD_LOCKED')) {
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
157
|
+
// Thread-level state: everything after the head would fail too.
|
|
158
|
+
await withThreadLock(dir, threadId, 2_000, () => {
|
|
159
|
+
bumpAttempts(dir, threadId, head.item.id);
|
|
160
|
+
});
|
|
161
|
+
break;
|
|
108
162
|
}
|
|
163
|
+
skipIds.add(head.item.id); // unknown state: keep, try again next drain
|
|
109
164
|
}
|
|
110
165
|
}
|
|
111
|
-
const
|
|
112
|
-
if (
|
|
113
|
-
writeItems(dir, threadId, remaining);
|
|
114
|
-
if (delivered > 0 || remaining.length > 0 || dropped > 0) {
|
|
166
|
+
const remaining = countItems(dir, threadId);
|
|
167
|
+
if (delivered > 0 || remaining > 0 || dropped > 0) {
|
|
115
168
|
results.push({
|
|
116
169
|
threadId,
|
|
117
|
-
toName:
|
|
170
|
+
toName: toName || shortThread(threadId),
|
|
118
171
|
delivered,
|
|
119
|
-
remaining
|
|
172
|
+
remaining,
|
|
120
173
|
dropped,
|
|
121
174
|
});
|
|
122
175
|
}
|
|
123
176
|
}
|
|
124
177
|
return results;
|
|
125
178
|
}
|
|
126
|
-
/** Parked item count per thread (doctor display); corrupt
|
|
179
|
+
/** Parked item count + oldest age per thread (doctor display); corrupt files are skipped. */
|
|
127
180
|
export function outboxSummary(dir) {
|
|
128
181
|
let files;
|
|
129
182
|
try {
|
|
@@ -138,18 +191,62 @@ export function outboxSummary(dir) {
|
|
|
138
191
|
continue;
|
|
139
192
|
const threadId = file.slice(0, -'.json'.length);
|
|
140
193
|
const items = readItems(dir, threadId);
|
|
141
|
-
if (items !== undefined && items.length > 0)
|
|
142
|
-
summary.push({
|
|
194
|
+
if (items !== undefined && items.length > 0) {
|
|
195
|
+
summary.push({
|
|
196
|
+
threadId,
|
|
197
|
+
count: items.length,
|
|
198
|
+
oldestParkedAt: Math.min(...items.map((item) => item.parkedAt)),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
143
201
|
}
|
|
144
202
|
return summary;
|
|
145
203
|
}
|
|
204
|
+
/** Queue depth for one thread (0 when nothing is parked). */
|
|
205
|
+
export function outboxCount(dir, threadId) {
|
|
206
|
+
return readItems(dir, threadId)?.length ?? 0;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Parked counts keyed by unordered pair key — caller identity × `codex:<thread>`,
|
|
210
|
+
* joined with the same sort + '\n' convention as conversations.ts pairKeyOf
|
|
211
|
+
* (status batch B0). Items without a caller key count under 'human', the same
|
|
212
|
+
* fallback the drain's rate limiter uses. Corrupt files are skipped.
|
|
213
|
+
*/
|
|
214
|
+
export function outboxPairCounts(dir) {
|
|
215
|
+
let files;
|
|
216
|
+
try {
|
|
217
|
+
files = readdirSync(dir);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return new Map();
|
|
221
|
+
}
|
|
222
|
+
const counts = new Map();
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
if (!file.endsWith('.json'))
|
|
225
|
+
continue;
|
|
226
|
+
const threadId = file.slice(0, -'.json'.length);
|
|
227
|
+
for (const item of readItems(dir, threadId) ?? []) {
|
|
228
|
+
const key = [item.callerKey ?? 'human', `codex:${threadId}`].sort().join('\n');
|
|
229
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return counts;
|
|
233
|
+
}
|
|
234
|
+
function shortThread(threadId) {
|
|
235
|
+
return `codex/${threadId.slice(0, 8)}`;
|
|
236
|
+
}
|
|
146
237
|
function threadFile(dir, threadId) {
|
|
147
238
|
return join(dir, `${threadId}.json`);
|
|
148
239
|
}
|
|
240
|
+
function lockFile(dir, threadId) {
|
|
241
|
+
return join(dir, `${threadId}.lock`);
|
|
242
|
+
}
|
|
149
243
|
function readItems(dir, threadId) {
|
|
150
244
|
try {
|
|
151
245
|
const parsed = JSON.parse(readFileSync(threadFile(dir, threadId), 'utf8'));
|
|
152
|
-
|
|
246
|
+
if (!Array.isArray(parsed.items))
|
|
247
|
+
return undefined;
|
|
248
|
+
// Pre-B12 files carry no id: synthesize a stable one from parkedAt+index.
|
|
249
|
+
return parsed.items.map((item, index) => item.id === undefined ? { ...item, id: `${item.parkedAt.toString(36)}-${index.toString(36)}` } : item);
|
|
153
250
|
}
|
|
154
251
|
catch {
|
|
155
252
|
return undefined;
|
|
@@ -163,10 +260,91 @@ function writeItems(dir, threadId, items) {
|
|
|
163
260
|
return;
|
|
164
261
|
}
|
|
165
262
|
mkdirSync(dir, { recursive: true });
|
|
166
|
-
// ponytail: no cross-process locking; two concurrent drains can double-deliver
|
|
167
|
-
// one item — rare and downstream-idempotent enough; add a lockfile if it bites.
|
|
168
263
|
const tmp = join(dir, `${threadId}.${process.pid}.${Date.now()}.tmp`);
|
|
169
264
|
writeFileSync(tmp, JSON.stringify({ items }), 'utf8');
|
|
170
265
|
renameSync(tmp, file);
|
|
171
266
|
}
|
|
267
|
+
function removeById(dir, threadId, id) {
|
|
268
|
+
const items = readItems(dir, threadId);
|
|
269
|
+
if (items === undefined)
|
|
270
|
+
return;
|
|
271
|
+
writeItems(dir, threadId, items.filter((item) => item.id !== id));
|
|
272
|
+
}
|
|
273
|
+
function bumpAttempts(dir, threadId, id) {
|
|
274
|
+
const items = readItems(dir, threadId);
|
|
275
|
+
if (items === undefined)
|
|
276
|
+
return;
|
|
277
|
+
writeItems(dir, threadId, items.map((item) => (item.id === id ? { ...item, attempts: item.attempts + 1 } : item)));
|
|
278
|
+
}
|
|
279
|
+
function countItems(dir, threadId) {
|
|
280
|
+
return readItems(dir, threadId)?.length ?? 0;
|
|
281
|
+
}
|
|
282
|
+
function appendMailbox(mailboxDir, threadId, text) {
|
|
283
|
+
try {
|
|
284
|
+
mkdirSync(mailboxDir, { recursive: true });
|
|
285
|
+
appendFileSync(join(mailboxDir, `${threadId}.md`), text, 'utf8');
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// ponytail: the mirror is best-effort observability; outbox json stays authoritative.
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function acquireThreadLock(dir, threadId, nowMs) {
|
|
292
|
+
const file = lockFile(dir, threadId);
|
|
293
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
294
|
+
try {
|
|
295
|
+
const fd = openExclusive(file);
|
|
296
|
+
writeSyncClose(fd, `${process.pid} ${nowMs}`);
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
catch (err) {
|
|
300
|
+
if (err.code !== 'EEXIST')
|
|
301
|
+
return false;
|
|
302
|
+
try {
|
|
303
|
+
const [, ts] = readFileSync(file, 'utf8').trim().split(/\s+/);
|
|
304
|
+
const written = Number(ts);
|
|
305
|
+
if (Number.isFinite(written) && nowMs - written > LOCK_STALE_MS) {
|
|
306
|
+
rmSync(file, { force: true }); // steal a crashed holder's lock
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
function releaseThreadLock(dir, threadId) {
|
|
319
|
+
rmSync(lockFile(dir, threadId), { force: true });
|
|
320
|
+
}
|
|
321
|
+
/** Run fn under the per-thread lock, waiting up to waitMs; undefined = never ran. */
|
|
322
|
+
async function withThreadLock(dir, threadId, waitMs, fn) {
|
|
323
|
+
const deadline = Date.now() + waitMs;
|
|
324
|
+
for (;;) {
|
|
325
|
+
if (acquireThreadLock(dir, threadId, Date.now())) {
|
|
326
|
+
try {
|
|
327
|
+
return fn();
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
releaseThreadLock(dir, threadId);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (Date.now() >= deadline)
|
|
334
|
+
return undefined;
|
|
335
|
+
await sleep(25);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
// fs.open with "wx" lives behind openSync's flag form; keep it tiny and local.
|
|
339
|
+
function openExclusive(file) {
|
|
340
|
+
return openSync(file, 'wx');
|
|
341
|
+
}
|
|
342
|
+
function writeSyncClose(fd, text) {
|
|
343
|
+
try {
|
|
344
|
+
writeSync(fd, text, null, 'utf8');
|
|
345
|
+
}
|
|
346
|
+
finally {
|
|
347
|
+
closeSync(fd);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
172
350
|
//# sourceMappingURL=outbox.js.map
|
package/dist/outbox.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"outbox.js","sourceRoot":"","sources":["../src/outbox.ts"],"names":[],"mappings":"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"}
|