@dudousxd/nestjs-agent-opencode 0.5.0 → 1.1.0
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 +89 -1
- package/dist/durable/index.cjs +673 -97
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +11 -5
- package/dist/durable/index.d.ts +11 -5
- package/dist/durable/index.js +615 -39
- package/dist/durable/index.js.map +1 -1
- package/dist/{engine-BX6MLzxs.d.cts → engine-yFh3eD8j.d.cts} +241 -7
- package/dist/{engine-BX6MLzxs.d.ts → engine-yFh3eD8j.d.ts} +241 -7
- package/dist/index.cjs +653 -82
- 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 +596 -26
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -39,6 +39,7 @@ __export(index_exports, {
|
|
|
39
39
|
OpenCodeToolsTokens: () => OpenCodeToolsTokens,
|
|
40
40
|
OpenCodeTurn: () => OpenCodeTurn,
|
|
41
41
|
OpenCodeTurns: () => OpenCodeTurns,
|
|
42
|
+
STOPPED_ERROR_CODE: () => STOPPED_ERROR_CODE,
|
|
42
43
|
addUsage: () => addUsage,
|
|
43
44
|
emptyUsage: () => emptyUsage,
|
|
44
45
|
keyValueOpenCodeSessionStore: () => keyValueOpenCodeSessionStore,
|
|
@@ -89,6 +90,7 @@ function keyValueOpenCodeSessionStore(kv, prefix = "aviary:opencode:session:") {
|
|
|
89
90
|
__name(keyValueOpenCodeSessionStore, "keyValueOpenCodeSessionStore");
|
|
90
91
|
|
|
91
92
|
// src/mcp.controller.ts
|
|
93
|
+
var import_nestjs_agent_core = require("@dudousxd/nestjs-agent-core");
|
|
92
94
|
var import_common = require("@nestjs/common");
|
|
93
95
|
|
|
94
96
|
// src/tokens.ts
|
|
@@ -185,6 +187,7 @@ _ts_decorate([
|
|
|
185
187
|
_ts_metadata("design:returntype", void 0)
|
|
186
188
|
], OpenCodeMcpController.prototype, "delete", null);
|
|
187
189
|
OpenCodeMcpController = _ts_decorate([
|
|
190
|
+
(0, import_nestjs_agent_core.CanAnyone)(),
|
|
188
191
|
(0, import_common.Controller)("opencode/mcp"),
|
|
189
192
|
_ts_param(0, (0, import_common.Inject)(OPENCODE_MCP_ENDPOINT)),
|
|
190
193
|
_ts_metadata("design:type", Function),
|
|
@@ -195,7 +198,7 @@ OpenCodeMcpController = _ts_decorate([
|
|
|
195
198
|
|
|
196
199
|
// src/mcp.ts
|
|
197
200
|
var import_node_crypto = require("crypto");
|
|
198
|
-
var
|
|
201
|
+
var import_nestjs_agent_core4 = require("@dudousxd/nestjs-agent-core");
|
|
199
202
|
var import_genui = require("@dudousxd/nestjs-agent-core/genui");
|
|
200
203
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
201
204
|
var import_streamableHttp = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
|
|
@@ -203,11 +206,11 @@ var import_zod_json_schema_compat = require("@modelcontextprotocol/sdk/server/zo
|
|
|
203
206
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
204
207
|
|
|
205
208
|
// src/turn.ts
|
|
206
|
-
var
|
|
209
|
+
var import_nestjs_agent_core3 = require("@dudousxd/nestjs-agent-core");
|
|
207
210
|
var import_common2 = require("@nestjs/common");
|
|
208
211
|
|
|
209
212
|
// src/forms.ts
|
|
210
|
-
var
|
|
213
|
+
var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
|
|
211
214
|
function toQuestion(field) {
|
|
212
215
|
const required = field.required === true ? {
|
|
213
216
|
required: true
|
|
@@ -277,7 +280,7 @@ function toFormAnswer(form, answers = {}) {
|
|
|
277
280
|
for (const field of formFields(form)) {
|
|
278
281
|
const question = toQuestion(field);
|
|
279
282
|
const values = answers[field.key] ?? question.defaults ?? [];
|
|
280
|
-
const problem = (0,
|
|
283
|
+
const problem = (0, import_nestjs_agent_core2.validateElicitationAnswer)(question, values);
|
|
281
284
|
if (problem) throw new FormAnswerError(`answers["${field.key}"] ${problem}`);
|
|
282
285
|
const present = values.filter((v) => v !== "");
|
|
283
286
|
const [value] = present;
|
|
@@ -589,7 +592,7 @@ var OpenCodeTurn = class {
|
|
|
589
592
|
});
|
|
590
593
|
}
|
|
591
594
|
async write(event) {
|
|
592
|
-
await this.a.writer.write((0,
|
|
595
|
+
await this.a.writer.write((0, import_nestjs_agent_core3.encodeStreamEvent)(event));
|
|
593
596
|
}
|
|
594
597
|
async step() {
|
|
595
598
|
if (this.stepOpen) return;
|
|
@@ -1123,7 +1126,7 @@ ${delta}` : delta;
|
|
|
1123
1126
|
const ttlMs = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) && requirement.ttlMs > 0 ? Math.floor(requirement.ttlMs) : void 0;
|
|
1124
1127
|
const expiresAt = ttlMs !== void 0 ? new Date(Date.now() + ttlMs).toISOString() : void 0;
|
|
1125
1128
|
const tool = this.a.approvalPresentation?.(action);
|
|
1126
|
-
const confirmation = tool !== void 0 ? (0,
|
|
1129
|
+
const confirmation = tool !== void 0 ? (0, import_nestjs_agent_core3.resolveToolConfirmation)(tool.name, args, tool.presentation) : void 0;
|
|
1127
1130
|
this.segment.calls.push({
|
|
1128
1131
|
id: req.id,
|
|
1129
1132
|
name: action,
|
|
@@ -1350,7 +1353,7 @@ ${delta}` : delta;
|
|
|
1350
1353
|
answer: toFormAnswer(form, answer.answers)
|
|
1351
1354
|
});
|
|
1352
1355
|
}
|
|
1353
|
-
const result = (0,
|
|
1356
|
+
const result = (0, import_nestjs_agent_core3.settleElicitation)(request, answer);
|
|
1354
1357
|
await this.a.store.updateToolCall({
|
|
1355
1358
|
toolCallId: form.id,
|
|
1356
1359
|
status: result.skipped ? "rejected" : "executed",
|
|
@@ -1705,7 +1708,7 @@ function isZodSchema(schema) {
|
|
|
1705
1708
|
}
|
|
1706
1709
|
__name(isZodSchema, "isZodSchema");
|
|
1707
1710
|
function toInputSchema(schema) {
|
|
1708
|
-
const extension = (0,
|
|
1711
|
+
const extension = (0, import_nestjs_agent_core4.schemaExtensionOf)(schema);
|
|
1709
1712
|
if (extension !== void 0) {
|
|
1710
1713
|
const base = toInputSchema(extension.base);
|
|
1711
1714
|
const properties = {
|
|
@@ -1749,7 +1752,7 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1749
1752
|
turns;
|
|
1750
1753
|
tokens;
|
|
1751
1754
|
/** Genui telemetry of the UI calls OpenCode makes (its code mode calls tools here, not in the loop). */
|
|
1752
|
-
genui = new
|
|
1755
|
+
genui = new import_nestjs_agent_core4.GenuiRunRecorder();
|
|
1753
1756
|
constructor(turns, tokens) {
|
|
1754
1757
|
this.turns = turns;
|
|
1755
1758
|
this.tokens = tokens;
|
|
@@ -1830,9 +1833,9 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1830
1833
|
}));
|
|
1831
1834
|
if (this.turns.memoryWritable()) {
|
|
1832
1835
|
tools.push({
|
|
1833
|
-
name:
|
|
1834
|
-
description:
|
|
1835
|
-
inputSchema: toInputSchema(
|
|
1836
|
+
name: import_nestjs_agent_core4.REMEMBER_TOOL_NAME,
|
|
1837
|
+
description: import_nestjs_agent_core4.REMEMBER_TOOL_DESCRIPTION,
|
|
1838
|
+
inputSchema: toInputSchema(import_nestjs_agent_core4.rememberInputSchema),
|
|
1836
1839
|
annotations: {
|
|
1837
1840
|
readOnlyHint: false
|
|
1838
1841
|
}
|
|
@@ -1849,7 +1852,7 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1849
1852
|
if (turn === void 0) {
|
|
1850
1853
|
throw new OpenCodeToolRefusedError("This endpoint serves OpenCode turns: no turn of yours is running on this session.");
|
|
1851
1854
|
}
|
|
1852
|
-
const output = name ===
|
|
1855
|
+
const output = name === import_nestjs_agent_core4.REMEMBER_TOOL_NAME && this.turns.memoryWritable() ? await this.remember(turn, args) : await this.invoke(turn, actor, name, args, String(extra.requestId ?? ""));
|
|
1853
1856
|
return {
|
|
1854
1857
|
content: [
|
|
1855
1858
|
{
|
|
@@ -1875,9 +1878,9 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1875
1878
|
async invoke(turn, actor, name, args, requestId) {
|
|
1876
1879
|
const { registry } = this.turns;
|
|
1877
1880
|
const spec = registry.spec(name);
|
|
1878
|
-
if (spec === void 0 || LOOP_SERVED_KINDS.has(spec.kind)) throw new
|
|
1881
|
+
if (spec === void 0 || LOOP_SERVED_KINDS.has(spec.kind)) throw new import_nestjs_agent_core4.ToolNotFoundError(name);
|
|
1879
1882
|
const allowed = this.turns.allowedTools(turn.input);
|
|
1880
|
-
if (allowed !== void 0 && !allowed.includes(name)) throw new
|
|
1883
|
+
if (allowed !== void 0 && !allowed.includes(name)) throw new import_nestjs_agent_core4.ToolNotFoundError(name);
|
|
1881
1884
|
if (spec.kind === "action" && !await this.turns.spendApproval(turn.runId, name, turn.input)) {
|
|
1882
1885
|
throw new OpenCodeToolRefusedError(`"${name}" is an action and nobody approved this call; ask for the permission first.`);
|
|
1883
1886
|
}
|
|
@@ -1919,7 +1922,7 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1919
1922
|
}
|
|
1920
1923
|
/** `remember`: one fact, at the actor's own scope only — as the loop serves it. */
|
|
1921
1924
|
async remember(turn, args) {
|
|
1922
|
-
const parsed = await
|
|
1925
|
+
const parsed = await import_nestjs_agent_core4.rememberInputSchema["~standard"].validate(args ?? {});
|
|
1923
1926
|
if (parsed.issues !== void 0) {
|
|
1924
1927
|
throw new OpenCodeToolRefusedError(`remember: ${parsed.issues.map((issue) => issue.message).join("; ")}`);
|
|
1925
1928
|
}
|
|
@@ -1928,13 +1931,13 @@ var OpenCodeMcpEndpoint2 = class {
|
|
|
1928
1931
|
};
|
|
1929
1932
|
|
|
1930
1933
|
// src/runner.ts
|
|
1931
|
-
var
|
|
1934
|
+
var import_nestjs_agent_core6 = require("@dudousxd/nestjs-agent-core");
|
|
1932
1935
|
var import_common5 = require("@nestjs/common");
|
|
1933
1936
|
|
|
1934
1937
|
// src/turns.ts
|
|
1935
1938
|
var import_node_crypto2 = require("crypto");
|
|
1936
1939
|
var import_nestjs_agent = require("@dudousxd/nestjs-agent");
|
|
1937
|
-
var
|
|
1940
|
+
var import_nestjs_agent_core5 = require("@dudousxd/nestjs-agent-core");
|
|
1938
1941
|
var import_common4 = require("@nestjs/common");
|
|
1939
1942
|
|
|
1940
1943
|
// src/event-hub.ts
|
|
@@ -2049,6 +2052,37 @@ var OpenCodeEventHub = class _OpenCodeEventHub {
|
|
|
2049
2052
|
}
|
|
2050
2053
|
};
|
|
2051
2054
|
|
|
2055
|
+
// src/turn-limits.ts
|
|
2056
|
+
var STOPPED_ERROR_CODE = "opencode_stopped";
|
|
2057
|
+
function stopErrorText(stop) {
|
|
2058
|
+
switch (stop.reason) {
|
|
2059
|
+
case "steps":
|
|
2060
|
+
return `The turn was stopped after ${stop.counts.steps - 1} steps, its limit.`;
|
|
2061
|
+
case "compactions":
|
|
2062
|
+
return `The turn was stopped after ${stop.counts.compactions - 1} compactions, its limit.`;
|
|
2063
|
+
case "wall-clock":
|
|
2064
|
+
return "The turn was stopped: it ran longer than allowed.";
|
|
2065
|
+
default:
|
|
2066
|
+
return "The turn was stopped.";
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
__name(stopErrorText, "stopErrorText");
|
|
2070
|
+
function cleanLimits(limits) {
|
|
2071
|
+
const ok = /* @__PURE__ */ __name((n) => typeof n === "number" && Number.isFinite(n) && n >= 0, "ok");
|
|
2072
|
+
return {
|
|
2073
|
+
...ok(limits?.steps) ? {
|
|
2074
|
+
steps: limits.steps
|
|
2075
|
+
} : {},
|
|
2076
|
+
...ok(limits?.compactions) ? {
|
|
2077
|
+
compactions: limits.compactions
|
|
2078
|
+
} : {},
|
|
2079
|
+
...ok(limits?.wallClockMs) ? {
|
|
2080
|
+
wallClockMs: limits.wallClockMs
|
|
2081
|
+
} : {}
|
|
2082
|
+
};
|
|
2083
|
+
}
|
|
2084
|
+
__name(cleanLimits, "cleanLimits");
|
|
2085
|
+
|
|
2052
2086
|
// src/turns.ts
|
|
2053
2087
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
2054
2088
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
@@ -2078,6 +2112,16 @@ function _ts_param2(paramIndex, decorator) {
|
|
|
2078
2112
|
__name(_ts_param2, "_ts_param");
|
|
2079
2113
|
var DEFAULT_TURN_TIMEOUT_MS = 30 * 6e4;
|
|
2080
2114
|
var DEFAULT_TOOLS_TTL_MS = 7 * 24 * 60 * 6e4;
|
|
2115
|
+
var DEFAULT_HOOK_TIMEOUT_MS = 5e3;
|
|
2116
|
+
var PARKED_RECHECKS_MS = [
|
|
2117
|
+
250,
|
|
2118
|
+
1e3,
|
|
2119
|
+
3e3,
|
|
2120
|
+
1e4,
|
|
2121
|
+
3e4
|
|
2122
|
+
];
|
|
2123
|
+
var PARKED_READ_GAP_MS = 250;
|
|
2124
|
+
var RELEASED_USAGE_MS = 10 * 6e4;
|
|
2081
2125
|
var OpenCodeTurns = class {
|
|
2082
2126
|
static {
|
|
2083
2127
|
__name(this, "OpenCodeTurns");
|
|
@@ -2101,6 +2145,21 @@ var OpenCodeTurns = class {
|
|
|
2101
2145
|
spent = /* @__PURE__ */ new Set();
|
|
2102
2146
|
/** When the tools endpoint was last registered for a location (`server|boot|directory|actor`). */
|
|
2103
2147
|
toolsIssued = /* @__PURE__ */ new Map();
|
|
2148
|
+
/**
|
|
2149
|
+
* What a turn let go of here had spent, for a settle that follows here (a cancel in this process
|
|
2150
|
+
* that settles after the turn was released). Dropped when the run settles here, or after
|
|
2151
|
+
* {@link RELEASED_USAGE_MS} when it settles elsewhere, so no run is kept for good.
|
|
2152
|
+
*/
|
|
2153
|
+
releasedUsage = /* @__PURE__ */ new Map();
|
|
2154
|
+
keepReleasedUsage(runId, usage) {
|
|
2155
|
+
this.releasedUsage.set(runId, usage);
|
|
2156
|
+
setTimeout(() => {
|
|
2157
|
+
if (this.releasedUsage.get(runId) === usage) this.releasedUsage.delete(runId);
|
|
2158
|
+
}, RELEASED_USAGE_MS).unref?.();
|
|
2159
|
+
}
|
|
2160
|
+
/** The turns this process executes, and which turn owns each session (`server|session`). */
|
|
2161
|
+
watches = /* @__PURE__ */ new Map();
|
|
2162
|
+
owners = /* @__PURE__ */ new Map();
|
|
2104
2163
|
constructor(host, sessions, settings, store, sink, deps, registry, skills, memory, queue, toolsTokens2, pricingStore, agentOptions) {
|
|
2105
2164
|
this.host = host;
|
|
2106
2165
|
this.sessions = sessions;
|
|
@@ -2113,7 +2172,11 @@ var OpenCodeTurns = class {
|
|
|
2113
2172
|
this.memory = memory;
|
|
2114
2173
|
this.queue = queue;
|
|
2115
2174
|
this.toolsTokens = toolsTokens2;
|
|
2116
|
-
|
|
2175
|
+
const hookTimeoutMs = settings.hookTimeoutMs;
|
|
2176
|
+
if (hookTimeoutMs !== void 0 && !(Number.isFinite(hookTimeoutMs) && hookTimeoutMs > 0)) {
|
|
2177
|
+
throw new Error(`hookTimeoutMs must be a positive number of milliseconds, not ${hookTimeoutMs}`);
|
|
2178
|
+
}
|
|
2179
|
+
this.prices = new import_nestjs_agent_core5.ModelPriceResolver({
|
|
2117
2180
|
pricingStore,
|
|
2118
2181
|
catalog: agentOptions?.priceCatalog,
|
|
2119
2182
|
log: {
|
|
@@ -2133,10 +2196,10 @@ var OpenCodeTurns = class {
|
|
|
2133
2196
|
const tokens = usage.inputTokens + usage.outputTokens;
|
|
2134
2197
|
const reported = reportedCostUsd !== void 0 && (reportedCostUsd > 0 || tokens === 0);
|
|
2135
2198
|
if (reported && this.settings.cost !== "estimate") {
|
|
2136
|
-
return (0,
|
|
2199
|
+
return (0, import_nestjs_agent_core5.resolveUsageCost)(usage, reportedCostUsd, void 0);
|
|
2137
2200
|
}
|
|
2138
2201
|
const price = model !== void 0 && tokens > 0 ? await this.prices.priceFor(priceRef(model)) : void 0;
|
|
2139
|
-
return (0,
|
|
2202
|
+
return (0, import_nestjs_agent_core5.resolveUsageCost)(usage, price === void 0 ? reportedCostUsd : void 0, price);
|
|
2140
2203
|
}
|
|
2141
2204
|
/** The tools server's name in OpenCode. */
|
|
2142
2205
|
get toolsServer() {
|
|
@@ -2191,7 +2254,7 @@ var OpenCodeTurns = class {
|
|
|
2191
2254
|
const memory = this.memory;
|
|
2192
2255
|
const write = memory?.provider.write?.bind(memory.provider);
|
|
2193
2256
|
if (memory === void 0 || write === void 0) return "Memory is read-only here.";
|
|
2194
|
-
const max = memory.maxFactChars ??
|
|
2257
|
+
const max = memory.maxFactChars ?? import_nestjs_agent_core5.DEFAULT_MAX_FACT_CHARS;
|
|
2195
2258
|
if (input.fact.length > max) return `Not recorded: a fact is at most ${max} characters.`;
|
|
2196
2259
|
const actor = turn.input.actor;
|
|
2197
2260
|
const scopeCtx = {
|
|
@@ -2201,9 +2264,9 @@ var OpenCodeTurns = class {
|
|
|
2201
2264
|
agentName: turn.input.agentName
|
|
2202
2265
|
} : {}
|
|
2203
2266
|
};
|
|
2204
|
-
const scopes = await (memory.scopes ??
|
|
2205
|
-
const scope = (0,
|
|
2206
|
-
const verdict = (0,
|
|
2267
|
+
const scopes = await (memory.scopes ?? import_nestjs_agent_core5.defaultScopeResolver).resolve(scopeCtx);
|
|
2268
|
+
const scope = (0, import_nestjs_agent_core5.actorScope)(actor);
|
|
2269
|
+
const verdict = (0, import_nestjs_agent_core5.memoryWriteVerdict)({
|
|
2207
2270
|
scope,
|
|
2208
2271
|
scopes,
|
|
2209
2272
|
actor,
|
|
@@ -2285,7 +2348,7 @@ var OpenCodeTurns = class {
|
|
|
2285
2348
|
runId,
|
|
2286
2349
|
emitUi: uiEmitter(scope(runId), async (component) => {
|
|
2287
2350
|
const writer = await this.sink.open(runId);
|
|
2288
|
-
await writer.write((0,
|
|
2351
|
+
await writer.write((0, import_nestjs_agent_core5.encodeStreamEvent)({
|
|
2289
2352
|
kind: "ui",
|
|
2290
2353
|
...component
|
|
2291
2354
|
}));
|
|
@@ -2384,8 +2447,12 @@ var OpenCodeTurns = class {
|
|
|
2384
2447
|
}
|
|
2385
2448
|
}
|
|
2386
2449
|
onApplicationShutdown() {
|
|
2450
|
+
for (const runId of [
|
|
2451
|
+
...this.watches.keys()
|
|
2452
|
+
]) this.release(runId, "released");
|
|
2387
2453
|
for (const live of this.live.values()) live.stop();
|
|
2388
2454
|
this.live.clear();
|
|
2455
|
+
this.owners.clear();
|
|
2389
2456
|
this.events.close();
|
|
2390
2457
|
}
|
|
2391
2458
|
// ---- begin ---------------------------------------------------------------------------------
|
|
@@ -2395,7 +2462,8 @@ var OpenCodeTurns = class {
|
|
|
2395
2462
|
* durable `begin` re-run after a crash): the run's user message is written once.
|
|
2396
2463
|
*/
|
|
2397
2464
|
async begin(runId, input) {
|
|
2398
|
-
const
|
|
2465
|
+
const startedAt = this.watches.get(runId)?.startedAt ?? Date.now();
|
|
2466
|
+
const { earlier, rerun } = await this.prepareThread(runId, input);
|
|
2399
2467
|
const server = await this.host.server(input.actor);
|
|
2400
2468
|
const known = await this.sessions.get(input.threadId);
|
|
2401
2469
|
const reusable = known !== null && known.serverKey === server.key && known.bootId === server.bootId && (await this.host.reuse?.({
|
|
@@ -2441,7 +2509,12 @@ var OpenCodeTurns = class {
|
|
|
2441
2509
|
created
|
|
2442
2510
|
});
|
|
2443
2511
|
await this.refreshInstructions(runId, input, server.client, handle.sessionId);
|
|
2444
|
-
|
|
2512
|
+
const begun = this.limitsFor(input).wallClockMs !== void 0 ? {
|
|
2513
|
+
...handle,
|
|
2514
|
+
startedAt
|
|
2515
|
+
} : handle;
|
|
2516
|
+
await this.adopt(runId, input, begun, rerun, void 0, startedAt);
|
|
2517
|
+
return begun;
|
|
2445
2518
|
}
|
|
2446
2519
|
async createSession(runId, input, server, earlier, note) {
|
|
2447
2520
|
const { client } = server;
|
|
@@ -2511,10 +2584,16 @@ ${transcript}` : "",
|
|
|
2511
2584
|
}
|
|
2512
2585
|
const firstDropped = messages[lastUser + 1];
|
|
2513
2586
|
if (firstDropped !== void 0) await this.store.truncateFrom(input.threadId, firstDropped.id);
|
|
2514
|
-
return
|
|
2587
|
+
return {
|
|
2588
|
+
earlier: messages.slice(0, Math.max(lastUser, 0)),
|
|
2589
|
+
rerun: false
|
|
2590
|
+
};
|
|
2515
2591
|
}
|
|
2516
2592
|
const mine = messages.findIndex((m) => m.role === "user" && m.runId === runId);
|
|
2517
|
-
if (mine >= 0) return
|
|
2593
|
+
if (mine >= 0) return {
|
|
2594
|
+
earlier: messages.slice(0, mine),
|
|
2595
|
+
rerun: true
|
|
2596
|
+
};
|
|
2518
2597
|
await this.store.appendMessage({
|
|
2519
2598
|
threadId: input.threadId,
|
|
2520
2599
|
role: "user",
|
|
@@ -2530,7 +2609,10 @@ ${transcript}` : "",
|
|
|
2530
2609
|
if (thread !== null && (thread.title === "" || thread.title === "New chat")) {
|
|
2531
2610
|
await this.store.setTitle(input.threadId, titleOf(input.userText));
|
|
2532
2611
|
}
|
|
2533
|
-
return
|
|
2612
|
+
return {
|
|
2613
|
+
earlier: messages,
|
|
2614
|
+
rerun: false
|
|
2615
|
+
};
|
|
2534
2616
|
}
|
|
2535
2617
|
/** Rewind the session to before its last user message, so the regenerated answer replaces it. */
|
|
2536
2618
|
async revertLastExchange(client, sessionId) {
|
|
@@ -2613,12 +2695,12 @@ ${transcript}` : "",
|
|
|
2613
2695
|
const memory = this.memory;
|
|
2614
2696
|
if (memory === void 0) return void 0;
|
|
2615
2697
|
const ctx = this.scopeContext(input);
|
|
2616
|
-
const scopes = await (memory.scopes ??
|
|
2698
|
+
const scopes = await (memory.scopes ?? import_nestjs_agent_core5.defaultScopeResolver).resolve(ctx);
|
|
2617
2699
|
const records = await memory.provider.list({
|
|
2618
2700
|
scopes,
|
|
2619
2701
|
ctx
|
|
2620
2702
|
});
|
|
2621
|
-
const digest = (0,
|
|
2703
|
+
const digest = (0, import_nestjs_agent_core5.resolveMemoryDigest)({
|
|
2622
2704
|
records,
|
|
2623
2705
|
scopes,
|
|
2624
2706
|
...memory.maxMemories !== void 0 ? {
|
|
@@ -2626,7 +2708,7 @@ ${transcript}` : "",
|
|
|
2626
2708
|
} : {}
|
|
2627
2709
|
});
|
|
2628
2710
|
if (digest.entries.length === 0) return void 0;
|
|
2629
|
-
return (0,
|
|
2711
|
+
return (0, import_nestjs_agent_core5.buildMemoryBlock)({
|
|
2630
2712
|
entries: digest.entries,
|
|
2631
2713
|
writable: this.memoryWritable(),
|
|
2632
2714
|
partial: digest.omitted > 0
|
|
@@ -2640,12 +2722,12 @@ ${transcript}` : "",
|
|
|
2640
2722
|
const skills = this.skills;
|
|
2641
2723
|
if (skills === void 0 || client.file === void 0 || directory === void 0) return [];
|
|
2642
2724
|
const ctx = this.scopeContext(input);
|
|
2643
|
-
const scopes = await (skills.scopes ??
|
|
2725
|
+
const scopes = await (skills.scopes ?? import_nestjs_agent_core5.defaultScopeResolver).resolve(ctx);
|
|
2644
2726
|
const summaries = await skills.provider.list({
|
|
2645
2727
|
scopes,
|
|
2646
2728
|
ctx
|
|
2647
2729
|
});
|
|
2648
|
-
const { entries } = (0,
|
|
2730
|
+
const { entries } = (0, import_nestjs_agent_core5.resolveSkillCatalog)(summaries, scopes, skills.maxSkills);
|
|
2649
2731
|
const dir = this.settings.skillsDir ?? ".opencode/skills";
|
|
2650
2732
|
const written = [];
|
|
2651
2733
|
for (const entry of entries) {
|
|
@@ -2746,6 +2828,7 @@ ${body}
|
|
|
2746
2828
|
// ---- prompt / observe / reply --------------------------------------------------------------
|
|
2747
2829
|
/** Start listening to the session (so nothing it says is missed), then send the user message. */
|
|
2748
2830
|
async prompt(runId, input, handle, spent) {
|
|
2831
|
+
await this.adopt(runId, input, handle, true);
|
|
2749
2832
|
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
2750
2833
|
try {
|
|
2751
2834
|
const prompt = await this.host.promptFor?.({
|
|
@@ -2788,7 +2871,24 @@ ${body}
|
|
|
2788
2871
|
* back from the session's messages, when the execution ends, what nobody heard after that.
|
|
2789
2872
|
*/
|
|
2790
2873
|
async observe(runId, input, handle, spent, progress) {
|
|
2874
|
+
const watch = await this.adopt(runId, input, handle, true, progress?.counts);
|
|
2791
2875
|
const live = await this.ensureLive(runId, input, handle, true, spent, progress);
|
|
2876
|
+
watch.parkedOn = void 0;
|
|
2877
|
+
watch.parkedEnd = void 0;
|
|
2878
|
+
watch.series = false;
|
|
2879
|
+
watch.observing = true;
|
|
2880
|
+
const wall = this.watchClock(watch);
|
|
2881
|
+
try {
|
|
2882
|
+
if (watch.pending !== void 0) await this.requestStop(runId, watch.pending);
|
|
2883
|
+
const milestone = this.marked(watch, await this.observeLive(runId, input, handle, live));
|
|
2884
|
+
if (milestone.kind === "ask") watch.parkedOn = milestone.ask.id;
|
|
2885
|
+
return milestone;
|
|
2886
|
+
} finally {
|
|
2887
|
+
watch.observing = false;
|
|
2888
|
+
if (wall !== void 0) clearTimeout(wall);
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
async observeLive(runId, input, handle, live) {
|
|
2792
2892
|
const { turn, client } = live;
|
|
2793
2893
|
if (turn.hasMilestone()) return this.reached(runId, input, handle, await turn.next());
|
|
2794
2894
|
let done = false;
|
|
@@ -2828,19 +2928,64 @@ ${body}
|
|
|
2828
2928
|
* to go on.
|
|
2829
2929
|
*/
|
|
2830
2930
|
async reply(runId, input, handle, ask, reply, spent, progress) {
|
|
2931
|
+
const watch = await this.adopt(runId, input, handle, true, progress?.counts);
|
|
2932
|
+
watch.parkedOn = void 0;
|
|
2933
|
+
watch.parkedEnd = void 0;
|
|
2934
|
+
watch.series = false;
|
|
2935
|
+
const stopping = watch.pending !== void 0 || watch.stop !== void 0;
|
|
2936
|
+
const answer = stopping ? refusalOf(ask) : reply;
|
|
2831
2937
|
const server = await this.host.server(input.actor);
|
|
2832
2938
|
if (server.key !== handle.serverKey || server.bootId !== handle.bootId) {
|
|
2833
|
-
return this.
|
|
2939
|
+
if (stopping) return this.stopAfterRestart(watch, runId, input, handle, ask, answer, spent);
|
|
2940
|
+
return this.continueAfterRestart(runId, input, server, ask, answer, spent, handle.startedAt);
|
|
2834
2941
|
}
|
|
2835
2942
|
const live = await this.ensureLive(runId, input, handle, false, spent, progress);
|
|
2836
|
-
await live.turn.decide(ask,
|
|
2943
|
+
await live.turn.decide(ask, answer);
|
|
2944
|
+
return handle;
|
|
2945
|
+
}
|
|
2946
|
+
/**
|
|
2947
|
+
* A stopping turn whose OpenCode server restarted while it waited: there is no session left to
|
|
2948
|
+
* abort, and none is opened or prompted. The ask is settled as refused and the turn ends stopped.
|
|
2949
|
+
*/
|
|
2950
|
+
async stopAfterRestart(watch, runId, input, handle, ask, answer, spent) {
|
|
2951
|
+
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
2952
|
+
await live.turn.decide(ask, answer, {
|
|
2953
|
+
tellOpenCode: false
|
|
2954
|
+
});
|
|
2955
|
+
const reason = watch.pending ?? watch.stop?.reason ?? "stopped";
|
|
2956
|
+
if (watch.stop === void 0) {
|
|
2957
|
+
const stop = {
|
|
2958
|
+
reason,
|
|
2959
|
+
counts: this.counts(watch)
|
|
2960
|
+
};
|
|
2961
|
+
watch.stop = stop;
|
|
2962
|
+
this.hook("onStop", {
|
|
2963
|
+
turnId: runId,
|
|
2964
|
+
reason,
|
|
2965
|
+
counts: stop.counts
|
|
2966
|
+
});
|
|
2967
|
+
}
|
|
2968
|
+
watch.aborted = true;
|
|
2969
|
+
watch.pending = void 0;
|
|
2970
|
+
live.turn.handle({
|
|
2971
|
+
type: "session.execution.interrupted",
|
|
2972
|
+
data: {
|
|
2973
|
+
sessionID: handle.sessionId
|
|
2974
|
+
}
|
|
2975
|
+
});
|
|
2837
2976
|
return handle;
|
|
2838
2977
|
}
|
|
2839
|
-
async continueAfterRestart(runId, input, server, ask, reply, spent) {
|
|
2978
|
+
async continueAfterRestart(runId, input, server, ask, reply, spent, startedAt) {
|
|
2840
2979
|
const approved = "approved" in reply && reply.approved === true;
|
|
2841
2980
|
const note = ask.kind === "approval" ? approved ? `The user approved the action ${ask.action} you asked permission for. Continue the task.` : `The user did not approve the action ${ask.action}. Continue without it and explain.` : `The user answered your question: ${JSON.stringify("answers" in reply ? reply.answers : {})}. Continue.`;
|
|
2842
2981
|
const thread = await this.store.getThread(input.threadId);
|
|
2843
|
-
const
|
|
2982
|
+
const created = await this.createSession(runId, input, server, thread?.messages ?? [], note);
|
|
2983
|
+
const handle = {
|
|
2984
|
+
...created,
|
|
2985
|
+
...startedAt !== void 0 ? {
|
|
2986
|
+
startedAt
|
|
2987
|
+
} : {}
|
|
2988
|
+
};
|
|
2844
2989
|
await this.refreshInstructions(runId, input, server.client, handle.sessionId);
|
|
2845
2990
|
this.drop(runId);
|
|
2846
2991
|
const live = await this.ensureLive(runId, input, handle, false, spent);
|
|
@@ -2884,7 +3029,7 @@ ${body}
|
|
|
2884
3029
|
current?.stop();
|
|
2885
3030
|
const server = await this.host.server(input.actor);
|
|
2886
3031
|
const deps = this.deps.forAgent(input.agentName);
|
|
2887
|
-
const policy = deps.approvalPolicy ?? new
|
|
3032
|
+
const policy = deps.approvalPolicy ?? new import_nestjs_agent_core5.DefaultApprovalPolicy();
|
|
2888
3033
|
const writer = await this.sink.open(input.sinkRunId ?? runId);
|
|
2889
3034
|
const turn = new OpenCodeTurn({
|
|
2890
3035
|
runId,
|
|
@@ -2925,7 +3070,11 @@ ${body}
|
|
|
2925
3070
|
this.grants.set(runId, grants);
|
|
2926
3071
|
}, "onGranted")
|
|
2927
3072
|
});
|
|
2928
|
-
const
|
|
3073
|
+
const sessionId = handle.sessionId;
|
|
3074
|
+
const stop = await this.events.listen(server.key, server.client, sessionId, (event) => {
|
|
3075
|
+
turn.handle(event);
|
|
3076
|
+
this.sawEvent(runId, sessionId, event);
|
|
3077
|
+
});
|
|
2929
3078
|
const live = {
|
|
2930
3079
|
turn,
|
|
2931
3080
|
input,
|
|
@@ -2934,6 +3083,15 @@ ${body}
|
|
|
2934
3083
|
stop
|
|
2935
3084
|
};
|
|
2936
3085
|
this.live.set(runId, live);
|
|
3086
|
+
this.owners.set(ownerKey(handle), runId);
|
|
3087
|
+
const watch = this.watches.get(runId);
|
|
3088
|
+
if (watch !== void 0 && watch.handle.sessionId !== sessionId) {
|
|
3089
|
+
watch.handle = {
|
|
3090
|
+
...handle,
|
|
3091
|
+
startedAt: watch.startedAt
|
|
3092
|
+
};
|
|
3093
|
+
watch.ending = false;
|
|
3094
|
+
}
|
|
2937
3095
|
if (resumed) await turn.catchUp().catch(() => void 0);
|
|
2938
3096
|
return live;
|
|
2939
3097
|
}
|
|
@@ -2942,11 +3100,46 @@ ${body}
|
|
|
2942
3100
|
this.live.get(runId)?.stop();
|
|
2943
3101
|
this.live.delete(runId);
|
|
2944
3102
|
}
|
|
2945
|
-
/**
|
|
3103
|
+
/**
|
|
3104
|
+
* Stop following a run and drop the approvals it was granted: it is over. Returns the turn this
|
|
3105
|
+
* process executed, now settled (no stop reaches it any more), for its `onTurnEnd`.
|
|
3106
|
+
*/
|
|
2946
3107
|
forget(runId) {
|
|
2947
3108
|
this.drop(runId);
|
|
3109
|
+
this.releasedUsage.delete(runId);
|
|
2948
3110
|
for (const grant of this.grants.get(runId) ?? []) this.spent.delete(grant.id);
|
|
2949
3111
|
this.grants.delete(runId);
|
|
3112
|
+
const watch = this.watches.get(runId);
|
|
3113
|
+
if (watch === void 0) return void 0;
|
|
3114
|
+
watch.settled = true;
|
|
3115
|
+
this.watches.delete(runId);
|
|
3116
|
+
for (const [key, owner] of this.owners) if (owner === runId) this.owners.delete(key);
|
|
3117
|
+
return watch;
|
|
3118
|
+
}
|
|
3119
|
+
/**
|
|
3120
|
+
* A run that ended without settling here (cancelled while this process executed it; whoever
|
|
3121
|
+
* cancelled settled it): stop following it, and end the turn this process executed.
|
|
3122
|
+
*/
|
|
3123
|
+
async abandon(runId) {
|
|
3124
|
+
const watch = this.forget(runId);
|
|
3125
|
+
if (watch !== void 0) this.ended(watch, "interrupted", void 0);
|
|
3126
|
+
}
|
|
3127
|
+
/**
|
|
3128
|
+
* Let go of a turn this process executed without settling it here: it was cancelled while parked
|
|
3129
|
+
* (its session ended under it, `interrupted`), another worker took it over, or this process shuts
|
|
3130
|
+
* down (`released`). The per-turn state goes, `onTurnEnd` fires once, and a later stop of the turn
|
|
3131
|
+
* here is ignored. Nothing settles the run here afterwards: whoever answered or cancelled it does.
|
|
3132
|
+
*/
|
|
3133
|
+
release(runId, status) {
|
|
3134
|
+
const watch = this.watches.get(runId);
|
|
3135
|
+
if (watch === void 0) return;
|
|
3136
|
+
const seen = this.live.get(runId)?.turn.totalUsage();
|
|
3137
|
+
if (seen !== void 0) this.keepReleasedUsage(runId, seen);
|
|
3138
|
+
this.drop(runId);
|
|
3139
|
+
watch.settled = true;
|
|
3140
|
+
this.watches.delete(runId);
|
|
3141
|
+
for (const [key, owner] of this.owners) if (owner === runId) this.owners.delete(key);
|
|
3142
|
+
this.ended(watch, status, void 0);
|
|
2950
3143
|
}
|
|
2951
3144
|
// ---- settle --------------------------------------------------------------------------------
|
|
2952
3145
|
/** A person was asked something: tell the host (cards in other channels, wake-ups). */
|
|
@@ -2994,7 +3187,7 @@ ${body}
|
|
|
2994
3187
|
const ui = amendment.ui ?? [];
|
|
2995
3188
|
const last = result.messages.at(-1);
|
|
2996
3189
|
if (ui.length > 0) {
|
|
2997
|
-
for (const component of ui) await writer.write((0,
|
|
3190
|
+
for (const component of ui) await writer.write((0, import_nestjs_agent_core5.encodeStreamEvent)({
|
|
2998
3191
|
kind: "ui",
|
|
2999
3192
|
...component
|
|
3000
3193
|
}));
|
|
@@ -3019,7 +3212,7 @@ ${body}
|
|
|
3019
3212
|
* two milestones.
|
|
3020
3213
|
*/
|
|
3021
3214
|
spentBy(runId, spent) {
|
|
3022
|
-
const seen = this.live.get(runId)?.turn.totalUsage();
|
|
3215
|
+
const seen = this.live.get(runId)?.turn.totalUsage() ?? this.releasedUsage.get(runId);
|
|
3023
3216
|
if (spent === void 0) return seen ?? emptyUsage();
|
|
3024
3217
|
if (seen === void 0) return spent;
|
|
3025
3218
|
const tokens = /* @__PURE__ */ __name((u) => u.inputTokens + u.outputTokens, "tokens");
|
|
@@ -3029,30 +3222,30 @@ ${body}
|
|
|
3029
3222
|
* End the run's stream and bookkeeping for how its execution ended. `spent` is what the run spent
|
|
3030
3223
|
* (the sum of its milestones' `usage`); omit → what the turn this process follows saw.
|
|
3031
3224
|
*/
|
|
3032
|
-
async settle(runId, input,
|
|
3225
|
+
async settle(runId, input, ended, durationMs, spent) {
|
|
3226
|
+
const outcome = this.withStop(ended, this.watches.get(runId));
|
|
3033
3227
|
if (outcome.status === "failed") {
|
|
3034
|
-
|
|
3035
|
-
return;
|
|
3228
|
+
return this.settleFailed(runId, input, outcome.error, durationMs, spent, outcome.stop);
|
|
3036
3229
|
}
|
|
3037
3230
|
const usage = this.spentBy(runId, spent);
|
|
3038
|
-
this.forget(runId);
|
|
3231
|
+
const watch = this.forget(runId);
|
|
3039
3232
|
const writer = await this.sink.open(runId);
|
|
3040
3233
|
const result = await this.runResult(runId, input, outcome, durationMs, usage);
|
|
3041
3234
|
await this.amend(writer, result);
|
|
3042
3235
|
if (outcome.status === "interrupted") {
|
|
3043
3236
|
this.logger.log(`agent run ${runId} cancelled`);
|
|
3044
|
-
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId,
|
|
3237
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId, import_nestjs_agent_core5.RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
|
|
3045
3238
|
await this.handoff(writer, input, runId, "cancelled");
|
|
3046
|
-
await (0,
|
|
3047
|
-
await writer.write((0,
|
|
3239
|
+
await (0, import_nestjs_agent_core5.releaseThreadRun)(this.store, input.threadId, runId);
|
|
3240
|
+
await writer.write((0, import_nestjs_agent_core5.encodeStreamEvent)({
|
|
3048
3241
|
kind: "cancelled"
|
|
3049
3242
|
}));
|
|
3050
3243
|
await writer.end();
|
|
3051
|
-
(0,
|
|
3244
|
+
(0, import_nestjs_agent_core5.publishAgentRunCancelled)({
|
|
3052
3245
|
runId,
|
|
3053
3246
|
threadId: input.threadId,
|
|
3054
3247
|
durationMs,
|
|
3055
|
-
context: (0,
|
|
3248
|
+
context: (0, import_nestjs_agent_core5.agentRunContext)(input)
|
|
3056
3249
|
});
|
|
3057
3250
|
await this.store.recordRunEnd?.({
|
|
3058
3251
|
runId,
|
|
@@ -3061,7 +3254,7 @@ ${body}
|
|
|
3061
3254
|
});
|
|
3062
3255
|
} else {
|
|
3063
3256
|
await this.handoff(writer, input, runId, "completed");
|
|
3064
|
-
await (0,
|
|
3257
|
+
await (0, import_nestjs_agent_core5.releaseThreadRun)(this.store, input.threadId, runId);
|
|
3065
3258
|
await writer.end();
|
|
3066
3259
|
await this.store.recordRunEnd?.({
|
|
3067
3260
|
runId,
|
|
@@ -3070,47 +3263,402 @@ ${body}
|
|
|
3070
3263
|
});
|
|
3071
3264
|
}
|
|
3072
3265
|
await this.settled(result);
|
|
3266
|
+
if (watch !== void 0) this.ended(watch, outcome.status, outcome.status === "succeeded" ? outcome.stop : void 0);
|
|
3267
|
+
return outcome;
|
|
3073
3268
|
}
|
|
3074
|
-
async settleFailed(runId, input, error, durationMs = 0, spent) {
|
|
3269
|
+
async settleFailed(runId, input, error, durationMs = 0, spent, stop) {
|
|
3075
3270
|
const usage = this.spentBy(runId, spent);
|
|
3076
|
-
this.forget(runId);
|
|
3271
|
+
const watch = this.forget(runId);
|
|
3272
|
+
const stopped = stop ?? watch?.stop;
|
|
3077
3273
|
const writer = await this.sink.open(runId);
|
|
3078
3274
|
const result = await this.runResult(runId, input, {
|
|
3079
3275
|
status: "failed",
|
|
3080
|
-
error
|
|
3276
|
+
error,
|
|
3277
|
+
...stopped !== void 0 ? {
|
|
3278
|
+
stop: stopped
|
|
3279
|
+
} : {}
|
|
3081
3280
|
}, durationMs, usage);
|
|
3082
3281
|
const worded = (await this.amend(writer, result)).error;
|
|
3083
|
-
const message = worded ?? (0,
|
|
3084
|
-
|
|
3085
|
-
(0
|
|
3282
|
+
const message = worded ?? (stopped !== void 0 ? error : (0, import_nestjs_agent_core5.streamFailure)(new Error(error)).message);
|
|
3283
|
+
const code = stopped !== void 0 ? STOPPED_ERROR_CODE : "opencode_failed";
|
|
3284
|
+
if (stopped !== void 0) this.logger.log(`agent run ${runId} stopped: ${stopped.reason}`);
|
|
3285
|
+
else this.logger.error(`agent run ${runId} failed: ${error}`);
|
|
3286
|
+
(0, import_nestjs_agent_core5.publishAgentRunFailed)({
|
|
3086
3287
|
runId,
|
|
3087
3288
|
threadId: input.threadId,
|
|
3088
|
-
code
|
|
3289
|
+
code,
|
|
3089
3290
|
message: error,
|
|
3090
3291
|
durationMs,
|
|
3091
|
-
context: (0,
|
|
3292
|
+
context: (0, import_nestjs_agent_core5.agentRunContext)(input)
|
|
3092
3293
|
});
|
|
3093
3294
|
await this.store.recordRunEnd?.({
|
|
3094
3295
|
runId,
|
|
3095
3296
|
status: "failed",
|
|
3096
3297
|
durationMs,
|
|
3097
|
-
errorCode:
|
|
3298
|
+
errorCode: code,
|
|
3098
3299
|
errorMessage: error
|
|
3099
3300
|
});
|
|
3100
|
-
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId,
|
|
3301
|
+
await Promise.resolve(this.store.failUnsettledToolCalls?.(runId, import_nestjs_agent_core5.RUN_ENDED_BEFORE_TOOL_CALL)).catch(() => 0);
|
|
3101
3302
|
await this.handoff(writer, input, runId, "failed", error);
|
|
3102
|
-
await (0,
|
|
3303
|
+
await (0, import_nestjs_agent_core5.releaseThreadRun)(this.store, input.threadId, runId);
|
|
3103
3304
|
await writer.fail({
|
|
3104
|
-
code
|
|
3305
|
+
code,
|
|
3105
3306
|
message
|
|
3106
3307
|
});
|
|
3107
3308
|
await this.settled({
|
|
3108
3309
|
...result,
|
|
3109
3310
|
outcome: {
|
|
3110
3311
|
status: "failed",
|
|
3111
|
-
error: worded ?? error
|
|
3312
|
+
error: worded ?? error,
|
|
3313
|
+
...stopped !== void 0 ? {
|
|
3314
|
+
stop: stopped
|
|
3315
|
+
} : {}
|
|
3112
3316
|
}
|
|
3113
3317
|
});
|
|
3318
|
+
if (watch !== void 0) this.ended(watch, "failed", stopped);
|
|
3319
|
+
return {
|
|
3320
|
+
status: "failed",
|
|
3321
|
+
error,
|
|
3322
|
+
...stopped !== void 0 ? {
|
|
3323
|
+
stop: stopped
|
|
3324
|
+
} : {}
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
// ---- limits, stops and the turn's lifecycle -------------------------------------------------
|
|
3328
|
+
/**
|
|
3329
|
+
* Stop a turn's own work: abort its session so OpenCode starts no new round, only while the turn
|
|
3330
|
+
* still owns the session (a thread's session is reused by its next turn) — checked immediately
|
|
3331
|
+
* before the abort. Acts in the process executing the turn; see {@link OpenCodeStopResult}. The
|
|
3332
|
+
* turn ends failed with `outcome.stop` (code `opencode_stopped`), and `onStop` is called.
|
|
3333
|
+
*/
|
|
3334
|
+
stopTurn(turnId, reason) {
|
|
3335
|
+
return this.requestStop(turnId, reason);
|
|
3336
|
+
}
|
|
3337
|
+
/** The turn this process executes, made known here on its first step in this process. */
|
|
3338
|
+
async adopt(runId, input, handle, resumed, counts, startedAt) {
|
|
3339
|
+
const known = this.watches.get(runId);
|
|
3340
|
+
if (known !== void 0) return known;
|
|
3341
|
+
const watch = {
|
|
3342
|
+
runId,
|
|
3343
|
+
input,
|
|
3344
|
+
handle,
|
|
3345
|
+
limits: this.limitsFor(input),
|
|
3346
|
+
startedAt: handle.startedAt ?? startedAt ?? Date.now(),
|
|
3347
|
+
steps: counts?.steps ?? 0,
|
|
3348
|
+
compactions: counts?.compactions ?? 0,
|
|
3349
|
+
observing: false,
|
|
3350
|
+
ending: false,
|
|
3351
|
+
settled: false,
|
|
3352
|
+
aborted: false,
|
|
3353
|
+
checking: false,
|
|
3354
|
+
lastRead: 0
|
|
3355
|
+
};
|
|
3356
|
+
this.watches.set(runId, watch);
|
|
3357
|
+
await this.hook("onTurnStart", {
|
|
3358
|
+
turnId: runId,
|
|
3359
|
+
threadId: input.threadId,
|
|
3360
|
+
sessionId: handle.sessionId,
|
|
3361
|
+
resumed
|
|
3362
|
+
});
|
|
3363
|
+
return watch;
|
|
3364
|
+
}
|
|
3365
|
+
limitsFor(input) {
|
|
3366
|
+
const limits = this.settings.limits;
|
|
3367
|
+
return cleanLimits(typeof limits === "function" ? limits(input) : limits);
|
|
3368
|
+
}
|
|
3369
|
+
/**
|
|
3370
|
+
* One event of the turn's session: counted for the limits, and told to `onTurnEvent`. While the
|
|
3371
|
+
* turn is parked on a person here, it may also say the turn went on without this process
|
|
3372
|
+
* ({@link wentOnElsewhere}).
|
|
3373
|
+
*/
|
|
3374
|
+
sawEvent(runId, sessionId, event) {
|
|
3375
|
+
if (sessionOf(event) !== sessionId) return;
|
|
3376
|
+
const watch = this.watches.get(runId);
|
|
3377
|
+
if (watch === void 0 || watch.settled || watch.handle.sessionId !== sessionId) return;
|
|
3378
|
+
if (watch.parkedOn !== void 0 && this.wentOnElsewhere(watch, event)) return;
|
|
3379
|
+
let reached;
|
|
3380
|
+
if (event.type.startsWith("session.execution.")) watch.ending = true;
|
|
3381
|
+
else if (event.type === "session.step.started") {
|
|
3382
|
+
watch.steps += 1;
|
|
3383
|
+
if (passed(watch.steps, watch.limits.steps)) reached = "steps";
|
|
3384
|
+
} else if (event.type === "session.compaction.started") {
|
|
3385
|
+
watch.compactions += 1;
|
|
3386
|
+
if (passed(watch.compactions, watch.limits.compactions)) reached = "compactions";
|
|
3387
|
+
}
|
|
3388
|
+
this.hook("onTurnEvent", {
|
|
3389
|
+
turnId: runId,
|
|
3390
|
+
type: event.type,
|
|
3391
|
+
sessionId
|
|
3392
|
+
});
|
|
3393
|
+
if (reached !== void 0) void this.limitStop(runId, reached);
|
|
3394
|
+
}
|
|
3395
|
+
/**
|
|
3396
|
+
* Whether a turn parked here went on without this process, which then lets it go. While a tool of
|
|
3397
|
+
* the step waits on a person, OpenCode keeps running the step's other tools, so their events say
|
|
3398
|
+
* nothing. What does: a new round (`session.step.started`, which cannot begin while the step
|
|
3399
|
+
* waits), or the store showing the parked ask decided by someone else (another worker answered
|
|
3400
|
+
* it, or a cancel elsewhere settled it) — this process clears the park before it answers. When
|
|
3401
|
+
* the session's execution ends while parked, the store is asked again for a while, as a cancel
|
|
3402
|
+
* elsewhere settles the ask just after it interrupted the session; with the ask still open, the
|
|
3403
|
+
* turn stays here for its answer.
|
|
3404
|
+
*/
|
|
3405
|
+
wentOnElsewhere(watch, event) {
|
|
3406
|
+
if (event.type === "session.step.started") {
|
|
3407
|
+
this.release(watch.runId, "released");
|
|
3408
|
+
return true;
|
|
3409
|
+
}
|
|
3410
|
+
const ended = event.type.startsWith("session.execution.");
|
|
3411
|
+
if (ended) watch.parkedEnd = event.type === "session.execution.interrupted" ? "interrupted" : "released";
|
|
3412
|
+
this.checkParked(watch, ended);
|
|
3413
|
+
return false;
|
|
3414
|
+
}
|
|
3415
|
+
/**
|
|
3416
|
+
* Ask the store whether the parked ask was decided elsewhere, and let the turn go when it was —
|
|
3417
|
+
* one read at a time, at most one every {@link PARKED_READ_GAP_MS}. `series`: the session's
|
|
3418
|
+
* execution ended, so the store is asked again over {@link parkedRechecksMs}. An execution that
|
|
3419
|
+
* was interrupted under a parked turn means a cancel elsewhere: when the store cannot say (it has
|
|
3420
|
+
* no `toolCallApproval`, the read fails, the ask is not on file) past the first recheck, or the
|
|
3421
|
+
* rechecks run out with the ask still open, the turn is let go as `interrupted` anyway.
|
|
3422
|
+
*/
|
|
3423
|
+
checkParked(watch, series) {
|
|
3424
|
+
if (series) watch.series = true;
|
|
3425
|
+
if (watch.checking) {
|
|
3426
|
+
watch.again = true;
|
|
3427
|
+
return;
|
|
3428
|
+
}
|
|
3429
|
+
watch.checking = true;
|
|
3430
|
+
void this.runParkedChecks(watch);
|
|
3431
|
+
}
|
|
3432
|
+
/**
|
|
3433
|
+
* The checks of {@link checkParked}. `checking` is cleared in the same step the loop returns in,
|
|
3434
|
+
* so a check asked for afterwards starts a new loop; one asked for while it ran (`again`) but not
|
|
3435
|
+
* read because the loop returned starts one too.
|
|
3436
|
+
*/
|
|
3437
|
+
async runParkedChecks(watch) {
|
|
3438
|
+
try {
|
|
3439
|
+
await this.parkedChecks(watch);
|
|
3440
|
+
} finally {
|
|
3441
|
+
watch.checking = false;
|
|
3442
|
+
if (watch.again && !watch.settled && watch.parkedOn !== void 0) {
|
|
3443
|
+
watch.checking = true;
|
|
3444
|
+
void this.runParkedChecks(watch);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
async parkedChecks(watch) {
|
|
3449
|
+
const ask = watch.parkedOn;
|
|
3450
|
+
const gone = /* @__PURE__ */ __name(() => watch.settled || ask === void 0 || watch.parkedOn !== ask, "gone");
|
|
3451
|
+
let step = 0;
|
|
3452
|
+
for (; ; ) {
|
|
3453
|
+
watch.again = false;
|
|
3454
|
+
const wait = watch.lastRead + PARKED_READ_GAP_MS - Date.now();
|
|
3455
|
+
if (wait > 0) await sleep(wait);
|
|
3456
|
+
if (gone()) return;
|
|
3457
|
+
const answer = await this.parkedAsk(ask);
|
|
3458
|
+
watch.lastRead = Date.now();
|
|
3459
|
+
if (gone()) return;
|
|
3460
|
+
if (answer === "decided") {
|
|
3461
|
+
this.release(watch.runId, watch.parkedEnd ?? "released");
|
|
3462
|
+
return;
|
|
3463
|
+
}
|
|
3464
|
+
if (watch.series) {
|
|
3465
|
+
const cancelled = watch.parkedEnd === "interrupted";
|
|
3466
|
+
if (cancelled && answer === "unknown" && step > 0) {
|
|
3467
|
+
this.release(watch.runId, "interrupted");
|
|
3468
|
+
return;
|
|
3469
|
+
}
|
|
3470
|
+
const gap = this.parkedRechecksMs[step];
|
|
3471
|
+
step += 1;
|
|
3472
|
+
if (gap === void 0) {
|
|
3473
|
+
if (cancelled) this.release(watch.runId, "interrupted");
|
|
3474
|
+
return;
|
|
3475
|
+
}
|
|
3476
|
+
await sleep(gap);
|
|
3477
|
+
continue;
|
|
3478
|
+
}
|
|
3479
|
+
if (!watch.again) return;
|
|
3480
|
+
}
|
|
3481
|
+
}
|
|
3482
|
+
/** What the store says of an ask: decided, still open, or nothing it can tell. */
|
|
3483
|
+
async parkedAsk(id) {
|
|
3484
|
+
if (this.store.toolCallApproval === void 0) return "unknown";
|
|
3485
|
+
try {
|
|
3486
|
+
const state = await this.store.toolCallApproval(id);
|
|
3487
|
+
if (state == null) return "unknown";
|
|
3488
|
+
return state.status === "pending_approval" ? "open" : "decided";
|
|
3489
|
+
} catch {
|
|
3490
|
+
return "unknown";
|
|
3491
|
+
}
|
|
3492
|
+
}
|
|
3493
|
+
/**
|
|
3494
|
+
* The waits between the store's rereads of a parked ask after its execution ended (cumulative
|
|
3495
|
+
* 0.25, 1.25, 4.25, 14.25 and 44.25 seconds after the first). Overridden by tests only.
|
|
3496
|
+
*
|
|
3497
|
+
* @internal
|
|
3498
|
+
*/
|
|
3499
|
+
parkedRechecksMs = PARKED_RECHECKS_MS;
|
|
3500
|
+
/** A limit's stop: what it did is the engine's to log, there is no caller to tell. */
|
|
3501
|
+
async limitStop(runId, reason) {
|
|
3502
|
+
const result = await this.requestStop(runId, reason);
|
|
3503
|
+
if (result === "unconfirmed") this.logger.warn(`run ${runId}: passed its ${reason} limit, but its owner could not be read`);
|
|
3504
|
+
}
|
|
3505
|
+
/** The wall-clock bound of an observation: a timer for what is left of it, if anything. */
|
|
3506
|
+
watchClock(watch) {
|
|
3507
|
+
const max = watch.limits.wallClockMs;
|
|
3508
|
+
if (max === void 0) return void 0;
|
|
3509
|
+
const left = watch.startedAt + max - Date.now();
|
|
3510
|
+
if (left <= 0) {
|
|
3511
|
+
watch.pending ??= "wall-clock";
|
|
3512
|
+
return void 0;
|
|
3513
|
+
}
|
|
3514
|
+
const timer = setTimeout(() => void this.limitStop(watch.runId, "wall-clock"), left);
|
|
3515
|
+
timer.unref?.();
|
|
3516
|
+
return timer;
|
|
3517
|
+
}
|
|
3518
|
+
counts(watch) {
|
|
3519
|
+
return {
|
|
3520
|
+
steps: watch.steps,
|
|
3521
|
+
compactions: watch.compactions,
|
|
3522
|
+
elapsedMs: Date.now() - watch.startedAt
|
|
3523
|
+
};
|
|
3524
|
+
}
|
|
3525
|
+
async requestStop(runId, reason) {
|
|
3526
|
+
const watch = this.watches.get(runId);
|
|
3527
|
+
if (watch === void 0 || watch.settled || watch.stop !== void 0) return "ignored";
|
|
3528
|
+
const live = this.live.get(runId);
|
|
3529
|
+
if (watch.parkedOn !== void 0) {
|
|
3530
|
+
watch.pending ??= reason;
|
|
3531
|
+
return "pending";
|
|
3532
|
+
}
|
|
3533
|
+
if (watch.ending || live?.turn.finished === true) return this.landed(watch, reason);
|
|
3534
|
+
if (!watch.observing || live === void 0) {
|
|
3535
|
+
watch.pending ??= reason;
|
|
3536
|
+
return "pending";
|
|
3537
|
+
}
|
|
3538
|
+
watch.pending = void 0;
|
|
3539
|
+
let active;
|
|
3540
|
+
try {
|
|
3541
|
+
active = await this.activeRun(watch.input.threadId);
|
|
3542
|
+
} catch (error) {
|
|
3543
|
+
this.logger.warn(`run ${runId}: could not read the thread's run: ${errorText(error, "error")}`);
|
|
3544
|
+
return "unconfirmed";
|
|
3545
|
+
}
|
|
3546
|
+
if (watch.settled || watch.stop !== void 0 || this.live.get(runId) !== live) return "ignored";
|
|
3547
|
+
if (active !== null && active !== runId) return "ignored";
|
|
3548
|
+
if (this.owners.get(ownerKey(watch.handle)) !== runId) return "ignored";
|
|
3549
|
+
if (watch.ending || live.turn.finished) return this.landed(watch, reason);
|
|
3550
|
+
const stop = {
|
|
3551
|
+
reason,
|
|
3552
|
+
counts: this.counts(watch)
|
|
3553
|
+
};
|
|
3554
|
+
watch.stop = stop;
|
|
3555
|
+
watch.aborted = true;
|
|
3556
|
+
try {
|
|
3557
|
+
await live.client.session.interrupt({
|
|
3558
|
+
sessionID: watch.handle.sessionId
|
|
3559
|
+
});
|
|
3560
|
+
} catch (error) {
|
|
3561
|
+
this.logger.warn(`run ${runId}: could not abort the session: ${errorText(error, "error")}`);
|
|
3562
|
+
}
|
|
3563
|
+
this.hook("onStop", {
|
|
3564
|
+
turnId: runId,
|
|
3565
|
+
reason,
|
|
3566
|
+
counts: stop.counts
|
|
3567
|
+
});
|
|
3568
|
+
return "aborted";
|
|
3569
|
+
}
|
|
3570
|
+
/** A stop that arrived while the answer settles: recorded and reported, the answer kept. */
|
|
3571
|
+
landed(watch, reason) {
|
|
3572
|
+
const stop = {
|
|
3573
|
+
reason,
|
|
3574
|
+
counts: this.counts(watch)
|
|
3575
|
+
};
|
|
3576
|
+
watch.stop = stop;
|
|
3577
|
+
watch.pending = void 0;
|
|
3578
|
+
this.hook("onStop", {
|
|
3579
|
+
turnId: watch.runId,
|
|
3580
|
+
reason,
|
|
3581
|
+
counts: stop.counts
|
|
3582
|
+
});
|
|
3583
|
+
return "settling";
|
|
3584
|
+
}
|
|
3585
|
+
/** The milestone as the runner journals it: the limits' counts on an ask, the stop on an end. */
|
|
3586
|
+
marked(watch, milestone) {
|
|
3587
|
+
if (milestone.kind === "finished") return {
|
|
3588
|
+
...milestone,
|
|
3589
|
+
outcome: this.withStop(milestone.outcome, watch)
|
|
3590
|
+
};
|
|
3591
|
+
if (milestone.progress === void 0) return milestone;
|
|
3592
|
+
const counts = {
|
|
3593
|
+
steps: watch.steps,
|
|
3594
|
+
compactions: watch.compactions
|
|
3595
|
+
};
|
|
3596
|
+
return {
|
|
3597
|
+
...milestone,
|
|
3598
|
+
progress: {
|
|
3599
|
+
...milestone.progress,
|
|
3600
|
+
counts
|
|
3601
|
+
}
|
|
3602
|
+
};
|
|
3603
|
+
}
|
|
3604
|
+
/**
|
|
3605
|
+
* How a turn a stop landed on ends. An answer that settled anyway is kept, with the stop on it; an
|
|
3606
|
+
* execution that failed on its own keeps its error, with the stop on it; an execution the stop
|
|
3607
|
+
* aborted ends failed with the stop's own text. One interrupted by a person's cancel stays so.
|
|
3608
|
+
*/
|
|
3609
|
+
withStop(outcome, watch) {
|
|
3610
|
+
const stop = watch?.stop;
|
|
3611
|
+
if (stop === void 0) return outcome;
|
|
3612
|
+
if (outcome.status !== "interrupted") return outcome.stop !== void 0 ? outcome : {
|
|
3613
|
+
...outcome,
|
|
3614
|
+
stop
|
|
3615
|
+
};
|
|
3616
|
+
if (!watch?.aborted) return outcome;
|
|
3617
|
+
return {
|
|
3618
|
+
status: "failed",
|
|
3619
|
+
error: stopErrorText(stop),
|
|
3620
|
+
stop
|
|
3621
|
+
};
|
|
3622
|
+
}
|
|
3623
|
+
/** `onTurnEnd`, once per turn this process executed; not waited for. */
|
|
3624
|
+
ended(watch, status, stop) {
|
|
3625
|
+
this.hook("onTurnEnd", {
|
|
3626
|
+
turnId: watch.runId,
|
|
3627
|
+
status,
|
|
3628
|
+
...stop !== void 0 ? {
|
|
3629
|
+
stop
|
|
3630
|
+
} : {}
|
|
3631
|
+
});
|
|
3632
|
+
}
|
|
3633
|
+
/**
|
|
3634
|
+
* A lifecycle hook, from the settings and the host, called at once. The promise settles when both
|
|
3635
|
+
* have, or after `hookTimeoutMs` (default 5 s) — only `onTurnStart` is waited for. Errors are
|
|
3636
|
+
* logged, never the turn's.
|
|
3637
|
+
*/
|
|
3638
|
+
hook(name, event) {
|
|
3639
|
+
const calls = [];
|
|
3640
|
+
for (const target of [
|
|
3641
|
+
this.settings,
|
|
3642
|
+
this.host
|
|
3643
|
+
]) {
|
|
3644
|
+
const fn = target[name];
|
|
3645
|
+
if (typeof fn !== "function") continue;
|
|
3646
|
+
calls.push(Promise.resolve().then(() => fn.call(target, event)).catch((error) => this.logger.warn(`${name} failed: ${errorText(error, "error")}`)));
|
|
3647
|
+
}
|
|
3648
|
+
if (calls.length === 0) return Promise.resolve();
|
|
3649
|
+
const ms = this.settings.hookTimeoutMs ?? DEFAULT_HOOK_TIMEOUT_MS;
|
|
3650
|
+
let timer;
|
|
3651
|
+
const late = new Promise((resolve) => {
|
|
3652
|
+
timer = setTimeout(() => {
|
|
3653
|
+
this.logger.warn(`${name} took longer than ${ms}ms; the turn went on`);
|
|
3654
|
+
resolve();
|
|
3655
|
+
}, ms);
|
|
3656
|
+
timer.unref?.();
|
|
3657
|
+
});
|
|
3658
|
+
return Promise.race([
|
|
3659
|
+
Promise.all(calls).then(() => void 0),
|
|
3660
|
+
late
|
|
3661
|
+
]).finally(() => clearTimeout(timer));
|
|
3114
3662
|
}
|
|
3115
3663
|
/** Starts the next queued message of the thread; set by the runner (it owns `start`). */
|
|
3116
3664
|
startNext;
|
|
@@ -3129,7 +3677,7 @@ ${body}
|
|
|
3129
3677
|
error
|
|
3130
3678
|
} : {}
|
|
3131
3679
|
}, (next, nextRunId) => startNext(next, nextRunId));
|
|
3132
|
-
if (frame !== void 0) await writer.write((0,
|
|
3680
|
+
if (frame !== void 0) await writer.write((0, import_nestjs_agent_core5.encodeStreamEvent)(frame));
|
|
3133
3681
|
} catch (failure) {
|
|
3134
3682
|
this.logger.error(`could not advance the queue of thread ${input.threadId} after run ${runId}: ${failure instanceof Error ? failure.message : String(failure)}`);
|
|
3135
3683
|
}
|
|
@@ -3140,22 +3688,22 @@ OpenCodeTurns = _ts_decorate2([
|
|
|
3140
3688
|
_ts_param2(0, (0, import_common4.Inject)(OPENCODE_HOST)),
|
|
3141
3689
|
_ts_param2(1, (0, import_common4.Inject)(OPENCODE_SESSIONS)),
|
|
3142
3690
|
_ts_param2(2, (0, import_common4.Inject)(OPENCODE_OPTIONS)),
|
|
3143
|
-
_ts_param2(3, (0, import_common4.Inject)(
|
|
3144
|
-
_ts_param2(4, (0, import_common4.Inject)(
|
|
3145
|
-
_ts_param2(5, (0, import_common4.Inject)(
|
|
3146
|
-
_ts_param2(6, (0, import_common4.Inject)(
|
|
3691
|
+
_ts_param2(3, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_STORE)),
|
|
3692
|
+
_ts_param2(4, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_SINK)),
|
|
3693
|
+
_ts_param2(5, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_DEPS_FACTORY)),
|
|
3694
|
+
_ts_param2(6, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_TOOL_REGISTRY)),
|
|
3147
3695
|
_ts_param2(7, (0, import_common4.Optional)()),
|
|
3148
|
-
_ts_param2(7, (0, import_common4.Inject)(
|
|
3696
|
+
_ts_param2(7, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_SKILLS)),
|
|
3149
3697
|
_ts_param2(8, (0, import_common4.Optional)()),
|
|
3150
|
-
_ts_param2(8, (0, import_common4.Inject)(
|
|
3698
|
+
_ts_param2(8, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_MEMORY)),
|
|
3151
3699
|
_ts_param2(9, (0, import_common4.Optional)()),
|
|
3152
3700
|
_ts_param2(9, (0, import_common4.Inject)(import_nestjs_agent.AGENT_CHAT_QUEUE)),
|
|
3153
3701
|
_ts_param2(10, (0, import_common4.Optional)()),
|
|
3154
3702
|
_ts_param2(10, (0, import_common4.Inject)(OPENCODE_TOOLS_TOKENS)),
|
|
3155
3703
|
_ts_param2(11, (0, import_common4.Optional)()),
|
|
3156
|
-
_ts_param2(11, (0, import_common4.Inject)(
|
|
3704
|
+
_ts_param2(11, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_PRICING_STORE)),
|
|
3157
3705
|
_ts_param2(12, (0, import_common4.Optional)()),
|
|
3158
|
-
_ts_param2(12, (0, import_common4.Inject)(
|
|
3706
|
+
_ts_param2(12, (0, import_common4.Inject)(import_nestjs_agent_core5.AGENT_OPTIONS)),
|
|
3159
3707
|
_ts_metadata2("design:type", Function),
|
|
3160
3708
|
_ts_metadata2("design:paramtypes", [
|
|
3161
3709
|
typeof OpenCodeHost === "undefined" ? Object : OpenCodeHost,
|
|
@@ -3173,6 +3721,28 @@ OpenCodeTurns = _ts_decorate2([
|
|
|
3173
3721
|
Object
|
|
3174
3722
|
])
|
|
3175
3723
|
], OpenCodeTurns);
|
|
3724
|
+
function passed(count, limit) {
|
|
3725
|
+
return limit !== void 0 && count > limit;
|
|
3726
|
+
}
|
|
3727
|
+
__name(passed, "passed");
|
|
3728
|
+
function sleep(ms) {
|
|
3729
|
+
return new Promise((resolve) => setTimeout(resolve, ms).unref?.());
|
|
3730
|
+
}
|
|
3731
|
+
__name(sleep, "sleep");
|
|
3732
|
+
function refusalOf(ask) {
|
|
3733
|
+
return ask.kind === "approval" ? {
|
|
3734
|
+
approved: false,
|
|
3735
|
+
reason: "The turn was stopped."
|
|
3736
|
+
} : {
|
|
3737
|
+
answers: {},
|
|
3738
|
+
skipped: true
|
|
3739
|
+
};
|
|
3740
|
+
}
|
|
3741
|
+
__name(refusalOf, "refusalOf");
|
|
3742
|
+
function ownerKey(handle) {
|
|
3743
|
+
return `${handle.serverKey}|${handle.sessionId}`;
|
|
3744
|
+
}
|
|
3745
|
+
__name(ownerKey, "ownerKey");
|
|
3176
3746
|
function titleOf(text) {
|
|
3177
3747
|
const trimmed = text.trim().replace(/\s+/g, " ");
|
|
3178
3748
|
return trimmed.length > 60 ? `${trimmed.slice(0, 57)}...` : trimmed || "New chat";
|
|
@@ -3217,7 +3787,7 @@ function priceRef(model) {
|
|
|
3217
3787
|
model.id
|
|
3218
3788
|
];
|
|
3219
3789
|
const tail = model.id.slice(model.id.lastIndexOf("/") + 1);
|
|
3220
|
-
if (tail !== model.id && (0,
|
|
3790
|
+
if (tail !== model.id && (0, import_nestjs_agent_core5.parseBedrockModelId)(tail) !== void 0) aliases.push(tail);
|
|
3221
3791
|
return {
|
|
3222
3792
|
modelId: `${model.providerID}/${model.id}`,
|
|
3223
3793
|
provider: model.providerID,
|
|
@@ -3317,7 +3887,7 @@ var OpenCodeAgentRunner = class {
|
|
|
3317
3887
|
]) {
|
|
3318
3888
|
if (!key.startsWith(`${runId}:`)) continue;
|
|
3319
3889
|
this.pending.delete(key);
|
|
3320
|
-
waiter.reject(new
|
|
3890
|
+
waiter.reject(new import_nestjs_agent_core6.RunCancelledError());
|
|
3321
3891
|
}
|
|
3322
3892
|
const input = this.inputs.get(runId);
|
|
3323
3893
|
if (input !== void 0) await this.turns.interrupt(input).catch(() => void 0);
|
|
@@ -3329,7 +3899,7 @@ var OpenCodeAgentRunner = class {
|
|
|
3329
3899
|
let spent = emptyUsage();
|
|
3330
3900
|
try {
|
|
3331
3901
|
let handle = await this.turns.begin(runId, input);
|
|
3332
|
-
if (this.cancelled.has(runId)) throw new
|
|
3902
|
+
if (this.cancelled.has(runId)) throw new import_nestjs_agent_core6.RunCancelledError();
|
|
3333
3903
|
await this.turns.prompt(runId, input, handle, spent);
|
|
3334
3904
|
for (; ; ) {
|
|
3335
3905
|
const milestone = await this.turns.observe(runId, input, handle, spent);
|
|
@@ -3342,11 +3912,11 @@ var OpenCodeAgentRunner = class {
|
|
|
3342
3912
|
handle = await this.turns.reply(runId, input, handle, milestone.ask, reply, spent);
|
|
3343
3913
|
}
|
|
3344
3914
|
} catch (error) {
|
|
3345
|
-
if (error instanceof
|
|
3915
|
+
if (error instanceof import_nestjs_agent_core6.RunCancelledError) {
|
|
3346
3916
|
await this.turns.interrupt(input).catch(() => void 0);
|
|
3347
3917
|
await this.turns.settle(runId, input, {
|
|
3348
3918
|
status: "interrupted"
|
|
3349
|
-
}, Date.now() - started);
|
|
3919
|
+
}, Date.now() - started, spent);
|
|
3350
3920
|
return;
|
|
3351
3921
|
}
|
|
3352
3922
|
await this.turns.settleFailed(runId, input, errorText(error, "the turn failed"), Date.now() - started);
|
|
@@ -3503,6 +4073,7 @@ __name(openCodeProviders, "openCodeProviders");
|
|
|
3503
4073
|
OpenCodeToolsTokens,
|
|
3504
4074
|
OpenCodeTurn,
|
|
3505
4075
|
OpenCodeTurns,
|
|
4076
|
+
STOPPED_ERROR_CODE,
|
|
3506
4077
|
addUsage,
|
|
3507
4078
|
emptyUsage,
|
|
3508
4079
|
keyValueOpenCodeSessionStore,
|