@dudousxd/nestjs-agent-opencode 0.2.0 → 0.3.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/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-JnBbcRxh.cjs';
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 OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, C as OpenCodeUsage, P as PendingAsk, S as SessionHandle, D as StepCost, E as StepModel, T as TurnOutcome, F as addUsage, G as emptyUsage, H as keyValueOpenCodeSessionStore, J as openCode, K as openCodeControllers, L as openCodeProviders, N as usageOf } from './engine-JnBbcRxh.cjs';
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-Cp5uysaN.cjs';
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 OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, C as OpenCodeUsage, P as PendingAsk, S as SessionHandle, D as StepCost, E as StepModel, T as TurnOutcome, F as addUsage, G as emptyUsage, H as keyValueOpenCodeSessionStore, J as openCode, K as openCodeControllers, L as openCodeProviders, N as usageOf } from './engine-Cp5uysaN.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-JnBbcRxh.js';
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 OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, C as OpenCodeUsage, P as PendingAsk, S as SessionHandle, D as StepCost, E as StepModel, T as TurnOutcome, F as addUsage, G as emptyUsage, H as keyValueOpenCodeSessionStore, J as openCode, K as openCodeControllers, L as openCodeProviders, N as usageOf } from './engine-JnBbcRxh.js';
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-Cp5uysaN.js';
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 OpenCodeModelRef, n as OpenCodePermissionRequest, o as OpenCodePermissionRule, p as OpenCodeReplyMismatchError, q as OpenCodeRunResult, r as OpenCodeServer, s as OpenCodeSessionCreate, t as OpenCodeSessionRef, u as OpenCodeSessionStore, v as OpenCodeToolCall, w as OpenCodeToolRefusedError, x as OpenCodeToolsClaims, y as OpenCodeToolsOptions, z as OpenCodeToolsTokens, A as OpenCodeTurn, B as OpenCodeTurnContext, C as OpenCodeUsage, P as PendingAsk, S as SessionHandle, D as StepCost, E as StepModel, T as TurnOutcome, F as addUsage, G as emptyUsage, H as keyValueOpenCodeSessionStore, J as openCode, K as openCodeControllers, L as openCodeProviders, N as usageOf } from './engine-Cp5uysaN.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
@@ -150,7 +150,7 @@ import { toJsonSchemaCompat } from "@modelcontextprotocol/sdk/server/zod-json-sc
150
150
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
151
151
 
152
152
  // src/turn.ts
153
- import { encodeStreamEvent, settleElicitation } from "@dudousxd/nestjs-agent-core";
153
+ import { encodeStreamEvent, resolveToolConfirmation, settleElicitation } from "@dudousxd/nestjs-agent-core";
154
154
  import { ConflictException } from "@nestjs/common";
155
155
 
156
156
  // src/forms.ts
@@ -344,6 +344,17 @@ var OpenCodeTurn = class {
344
344
  reported = emptyUsage();
345
345
  priced = false;
346
346
  model;
347
+ /** What the session had spent when this turn prompted it; what OpenCode reported since. */
348
+ baseline;
349
+ seen = {
350
+ cost: 0,
351
+ input: 0,
352
+ output: 0,
353
+ reasoning: 0,
354
+ read: 0,
355
+ write: 0
356
+ };
357
+ compacted = false;
347
358
  stepOpen = false;
348
359
  sawText = false;
349
360
  separator = false;
@@ -355,6 +366,19 @@ var OpenCodeTurn = class {
355
366
  this.a = a;
356
367
  this.model = modelOfLabel(a.input.model);
357
368
  }
369
+ /**
370
+ * What the session had spent before this turn prompted it (`session.get`). When the execution
371
+ * ends, what the session spent since that this turn's events did not report — the title OpenCode
372
+ * generates, a compaction: OpenCode records those calls on the session without streaming them — is
373
+ * recorded as a usage row of its own.
374
+ */
375
+ setBaseline(info) {
376
+ if (info?.tokens === void 0) return;
377
+ this.baseline = {
378
+ cost: Number(info.cost) || 0,
379
+ tokens: info.tokens
380
+ };
381
+ }
358
382
  /** What the run spent so far: carried from before this turn object, plus what it saw. */
359
383
  totalUsage() {
360
384
  return addUsage(this.a.carried, this.spent);
@@ -611,6 +635,10 @@ var OpenCodeTurn = class {
611
635
  }
612
636
  if (this.ended) return;
613
637
  switch (e.type) {
638
+ case "session.compaction.started":
639
+ case "session.compaction.ended":
640
+ this.compacted = true;
641
+ return;
614
642
  case "session.step.started":
615
643
  case "session.model.selected": {
616
644
  const model = d.model;
@@ -780,6 +808,13 @@ ${delta}` : delta;
780
808
  async spend(d, purpose) {
781
809
  const usage = usageOf(d.tokens);
782
810
  const reportedCostUsd = typeof d.cost === "number" && Number.isFinite(d.cost) && d.cost >= 0 ? d.cost : void 0;
811
+ const n = /* @__PURE__ */ __name((v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0, "n");
812
+ this.seen.cost += reportedCostUsd ?? 0;
813
+ this.seen.input += n(d.tokens?.input);
814
+ this.seen.output += n(d.tokens?.output);
815
+ this.seen.reasoning += n(d.tokens?.reasoning);
816
+ this.seen.read += n(d.tokens?.cache?.read);
817
+ this.seen.write += n(d.tokens?.cache?.write);
783
818
  const fallback = reportedCostUsd !== void 0 ? {
784
819
  costUsd: reportedCostUsd,
785
820
  costSource: "provider"
@@ -985,6 +1020,8 @@ ${delta}` : delta;
985
1020
  }
986
1021
  const ttlMs = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) && requirement.ttlMs > 0 ? Math.floor(requirement.ttlMs) : void 0;
987
1022
  const expiresAt = ttlMs !== void 0 ? new Date(Date.now() + ttlMs).toISOString() : void 0;
1023
+ const tool = this.a.approvalPresentation?.(action);
1024
+ const confirmation = tool !== void 0 ? resolveToolConfirmation(tool.name, args, tool.presentation) : void 0;
988
1025
  this.segment.calls.push({
989
1026
  id: req.id,
990
1027
  name: action,
@@ -999,6 +1036,9 @@ ${delta}` : delta;
999
1036
  toolType: "action",
1000
1037
  input: args,
1001
1038
  status: "pending_approval",
1039
+ ...confirmation !== void 0 ? {
1040
+ confirmation
1041
+ } : {},
1002
1042
  runId: this.a.runId,
1003
1043
  approver: requirement.approver,
1004
1044
  ...expiresAt !== void 0 ? {
@@ -1016,6 +1056,9 @@ ${delta}` : delta;
1016
1056
  kind: "approval-requested",
1017
1057
  id: req.id,
1018
1058
  approver: requirement.approver,
1059
+ ...confirmation !== void 0 ? {
1060
+ confirmation
1061
+ } : {},
1019
1062
  ...expiresAt !== void 0 ? {
1020
1063
  expiresAt
1021
1064
  } : {}
@@ -1247,9 +1290,39 @@ ${delta}` : delta;
1247
1290
  this.messageResults.set(messageId, results);
1248
1291
  await this.a.store.setMessageToolResults(messageId, results);
1249
1292
  }
1293
+ /** What the session spent since the baseline that no event of this turn reported. */
1294
+ async unreported() {
1295
+ const baseline = this.baseline;
1296
+ if (baseline === void 0) return;
1297
+ this.baseline = void 0;
1298
+ const info = await this.a.client.session.get?.({
1299
+ sessionID: this.a.sessionId
1300
+ }).catch(() => void 0);
1301
+ const after = info?.tokens;
1302
+ if (after === void 0) return;
1303
+ const before = baseline.tokens;
1304
+ const left = /* @__PURE__ */ __name((now, then, seen) => Math.max(0, (Number(now) || 0) - (Number(then) || 0) - seen), "left");
1305
+ const tokens = {
1306
+ input: left(after.input, before.input, this.seen.input),
1307
+ output: left(after.output, before.output, this.seen.output),
1308
+ reasoning: left(after.reasoning, before.reasoning, this.seen.reasoning),
1309
+ cache: {
1310
+ read: left(after.cache?.read, before.cache?.read, this.seen.read),
1311
+ write: left(after.cache?.write, before.cache?.write, this.seen.write)
1312
+ }
1313
+ };
1314
+ const total = tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write;
1315
+ if (total === 0) return;
1316
+ const cost = left(info?.cost, baseline.cost, this.seen.cost);
1317
+ await this.spend({
1318
+ tokens,
1319
+ cost: cost < 1e-9 ? 0 : cost
1320
+ }, this.compacted ? "history_summary" : "title");
1321
+ }
1250
1322
  async end(outcome) {
1251
1323
  if (this.ended) return;
1252
1324
  this.ended = true;
1325
+ await this.unreported().catch((error) => this.a.logger.warn(`run ${this.a.runId}: ${errorText(error, "could not read the session usage")}`));
1253
1326
  if (outcome.status !== "failed" || this.segment.text || this.segment.calls.length > 0 || this.segment.ui.length > 0) {
1254
1327
  await this.flush(true);
1255
1328
  }
@@ -1766,6 +1839,25 @@ var OpenCodeTurns = class {
1766
1839
  get toolsServer() {
1767
1840
  return this.settings.tools?.server ?? "aviary";
1768
1841
  }
1842
+ /** The app tool an OpenCode permission `action` names, and its presentation — `undefined` for a built-in. */
1843
+ presentationOf(action) {
1844
+ if (this.settings.tools === void 0) return void 0;
1845
+ const server = this.toolsServer;
1846
+ for (const prefix of [
1847
+ `${server}.`,
1848
+ `${server}_`
1849
+ ]) {
1850
+ if (!action.startsWith(prefix)) continue;
1851
+ const spec = this.registry.spec(action.slice(prefix.length));
1852
+ if (spec !== void 0) return {
1853
+ name: spec.name,
1854
+ ...spec.presentation !== void 0 ? {
1855
+ presentation: spec.presentation
1856
+ } : {}
1857
+ };
1858
+ }
1859
+ return void 0;
1860
+ }
1769
1861
  /** The roles policy a turn's tools are gated by (the agent's own). */
1770
1862
  rolesPolicyFor(input) {
1771
1863
  return this.deps.forAgent(input.agentName).rolesPolicy;
@@ -2366,6 +2458,7 @@ ${body}
2366
2458
  })
2367
2459
  } : {}
2368
2460
  };
2461
+ live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
2369
2462
  await live.client.session.prompt({
2370
2463
  sessionID: handle.sessionId,
2371
2464
  text: prompt.text,
@@ -2445,12 +2538,23 @@ ${body}
2445
2538
  await live.turn.decide(ask, reply, {
2446
2539
  tellOpenCode: false
2447
2540
  });
2541
+ live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
2448
2542
  await live.client.session.prompt({
2449
2543
  sessionID: handle.sessionId,
2450
2544
  text: note
2451
2545
  });
2452
2546
  return handle;
2453
2547
  }
2548
+ /** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
2549
+ async sessionUsage(client, sessionId) {
2550
+ try {
2551
+ return await client.session.get?.({
2552
+ sessionID: sessionId
2553
+ });
2554
+ } catch {
2555
+ return void 0;
2556
+ }
2557
+ }
2454
2558
  /** Interrupt the thread's session (cancel). */
2455
2559
  async interrupt(input) {
2456
2560
  const known = await this.sessions.get(input.threadId);
@@ -2486,6 +2590,9 @@ ${body}
2486
2590
  agentName: input.agentName
2487
2591
  } : {}
2488
2592
  }), "approvalFor"),
2593
+ // An app tool reaches OpenCode as `<server>.<name>` / `<server>_<name>`: its own presentation
2594
+ // words the approval card. OpenCode's built-ins (`bash`, `edit`) have none.
2595
+ approvalPresentation: /* @__PURE__ */ __name((action) => this.presentationOf(action), "approvalPresentation"),
2489
2596
  modelLabel: input.model ?? "opencode",
2490
2597
  priceStep: /* @__PURE__ */ __name((step) => this.priceStep(step), "priceStep"),
2491
2598
  ...spent !== void 0 ? {