@agentconnect.md/daemon 1.0.0-rc.34 → 1.0.0-rc.36
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/dist/index.js +132 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -23240,19 +23240,46 @@ var LocalStore = class {
|
|
|
23240
23240
|
mkdirSync(dirname(dbPath), { recursive: true });
|
|
23241
23241
|
this.db = new DatabaseSync(dbPath);
|
|
23242
23242
|
this.db.exec("PRAGMA journal_mode = WAL");
|
|
23243
|
+
this.migrateTranscript();
|
|
23243
23244
|
this.db.exec(`
|
|
23244
23245
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
23245
23246
|
key TEXT PRIMARY KEY, agentId TEXT, platform TEXT, channel TEXT, thread TEXT,
|
|
23246
23247
|
acpSessionId TEXT, state TEXT, lastDeliveredTs TEXT, updatedAt INTEGER
|
|
23247
23248
|
);
|
|
23248
23249
|
CREATE TABLE IF NOT EXISTS transcript (
|
|
23249
|
-
|
|
23250
|
-
|
|
23250
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23251
|
+
channel TEXT NOT NULL, thread TEXT NOT NULL, ts TEXT,
|
|
23252
|
+
sender TEXT NOT NULL, kind TEXT NOT NULL, text TEXT NOT NULL
|
|
23251
23253
|
);
|
|
23254
|
+
CREATE INDEX IF NOT EXISTS transcript_thread_seq ON transcript (channel, thread, seq);
|
|
23255
|
+
-- Dedup conversational rows by platform ts (double-fired inbound / redelivery);
|
|
23256
|
+
-- internal events have no platform ts and are intentionally never deduped here.
|
|
23257
|
+
CREATE UNIQUE INDEX IF NOT EXISTS transcript_text_ts
|
|
23258
|
+
ON transcript (channel, thread, ts) WHERE kind = 'text';
|
|
23252
23259
|
CREATE TABLE IF NOT EXISTS cp_routing (
|
|
23253
23260
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
23254
23261
|
routingEpoch INTEGER, assignments TEXT, globalRules TEXT
|
|
23255
23262
|
);
|
|
23263
|
+
`);
|
|
23264
|
+
}
|
|
23265
|
+
/**
|
|
23266
|
+
* Pre-`kind` transcript tables keyed on (channel, thread, ts) hold only conversational
|
|
23267
|
+
* text. Rebuild them onto the `seq`/`kind` schema, tagging legacy rows `text`. No-op on
|
|
23268
|
+
* a fresh DB (CREATE handles it) or an already-migrated one.
|
|
23269
|
+
*/
|
|
23270
|
+
migrateTranscript() {
|
|
23271
|
+
const cols = this.db.prepare("PRAGMA table_info(transcript)").all();
|
|
23272
|
+
if (cols.length === 0 || cols.some((c) => c.name === "kind")) return;
|
|
23273
|
+
this.db.exec(`
|
|
23274
|
+
ALTER TABLE transcript RENAME TO transcript_legacy;
|
|
23275
|
+
CREATE TABLE transcript (
|
|
23276
|
+
seq INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
23277
|
+
channel TEXT NOT NULL, thread TEXT NOT NULL, ts TEXT,
|
|
23278
|
+
sender TEXT NOT NULL, kind TEXT NOT NULL, text TEXT NOT NULL
|
|
23279
|
+
);
|
|
23280
|
+
INSERT INTO transcript (channel, thread, ts, sender, kind, text)
|
|
23281
|
+
SELECT channel, thread, ts, sender, 'text', text FROM transcript_legacy ORDER BY ts ASC;
|
|
23282
|
+
DROP TABLE transcript_legacy;
|
|
23256
23283
|
`);
|
|
23257
23284
|
}
|
|
23258
23285
|
getSession(key) {
|
|
@@ -23266,11 +23293,20 @@ var LocalStore = class {
|
|
|
23266
23293
|
lastDeliveredTs=excluded.lastDeliveredTs, updatedAt=excluded.updatedAt`).run(rec);
|
|
23267
23294
|
}
|
|
23268
23295
|
appendTranscript(e) {
|
|
23269
|
-
this.db.prepare("INSERT OR IGNORE INTO transcript (channel, thread, ts, sender, text) VALUES (@channel, @thread, @ts, @sender, @text)").run(e);
|
|
23296
|
+
this.db.prepare("INSERT OR IGNORE INTO transcript (channel, thread, ts, sender, kind, text) VALUES (@channel, @thread, @ts, @sender, @kind, @text)").run(e);
|
|
23270
23297
|
}
|
|
23298
|
+
/**
|
|
23299
|
+
* §8.5 cross-agent catch-up: conversational (`text`) rows only — tool/reasoning rows
|
|
23300
|
+
* are audit/UI data and must never be replayed back into an agent's prompt. Ordered by
|
|
23301
|
+
* platform `ts` (every text row carries one), compared against the session marker.
|
|
23302
|
+
*/
|
|
23271
23303
|
transcriptSince(channel, thread, sinceTs) {
|
|
23272
|
-
if (sinceTs === null) return this.db.prepare("SELECT * FROM transcript WHERE channel = ? AND thread = ? ORDER BY ts ASC").all(channel, thread);
|
|
23273
|
-
return this.db.prepare("SELECT * FROM transcript WHERE channel = ? AND thread = ? AND ts > ? ORDER BY ts ASC").all(channel, thread, sinceTs);
|
|
23304
|
+
if (sinceTs === null) return this.db.prepare("SELECT * FROM transcript WHERE channel = ? AND thread = ? AND kind = 'text' ORDER BY ts ASC").all(channel, thread);
|
|
23305
|
+
return this.db.prepare("SELECT * FROM transcript WHERE channel = ? AND thread = ? AND kind = 'text' AND ts > ? ORDER BY ts ASC").all(channel, thread, sinceTs);
|
|
23306
|
+
}
|
|
23307
|
+
/** Full activity log for a thread (all kinds), in insertion order — for the Web UI. */
|
|
23308
|
+
threadTranscript(channel, thread) {
|
|
23309
|
+
return this.db.prepare("SELECT * FROM transcript WHERE channel = ? AND thread = ? ORDER BY seq ASC").all(channel, thread);
|
|
23274
23310
|
}
|
|
23275
23311
|
openSessionAgents(channel, thread) {
|
|
23276
23312
|
return this.db.prepare("SELECT agentId FROM sessions WHERE channel = ? AND thread = ? AND state != 'closed'").all(channel, thread).map((r) => r.agentId);
|
|
@@ -23379,6 +23415,7 @@ var SessionManager = class {
|
|
|
23379
23415
|
thread,
|
|
23380
23416
|
ts,
|
|
23381
23417
|
sender: msg.sender.id,
|
|
23418
|
+
kind: "text",
|
|
23382
23419
|
text: mention ? `${msg.text}\n${mention}`.trim() : msg.text
|
|
23383
23420
|
});
|
|
23384
23421
|
let rec = this.deps.store.getSession(key);
|
|
@@ -23435,6 +23472,7 @@ var SessionManager = class {
|
|
|
23435
23472
|
thread,
|
|
23436
23473
|
ts: h.ts,
|
|
23437
23474
|
sender: h.sender,
|
|
23475
|
+
kind: "text",
|
|
23438
23476
|
text: h.text
|
|
23439
23477
|
});
|
|
23440
23478
|
}
|
|
@@ -23487,6 +23525,58 @@ function transcriptCoords(msg) {
|
|
|
23487
23525
|
};
|
|
23488
23526
|
}
|
|
23489
23527
|
//#endregion
|
|
23528
|
+
//#region src/session/transcript-recorder.ts
|
|
23529
|
+
/**
|
|
23530
|
+
* Turns the raw ACP `session/update` stream into the *full* activity log — tool calls
|
|
23531
|
+
* and reasoning — independent of the agent's Slack `output.mode`. (Output mode only
|
|
23532
|
+
* decides what reaches the platform; the transcript records everything.)
|
|
23533
|
+
*
|
|
23534
|
+
* It deliberately does NOT emit `text`: conversational rows are recorded at the
|
|
23535
|
+
* platform-send boundary instead, where the real message `ts` is known (so they sort
|
|
23536
|
+
* against the §8.5 replay marker). Reasoning chunks are coalesced into one block per
|
|
23537
|
+
* run; each tool invocation is recorded once (keyed by toolCallId, so the burst of
|
|
23538
|
+
* `tool_call_update`s for one call collapses to a single row).
|
|
23539
|
+
*/
|
|
23540
|
+
var TranscriptRecorder = class {
|
|
23541
|
+
thought = "";
|
|
23542
|
+
seenTools = /* @__PURE__ */ new Set();
|
|
23543
|
+
onUpdate(update) {
|
|
23544
|
+
switch (update.sessionUpdate) {
|
|
23545
|
+
case "agent_thought_chunk": {
|
|
23546
|
+
const content = update.content;
|
|
23547
|
+
this.thought += content?.text ?? "";
|
|
23548
|
+
return [];
|
|
23549
|
+
}
|
|
23550
|
+
case "tool_call":
|
|
23551
|
+
case "tool_call_update": {
|
|
23552
|
+
const flushed = this.flushThought();
|
|
23553
|
+
const u = update;
|
|
23554
|
+
const id = u.toolCallId ?? "";
|
|
23555
|
+
if (id && this.seenTools.has(id)) return flushed;
|
|
23556
|
+
if (id) this.seenTools.add(id);
|
|
23557
|
+
return [...flushed, {
|
|
23558
|
+
kind: "tool",
|
|
23559
|
+
text: u.title ?? (id || "tool")
|
|
23560
|
+
}];
|
|
23561
|
+
}
|
|
23562
|
+
case "agent_message_chunk": return this.flushThought();
|
|
23563
|
+
default: return [];
|
|
23564
|
+
}
|
|
23565
|
+
}
|
|
23566
|
+
/** Flush any trailing reasoning when the turn ends. */
|
|
23567
|
+
onFinal() {
|
|
23568
|
+
return this.flushThought();
|
|
23569
|
+
}
|
|
23570
|
+
flushThought() {
|
|
23571
|
+
const text = this.thought.trim();
|
|
23572
|
+
this.thought = "";
|
|
23573
|
+
return text ? [{
|
|
23574
|
+
kind: "reasoning",
|
|
23575
|
+
text
|
|
23576
|
+
}] : [];
|
|
23577
|
+
}
|
|
23578
|
+
};
|
|
23579
|
+
//#endregion
|
|
23490
23580
|
//#region src/mcp/ipc.ts
|
|
23491
23581
|
/** Frame a message for the wire: compact JSON + a single trailing newline. */
|
|
23492
23582
|
function encodeFrame(msg) {
|
|
@@ -80400,7 +80490,7 @@ var OutputConverger = class {
|
|
|
80400
80490
|
};
|
|
80401
80491
|
if (this.mode === "low") return [...this.flush(), clear];
|
|
80402
80492
|
const footer = link ? [{
|
|
80403
|
-
kind: "
|
|
80493
|
+
kind: "notice",
|
|
80404
80494
|
text: `:white_check_mark: done — <${link}|details>`
|
|
80405
80495
|
}] : [];
|
|
80406
80496
|
return [
|
|
@@ -81977,6 +82067,7 @@ var Daemon = class {
|
|
|
81977
82067
|
thread: thread ?? ctx.thread,
|
|
81978
82068
|
ts,
|
|
81979
82069
|
sender: ctx.agentId,
|
|
82070
|
+
kind: "text",
|
|
81980
82071
|
text
|
|
81981
82072
|
}),
|
|
81982
82073
|
maxAttachmentBytes: cfg.limits.maxAttachmentBytes
|
|
@@ -82272,6 +82363,7 @@ var Daemon = class {
|
|
|
82272
82363
|
thread,
|
|
82273
82364
|
ts,
|
|
82274
82365
|
sender: msg.sender.id,
|
|
82366
|
+
kind: "text",
|
|
82275
82367
|
text: msg.text
|
|
82276
82368
|
});
|
|
82277
82369
|
this.log.debug(`transcript: recorded unrouted msg ch=${msg.channel} thread=${thread} ts=${ts} (live session)`);
|
|
@@ -82383,6 +82475,7 @@ var Daemon = class {
|
|
|
82383
82475
|
}
|
|
82384
82476
|
async dispatch(agentId, msg, integrationId) {
|
|
82385
82477
|
const conv = new OutputConverger(this.agents.get(agentId).output.mode);
|
|
82478
|
+
const rec = new TranscriptRecorder();
|
|
82386
82479
|
const replyConn = this.replyConnFor(agentId, integrationId);
|
|
82387
82480
|
const wasRunning = this.hostStarts.has(agentId);
|
|
82388
82481
|
const statusThread = msg.thread ?? msg.msgId;
|
|
@@ -82390,6 +82483,8 @@ var Daemon = class {
|
|
|
82390
82483
|
const { sessionId, blocks } = await this.sessions.handle(agentId, msg);
|
|
82391
82484
|
const p = {
|
|
82392
82485
|
conv,
|
|
82486
|
+
rec,
|
|
82487
|
+
agentId,
|
|
82393
82488
|
channel: msg.channel,
|
|
82394
82489
|
thread: msg.thread,
|
|
82395
82490
|
statusThread,
|
|
@@ -82404,6 +82499,7 @@ var Daemon = class {
|
|
|
82404
82499
|
this.clearIdle(p);
|
|
82405
82500
|
const link = this.cfg.webAppUrl ? `${this.cfg.webAppUrl.replace(/\/$/, "")}/sessions/${sessionId}` : void 0;
|
|
82406
82501
|
for (const action of conv.onFinal(link)) this.enqueueApply(p, action);
|
|
82502
|
+
for (const ev of rec.onFinal()) this.recordEvent(agentId, msg.channel, statusThread, ev);
|
|
82407
82503
|
await p.applyChain;
|
|
82408
82504
|
} finally {
|
|
82409
82505
|
this.clearIdle(p);
|
|
@@ -82414,7 +82510,10 @@ var Daemon = class {
|
|
|
82414
82510
|
/**
|
|
82415
82511
|
* Apply one converger action against the session's Slack connection:
|
|
82416
82512
|
* - set-status → assistant.threads.setStatus (best-effort; '' clears)
|
|
82417
|
-
* - post → a new thread message
|
|
82513
|
+
* - post → a new thread message; ALSO recorded to the transcript as the
|
|
82514
|
+
* agent's reply text (sender = agentId), so other agents replaying the thread
|
|
82515
|
+
* see what it said. Keyed on `statusThread` (the §8.5 replay key).
|
|
82516
|
+
* - notice → posted to the thread but NOT recorded (system chrome, e.g. footer).
|
|
82418
82517
|
* - progress / plan → the single in-place message of that kind, posted once
|
|
82419
82518
|
* then chat.update-ed (§9.1 就地更新). The first post's ts is remembered on `p`.
|
|
82420
82519
|
*/
|
|
@@ -82425,7 +82524,19 @@ var Daemon = class {
|
|
|
82425
82524
|
case "set-status":
|
|
82426
82525
|
if (p.statusThread) await conn.setStatus(p.channel, p.statusThread, action.text, action.loadingMessages);
|
|
82427
82526
|
return;
|
|
82428
|
-
case "post":
|
|
82527
|
+
case "post": {
|
|
82528
|
+
const ts = await conn.postMessage(p.channel, action.text, p.thread);
|
|
82529
|
+
this.store.appendTranscript({
|
|
82530
|
+
channel: p.channel,
|
|
82531
|
+
thread: p.statusThread,
|
|
82532
|
+
ts: ts ?? `local-${Date.now()}`,
|
|
82533
|
+
sender: p.agentId,
|
|
82534
|
+
kind: "text",
|
|
82535
|
+
text: action.text
|
|
82536
|
+
});
|
|
82537
|
+
return;
|
|
82538
|
+
}
|
|
82539
|
+
case "notice":
|
|
82429
82540
|
await conn.postMessage(p.channel, action.text, p.thread);
|
|
82430
82541
|
return;
|
|
82431
82542
|
case "progress":
|
|
@@ -82469,6 +82580,19 @@ var Daemon = class {
|
|
|
82469
82580
|
if (!p) return;
|
|
82470
82581
|
for (const action of p.conv.onUpdate(update)) this.enqueueApply(p, action);
|
|
82471
82582
|
this.armIdle(p);
|
|
82583
|
+
for (const ev of p.rec.onUpdate(update)) this.recordEvent(p.agentId, p.channel, p.statusThread, ev);
|
|
82584
|
+
}
|
|
82585
|
+
/** Persist one internal activity event (tool/reasoning). Ordered by row `seq`, so its
|
|
82586
|
+
* `ts` is just a wall-clock stamp for display — never used for replay/sorting. */
|
|
82587
|
+
recordEvent(agentId, channel, thread, ev) {
|
|
82588
|
+
this.store.appendTranscript({
|
|
82589
|
+
channel,
|
|
82590
|
+
thread,
|
|
82591
|
+
ts: String(Date.now()),
|
|
82592
|
+
sender: agentId,
|
|
82593
|
+
kind: ev.kind,
|
|
82594
|
+
text: ev.text
|
|
82595
|
+
});
|
|
82472
82596
|
}
|
|
82473
82597
|
async ensureHostAsync(agentId) {
|
|
82474
82598
|
const host = this.ensureHost(agentId, this.cfg);
|