@harmonyos-arkts/opencode-acp 0.0.3 → 0.0.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/CHANGELOG.md +22 -0
- package/README.md +10 -10
- package/dist/index.cjs +239 -199
- package/dist/index.cjs.map +4 -4
- package/docs/mcp-extmethod-design.md +92 -76
- package/docs/provider-config-flow.md +35 -35
- package/docs/session-stats-to-vscode-design.md +18 -16
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -19631,9 +19631,11 @@ var SessionManager = class {
|
|
|
19631
19631
|
/**
|
|
19632
19632
|
* Load an existing session into the manager.
|
|
19633
19633
|
* Called by agent.loadSession() / agent.unstable_resumeSession().
|
|
19634
|
+
* Returns the session state plus the raw Session object from OpenCode.
|
|
19634
19635
|
*/
|
|
19635
19636
|
async load(sessionId, cwd, mcpServers, model) {
|
|
19636
|
-
await this.sdk.session.get({ sessionID: sessionId, directory: cwd }, { throwOnError: true });
|
|
19637
|
+
const response = await this.sdk.session.get({ sessionID: sessionId, directory: cwd }, { throwOnError: true });
|
|
19638
|
+
const session = response.data;
|
|
19637
19639
|
const state = {
|
|
19638
19640
|
id: sessionId,
|
|
19639
19641
|
cwd,
|
|
@@ -19642,7 +19644,7 @@ var SessionManager = class {
|
|
|
19642
19644
|
model
|
|
19643
19645
|
};
|
|
19644
19646
|
this.sessions.set(sessionId, state);
|
|
19645
|
-
return state;
|
|
19647
|
+
return { state, session };
|
|
19646
19648
|
}
|
|
19647
19649
|
/**
|
|
19648
19650
|
* Auto-register a session discovered via SSE events.
|
|
@@ -20401,7 +20403,7 @@ function sysLog(action, data) {
|
|
|
20401
20403
|
write("system", action, data);
|
|
20402
20404
|
}
|
|
20403
20405
|
function sanitize(obj, depth = 0) {
|
|
20404
|
-
if (!obj || depth >
|
|
20406
|
+
if (!obj || depth > 5) return obj;
|
|
20405
20407
|
const result = {};
|
|
20406
20408
|
for (const [key, val] of Object.entries(obj)) {
|
|
20407
20409
|
if (typeof val === "string" && val.length > 2e3) {
|
|
@@ -20414,33 +20416,6 @@ function sanitize(obj, depth = 0) {
|
|
|
20414
20416
|
}
|
|
20415
20417
|
return result;
|
|
20416
20418
|
}
|
|
20417
|
-
function acpAssembled(action, data) {
|
|
20418
|
-
if (!enabled) return;
|
|
20419
|
-
const entry = {
|
|
20420
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
20421
|
-
cat: "acp.out",
|
|
20422
|
-
action,
|
|
20423
|
-
...data && { data: sanitizeAssembled(data) }
|
|
20424
|
-
};
|
|
20425
|
-
try {
|
|
20426
|
-
(0, import_fs.appendFileSync)(logFile, JSON.stringify(entry) + "\n");
|
|
20427
|
-
} catch {
|
|
20428
|
-
}
|
|
20429
|
-
}
|
|
20430
|
-
function sanitizeAssembled(obj, depth = 0) {
|
|
20431
|
-
if (!obj || depth > 2) return obj;
|
|
20432
|
-
const result = {};
|
|
20433
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
20434
|
-
if (typeof val === "string" && val.length > 1e4) {
|
|
20435
|
-
result[key] = val.slice(0, 1e4) + `... [${val.length} chars total]`;
|
|
20436
|
-
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
20437
|
-
result[key] = sanitizeAssembled(val, depth + 1);
|
|
20438
|
-
} else {
|
|
20439
|
-
result[key] = val;
|
|
20440
|
-
}
|
|
20441
|
-
}
|
|
20442
|
-
return result;
|
|
20443
|
-
}
|
|
20444
20419
|
|
|
20445
20420
|
// src/event-handler.ts
|
|
20446
20421
|
var QUESTION_TIMEOUT_MS = 6e4;
|
|
@@ -20459,130 +20434,31 @@ var EventHandler = class {
|
|
|
20459
20434
|
permissionQueues = /* @__PURE__ */ new Map();
|
|
20460
20435
|
questionQueues = /* @__PURE__ */ new Map();
|
|
20461
20436
|
started = false;
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
sessionBufferIndex = /* @__PURE__ */ new Map();
|
|
20465
|
-
messageMetaCache = /* @__PURE__ */ new Map();
|
|
20437
|
+
/** partID → { type, ignored }, built from message.part.updated SSE events. */
|
|
20438
|
+
partMetaIndex = /* @__PURE__ */ new Map();
|
|
20466
20439
|
/** Track tool call counts per child session to detect completion. */
|
|
20467
20440
|
childToolCounts = /* @__PURE__ */ new Map();
|
|
20441
|
+
/** Cumulative code change stats per session, aggregated from session.diff SSE events. */
|
|
20442
|
+
diffStats = /* @__PURE__ */ new Map();
|
|
20468
20443
|
/**
|
|
20469
|
-
* Wrapper around connection.sessionUpdate that logs
|
|
20470
|
-
*
|
|
20444
|
+
* Wrapper around connection.sessionUpdate that logs ALL outgoing traffic to the ACP client.
|
|
20445
|
+
* Every call here corresponds to a real message sent over the wire.
|
|
20471
20446
|
*/
|
|
20472
20447
|
async sendToClient(params) {
|
|
20473
20448
|
const updateType = params.update.sessionUpdate;
|
|
20474
|
-
|
|
20475
|
-
|
|
20476
|
-
|
|
20477
|
-
|
|
20478
|
-
|
|
20479
|
-
toolCallId: params.update.toolCallId,
|
|
20480
|
-
tool: params.update.title,
|
|
20481
|
-
kind: params.update.kind,
|
|
20482
|
-
status: params.update.status
|
|
20483
|
-
} : updateType === "session_info_update" ? {
|
|
20484
|
-
title: params.update.title,
|
|
20485
|
-
_meta: params.update._meta
|
|
20486
|
-
} : {}
|
|
20487
|
-
});
|
|
20488
|
-
}
|
|
20449
|
+
const update = params.update;
|
|
20450
|
+
acpOut(`sessionUpdate.${updateType}`, {
|
|
20451
|
+
sessionId: params.sessionId.slice(0, 12),
|
|
20452
|
+
...updateType === "agent_message_chunk" || updateType === "agent_thought_chunk" ? { messageId: update.messageId?.slice(0, 12), delta: update.content?.text?.slice(0, 200) } : updateType === "tool_call" || updateType === "tool_call_update" ? { toolCallId: update.toolCallId, tool: update.title, kind: update.kind, status: update.status } : updateType === "session_info_update" ? { title: update.title, _meta: update._meta } : updateType === "usage_update" ? { used: update.used, size: update.size, cost: update.cost, _meta: update._meta } : {}
|
|
20453
|
+
});
|
|
20489
20454
|
return this.connection.sessionUpdate(params);
|
|
20490
20455
|
}
|
|
20491
|
-
|
|
20492
|
-
|
|
20493
|
-
* The buffer is flushed as a single log entry when a boundary event occurs
|
|
20494
|
-
* (e.g. tool_call starts, new message, session change).
|
|
20495
|
-
*/
|
|
20496
|
-
accumulateDelta(messageId, sessionId, kind, delta) {
|
|
20497
|
-
let buf = this.messageBuffers.get(messageId);
|
|
20498
|
-
if (!buf) {
|
|
20499
|
-
buf = { sessionId, text: "", thought: "" };
|
|
20500
|
-
this.messageBuffers.set(messageId, buf);
|
|
20501
|
-
const msgSet = this.sessionBufferIndex.get(sessionId);
|
|
20502
|
-
if (msgSet) {
|
|
20503
|
-
msgSet.add(messageId);
|
|
20504
|
-
} else {
|
|
20505
|
-
this.sessionBufferIndex.set(sessionId, /* @__PURE__ */ new Set([messageId]));
|
|
20506
|
-
}
|
|
20507
|
-
}
|
|
20508
|
-
buf[kind] += delta;
|
|
20456
|
+
getDiffStats(sessionId) {
|
|
20457
|
+
return this.diffStats.get(sessionId);
|
|
20509
20458
|
}
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
* text/thought phase is complete.
|
|
20514
|
-
*/
|
|
20515
|
-
flushMessageBuffer(sessionId, reason) {
|
|
20516
|
-
const msgIds = this.sessionBufferIndex.get(sessionId);
|
|
20517
|
-
if (!msgIds) return;
|
|
20518
|
-
for (const messageId of msgIds) {
|
|
20519
|
-
const buf = this.messageBuffers.get(messageId);
|
|
20520
|
-
if (!buf) continue;
|
|
20521
|
-
if (!buf.text && !buf.thought) {
|
|
20522
|
-
this.messageBuffers.delete(messageId);
|
|
20523
|
-
continue;
|
|
20524
|
-
}
|
|
20525
|
-
if (buf.text) {
|
|
20526
|
-
acpAssembled("sessionUpdate.agent_message_complete", {
|
|
20527
|
-
sessionId: sessionId.slice(0, 12),
|
|
20528
|
-
messageId: messageId.slice(0, 12),
|
|
20529
|
-
length: buf.text.length,
|
|
20530
|
-
text: buf.text,
|
|
20531
|
-
flushReason: reason
|
|
20532
|
-
});
|
|
20533
|
-
}
|
|
20534
|
-
if (buf.thought) {
|
|
20535
|
-
acpAssembled("sessionUpdate.agent_thought_complete", {
|
|
20536
|
-
sessionId: sessionId.slice(0, 12),
|
|
20537
|
-
messageId: messageId.slice(0, 12),
|
|
20538
|
-
length: buf.thought.length,
|
|
20539
|
-
text: buf.thought,
|
|
20540
|
-
flushReason: reason
|
|
20541
|
-
});
|
|
20542
|
-
}
|
|
20543
|
-
this.messageBuffers.delete(messageId);
|
|
20544
|
-
}
|
|
20545
|
-
this.sessionBufferIndex.delete(sessionId);
|
|
20546
|
-
}
|
|
20547
|
-
/** Flush all remaining buffers (e.g. on stop) */
|
|
20548
|
-
flushAllBuffers() {
|
|
20549
|
-
for (const sessionId of this.sessionBufferIndex.keys()) {
|
|
20550
|
-
this.flushMessageBuffer(sessionId, "stop");
|
|
20551
|
-
}
|
|
20552
|
-
}
|
|
20553
|
-
/**
|
|
20554
|
-
* Get or cache message metadata (role + parts) for a message.
|
|
20555
|
-
* Fetches from SDK only on first delta for a message, then serves from cache.
|
|
20556
|
-
*/
|
|
20557
|
-
async getOrCacheMessageMeta(sessionID, messageID, cwd) {
|
|
20558
|
-
const cached2 = this.messageMetaCache.get(messageID);
|
|
20559
|
-
if (cached2) return cached2;
|
|
20560
|
-
const message = await this.sdk.session.message({ sessionID, messageID, directory: cwd }, { throwOnError: true }).then((x) => x.data).catch(() => void 0);
|
|
20561
|
-
if (!message) return void 0;
|
|
20562
|
-
const parts = /* @__PURE__ */ new Map();
|
|
20563
|
-
for (const p of message.parts) {
|
|
20564
|
-
parts.set(p.id, { type: p.type, ignored: p.ignored });
|
|
20565
|
-
}
|
|
20566
|
-
const meta3 = { role: message.info.role, parts };
|
|
20567
|
-
this.messageMetaCache.set(messageID, meta3);
|
|
20568
|
-
if (this.messageMetaCache.size > 50) {
|
|
20569
|
-
const oldest = this.messageMetaCache.keys().next().value;
|
|
20570
|
-
if (oldest) this.messageMetaCache.delete(oldest);
|
|
20571
|
-
}
|
|
20572
|
-
return meta3;
|
|
20573
|
-
}
|
|
20574
|
-
/**
|
|
20575
|
-
* Flush previous message buffer for a session when messageId changes.
|
|
20576
|
-
*/
|
|
20577
|
-
flushPreviousBuffer(sessionId, currentMessageId) {
|
|
20578
|
-
const msgIds = this.sessionBufferIndex.get(sessionId);
|
|
20579
|
-
if (!msgIds) return;
|
|
20580
|
-
for (const messageId of msgIds) {
|
|
20581
|
-
if (messageId !== currentMessageId) {
|
|
20582
|
-
this.flushMessageBuffer(sessionId, "new_message");
|
|
20583
|
-
return;
|
|
20584
|
-
}
|
|
20585
|
-
}
|
|
20459
|
+
setDiffStats(sessionId, stats) {
|
|
20460
|
+
if (this.diffStats.has(sessionId)) return;
|
|
20461
|
+
this.diffStats.set(sessionId, stats);
|
|
20586
20462
|
}
|
|
20587
20463
|
constructor(deps) {
|
|
20588
20464
|
this.connection = deps.connection;
|
|
@@ -20598,7 +20474,6 @@ var EventHandler = class {
|
|
|
20598
20474
|
});
|
|
20599
20475
|
}
|
|
20600
20476
|
stop() {
|
|
20601
|
-
this.flushAllBuffers();
|
|
20602
20477
|
this.abort.abort();
|
|
20603
20478
|
}
|
|
20604
20479
|
async runSubscription() {
|
|
@@ -20630,9 +20505,13 @@ var EventHandler = class {
|
|
|
20630
20505
|
}
|
|
20631
20506
|
// ─── Event Handling ──────────────────────────────────────────────
|
|
20632
20507
|
async handleEvent(event) {
|
|
20508
|
+
if (event.type === "server.heartbeat") {
|
|
20509
|
+
return;
|
|
20510
|
+
}
|
|
20633
20511
|
switch (event.type) {
|
|
20634
20512
|
case "session.created":
|
|
20635
20513
|
case "session.updated": {
|
|
20514
|
+
ocEvent(event.type, event);
|
|
20636
20515
|
const props = event.properties;
|
|
20637
20516
|
const info = props.info ?? props;
|
|
20638
20517
|
const parentID = info.parentID;
|
|
@@ -20642,7 +20521,6 @@ var EventHandler = class {
|
|
|
20642
20521
|
const isNew = !this.sessionManager.tryGet(id);
|
|
20643
20522
|
this.sessionManager.registerDiscovered(id, parentID, title);
|
|
20644
20523
|
if (isNew) {
|
|
20645
|
-
ocEvent("session.created.child", { id, parentID, title });
|
|
20646
20524
|
const agentMatch = title?.match(/@(\w+)\s+subagent/);
|
|
20647
20525
|
const agentType = agentMatch?.[1];
|
|
20648
20526
|
const description = title?.replace(/\s*\(@\w+\s+subagent\)\s*$/, "") ?? title;
|
|
@@ -20654,6 +20532,7 @@ var EventHandler = class {
|
|
|
20654
20532
|
return;
|
|
20655
20533
|
}
|
|
20656
20534
|
case "permission.asked": {
|
|
20535
|
+
ocEvent("permission.asked", event);
|
|
20657
20536
|
const permission = event.properties;
|
|
20658
20537
|
const resolved = this.resolveSession(permission.sessionID);
|
|
20659
20538
|
if (!resolved) return;
|
|
@@ -20782,27 +20661,39 @@ var EventHandler = class {
|
|
|
20782
20661
|
return;
|
|
20783
20662
|
}
|
|
20784
20663
|
case "message.part.updated": {
|
|
20664
|
+
ocEvent("message.part.updated", event);
|
|
20785
20665
|
const props = event.properties;
|
|
20786
20666
|
const part = props.part;
|
|
20667
|
+
if (part.id) {
|
|
20668
|
+
this.partMetaIndex.set(part.id, { type: part.type, ignored: part.ignored });
|
|
20669
|
+
}
|
|
20787
20670
|
const resolved = this.resolveSession(part.sessionID);
|
|
20788
20671
|
if (!resolved) return;
|
|
20789
20672
|
if (part.type === "tool") {
|
|
20790
20673
|
await this.handleToolPart(resolved.sessionId, part);
|
|
20791
20674
|
}
|
|
20675
|
+
if (part.type === "text" && typeof part.text === "string" && part.ignored === true) {
|
|
20676
|
+
await this.sendToClient({
|
|
20677
|
+
sessionId: resolved.sessionId,
|
|
20678
|
+
update: {
|
|
20679
|
+
sessionUpdate: "agent_message_chunk",
|
|
20680
|
+
messageId: part.messageID,
|
|
20681
|
+
content: { type: "text", text: part.text }
|
|
20682
|
+
}
|
|
20683
|
+
}).catch(() => {
|
|
20684
|
+
});
|
|
20685
|
+
}
|
|
20792
20686
|
return;
|
|
20793
20687
|
}
|
|
20794
20688
|
case "message.part.delta": {
|
|
20689
|
+
ocEvent("message.part.delta", event);
|
|
20795
20690
|
const props = event.properties;
|
|
20796
20691
|
const resolved = this.resolveSession(props.sessionID);
|
|
20797
20692
|
if (!resolved) return;
|
|
20798
20693
|
const sessionId = resolved.sessionId;
|
|
20799
|
-
this.
|
|
20800
|
-
const meta3 = await this.getOrCacheMessageMeta(props.sessionID, props.messageID, resolved.cwd);
|
|
20801
|
-
if (!meta3 || meta3.role !== "assistant") return;
|
|
20802
|
-
const partMeta = meta3.parts.get(props.partID);
|
|
20694
|
+
const partMeta = this.partMetaIndex.get(props.partID);
|
|
20803
20695
|
if (!partMeta) return;
|
|
20804
20696
|
if (partMeta.type === "text" && props.field === "text" && partMeta.ignored !== true) {
|
|
20805
|
-
this.accumulateDelta(props.messageID, sessionId, "text", props.delta);
|
|
20806
20697
|
await this.sendToClient({
|
|
20807
20698
|
sessionId,
|
|
20808
20699
|
update: {
|
|
@@ -20815,7 +20706,6 @@ var EventHandler = class {
|
|
|
20815
20706
|
return;
|
|
20816
20707
|
}
|
|
20817
20708
|
if (partMeta.type === "reasoning" && props.field === "text") {
|
|
20818
|
-
this.accumulateDelta(props.messageID, sessionId, "thought", props.delta);
|
|
20819
20709
|
await this.sendToClient({
|
|
20820
20710
|
sessionId,
|
|
20821
20711
|
update: {
|
|
@@ -20828,6 +20718,25 @@ var EventHandler = class {
|
|
|
20828
20718
|
}
|
|
20829
20719
|
return;
|
|
20830
20720
|
}
|
|
20721
|
+
case "session.diff": {
|
|
20722
|
+
ocEvent("session.diff", event);
|
|
20723
|
+
const { sessionID, diff } = event.properties;
|
|
20724
|
+
const resolved = this.resolveSession(sessionID);
|
|
20725
|
+
if (!resolved) {
|
|
20726
|
+
return;
|
|
20727
|
+
}
|
|
20728
|
+
let additions = 0;
|
|
20729
|
+
let deletions = 0;
|
|
20730
|
+
for (const d of diff) {
|
|
20731
|
+
additions += d.additions;
|
|
20732
|
+
deletions += d.deletions;
|
|
20733
|
+
}
|
|
20734
|
+
this.diffStats.set(resolved.sessionId, { additions, deletions, files: diff.length });
|
|
20735
|
+
return;
|
|
20736
|
+
}
|
|
20737
|
+
default: {
|
|
20738
|
+
return;
|
|
20739
|
+
}
|
|
20831
20740
|
}
|
|
20832
20741
|
}
|
|
20833
20742
|
// ─── Child Session Announcement ──────────────────────────────────
|
|
@@ -21034,6 +20943,7 @@ var EventHandler = class {
|
|
|
21034
20943
|
const agentMatch = title.match(/@(\w+)\s+subagent/);
|
|
21035
20944
|
const agentType = agentMatch?.[1];
|
|
21036
20945
|
const description = title.replace(/\s*\(@\w+\s+subagent\)\s*$/, "");
|
|
20946
|
+
const childDiffStats = this.diffStats.get(childSessionId);
|
|
21037
20947
|
await this.sendToClient({
|
|
21038
20948
|
sessionId: childSessionId,
|
|
21039
20949
|
update: {
|
|
@@ -21046,7 +20956,8 @@ var EventHandler = class {
|
|
|
21046
20956
|
toolCallCount,
|
|
21047
20957
|
durationMs,
|
|
21048
20958
|
...agentType && { agentType },
|
|
21049
|
-
...description && { description }
|
|
20959
|
+
...description && { description },
|
|
20960
|
+
...childDiffStats && { codeChange: childDiffStats }
|
|
21050
20961
|
}
|
|
21051
20962
|
}
|
|
21052
20963
|
});
|
|
@@ -21068,6 +20979,49 @@ function simpleHash(str) {
|
|
|
21068
20979
|
return hash2.toString(36);
|
|
21069
20980
|
}
|
|
21070
20981
|
|
|
20982
|
+
// src/mcp-manager.ts
|
|
20983
|
+
var THROW = { throwOnError: true };
|
|
20984
|
+
var McpManager = class {
|
|
20985
|
+
constructor(sdk) {
|
|
20986
|
+
this.sdk = sdk;
|
|
20987
|
+
}
|
|
20988
|
+
async status(directory) {
|
|
20989
|
+
ocCall("mcp.status", { directory });
|
|
20990
|
+
const result = await this.sdk.mcp.status({ directory });
|
|
20991
|
+
return result.data ?? {};
|
|
20992
|
+
}
|
|
20993
|
+
async add(directory, name, config2) {
|
|
20994
|
+
ocCall("mcp.add", { directory, name });
|
|
20995
|
+
const result = await this.sdk.mcp.add({ directory, name, config: config2 }, THROW);
|
|
20996
|
+
return result.data ?? {};
|
|
20997
|
+
}
|
|
20998
|
+
async connect(directory, name) {
|
|
20999
|
+
ocCall("mcp.connect", { directory, name });
|
|
21000
|
+
await this.sdk.mcp.connect({ name, directory }, THROW);
|
|
21001
|
+
return { success: true };
|
|
21002
|
+
}
|
|
21003
|
+
async disconnect(directory, name) {
|
|
21004
|
+
ocCall("mcp.disconnect", { directory, name });
|
|
21005
|
+
await this.sdk.mcp.disconnect({ name, directory }, THROW);
|
|
21006
|
+
return { success: true };
|
|
21007
|
+
}
|
|
21008
|
+
async startAuth(directory, name) {
|
|
21009
|
+
ocCall("mcp.auth.start", { directory, name });
|
|
21010
|
+
const result = await this.sdk.mcp.auth.start({ name, directory }, THROW);
|
|
21011
|
+
return result.data ?? {};
|
|
21012
|
+
}
|
|
21013
|
+
async callbackAuth(directory, name, code) {
|
|
21014
|
+
ocCall("mcp.auth.callback", { directory, name });
|
|
21015
|
+
const result = await this.sdk.mcp.auth.callback({ name, directory, code }, THROW);
|
|
21016
|
+
return result.data ?? {};
|
|
21017
|
+
}
|
|
21018
|
+
async removeAuth(directory, name) {
|
|
21019
|
+
ocCall("mcp.auth.remove", { directory, name });
|
|
21020
|
+
await this.sdk.mcp.auth.remove({ name, directory }, THROW);
|
|
21021
|
+
return { success: true };
|
|
21022
|
+
}
|
|
21023
|
+
};
|
|
21024
|
+
|
|
21071
21025
|
// src/auth-provider.ts
|
|
21072
21026
|
var AuthProviderManager = class {
|
|
21073
21027
|
sdk;
|
|
@@ -21117,22 +21071,30 @@ var Agent = class {
|
|
|
21117
21071
|
sessionManager;
|
|
21118
21072
|
eventHandler;
|
|
21119
21073
|
authProvider;
|
|
21074
|
+
mcpManager;
|
|
21120
21075
|
/**
|
|
21121
21076
|
* Wrapper around connection.sessionUpdate that logs what's sent to the ACP client.
|
|
21122
21077
|
*/
|
|
21123
21078
|
async sendToClient(params) {
|
|
21124
21079
|
const updateType = params.update.sessionUpdate;
|
|
21125
|
-
|
|
21126
|
-
|
|
21127
|
-
|
|
21128
|
-
|
|
21129
|
-
|
|
21130
|
-
|
|
21131
|
-
|
|
21132
|
-
|
|
21133
|
-
|
|
21134
|
-
|
|
21135
|
-
|
|
21080
|
+
const update = params.update;
|
|
21081
|
+
acpOut(`sessionUpdate.${updateType}`, {
|
|
21082
|
+
sessionId: params.sessionId.slice(0, 12),
|
|
21083
|
+
...updateType === "agent_message_chunk" || updateType === "agent_thought_chunk" ? {
|
|
21084
|
+
messageId: update.messageId?.slice(0, 12),
|
|
21085
|
+
delta: update.content?.text?.slice(0, 200)
|
|
21086
|
+
} : updateType === "tool_call" || updateType === "tool_call_update" ? {
|
|
21087
|
+
toolCallId: update.toolCallId,
|
|
21088
|
+
tool: update.title,
|
|
21089
|
+
kind: update.kind,
|
|
21090
|
+
status: update.status
|
|
21091
|
+
} : updateType === "usage_update" ? {
|
|
21092
|
+
used: update.used,
|
|
21093
|
+
size: update.size,
|
|
21094
|
+
cost: update.cost,
|
|
21095
|
+
_meta: update._meta
|
|
21096
|
+
} : {}
|
|
21097
|
+
});
|
|
21136
21098
|
return this.connection.sessionUpdate(params);
|
|
21137
21099
|
}
|
|
21138
21100
|
constructor(config2) {
|
|
@@ -21140,6 +21102,7 @@ var Agent = class {
|
|
|
21140
21102
|
this.sdk = config2.sdk;
|
|
21141
21103
|
this.sessionManager = new SessionManager(config2.sdk);
|
|
21142
21104
|
this.authProvider = new AuthProviderManager(config2.sdk);
|
|
21105
|
+
this.mcpManager = new McpManager(config2.sdk);
|
|
21143
21106
|
}
|
|
21144
21107
|
init(connection) {
|
|
21145
21108
|
this.connection = connection;
|
|
@@ -21189,10 +21152,13 @@ var Agent = class {
|
|
|
21189
21152
|
// ─── Extension Methods ────────────────────────────────────────────
|
|
21190
21153
|
async extMethod(method, params) {
|
|
21191
21154
|
acpIn("extMethod", { method });
|
|
21192
|
-
if (
|
|
21193
|
-
|
|
21155
|
+
if (method.startsWith("provider/")) {
|
|
21156
|
+
return this.handleProviderMethod(method, params);
|
|
21157
|
+
}
|
|
21158
|
+
if (method.startsWith("mcp/")) {
|
|
21159
|
+
return this.handleMcpMethod(method, params);
|
|
21194
21160
|
}
|
|
21195
|
-
|
|
21161
|
+
throw new Error(`Unknown extMethod: ${method}`);
|
|
21196
21162
|
}
|
|
21197
21163
|
async handleProviderMethod(method, params) {
|
|
21198
21164
|
const sessionId = params.sessionId;
|
|
@@ -21248,6 +21214,67 @@ var Agent = class {
|
|
|
21248
21214
|
throw new Error(`Unknown provider extMethod: ${method}`);
|
|
21249
21215
|
}
|
|
21250
21216
|
}
|
|
21217
|
+
async handleMcpMethod(method, params) {
|
|
21218
|
+
const sessionId = params.sessionId;
|
|
21219
|
+
if (!sessionId) throw new Error("sessionId required");
|
|
21220
|
+
const directory = this.sessionManager.get(sessionId)?.cwd;
|
|
21221
|
+
if (!directory) throw new Error("Session not found");
|
|
21222
|
+
switch (method) {
|
|
21223
|
+
case "mcp/status": {
|
|
21224
|
+
const data = await this.mcpManager.status(directory);
|
|
21225
|
+
acpOut("extMethod.response", { method: "mcp/status" });
|
|
21226
|
+
return data;
|
|
21227
|
+
}
|
|
21228
|
+
case "mcp/add": {
|
|
21229
|
+
const name = params.name;
|
|
21230
|
+
const config2 = params.config;
|
|
21231
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21232
|
+
if (!config2) throw new Error("Missing required parameter: config");
|
|
21233
|
+
const data = await this.mcpManager.add(directory, name, config2);
|
|
21234
|
+
acpOut("extMethod.response", { method: "mcp/add", name });
|
|
21235
|
+
return data;
|
|
21236
|
+
}
|
|
21237
|
+
case "mcp/connect": {
|
|
21238
|
+
const name = params.name;
|
|
21239
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21240
|
+
const data = await this.mcpManager.connect(directory, name);
|
|
21241
|
+
acpOut("extMethod.response", { method: "mcp/connect", name });
|
|
21242
|
+
return data;
|
|
21243
|
+
}
|
|
21244
|
+
case "mcp/disconnect": {
|
|
21245
|
+
const name = params.name;
|
|
21246
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21247
|
+
const data = await this.mcpManager.disconnect(directory, name);
|
|
21248
|
+
acpOut("extMethod.response", { method: "mcp/disconnect", name });
|
|
21249
|
+
return data;
|
|
21250
|
+
}
|
|
21251
|
+
case "mcp/auth/start": {
|
|
21252
|
+
const name = params.name;
|
|
21253
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21254
|
+
const data = await this.mcpManager.startAuth(directory, name);
|
|
21255
|
+
acpOut("extMethod.response", { method: "mcp/auth/start", name });
|
|
21256
|
+
return data;
|
|
21257
|
+
}
|
|
21258
|
+
case "mcp/auth/callback": {
|
|
21259
|
+
const name = params.name;
|
|
21260
|
+
const code = params.code;
|
|
21261
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21262
|
+
if (!code) throw new Error("Missing required parameter: code");
|
|
21263
|
+
const data = await this.mcpManager.callbackAuth(directory, name, code);
|
|
21264
|
+
acpOut("extMethod.response", { method: "mcp/auth/callback", name });
|
|
21265
|
+
return data;
|
|
21266
|
+
}
|
|
21267
|
+
case "mcp/auth/remove": {
|
|
21268
|
+
const name = params.name;
|
|
21269
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21270
|
+
const data = await this.mcpManager.removeAuth(directory, name);
|
|
21271
|
+
acpOut("extMethod.response", { method: "mcp/auth/remove", name });
|
|
21272
|
+
return data;
|
|
21273
|
+
}
|
|
21274
|
+
default:
|
|
21275
|
+
throw new Error(`Unknown MCP method: ${method}`);
|
|
21276
|
+
}
|
|
21277
|
+
}
|
|
21251
21278
|
async buildProviderMeta() {
|
|
21252
21279
|
try {
|
|
21253
21280
|
const data = await this.authProvider.listProviders(this.config.cwd);
|
|
@@ -21294,7 +21321,14 @@ var Agent = class {
|
|
|
21294
21321
|
const directory = params.cwd;
|
|
21295
21322
|
const sessionId = params.sessionId;
|
|
21296
21323
|
const model = await this.defaultModel(directory);
|
|
21297
|
-
await this.sessionManager.load(sessionId, params.cwd, params.mcpServers, model);
|
|
21324
|
+
const { session } = await this.sessionManager.load(sessionId, params.cwd, params.mcpServers, model);
|
|
21325
|
+
if (session.summary) {
|
|
21326
|
+
this.eventHandler.setDiffStats(sessionId, {
|
|
21327
|
+
additions: session.summary.additions,
|
|
21328
|
+
deletions: session.summary.deletions,
|
|
21329
|
+
files: session.summary.files
|
|
21330
|
+
});
|
|
21331
|
+
}
|
|
21298
21332
|
const result = await this.loadSessionMode({
|
|
21299
21333
|
cwd: directory,
|
|
21300
21334
|
mcpServers: params.mcpServers,
|
|
@@ -21379,7 +21413,14 @@ var Agent = class {
|
|
|
21379
21413
|
const sessionId = params.sessionId;
|
|
21380
21414
|
const mcpServers = params.mcpServers ?? [];
|
|
21381
21415
|
const model = await this.defaultModel(directory);
|
|
21382
|
-
await this.sessionManager.load(sessionId, directory, mcpServers, model);
|
|
21416
|
+
const { session } = await this.sessionManager.load(sessionId, directory, mcpServers, model);
|
|
21417
|
+
if (session.summary) {
|
|
21418
|
+
this.eventHandler.setDiffStats(sessionId, {
|
|
21419
|
+
additions: session.summary.additions,
|
|
21420
|
+
deletions: session.summary.deletions,
|
|
21421
|
+
files: session.summary.files
|
|
21422
|
+
});
|
|
21423
|
+
}
|
|
21383
21424
|
const result = await this.loadSessionMode({
|
|
21384
21425
|
cwd: directory,
|
|
21385
21426
|
mcpServers,
|
|
@@ -21716,7 +21757,7 @@ var Agent = class {
|
|
|
21716
21757
|
{}
|
|
21717
21758
|
)
|
|
21718
21759
|
};
|
|
21719
|
-
await this.
|
|
21760
|
+
await this.mcpManager.add(params.cwd, server.name, config2).catch(() => {
|
|
21720
21761
|
});
|
|
21721
21762
|
})
|
|
21722
21763
|
);
|
|
@@ -21961,10 +22002,35 @@ var Agent = class {
|
|
|
21961
22002
|
childCost += childMessages.filter((m) => m.info.role === "assistant").reduce((sum, m) => sum + (m.info.cost ?? 0), 0);
|
|
21962
22003
|
}
|
|
21963
22004
|
}
|
|
22005
|
+
const stats = this.eventHandler.getDiffStats(sessionId);
|
|
22006
|
+
let childStats;
|
|
22007
|
+
for (const childId of children) {
|
|
22008
|
+
const cs = this.eventHandler.getDiffStats(childId);
|
|
22009
|
+
if (cs) {
|
|
22010
|
+
childStats = childStats ? {
|
|
22011
|
+
additions: childStats.additions + cs.additions,
|
|
22012
|
+
deletions: childStats.deletions + cs.deletions,
|
|
22013
|
+
files: childStats.files + cs.files
|
|
22014
|
+
} : { ...cs };
|
|
22015
|
+
}
|
|
22016
|
+
}
|
|
22017
|
+
const totalStats = stats ?? childStats;
|
|
21964
22018
|
const providers = await this.sdk.config.providers({ directory }).then((x) => x.data?.providers ?? []).catch(() => []);
|
|
21965
22019
|
const provider = providers.find((p) => p.id === msg.providerID);
|
|
21966
22020
|
const model = provider?.models[msg.modelID];
|
|
21967
22021
|
const size = model?.limit?.context ?? 0;
|
|
22022
|
+
const _meta = {};
|
|
22023
|
+
if (childCost > 0) {
|
|
22024
|
+
_meta.parentCost = totalCost;
|
|
22025
|
+
_meta.childCost = childCost;
|
|
22026
|
+
_meta.childSessionCount = children.length;
|
|
22027
|
+
}
|
|
22028
|
+
if (totalStats) {
|
|
22029
|
+
_meta.codeChange = totalStats;
|
|
22030
|
+
}
|
|
22031
|
+
if (childStats) {
|
|
22032
|
+
_meta.childCodeChange = childStats;
|
|
22033
|
+
}
|
|
21968
22034
|
await this.sendToClient({
|
|
21969
22035
|
sessionId,
|
|
21970
22036
|
update: {
|
|
@@ -21972,13 +22038,7 @@ var Agent = class {
|
|
|
21972
22038
|
used: msg.tokens.input + (msg.tokens.cache?.read ?? 0),
|
|
21973
22039
|
size,
|
|
21974
22040
|
cost: { amount: totalCost + childCost, currency: "USD" },
|
|
21975
|
-
...
|
|
21976
|
-
_meta: {
|
|
21977
|
-
parentCost: totalCost,
|
|
21978
|
-
childCost,
|
|
21979
|
-
childSessionCount: children.length
|
|
21980
|
-
}
|
|
21981
|
-
}
|
|
22041
|
+
...Object.keys(_meta).length > 0 && { _meta }
|
|
21982
22042
|
}
|
|
21983
22043
|
}).catch(() => {
|
|
21984
22044
|
});
|
|
@@ -22045,33 +22105,13 @@ var Agent = class {
|
|
|
22045
22105
|
return { name, args: rest.join(" ").trim() };
|
|
22046
22106
|
}
|
|
22047
22107
|
/**
|
|
22048
|
-
* Fetch the complete message content after prompt returns and log
|
|
22049
|
-
* This ensures the response text is always captured in logs, even if
|
|
22050
|
-
* SSE delta events arrive out of sync or are missed.
|
|
22108
|
+
* Fetch the complete message content after prompt returns and log tool usage.
|
|
22051
22109
|
*/
|
|
22052
22110
|
async logMessageContent(sessionId, messageId, directory) {
|
|
22053
22111
|
if (!messageId) return;
|
|
22054
22112
|
const message = await this.sdk.session.message({ sessionID: sessionId, messageID: messageId, directory }).then((x) => x.data).catch(() => void 0);
|
|
22055
22113
|
if (!message) return;
|
|
22056
22114
|
for (const part of message.parts) {
|
|
22057
|
-
if (part.type === "text" && part.text) {
|
|
22058
|
-
acpAssembled("sessionUpdate.agent_message_complete", {
|
|
22059
|
-
sessionId: sessionId.slice(0, 12),
|
|
22060
|
-
messageId: messageId.slice(0, 12),
|
|
22061
|
-
length: part.text.length,
|
|
22062
|
-
text: part.text,
|
|
22063
|
-
flushReason: "prompt_complete"
|
|
22064
|
-
});
|
|
22065
|
-
}
|
|
22066
|
-
if (part.type === "reasoning" && part.text) {
|
|
22067
|
-
acpAssembled("sessionUpdate.agent_thought_complete", {
|
|
22068
|
-
sessionId: sessionId.slice(0, 12),
|
|
22069
|
-
messageId: messageId.slice(0, 12),
|
|
22070
|
-
length: part.text.length,
|
|
22071
|
-
text: part.text,
|
|
22072
|
-
flushReason: "prompt_complete"
|
|
22073
|
-
});
|
|
22074
|
-
}
|
|
22075
22115
|
if (part.type === "tool") {
|
|
22076
22116
|
ocCall("session.prompt.tool", {
|
|
22077
22117
|
sessionId: sessionId.slice(0, 12),
|