@harmonyos-arkts/opencode-acp 0.0.3 → 0.0.6
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 +506 -300
- 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 +3 -2
package/dist/index.cjs
CHANGED
|
@@ -19586,6 +19586,11 @@ function createOpencodeClient(config2) {
|
|
|
19586
19586
|
// node_modules/@opencode-ai/sdk/dist/v2/server.js
|
|
19587
19587
|
var import_cross_spawn = __toESM(require_cross_spawn(), 1);
|
|
19588
19588
|
|
|
19589
|
+
// src/index.ts
|
|
19590
|
+
var http = __toESM(require("node:http"), 1);
|
|
19591
|
+
var https = __toESM(require("node:https"), 1);
|
|
19592
|
+
var import_node_stream = require("node:stream");
|
|
19593
|
+
|
|
19589
19594
|
// src/agent.ts
|
|
19590
19595
|
var import_url2 = require("url");
|
|
19591
19596
|
|
|
@@ -19631,9 +19636,11 @@ var SessionManager = class {
|
|
|
19631
19636
|
/**
|
|
19632
19637
|
* Load an existing session into the manager.
|
|
19633
19638
|
* Called by agent.loadSession() / agent.unstable_resumeSession().
|
|
19639
|
+
* Returns the session state plus the raw Session object from OpenCode.
|
|
19634
19640
|
*/
|
|
19635
19641
|
async load(sessionId, cwd, mcpServers, model) {
|
|
19636
|
-
await this.sdk.session.get({ sessionID: sessionId, directory: cwd }, { throwOnError: true });
|
|
19642
|
+
const response = await this.sdk.session.get({ sessionID: sessionId, directory: cwd }, { throwOnError: true });
|
|
19643
|
+
const session = response.data;
|
|
19637
19644
|
const state = {
|
|
19638
19645
|
id: sessionId,
|
|
19639
19646
|
cwd,
|
|
@@ -19642,7 +19649,7 @@ var SessionManager = class {
|
|
|
19642
19649
|
model
|
|
19643
19650
|
};
|
|
19644
19651
|
this.sessions.set(sessionId, state);
|
|
19645
|
-
return state;
|
|
19652
|
+
return { state, session };
|
|
19646
19653
|
}
|
|
19647
19654
|
/**
|
|
19648
19655
|
* Auto-register a session discovered via SSE events.
|
|
@@ -20277,65 +20284,6 @@ function applyStructuredPatch(source, patch, options = {}) {
|
|
|
20277
20284
|
|
|
20278
20285
|
// src/utils.ts
|
|
20279
20286
|
var import_url = require("url");
|
|
20280
|
-
function toToolKind(toolName) {
|
|
20281
|
-
const tool = toolName.toLowerCase();
|
|
20282
|
-
switch (tool) {
|
|
20283
|
-
case "bash":
|
|
20284
|
-
return "execute";
|
|
20285
|
-
case "webfetch":
|
|
20286
|
-
return "fetch";
|
|
20287
|
-
case "edit":
|
|
20288
|
-
case "patch":
|
|
20289
|
-
case "write":
|
|
20290
|
-
return "edit";
|
|
20291
|
-
case "grep":
|
|
20292
|
-
case "glob":
|
|
20293
|
-
case "context7_resolve_library_id":
|
|
20294
|
-
case "context7_get_library_docs":
|
|
20295
|
-
return "search";
|
|
20296
|
-
case "list":
|
|
20297
|
-
case "read":
|
|
20298
|
-
return "read";
|
|
20299
|
-
default:
|
|
20300
|
-
return "other";
|
|
20301
|
-
}
|
|
20302
|
-
}
|
|
20303
|
-
function toLocations(toolName, input) {
|
|
20304
|
-
const tool = toolName.toLowerCase();
|
|
20305
|
-
switch (tool) {
|
|
20306
|
-
case "read":
|
|
20307
|
-
case "edit":
|
|
20308
|
-
case "write":
|
|
20309
|
-
return input["filePath"] ? [{ path: input["filePath"] }] : [];
|
|
20310
|
-
case "glob":
|
|
20311
|
-
case "grep":
|
|
20312
|
-
return input["path"] ? [{ path: input["path"] }] : [];
|
|
20313
|
-
case "list":
|
|
20314
|
-
return input["path"] ? [{ path: input["path"] }] : [];
|
|
20315
|
-
default:
|
|
20316
|
-
return [];
|
|
20317
|
-
}
|
|
20318
|
-
}
|
|
20319
|
-
function parseUri(uri) {
|
|
20320
|
-
try {
|
|
20321
|
-
if (uri.startsWith("file://")) {
|
|
20322
|
-
const path = uri.slice(7);
|
|
20323
|
-
const name = path.split("/").pop() || path;
|
|
20324
|
-
return { type: "file", url: uri, filename: name, mime: "text/plain" };
|
|
20325
|
-
}
|
|
20326
|
-
if (uri.startsWith("zed://")) {
|
|
20327
|
-
const url2 = new URL(uri);
|
|
20328
|
-
const path = url2.searchParams.get("path");
|
|
20329
|
-
if (path) {
|
|
20330
|
-
const name = path.split("/").pop() || path;
|
|
20331
|
-
return { type: "file", url: (0, import_url.pathToFileURL)(path).href, filename: name, mime: "text/plain" };
|
|
20332
|
-
}
|
|
20333
|
-
}
|
|
20334
|
-
return { type: "text", text: uri };
|
|
20335
|
-
} catch {
|
|
20336
|
-
return { type: "text", text: uri };
|
|
20337
|
-
}
|
|
20338
|
-
}
|
|
20339
20287
|
|
|
20340
20288
|
// src/logger.ts
|
|
20341
20289
|
var import_fs = require("fs");
|
|
@@ -20401,49 +20349,97 @@ function sysLog(action, data) {
|
|
|
20401
20349
|
write("system", action, data);
|
|
20402
20350
|
}
|
|
20403
20351
|
function sanitize(obj, depth = 0) {
|
|
20404
|
-
if (!obj
|
|
20352
|
+
if (!obj) return obj;
|
|
20353
|
+
if (depth > 5) return { __truncated__: true, __depth__: depth };
|
|
20405
20354
|
const result = {};
|
|
20406
20355
|
for (const [key, val] of Object.entries(obj)) {
|
|
20407
20356
|
if (typeof val === "string" && val.length > 2e3) {
|
|
20408
20357
|
result[key] = val.slice(0, 2e3) + `... [${val.length} chars total]`;
|
|
20409
20358
|
} else if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
20410
20359
|
result[key] = sanitize(val, depth + 1);
|
|
20360
|
+
} else if (val && Array.isArray(val)) {
|
|
20361
|
+
result[key] = val.map(
|
|
20362
|
+
(item) => typeof item === "string" && item.length > 2e3 ? item.slice(0, 2e3) + `... [${item.length} chars total]` : item && typeof item === "object" ? sanitize(item, depth + 1) : item
|
|
20363
|
+
);
|
|
20411
20364
|
} else {
|
|
20412
20365
|
result[key] = val;
|
|
20413
20366
|
}
|
|
20414
20367
|
}
|
|
20415
20368
|
return result;
|
|
20416
20369
|
}
|
|
20417
|
-
|
|
20418
|
-
|
|
20419
|
-
|
|
20420
|
-
|
|
20421
|
-
|
|
20422
|
-
|
|
20423
|
-
|
|
20424
|
-
|
|
20425
|
-
|
|
20426
|
-
|
|
20427
|
-
|
|
20370
|
+
|
|
20371
|
+
// src/utils.ts
|
|
20372
|
+
function toToolKind(toolName) {
|
|
20373
|
+
const tool = toolName.toLowerCase();
|
|
20374
|
+
switch (tool) {
|
|
20375
|
+
case "bash":
|
|
20376
|
+
return "execute";
|
|
20377
|
+
case "webfetch":
|
|
20378
|
+
return "fetch";
|
|
20379
|
+
case "edit":
|
|
20380
|
+
case "patch":
|
|
20381
|
+
case "write":
|
|
20382
|
+
return "edit";
|
|
20383
|
+
case "grep":
|
|
20384
|
+
case "glob":
|
|
20385
|
+
case "context7_resolve_library_id":
|
|
20386
|
+
case "context7_get_library_docs":
|
|
20387
|
+
return "search";
|
|
20388
|
+
case "list":
|
|
20389
|
+
case "read":
|
|
20390
|
+
return "read";
|
|
20391
|
+
default:
|
|
20392
|
+
return "other";
|
|
20428
20393
|
}
|
|
20429
20394
|
}
|
|
20430
|
-
function
|
|
20431
|
-
|
|
20432
|
-
|
|
20433
|
-
|
|
20434
|
-
|
|
20435
|
-
|
|
20436
|
-
|
|
20437
|
-
|
|
20438
|
-
|
|
20439
|
-
|
|
20395
|
+
function toLocations(toolName, input) {
|
|
20396
|
+
const tool = toolName.toLowerCase();
|
|
20397
|
+
switch (tool) {
|
|
20398
|
+
case "read":
|
|
20399
|
+
case "edit":
|
|
20400
|
+
case "write":
|
|
20401
|
+
return input["filePath"] ? [{ path: input["filePath"] }] : [];
|
|
20402
|
+
case "glob":
|
|
20403
|
+
case "grep":
|
|
20404
|
+
return input["path"] ? [{ path: input["path"] }] : [];
|
|
20405
|
+
case "list":
|
|
20406
|
+
return input["path"] ? [{ path: input["path"] }] : [];
|
|
20407
|
+
default:
|
|
20408
|
+
return [];
|
|
20409
|
+
}
|
|
20410
|
+
}
|
|
20411
|
+
function parseUri(uri) {
|
|
20412
|
+
try {
|
|
20413
|
+
if (uri.startsWith("file://")) {
|
|
20414
|
+
const path = uri.slice(7);
|
|
20415
|
+
const name = path.split("/").pop() || path;
|
|
20416
|
+
return { type: "file", url: uri, filename: name, mime: "text/plain" };
|
|
20440
20417
|
}
|
|
20418
|
+
if (uri.startsWith("zed://")) {
|
|
20419
|
+
const url2 = new URL(uri);
|
|
20420
|
+
const path = url2.searchParams.get("path");
|
|
20421
|
+
if (path) {
|
|
20422
|
+
const name = path.split("/").pop() || path;
|
|
20423
|
+
return { type: "file", url: (0, import_url.pathToFileURL)(path).href, filename: name, mime: "text/plain" };
|
|
20424
|
+
}
|
|
20425
|
+
}
|
|
20426
|
+
return { type: "text", text: uri };
|
|
20427
|
+
} catch {
|
|
20428
|
+
return { type: "text", text: uri };
|
|
20441
20429
|
}
|
|
20442
|
-
|
|
20430
|
+
}
|
|
20431
|
+
async function sendToClient(connection, params) {
|
|
20432
|
+
const updateType = params.update.sessionUpdate;
|
|
20433
|
+
const update = params.update;
|
|
20434
|
+
acpOut(`sessionUpdate.${updateType}`, {
|
|
20435
|
+
sessionId: params.sessionId.slice(0, 12),
|
|
20436
|
+
...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 } : {}
|
|
20437
|
+
});
|
|
20438
|
+
return connection.sessionUpdate(params);
|
|
20443
20439
|
}
|
|
20444
20440
|
|
|
20445
20441
|
// src/event-handler.ts
|
|
20446
|
-
var QUESTION_TIMEOUT_MS =
|
|
20442
|
+
var QUESTION_TIMEOUT_MS = 3e5;
|
|
20447
20443
|
var permissionOptions = [
|
|
20448
20444
|
{ optionId: "once", kind: "allow_once", name: "Allow once" },
|
|
20449
20445
|
{ optionId: "always", kind: "allow_always", name: "Always allow" },
|
|
@@ -20459,131 +20455,14 @@ var EventHandler = class {
|
|
|
20459
20455
|
permissionQueues = /* @__PURE__ */ new Map();
|
|
20460
20456
|
questionQueues = /* @__PURE__ */ new Map();
|
|
20461
20457
|
started = false;
|
|
20462
|
-
|
|
20463
|
-
|
|
20464
|
-
sessionBufferIndex = /* @__PURE__ */ new Map();
|
|
20465
|
-
messageMetaCache = /* @__PURE__ */ new Map();
|
|
20458
|
+
/** partID → { type, ignored }, built from message.part.updated SSE events. */
|
|
20459
|
+
partMetaIndex = /* @__PURE__ */ new Map();
|
|
20466
20460
|
/** Track tool call counts per child session to detect completion. */
|
|
20467
20461
|
childToolCounts = /* @__PURE__ */ new Map();
|
|
20468
|
-
/**
|
|
20469
|
-
|
|
20470
|
-
|
|
20471
|
-
|
|
20472
|
-
async sendToClient(params) {
|
|
20473
|
-
const updateType = params.update.sessionUpdate;
|
|
20474
|
-
if (updateType !== "agent_message_chunk" && updateType !== "agent_thought_chunk") {
|
|
20475
|
-
this.flushMessageBuffer(params.sessionId, `boundary:${updateType}`);
|
|
20476
|
-
acpOut(`sessionUpdate.${updateType}`, {
|
|
20477
|
-
sessionId: params.sessionId.slice(0, 12),
|
|
20478
|
-
...updateType === "tool_call" || updateType === "tool_call_update" ? {
|
|
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
|
-
}
|
|
20489
|
-
return this.connection.sessionUpdate(params);
|
|
20490
|
-
}
|
|
20491
|
-
/**
|
|
20492
|
-
* Accumulate a text/thought delta into the message buffer.
|
|
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;
|
|
20509
|
-
}
|
|
20510
|
-
/**
|
|
20511
|
-
* Flush accumulated message content for a given session as assembled log entries.
|
|
20512
|
-
* Called when a boundary event (tool_call, session change, etc.) signals the
|
|
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
|
-
}
|
|
20586
|
-
}
|
|
20462
|
+
/** Cumulative file diff stats per session, aggregated from session.diff SSE events. */
|
|
20463
|
+
fileDiffStats = /* @__PURE__ */ new Map();
|
|
20464
|
+
/** AI code change stats per session, keyed by sessionId → filePath → { additions, deletions }. */
|
|
20465
|
+
aiCodeChangeStats = /* @__PURE__ */ new Map();
|
|
20587
20466
|
constructor(deps) {
|
|
20588
20467
|
this.connection = deps.connection;
|
|
20589
20468
|
this.sdk = deps.sdk;
|
|
@@ -20598,7 +20477,6 @@ var EventHandler = class {
|
|
|
20598
20477
|
});
|
|
20599
20478
|
}
|
|
20600
20479
|
stop() {
|
|
20601
|
-
this.flushAllBuffers();
|
|
20602
20480
|
this.abort.abort();
|
|
20603
20481
|
}
|
|
20604
20482
|
async runSubscription() {
|
|
@@ -20630,9 +20508,13 @@ var EventHandler = class {
|
|
|
20630
20508
|
}
|
|
20631
20509
|
// ─── Event Handling ──────────────────────────────────────────────
|
|
20632
20510
|
async handleEvent(event) {
|
|
20511
|
+
if (event.type === "server.heartbeat") {
|
|
20512
|
+
return;
|
|
20513
|
+
}
|
|
20633
20514
|
switch (event.type) {
|
|
20634
20515
|
case "session.created":
|
|
20635
20516
|
case "session.updated": {
|
|
20517
|
+
ocEvent(event.type, event);
|
|
20636
20518
|
const props = event.properties;
|
|
20637
20519
|
const info = props.info ?? props;
|
|
20638
20520
|
const parentID = info.parentID;
|
|
@@ -20642,7 +20524,6 @@ var EventHandler = class {
|
|
|
20642
20524
|
const isNew = !this.sessionManager.tryGet(id);
|
|
20643
20525
|
this.sessionManager.registerDiscovered(id, parentID, title);
|
|
20644
20526
|
if (isNew) {
|
|
20645
|
-
ocEvent("session.created.child", { id, parentID, title });
|
|
20646
20527
|
const agentMatch = title?.match(/@(\w+)\s+subagent/);
|
|
20647
20528
|
const agentType = agentMatch?.[1];
|
|
20648
20529
|
const description = title?.replace(/\s*\(@\w+\s+subagent\)\s*$/, "") ?? title;
|
|
@@ -20654,6 +20535,7 @@ var EventHandler = class {
|
|
|
20654
20535
|
return;
|
|
20655
20536
|
}
|
|
20656
20537
|
case "permission.asked": {
|
|
20538
|
+
ocEvent("permission.asked", event);
|
|
20657
20539
|
const permission = event.properties;
|
|
20658
20540
|
const resolved = this.resolveSession(permission.sessionID);
|
|
20659
20541
|
if (!resolved) return;
|
|
@@ -20723,15 +20605,26 @@ var EventHandler = class {
|
|
|
20723
20605
|
}
|
|
20724
20606
|
case "question.asked": {
|
|
20725
20607
|
const q = event.properties;
|
|
20726
|
-
|
|
20727
|
-
|
|
20608
|
+
ocEvent("question.asked", event);
|
|
20609
|
+
let resolved = this.resolveSession(q.sessionID);
|
|
20610
|
+
if (!resolved) {
|
|
20611
|
+
console.warn(`[event-handler] question.asked: session ${q.sessionID} not in manager, attempting auto-register`);
|
|
20612
|
+
try {
|
|
20613
|
+
const sessionResp = await this.sdk.session.get({ sessionID: q.sessionID, directory: "" });
|
|
20614
|
+
const fetchedSession = sessionResp.data;
|
|
20615
|
+
if (fetchedSession?.parentID) {
|
|
20616
|
+
this.sessionManager.registerDiscovered(fetchedSession.id, fetchedSession.parentID, fetchedSession.title);
|
|
20617
|
+
resolved = this.resolveSession(q.sessionID);
|
|
20618
|
+
} else if (fetchedSession) {
|
|
20619
|
+
console.warn(`[event-handler] question.asked: top-level session ${q.sessionID} not registered; was agent.newSession called?`);
|
|
20620
|
+
}
|
|
20621
|
+
} catch (err) {
|
|
20622
|
+
console.error(`[event-handler] question.asked: failed to fetch session ${q.sessionID}:`, err);
|
|
20623
|
+
}
|
|
20624
|
+
if (!resolved) return;
|
|
20625
|
+
}
|
|
20728
20626
|
const directory = resolved.cwd;
|
|
20729
20627
|
const sessionId = resolved.sessionId;
|
|
20730
|
-
ocEvent("question.asked", {
|
|
20731
|
-
id: q.id,
|
|
20732
|
-
sessionID: q.sessionID,
|
|
20733
|
-
questions: q.questions.length
|
|
20734
|
-
});
|
|
20735
20628
|
const prev = this.questionQueues.get(q.sessionID) ?? Promise.resolve();
|
|
20736
20629
|
const next = prev.then(async () => {
|
|
20737
20630
|
const extResult = await Promise.race([
|
|
@@ -20751,7 +20644,7 @@ var EventHandler = class {
|
|
|
20751
20644
|
if (!extResult || !extResult.answers) {
|
|
20752
20645
|
await this.sdk.question.reject({ requestID: q.id, directory }).catch(() => {
|
|
20753
20646
|
});
|
|
20754
|
-
|
|
20647
|
+
sysLog("question.rejected", { requestID: q.id });
|
|
20755
20648
|
return;
|
|
20756
20649
|
}
|
|
20757
20650
|
const answers = extResult.answers;
|
|
@@ -20759,7 +20652,7 @@ var EventHandler = class {
|
|
|
20759
20652
|
console.error("[event-handler] invalid answers shape from client:", JSON.stringify(answers));
|
|
20760
20653
|
await this.sdk.question.reject({ requestID: q.id, directory }).catch(() => {
|
|
20761
20654
|
});
|
|
20762
|
-
|
|
20655
|
+
sysLog("question.rejected", {
|
|
20763
20656
|
requestID: q.id,
|
|
20764
20657
|
reason: "invalid_answers"
|
|
20765
20658
|
});
|
|
@@ -20770,7 +20663,7 @@ var EventHandler = class {
|
|
|
20770
20663
|
directory,
|
|
20771
20664
|
answers
|
|
20772
20665
|
});
|
|
20773
|
-
|
|
20666
|
+
sysLog("question.reply", { requestID: q.id, answers });
|
|
20774
20667
|
}).catch((err) => {
|
|
20775
20668
|
console.error("[event-handler] question handling error:", err);
|
|
20776
20669
|
}).finally(() => {
|
|
@@ -20782,28 +20675,40 @@ var EventHandler = class {
|
|
|
20782
20675
|
return;
|
|
20783
20676
|
}
|
|
20784
20677
|
case "message.part.updated": {
|
|
20678
|
+
ocEvent("message.part.updated", event);
|
|
20785
20679
|
const props = event.properties;
|
|
20786
20680
|
const part = props.part;
|
|
20681
|
+
if (part.id) {
|
|
20682
|
+
this.partMetaIndex.set(part.id, { type: part.type, ignored: part.ignored });
|
|
20683
|
+
}
|
|
20787
20684
|
const resolved = this.resolveSession(part.sessionID);
|
|
20788
20685
|
if (!resolved) return;
|
|
20789
20686
|
if (part.type === "tool") {
|
|
20790
20687
|
await this.handleToolPart(resolved.sessionId, part);
|
|
20791
20688
|
}
|
|
20689
|
+
if (part.type === "text" && typeof part.text === "string" && part.ignored === true) {
|
|
20690
|
+
await sendToClient(this.connection, {
|
|
20691
|
+
sessionId: resolved.sessionId,
|
|
20692
|
+
update: {
|
|
20693
|
+
sessionUpdate: "agent_message_chunk",
|
|
20694
|
+
messageId: part.messageID,
|
|
20695
|
+
content: { type: "text", text: part.text }
|
|
20696
|
+
}
|
|
20697
|
+
}).catch(() => {
|
|
20698
|
+
});
|
|
20699
|
+
}
|
|
20792
20700
|
return;
|
|
20793
20701
|
}
|
|
20794
20702
|
case "message.part.delta": {
|
|
20703
|
+
ocEvent("message.part.delta", event);
|
|
20795
20704
|
const props = event.properties;
|
|
20796
20705
|
const resolved = this.resolveSession(props.sessionID);
|
|
20797
20706
|
if (!resolved) return;
|
|
20798
20707
|
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);
|
|
20708
|
+
const partMeta = this.partMetaIndex.get(props.partID);
|
|
20803
20709
|
if (!partMeta) return;
|
|
20804
20710
|
if (partMeta.type === "text" && props.field === "text" && partMeta.ignored !== true) {
|
|
20805
|
-
this.
|
|
20806
|
-
await this.sendToClient({
|
|
20711
|
+
await sendToClient(this.connection, {
|
|
20807
20712
|
sessionId,
|
|
20808
20713
|
update: {
|
|
20809
20714
|
sessionUpdate: "agent_message_chunk",
|
|
@@ -20815,8 +20720,7 @@ var EventHandler = class {
|
|
|
20815
20720
|
return;
|
|
20816
20721
|
}
|
|
20817
20722
|
if (partMeta.type === "reasoning" && props.field === "text") {
|
|
20818
|
-
this.
|
|
20819
|
-
await this.sendToClient({
|
|
20723
|
+
await sendToClient(this.connection, {
|
|
20820
20724
|
sessionId,
|
|
20821
20725
|
update: {
|
|
20822
20726
|
sessionUpdate: "agent_thought_chunk",
|
|
@@ -20828,6 +20732,25 @@ var EventHandler = class {
|
|
|
20828
20732
|
}
|
|
20829
20733
|
return;
|
|
20830
20734
|
}
|
|
20735
|
+
case "session.diff": {
|
|
20736
|
+
ocEvent("session.diff", event);
|
|
20737
|
+
const { sessionID, diff } = event.properties;
|
|
20738
|
+
const resolved = this.resolveSession(sessionID);
|
|
20739
|
+
if (!resolved) {
|
|
20740
|
+
return;
|
|
20741
|
+
}
|
|
20742
|
+
let additions = 0;
|
|
20743
|
+
let deletions = 0;
|
|
20744
|
+
for (const d of diff) {
|
|
20745
|
+
additions += d.additions;
|
|
20746
|
+
deletions += d.deletions;
|
|
20747
|
+
}
|
|
20748
|
+
this.fileDiffStats.set(resolved.sessionId, { additions, deletions, files: diff.length });
|
|
20749
|
+
return;
|
|
20750
|
+
}
|
|
20751
|
+
default: {
|
|
20752
|
+
return;
|
|
20753
|
+
}
|
|
20831
20754
|
}
|
|
20832
20755
|
}
|
|
20833
20756
|
// ─── Child Session Announcement ──────────────────────────────────
|
|
@@ -20838,7 +20761,7 @@ var EventHandler = class {
|
|
|
20838
20761
|
* and _meta containing structured metadata about the subagent.
|
|
20839
20762
|
*/
|
|
20840
20763
|
async announceChildSession(childSessionId, parentSessionId, title, meta3) {
|
|
20841
|
-
await this.
|
|
20764
|
+
await sendToClient(this.connection, {
|
|
20842
20765
|
sessionId: childSessionId,
|
|
20843
20766
|
update: {
|
|
20844
20767
|
sessionUpdate: "session_info_update",
|
|
@@ -20871,7 +20794,7 @@ var EventHandler = class {
|
|
|
20871
20794
|
});
|
|
20872
20795
|
}
|
|
20873
20796
|
}
|
|
20874
|
-
await this.
|
|
20797
|
+
await sendToClient(this.connection, {
|
|
20875
20798
|
sessionId,
|
|
20876
20799
|
update: {
|
|
20877
20800
|
sessionUpdate: "tool_call",
|
|
@@ -20903,7 +20826,7 @@ var EventHandler = class {
|
|
|
20903
20826
|
content: { type: "text", text: output }
|
|
20904
20827
|
});
|
|
20905
20828
|
}
|
|
20906
|
-
await this.
|
|
20829
|
+
await sendToClient(this.connection, {
|
|
20907
20830
|
sessionId,
|
|
20908
20831
|
update: {
|
|
20909
20832
|
sessionUpdate: "tool_call_update",
|
|
@@ -20956,12 +20879,13 @@ var EventHandler = class {
|
|
|
20956
20879
|
const oldText = typeof input["oldString"] === "string" ? input["oldString"] : "";
|
|
20957
20880
|
const newText = typeof input["newString"] === "string" ? input["newString"] : typeof input["content"] === "string" ? input["content"] : "";
|
|
20958
20881
|
content.push({ type: "diff", path: filePath, oldText, newText });
|
|
20882
|
+
this.accumulateAICodeChangeStats(sessionId, part);
|
|
20959
20883
|
}
|
|
20960
20884
|
if (part.tool === "todowrite") {
|
|
20961
20885
|
try {
|
|
20962
20886
|
const todos = JSON.parse(part.state.output);
|
|
20963
20887
|
if (Array.isArray(todos)) {
|
|
20964
|
-
await this.
|
|
20888
|
+
await sendToClient(this.connection, {
|
|
20965
20889
|
sessionId,
|
|
20966
20890
|
update: {
|
|
20967
20891
|
sessionUpdate: "plan",
|
|
@@ -20977,7 +20901,7 @@ var EventHandler = class {
|
|
|
20977
20901
|
} catch {
|
|
20978
20902
|
}
|
|
20979
20903
|
}
|
|
20980
|
-
await this.
|
|
20904
|
+
await sendToClient(this.connection, {
|
|
20981
20905
|
sessionId,
|
|
20982
20906
|
update: {
|
|
20983
20907
|
sessionUpdate: "tool_call_update",
|
|
@@ -20999,7 +20923,7 @@ var EventHandler = class {
|
|
|
20999
20923
|
case "error": {
|
|
21000
20924
|
this.toolStarts.delete(part.callID);
|
|
21001
20925
|
this.bashSnapshots.delete(part.callID);
|
|
21002
|
-
await this.
|
|
20926
|
+
await sendToClient(this.connection, {
|
|
21003
20927
|
sessionId,
|
|
21004
20928
|
update: {
|
|
21005
20929
|
sessionUpdate: "tool_call_update",
|
|
@@ -21034,7 +20958,7 @@ var EventHandler = class {
|
|
|
21034
20958
|
const agentMatch = title.match(/@(\w+)\s+subagent/);
|
|
21035
20959
|
const agentType = agentMatch?.[1];
|
|
21036
20960
|
const description = title.replace(/\s*\(@\w+\s+subagent\)\s*$/, "");
|
|
21037
|
-
await this.
|
|
20961
|
+
await sendToClient(this.connection, {
|
|
21038
20962
|
sessionId: childSessionId,
|
|
21039
20963
|
update: {
|
|
21040
20964
|
sessionUpdate: "session_info_update",
|
|
@@ -21058,6 +20982,44 @@ var EventHandler = class {
|
|
|
21058
20982
|
if (typeof output !== "string") return;
|
|
21059
20983
|
return output;
|
|
21060
20984
|
}
|
|
20985
|
+
// ─── Statistics Accessors ────────────────────────────────────────
|
|
20986
|
+
getFileDiffStats(sessionId) {
|
|
20987
|
+
return this.fileDiffStats.get(sessionId);
|
|
20988
|
+
}
|
|
20989
|
+
setFileDiffStats(sessionId, stats) {
|
|
20990
|
+
if (this.fileDiffStats.has(sessionId)) return;
|
|
20991
|
+
this.fileDiffStats.set(sessionId, stats);
|
|
20992
|
+
}
|
|
20993
|
+
/**
|
|
20994
|
+
* Accumulate AI code change stats from a completed tool part's metadata.
|
|
20995
|
+
* The harmony-code plugin injects `metadata.aiCodeChange` for edit/write tools.
|
|
20996
|
+
*/
|
|
20997
|
+
accumulateAICodeChangeStats(sessionId, part) {
|
|
20998
|
+
if (part.state.status !== "completed") return;
|
|
20999
|
+
const meta3 = part.state.metadata ?? {};
|
|
21000
|
+
const aiCodeChange = meta3["aiCodeChange"];
|
|
21001
|
+
if (!aiCodeChange || aiCodeChange.additions === 0 && aiCodeChange.deletions === 0) return;
|
|
21002
|
+
const fileMap = this.aiCodeChangeStats.get(sessionId) ?? /* @__PURE__ */ new Map();
|
|
21003
|
+
const existing = fileMap.get(aiCodeChange.file) ?? { additions: 0, deletions: 0 };
|
|
21004
|
+
fileMap.set(aiCodeChange.file, {
|
|
21005
|
+
additions: existing.additions + aiCodeChange.additions,
|
|
21006
|
+
deletions: existing.deletions + aiCodeChange.deletions
|
|
21007
|
+
});
|
|
21008
|
+
if (!this.aiCodeChangeStats.has(sessionId)) {
|
|
21009
|
+
this.aiCodeChangeStats.set(sessionId, fileMap);
|
|
21010
|
+
}
|
|
21011
|
+
}
|
|
21012
|
+
getAICodeChangeStats(sessionId) {
|
|
21013
|
+
const fileMap = this.aiCodeChangeStats.get(sessionId);
|
|
21014
|
+
if (!fileMap || fileMap.size === 0) return void 0;
|
|
21015
|
+
let additions = 0;
|
|
21016
|
+
let deletions = 0;
|
|
21017
|
+
for (const stats of fileMap.values()) {
|
|
21018
|
+
additions += stats.additions;
|
|
21019
|
+
deletions += stats.deletions;
|
|
21020
|
+
}
|
|
21021
|
+
return { additions, deletions, files: fileMap.size };
|
|
21022
|
+
}
|
|
21061
21023
|
};
|
|
21062
21024
|
function simpleHash(str) {
|
|
21063
21025
|
let hash2 = 0;
|
|
@@ -21068,6 +21030,49 @@ function simpleHash(str) {
|
|
|
21068
21030
|
return hash2.toString(36);
|
|
21069
21031
|
}
|
|
21070
21032
|
|
|
21033
|
+
// src/mcp-manager.ts
|
|
21034
|
+
var THROW = { throwOnError: true };
|
|
21035
|
+
var McpManager = class {
|
|
21036
|
+
constructor(sdk) {
|
|
21037
|
+
this.sdk = sdk;
|
|
21038
|
+
}
|
|
21039
|
+
async status(directory) {
|
|
21040
|
+
ocCall("mcp.status", { directory });
|
|
21041
|
+
const result = await this.sdk.mcp.status({ directory });
|
|
21042
|
+
return result.data ?? {};
|
|
21043
|
+
}
|
|
21044
|
+
async add(directory, name, config2) {
|
|
21045
|
+
ocCall("mcp.add", { directory, name });
|
|
21046
|
+
const result = await this.sdk.mcp.add({ directory, name, config: config2 }, THROW);
|
|
21047
|
+
return result.data ?? {};
|
|
21048
|
+
}
|
|
21049
|
+
async connect(directory, name) {
|
|
21050
|
+
ocCall("mcp.connect", { directory, name });
|
|
21051
|
+
await this.sdk.mcp.connect({ name, directory }, THROW);
|
|
21052
|
+
return { success: true };
|
|
21053
|
+
}
|
|
21054
|
+
async disconnect(directory, name) {
|
|
21055
|
+
ocCall("mcp.disconnect", { directory, name });
|
|
21056
|
+
await this.sdk.mcp.disconnect({ name, directory }, THROW);
|
|
21057
|
+
return { success: true };
|
|
21058
|
+
}
|
|
21059
|
+
async startAuth(directory, name) {
|
|
21060
|
+
ocCall("mcp.auth.start", { directory, name });
|
|
21061
|
+
const result = await this.sdk.mcp.auth.start({ name, directory }, THROW);
|
|
21062
|
+
return result.data ?? {};
|
|
21063
|
+
}
|
|
21064
|
+
async callbackAuth(directory, name, code) {
|
|
21065
|
+
ocCall("mcp.auth.callback", { directory, name });
|
|
21066
|
+
const result = await this.sdk.mcp.auth.callback({ name, directory, code }, THROW);
|
|
21067
|
+
return result.data ?? {};
|
|
21068
|
+
}
|
|
21069
|
+
async removeAuth(directory, name) {
|
|
21070
|
+
ocCall("mcp.auth.remove", { directory, name });
|
|
21071
|
+
await this.sdk.mcp.auth.remove({ name, directory }, THROW);
|
|
21072
|
+
return { success: true };
|
|
21073
|
+
}
|
|
21074
|
+
};
|
|
21075
|
+
|
|
21071
21076
|
// src/auth-provider.ts
|
|
21072
21077
|
var AuthProviderManager = class {
|
|
21073
21078
|
sdk;
|
|
@@ -21117,29 +21122,13 @@ var Agent = class {
|
|
|
21117
21122
|
sessionManager;
|
|
21118
21123
|
eventHandler;
|
|
21119
21124
|
authProvider;
|
|
21120
|
-
|
|
21121
|
-
* Wrapper around connection.sessionUpdate that logs what's sent to the ACP client.
|
|
21122
|
-
*/
|
|
21123
|
-
async sendToClient(params) {
|
|
21124
|
-
const updateType = params.update.sessionUpdate;
|
|
21125
|
-
if (updateType !== "agent_message_chunk" && updateType !== "agent_thought_chunk") {
|
|
21126
|
-
acpOut(`sessionUpdate.${updateType}`, {
|
|
21127
|
-
sessionId: params.sessionId.slice(0, 12),
|
|
21128
|
-
...updateType === "tool_call" || updateType === "tool_call_update" ? {
|
|
21129
|
-
toolCallId: params.update.toolCallId,
|
|
21130
|
-
tool: params.update.title,
|
|
21131
|
-
kind: params.update.kind,
|
|
21132
|
-
status: params.update.status
|
|
21133
|
-
} : {}
|
|
21134
|
-
});
|
|
21135
|
-
}
|
|
21136
|
-
return this.connection.sessionUpdate(params);
|
|
21137
|
-
}
|
|
21125
|
+
mcpManager;
|
|
21138
21126
|
constructor(config2) {
|
|
21139
21127
|
this.config = config2;
|
|
21140
21128
|
this.sdk = config2.sdk;
|
|
21141
21129
|
this.sessionManager = new SessionManager(config2.sdk);
|
|
21142
21130
|
this.authProvider = new AuthProviderManager(config2.sdk);
|
|
21131
|
+
this.mcpManager = new McpManager(config2.sdk);
|
|
21143
21132
|
}
|
|
21144
21133
|
init(connection) {
|
|
21145
21134
|
this.connection = connection;
|
|
@@ -21189,10 +21178,13 @@ var Agent = class {
|
|
|
21189
21178
|
// ─── Extension Methods ────────────────────────────────────────────
|
|
21190
21179
|
async extMethod(method, params) {
|
|
21191
21180
|
acpIn("extMethod", { method });
|
|
21192
|
-
if (
|
|
21193
|
-
|
|
21181
|
+
if (method.startsWith("provider/")) {
|
|
21182
|
+
return this.handleProviderMethod(method, params);
|
|
21194
21183
|
}
|
|
21195
|
-
|
|
21184
|
+
if (method.startsWith("mcp/")) {
|
|
21185
|
+
return this.handleMcpMethod(method, params);
|
|
21186
|
+
}
|
|
21187
|
+
throw new Error(`Unknown extMethod: ${method}`);
|
|
21196
21188
|
}
|
|
21197
21189
|
async handleProviderMethod(method, params) {
|
|
21198
21190
|
const sessionId = params.sessionId;
|
|
@@ -21248,6 +21240,67 @@ var Agent = class {
|
|
|
21248
21240
|
throw new Error(`Unknown provider extMethod: ${method}`);
|
|
21249
21241
|
}
|
|
21250
21242
|
}
|
|
21243
|
+
async handleMcpMethod(method, params) {
|
|
21244
|
+
const sessionId = params.sessionId;
|
|
21245
|
+
if (!sessionId) throw new Error("sessionId required");
|
|
21246
|
+
const directory = this.sessionManager.get(sessionId)?.cwd;
|
|
21247
|
+
if (!directory) throw new Error("Session not found");
|
|
21248
|
+
switch (method) {
|
|
21249
|
+
case "mcp/status": {
|
|
21250
|
+
const data = await this.mcpManager.status(directory);
|
|
21251
|
+
acpOut("extMethod.response", { method: "mcp/status" });
|
|
21252
|
+
return data;
|
|
21253
|
+
}
|
|
21254
|
+
case "mcp/add": {
|
|
21255
|
+
const name = params.name;
|
|
21256
|
+
const config2 = params.config;
|
|
21257
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21258
|
+
if (!config2) throw new Error("Missing required parameter: config");
|
|
21259
|
+
const data = await this.mcpManager.add(directory, name, config2);
|
|
21260
|
+
acpOut("extMethod.response", { method: "mcp/add", name });
|
|
21261
|
+
return data;
|
|
21262
|
+
}
|
|
21263
|
+
case "mcp/connect": {
|
|
21264
|
+
const name = params.name;
|
|
21265
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21266
|
+
const data = await this.mcpManager.connect(directory, name);
|
|
21267
|
+
acpOut("extMethod.response", { method: "mcp/connect", name });
|
|
21268
|
+
return data;
|
|
21269
|
+
}
|
|
21270
|
+
case "mcp/disconnect": {
|
|
21271
|
+
const name = params.name;
|
|
21272
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21273
|
+
const data = await this.mcpManager.disconnect(directory, name);
|
|
21274
|
+
acpOut("extMethod.response", { method: "mcp/disconnect", name });
|
|
21275
|
+
return data;
|
|
21276
|
+
}
|
|
21277
|
+
case "mcp/auth/start": {
|
|
21278
|
+
const name = params.name;
|
|
21279
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21280
|
+
const data = await this.mcpManager.startAuth(directory, name);
|
|
21281
|
+
acpOut("extMethod.response", { method: "mcp/auth/start", name });
|
|
21282
|
+
return data;
|
|
21283
|
+
}
|
|
21284
|
+
case "mcp/auth/callback": {
|
|
21285
|
+
const name = params.name;
|
|
21286
|
+
const code = params.code;
|
|
21287
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21288
|
+
if (!code) throw new Error("Missing required parameter: code");
|
|
21289
|
+
const data = await this.mcpManager.callbackAuth(directory, name, code);
|
|
21290
|
+
acpOut("extMethod.response", { method: "mcp/auth/callback", name });
|
|
21291
|
+
return data;
|
|
21292
|
+
}
|
|
21293
|
+
case "mcp/auth/remove": {
|
|
21294
|
+
const name = params.name;
|
|
21295
|
+
if (!name) throw new Error("Missing required parameter: name");
|
|
21296
|
+
const data = await this.mcpManager.removeAuth(directory, name);
|
|
21297
|
+
acpOut("extMethod.response", { method: "mcp/auth/remove", name });
|
|
21298
|
+
return data;
|
|
21299
|
+
}
|
|
21300
|
+
default:
|
|
21301
|
+
throw new Error(`Unknown MCP method: ${method}`);
|
|
21302
|
+
}
|
|
21303
|
+
}
|
|
21251
21304
|
async buildProviderMeta() {
|
|
21252
21305
|
try {
|
|
21253
21306
|
const data = await this.authProvider.listProviders(this.config.cwd);
|
|
@@ -21290,11 +21343,19 @@ var Agent = class {
|
|
|
21290
21343
|
}
|
|
21291
21344
|
}
|
|
21292
21345
|
async loadSession(params) {
|
|
21346
|
+
acpIn("loadSession", { sessionId: params.sessionId, cwd: params.cwd });
|
|
21293
21347
|
try {
|
|
21294
21348
|
const directory = params.cwd;
|
|
21295
21349
|
const sessionId = params.sessionId;
|
|
21296
21350
|
const model = await this.defaultModel(directory);
|
|
21297
|
-
await this.sessionManager.load(sessionId, params.cwd, params.mcpServers, model);
|
|
21351
|
+
const { session } = await this.sessionManager.load(sessionId, params.cwd, params.mcpServers, model);
|
|
21352
|
+
if (session.summary) {
|
|
21353
|
+
this.eventHandler.setFileDiffStats(sessionId, {
|
|
21354
|
+
additions: session.summary.additions,
|
|
21355
|
+
deletions: session.summary.deletions,
|
|
21356
|
+
files: session.summary.files
|
|
21357
|
+
});
|
|
21358
|
+
}
|
|
21298
21359
|
const result = await this.loadSessionMode({
|
|
21299
21360
|
cwd: directory,
|
|
21300
21361
|
mcpServers: params.mcpServers,
|
|
@@ -21330,6 +21391,7 @@ var Agent = class {
|
|
|
21330
21391
|
}
|
|
21331
21392
|
}
|
|
21332
21393
|
async listSessions(params) {
|
|
21394
|
+
acpIn("listSessions", { cwd: params.cwd, cursor: params.cursor });
|
|
21333
21395
|
const limit = 100;
|
|
21334
21396
|
const cursor = params.cursor ? Number(params.cursor) : void 0;
|
|
21335
21397
|
const sessions = await this.sdk.session.list({ directory: params.cwd ?? void 0, roots: true }).then((x) => x.data ?? []);
|
|
@@ -21346,9 +21408,11 @@ var Agent = class {
|
|
|
21346
21408
|
const next = filtered.length > limit && last ? String(last.time.updated) : void 0;
|
|
21347
21409
|
const response = { sessions: entries };
|
|
21348
21410
|
if (next) response.nextCursor = next;
|
|
21411
|
+
acpOut("listSessions.response", { count: entries.length, hasNext: !!next });
|
|
21349
21412
|
return response;
|
|
21350
21413
|
}
|
|
21351
21414
|
async unstable_forkSession(params) {
|
|
21415
|
+
acpIn("forkSession", { sessionId: params.sessionId, cwd: params.cwd });
|
|
21352
21416
|
try {
|
|
21353
21417
|
const directory = params.cwd;
|
|
21354
21418
|
const mcpServers = params.mcpServers ?? [];
|
|
@@ -21374,12 +21438,20 @@ var Agent = class {
|
|
|
21374
21438
|
}
|
|
21375
21439
|
}
|
|
21376
21440
|
async unstable_resumeSession(params) {
|
|
21441
|
+
acpIn("resumeSession", { sessionId: params.sessionId, cwd: params.cwd });
|
|
21377
21442
|
try {
|
|
21378
21443
|
const directory = params.cwd;
|
|
21379
21444
|
const sessionId = params.sessionId;
|
|
21380
21445
|
const mcpServers = params.mcpServers ?? [];
|
|
21381
21446
|
const model = await this.defaultModel(directory);
|
|
21382
|
-
await this.sessionManager.load(sessionId, directory, mcpServers, model);
|
|
21447
|
+
const { session } = await this.sessionManager.load(sessionId, directory, mcpServers, model);
|
|
21448
|
+
if (session.summary) {
|
|
21449
|
+
this.eventHandler.setFileDiffStats(sessionId, {
|
|
21450
|
+
additions: session.summary.additions,
|
|
21451
|
+
deletions: session.summary.deletions,
|
|
21452
|
+
files: session.summary.files
|
|
21453
|
+
});
|
|
21454
|
+
}
|
|
21383
21455
|
const result = await this.loadSessionMode({
|
|
21384
21456
|
cwd: directory,
|
|
21385
21457
|
mcpServers,
|
|
@@ -21419,6 +21491,16 @@ var Agent = class {
|
|
|
21419
21491
|
agent,
|
|
21420
21492
|
directory
|
|
21421
21493
|
});
|
|
21494
|
+
const rawErr = response2.error;
|
|
21495
|
+
ocCall("session.prompt.raw", {
|
|
21496
|
+
hasData: !!response2.data,
|
|
21497
|
+
hasError: !!rawErr,
|
|
21498
|
+
errorName: rawErr?.name,
|
|
21499
|
+
errorMessage: rawErr?.message,
|
|
21500
|
+
errorCode: rawErr?.code ?? rawErr?.cause?.code,
|
|
21501
|
+
errorCauseName: rawErr?.cause?.name,
|
|
21502
|
+
errorCauseMessage: rawErr?.cause?.message
|
|
21503
|
+
});
|
|
21422
21504
|
const msg2 = response2.data?.info;
|
|
21423
21505
|
ocCall("session.prompt.response", {
|
|
21424
21506
|
messageId: msg2?.id,
|
|
@@ -21505,6 +21587,7 @@ var Agent = class {
|
|
|
21505
21587
|
}
|
|
21506
21588
|
// ─── Configuration ────────────────────────────────────────────────
|
|
21507
21589
|
async setSessionMode(params) {
|
|
21590
|
+
acpIn("setSessionMode", { sessionId: params.sessionId, modeId: params.modeId });
|
|
21508
21591
|
const session = this.sessionManager.get(params.sessionId);
|
|
21509
21592
|
const agents = await this.sdk.app.agents({ directory: session.cwd }).then((x) => x.data ?? []).catch(() => []);
|
|
21510
21593
|
const availableModes = agents.filter((a) => a.mode !== "subagent" && !a.hidden);
|
|
@@ -21514,6 +21597,7 @@ var Agent = class {
|
|
|
21514
21597
|
this.sessionManager.setMode(params.sessionId, params.modeId);
|
|
21515
21598
|
}
|
|
21516
21599
|
async unstable_setSessionModel(params) {
|
|
21600
|
+
acpIn("setSessionModel", { sessionId: params.sessionId, modelId: params.modelId });
|
|
21517
21601
|
const session = this.sessionManager.get(params.sessionId);
|
|
21518
21602
|
const providers = await this.sdk.config.providers({ directory: session.cwd }).then((x) => x.data?.providers ?? []).catch(() => []);
|
|
21519
21603
|
const selection = parseModelSelection(params.modelId, providers);
|
|
@@ -21527,6 +21611,7 @@ var Agent = class {
|
|
|
21527
21611
|
};
|
|
21528
21612
|
}
|
|
21529
21613
|
async setSessionConfigOption(params) {
|
|
21614
|
+
acpIn("setSessionConfigOption", { sessionId: params.sessionId, configId: params.configId, value: params.value });
|
|
21530
21615
|
const session = this.sessionManager.get(params.sessionId);
|
|
21531
21616
|
const providers = await this.sdk.config.providers({ directory: session.cwd }).then((x) => x.data?.providers ?? []).catch(() => []);
|
|
21532
21617
|
const sortedProviders = [...providers].sort(
|
|
@@ -21612,7 +21697,7 @@ var Agent = class {
|
|
|
21612
21697
|
if (!childMessages?.length) return;
|
|
21613
21698
|
const title = `Subagent (${childSessionId.slice(0, 8)})`;
|
|
21614
21699
|
this.sessionManager.registerDiscovered(childSessionId, parentSessionId, title);
|
|
21615
|
-
await this.
|
|
21700
|
+
await sendToClient(this.connection, {
|
|
21616
21701
|
sessionId: childSessionId,
|
|
21617
21702
|
update: {
|
|
21618
21703
|
sessionUpdate: "session_info_update",
|
|
@@ -21716,7 +21801,7 @@ var Agent = class {
|
|
|
21716
21801
|
{}
|
|
21717
21802
|
)
|
|
21718
21803
|
};
|
|
21719
|
-
await this.
|
|
21804
|
+
await this.mcpManager.add(params.cwd, server.name, config2).catch(() => {
|
|
21720
21805
|
});
|
|
21721
21806
|
})
|
|
21722
21807
|
);
|
|
@@ -21729,7 +21814,7 @@ var Agent = class {
|
|
|
21729
21814
|
availableCommands.push({ name: "compact", description: "compact the session" });
|
|
21730
21815
|
}
|
|
21731
21816
|
setTimeout(() => {
|
|
21732
|
-
this.
|
|
21817
|
+
sendToClient(this.connection, {
|
|
21733
21818
|
sessionId: params.sessionId,
|
|
21734
21819
|
update: {
|
|
21735
21820
|
sessionUpdate: "available_commands_update",
|
|
@@ -21782,7 +21867,7 @@ var Agent = class {
|
|
|
21782
21867
|
await this.replayToolPart(sessionId, part);
|
|
21783
21868
|
} else if (part.type === "text" && part.text) {
|
|
21784
21869
|
const audience = part.synthetic ? ["assistant"] : part.ignored ? ["user"] : void 0;
|
|
21785
|
-
await this.
|
|
21870
|
+
await sendToClient(this.connection, {
|
|
21786
21871
|
sessionId,
|
|
21787
21872
|
update: {
|
|
21788
21873
|
sessionUpdate: messageChunk,
|
|
@@ -21800,7 +21885,7 @@ var Agent = class {
|
|
|
21800
21885
|
const filename = part.filename ?? "file";
|
|
21801
21886
|
const mime = part.mime || "application/octet-stream";
|
|
21802
21887
|
if (url2.startsWith("file://")) {
|
|
21803
|
-
await this.
|
|
21888
|
+
await sendToClient(this.connection, {
|
|
21804
21889
|
sessionId,
|
|
21805
21890
|
update: {
|
|
21806
21891
|
sessionUpdate: messageChunk,
|
|
@@ -21815,7 +21900,7 @@ var Agent = class {
|
|
|
21815
21900
|
const base64Data = base64Match?.[2] ?? "";
|
|
21816
21901
|
const effectiveMime = dataMime || mime;
|
|
21817
21902
|
if (effectiveMime.startsWith("image/")) {
|
|
21818
|
-
await this.
|
|
21903
|
+
await sendToClient(this.connection, {
|
|
21819
21904
|
sessionId,
|
|
21820
21905
|
update: {
|
|
21821
21906
|
sessionUpdate: messageChunk,
|
|
@@ -21837,7 +21922,7 @@ var Agent = class {
|
|
|
21837
21922
|
mimeType: effectiveMime,
|
|
21838
21923
|
text: Buffer.from(base64Data, "base64").toString("utf-8")
|
|
21839
21924
|
} : { uri: fileUri, mimeType: effectiveMime, blob: base64Data };
|
|
21840
|
-
await this.
|
|
21925
|
+
await sendToClient(this.connection, {
|
|
21841
21926
|
sessionId,
|
|
21842
21927
|
update: {
|
|
21843
21928
|
sessionUpdate: messageChunk,
|
|
@@ -21849,7 +21934,7 @@ var Agent = class {
|
|
|
21849
21934
|
}
|
|
21850
21935
|
}
|
|
21851
21936
|
} else if (part.type === "reasoning" && part.text) {
|
|
21852
|
-
await this.
|
|
21937
|
+
await sendToClient(this.connection, {
|
|
21853
21938
|
sessionId,
|
|
21854
21939
|
update: {
|
|
21855
21940
|
sessionUpdate: "agent_thought_chunk",
|
|
@@ -21862,7 +21947,7 @@ var Agent = class {
|
|
|
21862
21947
|
}
|
|
21863
21948
|
}
|
|
21864
21949
|
async replayToolPart(sessionId, part) {
|
|
21865
|
-
await this.
|
|
21950
|
+
await sendToClient(this.connection, {
|
|
21866
21951
|
sessionId,
|
|
21867
21952
|
update: {
|
|
21868
21953
|
sessionUpdate: "tool_call",
|
|
@@ -21887,12 +21972,13 @@ var Agent = class {
|
|
|
21887
21972
|
oldText: typeof input["oldString"] === "string" ? input["oldString"] : "",
|
|
21888
21973
|
newText: typeof input["newString"] === "string" ? input["newString"] : typeof input["content"] === "string" ? input["content"] : ""
|
|
21889
21974
|
});
|
|
21975
|
+
this.eventHandler.accumulateAICodeChangeStats(sessionId, part);
|
|
21890
21976
|
}
|
|
21891
21977
|
if (part.tool === "todowrite") {
|
|
21892
21978
|
try {
|
|
21893
21979
|
const todos = JSON.parse(part.state.output);
|
|
21894
21980
|
if (Array.isArray(todos)) {
|
|
21895
|
-
await this.
|
|
21981
|
+
await sendToClient(this.connection, {
|
|
21896
21982
|
sessionId,
|
|
21897
21983
|
update: {
|
|
21898
21984
|
sessionUpdate: "plan",
|
|
@@ -21908,7 +21994,7 @@ var Agent = class {
|
|
|
21908
21994
|
} catch {
|
|
21909
21995
|
}
|
|
21910
21996
|
}
|
|
21911
|
-
await this.
|
|
21997
|
+
await sendToClient(this.connection, {
|
|
21912
21998
|
sessionId,
|
|
21913
21999
|
update: {
|
|
21914
22000
|
sessionUpdate: "tool_call_update",
|
|
@@ -21925,7 +22011,7 @@ var Agent = class {
|
|
|
21925
22011
|
break;
|
|
21926
22012
|
}
|
|
21927
22013
|
case "error":
|
|
21928
|
-
await this.
|
|
22014
|
+
await sendToClient(this.connection, {
|
|
21929
22015
|
sessionId,
|
|
21930
22016
|
update: {
|
|
21931
22017
|
sessionUpdate: "tool_call_update",
|
|
@@ -21965,24 +22051,76 @@ var Agent = class {
|
|
|
21965
22051
|
const provider = providers.find((p) => p.id === msg.providerID);
|
|
21966
22052
|
const model = provider?.models[msg.modelID];
|
|
21967
22053
|
const size = model?.limit?.context ?? 0;
|
|
21968
|
-
|
|
22054
|
+
const _meta = {};
|
|
22055
|
+
if (childCost > 0) {
|
|
22056
|
+
_meta.parentCost = totalCost;
|
|
22057
|
+
_meta.childCost = childCost;
|
|
22058
|
+
_meta.childSessionCount = children.length;
|
|
22059
|
+
}
|
|
22060
|
+
this.aggregateFileDiffStats(sessionId, children, _meta);
|
|
22061
|
+
this.aggregateAICodeChangeStats(sessionId, children, _meta);
|
|
22062
|
+
await sendToClient(this.connection, {
|
|
21969
22063
|
sessionId,
|
|
21970
22064
|
update: {
|
|
21971
22065
|
sessionUpdate: "usage_update",
|
|
21972
22066
|
used: msg.tokens.input + (msg.tokens.cache?.read ?? 0),
|
|
21973
22067
|
size,
|
|
21974
22068
|
cost: { amount: totalCost + childCost, currency: "USD" },
|
|
21975
|
-
...
|
|
21976
|
-
_meta: {
|
|
21977
|
-
parentCost: totalCost,
|
|
21978
|
-
childCost,
|
|
21979
|
-
childSessionCount: children.length
|
|
21980
|
-
}
|
|
21981
|
-
}
|
|
22069
|
+
...Object.keys(_meta).length > 0 && { _meta }
|
|
21982
22070
|
}
|
|
21983
22071
|
}).catch(() => {
|
|
21984
22072
|
});
|
|
21985
22073
|
}
|
|
22074
|
+
/**
|
|
22075
|
+
* Aggregate file diff stats (git-based) for parent + child sessions into _meta.
|
|
22076
|
+
* 主 session 的 session.diff 已包含子任务的变更数据,直接使用,不累加子 session。
|
|
22077
|
+
*/
|
|
22078
|
+
aggregateFileDiffStats(sessionId, children, _meta) {
|
|
22079
|
+
const stats = this.eventHandler.getFileDiffStats(sessionId);
|
|
22080
|
+
let childFileDiffStats;
|
|
22081
|
+
for (const childId of children) {
|
|
22082
|
+
const cs = this.eventHandler.getFileDiffStats(childId);
|
|
22083
|
+
if (cs) {
|
|
22084
|
+
childFileDiffStats = childFileDiffStats ? {
|
|
22085
|
+
additions: childFileDiffStats.additions + cs.additions,
|
|
22086
|
+
deletions: childFileDiffStats.deletions + cs.deletions,
|
|
22087
|
+
files: childFileDiffStats.files + cs.files
|
|
22088
|
+
} : { ...cs };
|
|
22089
|
+
}
|
|
22090
|
+
}
|
|
22091
|
+
const totalFileDiffStats = stats ?? childFileDiffStats;
|
|
22092
|
+
if (totalFileDiffStats) {
|
|
22093
|
+
_meta.fileDiffStats = totalFileDiffStats;
|
|
22094
|
+
}
|
|
22095
|
+
if (childFileDiffStats) {
|
|
22096
|
+
_meta.childFileDiffStats = childFileDiffStats;
|
|
22097
|
+
}
|
|
22098
|
+
}
|
|
22099
|
+
/**
|
|
22100
|
+
* Aggregate AI code change stats (tool-based) for parent + child sessions into _meta.
|
|
22101
|
+
*/
|
|
22102
|
+
aggregateAICodeChangeStats(sessionId, children, _meta) {
|
|
22103
|
+
const parentStats = this.eventHandler.getAICodeChangeStats(sessionId);
|
|
22104
|
+
let childAICodeChangeStats;
|
|
22105
|
+
for (const childId of children) {
|
|
22106
|
+
const cs = this.eventHandler.getAICodeChangeStats(childId);
|
|
22107
|
+
if (cs) {
|
|
22108
|
+
childAICodeChangeStats = childAICodeChangeStats ? {
|
|
22109
|
+
additions: childAICodeChangeStats.additions + cs.additions,
|
|
22110
|
+
deletions: childAICodeChangeStats.deletions + cs.deletions,
|
|
22111
|
+
files: childAICodeChangeStats.files + cs.files
|
|
22112
|
+
} : { ...cs };
|
|
22113
|
+
}
|
|
22114
|
+
}
|
|
22115
|
+
const totalAICodeChangeStats = parentStats ? childAICodeChangeStats ? {
|
|
22116
|
+
additions: parentStats.additions + childAICodeChangeStats.additions,
|
|
22117
|
+
deletions: parentStats.deletions + childAICodeChangeStats.deletions,
|
|
22118
|
+
files: parentStats.files + childAICodeChangeStats.files
|
|
22119
|
+
} : parentStats : childAICodeChangeStats;
|
|
22120
|
+
if (totalAICodeChangeStats) {
|
|
22121
|
+
_meta.aiCodeChange = totalAICodeChangeStats;
|
|
22122
|
+
}
|
|
22123
|
+
}
|
|
21986
22124
|
convertPromptParts(parts) {
|
|
21987
22125
|
const result = [];
|
|
21988
22126
|
for (const part of parts) {
|
|
@@ -22045,33 +22183,13 @@ var Agent = class {
|
|
|
22045
22183
|
return { name, args: rest.join(" ").trim() };
|
|
22046
22184
|
}
|
|
22047
22185
|
/**
|
|
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.
|
|
22186
|
+
* Fetch the complete message content after prompt returns and log tool usage.
|
|
22051
22187
|
*/
|
|
22052
22188
|
async logMessageContent(sessionId, messageId, directory) {
|
|
22053
22189
|
if (!messageId) return;
|
|
22054
22190
|
const message = await this.sdk.session.message({ sessionID: sessionId, messageID: messageId, directory }).then((x) => x.data).catch(() => void 0);
|
|
22055
22191
|
if (!message) return;
|
|
22056
22192
|
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
22193
|
if (part.type === "tool") {
|
|
22076
22194
|
ocCall("session.prompt.tool", {
|
|
22077
22195
|
sessionId: sessionId.slice(0, 12),
|
|
@@ -22128,6 +22246,91 @@ function parseModelSelection(modelId, providers) {
|
|
|
22128
22246
|
}
|
|
22129
22247
|
|
|
22130
22248
|
// src/index.ts
|
|
22249
|
+
var nativeFetch = (input, init) => {
|
|
22250
|
+
return new Promise((resolve, reject) => {
|
|
22251
|
+
let url2;
|
|
22252
|
+
let requestMethod;
|
|
22253
|
+
let requestHeaders;
|
|
22254
|
+
let requestBody = null;
|
|
22255
|
+
if (typeof input === "string") {
|
|
22256
|
+
url2 = new URL(input);
|
|
22257
|
+
} else if (input instanceof URL) {
|
|
22258
|
+
url2 = input;
|
|
22259
|
+
} else {
|
|
22260
|
+
url2 = new URL(input.url);
|
|
22261
|
+
requestMethod = input.method;
|
|
22262
|
+
requestHeaders = input.headers;
|
|
22263
|
+
requestBody = input.body;
|
|
22264
|
+
}
|
|
22265
|
+
const method = (init?.method ?? requestMethod ?? "GET").toUpperCase();
|
|
22266
|
+
const mergedHeaders = new Headers(init?.headers ?? requestHeaders ?? void 0);
|
|
22267
|
+
const headerObj = {};
|
|
22268
|
+
mergedHeaders.forEach((value, key) => {
|
|
22269
|
+
headerObj[key] = value;
|
|
22270
|
+
});
|
|
22271
|
+
const body = init?.body !== void 0 ? init.body : requestBody;
|
|
22272
|
+
const signal = init?.signal;
|
|
22273
|
+
if (signal?.aborted) {
|
|
22274
|
+
return reject(
|
|
22275
|
+
signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted.", "AbortError")
|
|
22276
|
+
);
|
|
22277
|
+
}
|
|
22278
|
+
const mod = url2.protocol === "https:" ? https : http;
|
|
22279
|
+
const req = mod.request(
|
|
22280
|
+
url2,
|
|
22281
|
+
{
|
|
22282
|
+
method,
|
|
22283
|
+
headers: headerObj
|
|
22284
|
+
// Note: do NOT set `timeout` here. Leaving it unset means
|
|
22285
|
+
// node:http will not apply any inactivity-based abort.
|
|
22286
|
+
},
|
|
22287
|
+
(res) => {
|
|
22288
|
+
const respHeaders = new Headers();
|
|
22289
|
+
for (const [k, v] of Object.entries(res.headers)) {
|
|
22290
|
+
if (v == null) continue;
|
|
22291
|
+
if (Array.isArray(v)) {
|
|
22292
|
+
for (const item of v) respHeaders.append(k, item);
|
|
22293
|
+
} else {
|
|
22294
|
+
respHeaders.set(k, String(v));
|
|
22295
|
+
}
|
|
22296
|
+
}
|
|
22297
|
+
const webStream = import_node_stream.Readable.toWeb(res);
|
|
22298
|
+
resolve(
|
|
22299
|
+
new Response(webStream, {
|
|
22300
|
+
status: res.statusCode ?? 200,
|
|
22301
|
+
statusText: res.statusMessage ?? "",
|
|
22302
|
+
headers: respHeaders
|
|
22303
|
+
})
|
|
22304
|
+
);
|
|
22305
|
+
}
|
|
22306
|
+
);
|
|
22307
|
+
req.on("error", reject);
|
|
22308
|
+
if (signal) {
|
|
22309
|
+
signal.addEventListener(
|
|
22310
|
+
"abort",
|
|
22311
|
+
() => {
|
|
22312
|
+
req.destroy(
|
|
22313
|
+
signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted.", "AbortError")
|
|
22314
|
+
);
|
|
22315
|
+
},
|
|
22316
|
+
{ once: true }
|
|
22317
|
+
);
|
|
22318
|
+
}
|
|
22319
|
+
if (body == null) {
|
|
22320
|
+
req.end();
|
|
22321
|
+
} else if (typeof body === "string") {
|
|
22322
|
+
req.end(body);
|
|
22323
|
+
} else if (body instanceof Uint8Array || Buffer.isBuffer(body)) {
|
|
22324
|
+
req.end(body);
|
|
22325
|
+
} else if (body instanceof URLSearchParams) {
|
|
22326
|
+
req.end(body.toString());
|
|
22327
|
+
} else if (typeof body.pipeTo === "function") {
|
|
22328
|
+
import_node_stream.Readable.fromWeb(body).pipe(req);
|
|
22329
|
+
} else {
|
|
22330
|
+
req.end(String(body));
|
|
22331
|
+
}
|
|
22332
|
+
});
|
|
22333
|
+
};
|
|
22131
22334
|
function parseArgs(args) {
|
|
22132
22335
|
let server = "http://localhost:4096";
|
|
22133
22336
|
let cwd = process.cwd();
|
|
@@ -22167,7 +22370,10 @@ async function main() {
|
|
|
22167
22370
|
initLogger();
|
|
22168
22371
|
if (log) setLogEnabled(true);
|
|
22169
22372
|
sysLog("starting", { server, cwd });
|
|
22170
|
-
const sdk = createOpencodeClient({
|
|
22373
|
+
const sdk = createOpencodeClient({
|
|
22374
|
+
baseUrl: server,
|
|
22375
|
+
fetch: nativeFetch
|
|
22376
|
+
});
|
|
22171
22377
|
try {
|
|
22172
22378
|
await sdk.global.health();
|
|
22173
22379
|
sysLog("server_connected");
|