@dudousxd/nestjs-agent-opencode 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/durable/index.cjs +283 -14
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +3 -2
- package/dist/durable/index.d.ts +3 -2
- package/dist/durable/index.js +284 -15
- package/dist/durable/index.js.map +1 -1
- package/dist/{engine-Cp5uysaN.d.cts → engine-BX6MLzxs.d.cts} +109 -10
- package/dist/{engine-Cp5uysaN.d.ts → engine-BX6MLzxs.d.ts} +109 -10
- package/dist/index.cjs +279 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +279 -13
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-
|
|
2
|
-
export { I as InMemoryOpenCodeSessionStore, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as
|
|
1
|
+
import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-BX6MLzxs.cjs';
|
|
2
|
+
export { I as InMemoryOpenCodeSessionStore, L as LOST_ANSWER_ERROR, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as OpenCodeMessage, n as OpenCodeMessagePart, o as OpenCodeModelRef, p as OpenCodePermissionRequest, q as OpenCodePermissionRule, r as OpenCodeReplyMismatchError, s as OpenCodeRunResult, t as OpenCodeServer, u as OpenCodeSessionCreate, v as OpenCodeSessionRef, w as OpenCodeSessionStore, x as OpenCodeToolCall, y as OpenCodeToolRefusedError, z as OpenCodeToolsClaims, A as OpenCodeToolsOptions, B as OpenCodeToolsTokens, C as OpenCodeTurn, D as OpenCodeTurnContext, E as OpenCodeUsage, P as PendingAsk, S as SessionHandle, F as StepCost, G as StepModel, T as TurnOutcome, H as TurnProgress, J as addUsage, K as emptyUsage, N as keyValueOpenCodeSessionStore, Q as openCode, R as openCodeControllers, U as openCodeProviders, V as usageOf } from './engine-BX6MLzxs.cjs';
|
|
3
3
|
import { ElicitationRequest, ElicitationQuestion, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply } from '@dudousxd/nestjs-agent-core';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import '@dudousxd/nestjs-agent';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-
|
|
2
|
-
export { I as InMemoryOpenCodeSessionStore, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as
|
|
1
|
+
import { d as OpenCodeClient, e as OpenCodeEvent, f as OpenCodeForm, g as OpenCodeFormValue, h as OpenCodeFormField, i as OpenCodeMcpEndpoint, O as OpenCodeTurns, a as OpenCodeEngineSettings } from './engine-BX6MLzxs.js';
|
|
2
|
+
export { I as InMemoryOpenCodeSessionStore, L as LOST_ANSWER_ERROR, M as Milestone, j as OpenCodeAmendment, k as OpenCodeCallContext, c as OpenCodeEngineOptions, b as OpenCodeHost, l as OpenCodeKeyValue, m as OpenCodeMessage, n as OpenCodeMessagePart, o as OpenCodeModelRef, p as OpenCodePermissionRequest, q as OpenCodePermissionRule, r as OpenCodeReplyMismatchError, s as OpenCodeRunResult, t as OpenCodeServer, u as OpenCodeSessionCreate, v as OpenCodeSessionRef, w as OpenCodeSessionStore, x as OpenCodeToolCall, y as OpenCodeToolRefusedError, z as OpenCodeToolsClaims, A as OpenCodeToolsOptions, B as OpenCodeToolsTokens, C as OpenCodeTurn, D as OpenCodeTurnContext, E as OpenCodeUsage, P as PendingAsk, S as SessionHandle, F as StepCost, G as StepModel, T as TurnOutcome, H as TurnProgress, J as addUsage, K as emptyUsage, N as keyValueOpenCodeSessionStore, Q as openCode, R as openCodeControllers, U as openCodeProviders, V as usageOf } from './engine-BX6MLzxs.js';
|
|
3
3
|
import { ElicitationRequest, ElicitationQuestion, AgentRunner, AgentRunInput, AgentRunStartOptions, HumanReply } from '@dudousxd/nestjs-agent-core';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import '@dudousxd/nestjs-agent';
|
package/dist/index.js
CHANGED
|
@@ -143,7 +143,8 @@ OpenCodeMcpController = _ts_decorate([
|
|
|
143
143
|
|
|
144
144
|
// src/mcp.ts
|
|
145
145
|
import { createHash, createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
146
|
-
import { REMEMBER_TOOL_DESCRIPTION, REMEMBER_TOOL_NAME, ToolNotFoundError, rememberInputSchema, schemaExtensionOf } from "@dudousxd/nestjs-agent-core";
|
|
146
|
+
import { GenuiRunRecorder, REMEMBER_TOOL_DESCRIPTION, REMEMBER_TOOL_NAME, ToolNotFoundError, rememberInputSchema, schemaExtensionOf } from "@dudousxd/nestjs-agent-core";
|
|
147
|
+
import { turnChannel } from "@dudousxd/nestjs-agent-core/genui";
|
|
147
148
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
148
149
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
149
150
|
import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-schema-compat.js";
|
|
@@ -274,6 +275,9 @@ function subUsage(a, b) {
|
|
|
274
275
|
};
|
|
275
276
|
}
|
|
276
277
|
__name(subUsage, "subUsage");
|
|
278
|
+
var LOST_ANSWER_ERROR = "The answer was lost while the agent restarted; ask again.";
|
|
279
|
+
var RECOVER_PAGES = 10;
|
|
280
|
+
var RECOVER_PAGE_SIZE = 100;
|
|
277
281
|
var ASK_TOOL_NAME = "ask";
|
|
278
282
|
var FAILED_STATUSES = /* @__PURE__ */ new Set([
|
|
279
283
|
"error",
|
|
@@ -362,6 +366,14 @@ var OpenCodeTurn = class {
|
|
|
362
366
|
wroteMessage = false;
|
|
363
367
|
ended = false;
|
|
364
368
|
stepError;
|
|
369
|
+
/** What this turn object took from the session's assistant messages (see {@link TurnProgress}). */
|
|
370
|
+
took = {
|
|
371
|
+
text: /* @__PURE__ */ new Map(),
|
|
372
|
+
reasoning: /* @__PURE__ */ new Map(),
|
|
373
|
+
called: /* @__PURE__ */ new Set(),
|
|
374
|
+
settled: /* @__PURE__ */ new Set(),
|
|
375
|
+
steps: /* @__PURE__ */ new Set()
|
|
376
|
+
};
|
|
365
377
|
constructor(a) {
|
|
366
378
|
this.a = a;
|
|
367
379
|
this.model = modelOfLabel(a.input.model);
|
|
@@ -379,6 +391,30 @@ var OpenCodeTurn = class {
|
|
|
379
391
|
tokens: info.tokens
|
|
380
392
|
};
|
|
381
393
|
}
|
|
394
|
+
/** What the turn took so far: carried from before this turn object, plus what it took. */
|
|
395
|
+
progress() {
|
|
396
|
+
const before = this.a.progress;
|
|
397
|
+
const merged = /* @__PURE__ */ __name((carried, own) => {
|
|
398
|
+
const out = {
|
|
399
|
+
...carried ?? {}
|
|
400
|
+
};
|
|
401
|
+
for (const [id, n] of own) out[id] = (out[id] ?? 0) + n;
|
|
402
|
+
return out;
|
|
403
|
+
}, "merged");
|
|
404
|
+
const union = /* @__PURE__ */ __name((carried, own) => [
|
|
405
|
+
.../* @__PURE__ */ new Set([
|
|
406
|
+
...carried ?? [],
|
|
407
|
+
...own
|
|
408
|
+
])
|
|
409
|
+
], "union");
|
|
410
|
+
return {
|
|
411
|
+
text: merged(before?.text, this.took.text),
|
|
412
|
+
reasoning: merged(before?.reasoning, this.took.reasoning),
|
|
413
|
+
called: union(before?.called, this.took.called),
|
|
414
|
+
settled: union(before?.settled, this.took.settled),
|
|
415
|
+
steps: union(before?.steps, this.took.steps)
|
|
416
|
+
};
|
|
417
|
+
}
|
|
382
418
|
/** What the run spent so far: carried from before this turn object, plus what it saw. */
|
|
383
419
|
totalUsage() {
|
|
384
420
|
return addUsage(this.a.carried, this.spent);
|
|
@@ -469,7 +505,11 @@ var OpenCodeTurn = class {
|
|
|
469
505
|
this.reported = {
|
|
470
506
|
...this.spent
|
|
471
507
|
};
|
|
472
|
-
const milestone = {
|
|
508
|
+
const milestone = reached.kind === "ask" ? {
|
|
509
|
+
...reached,
|
|
510
|
+
usage,
|
|
511
|
+
progress: this.progress()
|
|
512
|
+
} : {
|
|
473
513
|
...reached,
|
|
474
514
|
usage
|
|
475
515
|
};
|
|
@@ -524,6 +564,7 @@ var OpenCodeTurn = class {
|
|
|
524
564
|
innerSettled: /* @__PURE__ */ new Set()
|
|
525
565
|
};
|
|
526
566
|
this.tools.set(id, state);
|
|
567
|
+
this.took.called.add(id);
|
|
527
568
|
this.segment.calls.push({
|
|
528
569
|
id,
|
|
529
570
|
name,
|
|
@@ -651,6 +692,7 @@ var OpenCodeTurn = class {
|
|
|
651
692
|
case "session.text.delta": {
|
|
652
693
|
const delta = typeof d.delta === "string" ? d.delta : "";
|
|
653
694
|
if (!delta) return;
|
|
695
|
+
this.count(this.took.text, d.assistantMessageID, delta.length);
|
|
654
696
|
this.endReasoning();
|
|
655
697
|
if (!this.sawText && !delta.trim()) return;
|
|
656
698
|
await this.step();
|
|
@@ -672,6 +714,7 @@ ${delta}` : delta;
|
|
|
672
714
|
case "session.reasoning.delta": {
|
|
673
715
|
const delta = typeof d.delta === "string" ? d.delta : "";
|
|
674
716
|
if (!delta) return;
|
|
717
|
+
this.count(this.took.reasoning, d.assistantMessageID, delta.length);
|
|
675
718
|
this.reasoningSince ??= Date.now();
|
|
676
719
|
await this.step();
|
|
677
720
|
this.segment.reasoning += delta;
|
|
@@ -726,6 +769,7 @@ ${delta}` : delta;
|
|
|
726
769
|
}
|
|
727
770
|
case "session.step.ended":
|
|
728
771
|
this.endReasoning();
|
|
772
|
+
if (typeof d.assistantMessageID === "string") this.took.steps.add(d.assistantMessageID);
|
|
729
773
|
await this.stepEnded(d);
|
|
730
774
|
return;
|
|
731
775
|
case "session.step.failed":
|
|
@@ -766,10 +810,16 @@ ${delta}` : delta;
|
|
|
766
810
|
return;
|
|
767
811
|
}
|
|
768
812
|
}
|
|
813
|
+
/** Characters taken from an assistant message's text or reasoning. */
|
|
814
|
+
count(into, messageId, n) {
|
|
815
|
+
if (typeof messageId !== "string") return;
|
|
816
|
+
into.set(messageId, (into.get(messageId) ?? 0) + n);
|
|
817
|
+
}
|
|
769
818
|
async settleTool(id, ok, error = "failed") {
|
|
770
819
|
const state = this.tool(id);
|
|
771
820
|
if (state.settled) return;
|
|
772
821
|
state.settled = true;
|
|
822
|
+
this.took.settled.add(id);
|
|
773
823
|
if (ok) {
|
|
774
824
|
this.segment.results.push({
|
|
775
825
|
id,
|
|
@@ -1319,9 +1369,171 @@ ${delta}` : delta;
|
|
|
1319
1369
|
cost: cost < 1e-9 ? 0 : cost
|
|
1320
1370
|
}, this.compacted ? "history_summary" : "title");
|
|
1321
1371
|
}
|
|
1322
|
-
|
|
1372
|
+
/**
|
|
1373
|
+
* The session's messages since this turn's prompt, oldest first: everything after its newest user
|
|
1374
|
+
* message. `undefined` when they cannot be read (no `message.list`, no prompt within reach); empty
|
|
1375
|
+
* when the newest prompt is another run's (this turn's never reached the session).
|
|
1376
|
+
*/
|
|
1377
|
+
async messagesSincePrompt() {
|
|
1378
|
+
const messages = this.a.client.message;
|
|
1379
|
+
if (messages === void 0) return void 0;
|
|
1380
|
+
const since = [];
|
|
1381
|
+
let cursor;
|
|
1382
|
+
for (let page = 0; page < RECOVER_PAGES; page += 1) {
|
|
1383
|
+
const listed = await messages.list({
|
|
1384
|
+
sessionID: this.a.sessionId,
|
|
1385
|
+
order: "desc",
|
|
1386
|
+
limit: RECOVER_PAGE_SIZE,
|
|
1387
|
+
...cursor !== void 0 ? {
|
|
1388
|
+
cursor
|
|
1389
|
+
} : {}
|
|
1390
|
+
});
|
|
1391
|
+
for (const message of listed.data) {
|
|
1392
|
+
if (message.type === "user") {
|
|
1393
|
+
const runId = message.metadata?.runId;
|
|
1394
|
+
if (typeof runId === "string" && runId !== this.a.runId) return [];
|
|
1395
|
+
return since.reverse();
|
|
1396
|
+
}
|
|
1397
|
+
since.push(message);
|
|
1398
|
+
}
|
|
1399
|
+
const next = listed.cursor?.next;
|
|
1400
|
+
if (typeof next !== "string" || next.length === 0 || listed.data.length === 0) break;
|
|
1401
|
+
cursor = next;
|
|
1402
|
+
}
|
|
1403
|
+
return void 0;
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* A turn rebuilt after a restart heard only what the session said once it re-attached: what it
|
|
1407
|
+
* said in between — read back from the session's messages since the prompt, less what an earlier
|
|
1408
|
+
* process already took ({@link TurnProgress}) and what this one heard — is taken now, as its
|
|
1409
|
+
* events would have been: text, reasoning, tool calls and results, each step's usage. A session
|
|
1410
|
+
* that left nothing for the turn fails it ({@link LOST_ANSWER_ERROR}) rather than reading as an
|
|
1411
|
+
* empty answer.
|
|
1412
|
+
*/
|
|
1413
|
+
async recover(outcome) {
|
|
1414
|
+
const messages = await this.messagesSincePrompt().catch((error) => {
|
|
1415
|
+
this.a.logger.warn(`run ${this.a.runId}: ${errorText(error, "could not read the session messages")}`);
|
|
1416
|
+
return void 0;
|
|
1417
|
+
});
|
|
1418
|
+
const before = this.a.progress;
|
|
1419
|
+
const text = [];
|
|
1420
|
+
let reasoning = "";
|
|
1421
|
+
let lastError;
|
|
1422
|
+
for (const message of messages ?? []) {
|
|
1423
|
+
if (message.type !== "assistant") continue;
|
|
1424
|
+
const id = message.id;
|
|
1425
|
+
const parts = message.content ?? [];
|
|
1426
|
+
text.push(...missed(parts, "text", before?.text[id] ?? 0, this.took.text.get(id) ?? 0));
|
|
1427
|
+
reasoning += missed(parts, "reasoning", before?.reasoning[id] ?? 0, this.took.reasoning.get(id) ?? 0).map((piece) => piece.text).join("");
|
|
1428
|
+
for (const part of parts) if (part.type === "tool") await this.missedTool(part);
|
|
1429
|
+
if (message.tokens !== void 0 && !before?.steps.includes(id) && !this.took.steps.has(id)) {
|
|
1430
|
+
this.took.steps.add(id);
|
|
1431
|
+
const model = message.model;
|
|
1432
|
+
if (typeof model?.id === "string" && typeof model.providerID === "string") this.model = {
|
|
1433
|
+
providerID: model.providerID,
|
|
1434
|
+
id: model.id
|
|
1435
|
+
};
|
|
1436
|
+
await this.spend({
|
|
1437
|
+
tokens: message.tokens,
|
|
1438
|
+
cost: message.cost
|
|
1439
|
+
}, "chat");
|
|
1440
|
+
}
|
|
1441
|
+
if (message.error?.message) lastError = message.error.message;
|
|
1442
|
+
}
|
|
1443
|
+
const recovered = text.map((piece) => piece.text).join("\n\n").trimStart();
|
|
1444
|
+
if (recovered.trim()) {
|
|
1445
|
+
await this.step();
|
|
1446
|
+
const joint = this.segment.text && !text.at(-1)?.continues ? "\n\n" : "";
|
|
1447
|
+
this.segment.text = `${recovered}${joint}${this.segment.text}`;
|
|
1448
|
+
this.sawText = true;
|
|
1449
|
+
await this.write({
|
|
1450
|
+
kind: "text",
|
|
1451
|
+
text: recovered
|
|
1452
|
+
});
|
|
1453
|
+
}
|
|
1454
|
+
if (reasoning) {
|
|
1455
|
+
await this.step();
|
|
1456
|
+
this.segment.reasoning = `${reasoning}${this.segment.reasoning}`;
|
|
1457
|
+
await this.write({
|
|
1458
|
+
kind: "reasoning",
|
|
1459
|
+
text: reasoning
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
if (outcome.status !== "succeeded") return outcome;
|
|
1463
|
+
const idle = (messages ?? []).filter((m) => m.type === "idle").at(-1);
|
|
1464
|
+
if (idle?.outcome === "failed") return {
|
|
1465
|
+
status: "failed",
|
|
1466
|
+
error: lastError ?? this.stepError ?? "the execution failed"
|
|
1467
|
+
};
|
|
1468
|
+
if (idle?.outcome === "interrupted") return {
|
|
1469
|
+
status: "interrupted"
|
|
1470
|
+
};
|
|
1471
|
+
const s = this.segment;
|
|
1472
|
+
const said = s.text || s.reasoning || s.calls.length > 0 || s.ui.length > 0;
|
|
1473
|
+
const progress = this.progress();
|
|
1474
|
+
const tookAny = Object.keys(progress.text).length > 0 || progress.called.length > 0 || progress.steps.length > 0;
|
|
1475
|
+
if (!said && !this.wroteMessage && !tookAny) return {
|
|
1476
|
+
status: "failed",
|
|
1477
|
+
error: LOST_ANSWER_ERROR
|
|
1478
|
+
};
|
|
1479
|
+
return outcome;
|
|
1480
|
+
}
|
|
1481
|
+
/** A tool part of a message the turn did not hear: its call, its result, whichever it lacks. */
|
|
1482
|
+
async missedTool(part) {
|
|
1483
|
+
const id = part.id;
|
|
1484
|
+
const name = part.name ?? "tool";
|
|
1485
|
+
if (id === void 0 || HIDDEN_TOOLS.has(name)) return;
|
|
1486
|
+
const state = part.state ?? {};
|
|
1487
|
+
const before = this.a.progress;
|
|
1488
|
+
if (!before?.called.includes(id) && !this.tools.has(id)) {
|
|
1489
|
+
await this.step();
|
|
1490
|
+
this.setInput(id, state.input, name);
|
|
1491
|
+
await this.makeAvailable(id);
|
|
1492
|
+
}
|
|
1493
|
+
const ok = state.status === "completed";
|
|
1494
|
+
if (!ok && state.status !== "error" || before?.settled.includes(id)) return;
|
|
1495
|
+
const error = errorText(state.error, "tool failed");
|
|
1496
|
+
if (this.tools.has(id)) {
|
|
1497
|
+
await this.settleTool(id, ok, error);
|
|
1498
|
+
return;
|
|
1499
|
+
}
|
|
1500
|
+
this.took.settled.add(id);
|
|
1501
|
+
const result = ok ? {
|
|
1502
|
+
id,
|
|
1503
|
+
name,
|
|
1504
|
+
output: {
|
|
1505
|
+
ok: true
|
|
1506
|
+
}
|
|
1507
|
+
} : {
|
|
1508
|
+
id,
|
|
1509
|
+
name,
|
|
1510
|
+
output: {
|
|
1511
|
+
ok: false
|
|
1512
|
+
},
|
|
1513
|
+
error
|
|
1514
|
+
};
|
|
1515
|
+
const messageId = await this.messageOf(id);
|
|
1516
|
+
if (messageId !== void 0) await this.addResult(messageId, result);
|
|
1517
|
+
else this.segment.results.push(result);
|
|
1518
|
+
await this.write(ok ? {
|
|
1519
|
+
kind: "tool-output",
|
|
1520
|
+
id,
|
|
1521
|
+
output: {
|
|
1522
|
+
ok: true
|
|
1523
|
+
}
|
|
1524
|
+
} : {
|
|
1525
|
+
kind: "tool-output-error",
|
|
1526
|
+
id,
|
|
1527
|
+
error
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
async end(ended) {
|
|
1323
1531
|
if (this.ended) return;
|
|
1324
1532
|
this.ended = true;
|
|
1533
|
+
const outcome = this.a.resumed === true ? await this.recover(ended).catch((error) => {
|
|
1534
|
+
this.a.logger.warn(`run ${this.a.runId}: ${errorText(error, "could not recover the turn")}`);
|
|
1535
|
+
return ended;
|
|
1536
|
+
}) : ended;
|
|
1325
1537
|
await this.unreported().catch((error) => this.a.logger.warn(`run ${this.a.runId}: ${errorText(error, "could not read the session usage")}`));
|
|
1326
1538
|
if (outcome.status !== "failed" || this.segment.text || this.segment.calls.length > 0 || this.segment.ui.length > 0) {
|
|
1327
1539
|
await this.flush(true);
|
|
@@ -1338,6 +1550,26 @@ ${delta}` : delta;
|
|
|
1338
1550
|
});
|
|
1339
1551
|
}
|
|
1340
1552
|
};
|
|
1553
|
+
function missed(parts, type, head, tail) {
|
|
1554
|
+
const texts = parts.flatMap((p) => p.type === type && typeof p.text === "string" ? [
|
|
1555
|
+
p.text
|
|
1556
|
+
] : []);
|
|
1557
|
+
const total = texts.reduce((n, t) => n + t.length, 0);
|
|
1558
|
+
const end = total - tail;
|
|
1559
|
+
const out = [];
|
|
1560
|
+
let at = 0;
|
|
1561
|
+
for (const t of texts) {
|
|
1562
|
+
const from = Math.max(head, at);
|
|
1563
|
+
const to = Math.min(end, at + t.length);
|
|
1564
|
+
if (to > from) out.push({
|
|
1565
|
+
text: t.slice(from - at, to - at),
|
|
1566
|
+
continues: to < at + t.length
|
|
1567
|
+
});
|
|
1568
|
+
at += t.length;
|
|
1569
|
+
}
|
|
1570
|
+
return out;
|
|
1571
|
+
}
|
|
1572
|
+
__name(missed, "missed");
|
|
1341
1573
|
function modelOfLabel(label) {
|
|
1342
1574
|
const slash = label?.indexOf("/") ?? -1;
|
|
1343
1575
|
if (label === void 0 || slash <= 0 || slash === label.length - 1) return void 0;
|
|
@@ -1464,6 +1696,8 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1464
1696
|
}
|
|
1465
1697
|
turns;
|
|
1466
1698
|
tokens;
|
|
1699
|
+
/** Genui telemetry of the UI calls OpenCode makes (its code mode calls tools here, not in the loop). */
|
|
1700
|
+
genui = new GenuiRunRecorder();
|
|
1467
1701
|
constructor(turns, tokens) {
|
|
1468
1702
|
this.turns = turns;
|
|
1469
1703
|
this.tokens = tokens;
|
|
@@ -1595,7 +1829,7 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1595
1829
|
if (spec.kind === "action" && !await this.turns.spendApproval(turn.runId, name, turn.input)) {
|
|
1596
1830
|
throw new OpenCodeToolRefusedError(`"${name}" is an action and nobody approved this call; ask for the permission first.`);
|
|
1597
1831
|
}
|
|
1598
|
-
|
|
1832
|
+
const invoke = /* @__PURE__ */ __name(() => registry.invoke(name, args ?? {}, {
|
|
1599
1833
|
actor,
|
|
1600
1834
|
threadId: turn.ctx.threadId,
|
|
1601
1835
|
runId: turn.runId,
|
|
@@ -1612,7 +1846,24 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1612
1846
|
} : {}
|
|
1613
1847
|
}, this.turns.rolesPolicyFor(turn.input), allowed !== void 0 ? {
|
|
1614
1848
|
allowedTools: allowed
|
|
1615
|
-
} : {});
|
|
1849
|
+
} : {}), "invoke");
|
|
1850
|
+
return this.genui.track({
|
|
1851
|
+
store: this.turns.store,
|
|
1852
|
+
marker: spec.genui,
|
|
1853
|
+
runId: turn.runId,
|
|
1854
|
+
threadId: turn.ctx.threadId,
|
|
1855
|
+
actorRef: actor.id,
|
|
1856
|
+
...turn.input.agentName !== void 0 ? {
|
|
1857
|
+
agentName: turn.input.agentName
|
|
1858
|
+
} : {},
|
|
1859
|
+
channel: turnChannel(turn.input.pageContext),
|
|
1860
|
+
modelId: turn.input.model ?? "opencode",
|
|
1861
|
+
call: {
|
|
1862
|
+
id: `mcp:${turn.runId}:${requestId}`,
|
|
1863
|
+
name,
|
|
1864
|
+
input: args ?? {}
|
|
1865
|
+
}
|
|
1866
|
+
}, invoke);
|
|
1616
1867
|
}
|
|
1617
1868
|
/** `remember`: one fact, at the actor's own scope only — as the loop serves it. */
|
|
1618
1869
|
async remember(turn, args) {
|
|
@@ -2464,7 +2715,11 @@ ${body}
|
|
|
2464
2715
|
text: prompt.text,
|
|
2465
2716
|
...prompt.files?.length ? {
|
|
2466
2717
|
files: prompt.files
|
|
2467
|
-
} : {}
|
|
2718
|
+
} : {},
|
|
2719
|
+
// Marks the prompt as this run's: a turn resumed after a restart reads what came after it.
|
|
2720
|
+
metadata: {
|
|
2721
|
+
runId
|
|
2722
|
+
}
|
|
2468
2723
|
});
|
|
2469
2724
|
} catch (error) {
|
|
2470
2725
|
live.turn.fail(`OpenCode refused the message: ${error.message}`);
|
|
@@ -2477,9 +2732,11 @@ ${body}
|
|
|
2477
2732
|
*
|
|
2478
2733
|
* `spent` is what the run spent before (the sum of the `usage` of its earlier milestones): a turn
|
|
2479
2734
|
* rebuilt here starts from it, so the answer it persists carries the whole run's usage.
|
|
2735
|
+
* `progress` is what the turn took before (its last `ask` milestone's): a turn rebuilt here reads
|
|
2736
|
+
* back from the session's messages, when the execution ends, what nobody heard after that.
|
|
2480
2737
|
*/
|
|
2481
|
-
async observe(runId, input, handle, spent) {
|
|
2482
|
-
const live = await this.ensureLive(runId, input, handle, true, spent);
|
|
2738
|
+
async observe(runId, input, handle, spent, progress) {
|
|
2739
|
+
const live = await this.ensureLive(runId, input, handle, true, spent, progress);
|
|
2483
2740
|
const { turn, client } = live;
|
|
2484
2741
|
if (turn.hasMilestone()) return this.reached(runId, input, handle, await turn.next());
|
|
2485
2742
|
let done = false;
|
|
@@ -2518,12 +2775,12 @@ ${body}
|
|
|
2518
2775
|
* request is gone with it: a new session is opened, told what the person decided, and prompted
|
|
2519
2776
|
* to go on.
|
|
2520
2777
|
*/
|
|
2521
|
-
async reply(runId, input, handle, ask, reply, spent) {
|
|
2778
|
+
async reply(runId, input, handle, ask, reply, spent, progress) {
|
|
2522
2779
|
const server = await this.host.server(input.actor);
|
|
2523
2780
|
if (server.key !== handle.serverKey || server.bootId !== handle.bootId) {
|
|
2524
2781
|
return this.continueAfterRestart(runId, input, server, ask, reply, spent);
|
|
2525
2782
|
}
|
|
2526
|
-
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
2783
|
+
const live = await this.ensureLive(runId, input, handle, false, spent, progress);
|
|
2527
2784
|
await live.turn.decide(ask, reply);
|
|
2528
2785
|
return handle;
|
|
2529
2786
|
}
|
|
@@ -2541,7 +2798,10 @@ ${body}
|
|
|
2541
2798
|
live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
|
|
2542
2799
|
await live.client.session.prompt({
|
|
2543
2800
|
sessionID: handle.sessionId,
|
|
2544
|
-
text: note
|
|
2801
|
+
text: note,
|
|
2802
|
+
metadata: {
|
|
2803
|
+
runId
|
|
2804
|
+
}
|
|
2545
2805
|
});
|
|
2546
2806
|
return handle;
|
|
2547
2807
|
}
|
|
@@ -2565,9 +2825,10 @@ ${body}
|
|
|
2565
2825
|
sessionID: known.sessionId
|
|
2566
2826
|
});
|
|
2567
2827
|
}
|
|
2568
|
-
async ensureLive(runId, input, handle, catchUp, spent) {
|
|
2828
|
+
async ensureLive(runId, input, handle, catchUp, spent, progress) {
|
|
2569
2829
|
const current = this.live.get(runId);
|
|
2570
2830
|
if (current !== void 0 && current.handle.sessionId === handle.sessionId) return current;
|
|
2831
|
+
const resumed = catchUp && current === void 0;
|
|
2571
2832
|
current?.stop();
|
|
2572
2833
|
const server = await this.host.server(input.actor);
|
|
2573
2834
|
const deps = this.deps.forAgent(input.agentName);
|
|
@@ -2598,6 +2859,10 @@ ${body}
|
|
|
2598
2859
|
...spent !== void 0 ? {
|
|
2599
2860
|
carried: spent
|
|
2600
2861
|
} : {},
|
|
2862
|
+
...progress !== void 0 ? {
|
|
2863
|
+
progress
|
|
2864
|
+
} : {},
|
|
2865
|
+
resumed,
|
|
2601
2866
|
logger: this.logger,
|
|
2602
2867
|
onGranted: /* @__PURE__ */ __name((action, id) => {
|
|
2603
2868
|
const grants = this.grants.get(runId) ?? [];
|
|
@@ -2617,7 +2882,7 @@ ${body}
|
|
|
2617
2882
|
stop
|
|
2618
2883
|
};
|
|
2619
2884
|
this.live.set(runId, live);
|
|
2620
|
-
if (
|
|
2885
|
+
if (resumed) await turn.catchUp().catch(() => void 0);
|
|
2621
2886
|
return live;
|
|
2622
2887
|
}
|
|
2623
2888
|
/** Stop following a run in this process. */
|
|
@@ -3159,6 +3424,7 @@ __name(openCodeProviders, "openCodeProviders");
|
|
|
3159
3424
|
export {
|
|
3160
3425
|
FormAnswerError,
|
|
3161
3426
|
InMemoryOpenCodeSessionStore,
|
|
3427
|
+
LOST_ANSWER_ERROR,
|
|
3162
3428
|
OPENCODE_HOST,
|
|
3163
3429
|
OPENCODE_MCP_ENDPOINT,
|
|
3164
3430
|
OPENCODE_OPTIONS,
|