@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/README.md +6 -1
- package/dist/durable/index.cjs +107 -0
- package/dist/durable/index.cjs.map +1 -1
- package/dist/durable/index.d.cts +1 -1
- package/dist/durable/index.d.ts +1 -1
- package/dist/durable/index.js +108 -1
- package/dist/durable/index.js.map +1 -1
- package/dist/{engine-JnBbcRxh.d.cts → engine-Cp5uysaN.d.cts} +46 -1
- package/dist/{engine-JnBbcRxh.d.ts → engine-Cp5uysaN.d.ts} +46 -1
- package/dist/index.cjs +107 -0
- 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 +108 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentDepsFactory, ChatQueueService, AgentEngine } from '@dudousxd/nestjs-agent';
|
|
2
2
|
import { ConflictException, Logger, OnApplicationShutdown, Type, InjectionToken, Provider } from '@nestjs/common';
|
|
3
|
-
import { ElicitationRequest, CostSource, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, MessageUsage, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, AgentPricingStore, PriceCatalogOptions, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
|
|
3
|
+
import { ElicitationRequest, CostSource, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, ToolPresentation, MessageUsage, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, AgentPricingStore, PriceCatalogOptions, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
6
|
|
|
@@ -40,6 +40,9 @@ interface OpenCodeClient {
|
|
|
40
40
|
metadata?: {
|
|
41
41
|
readonly [key: string]: OpenCodeJson;
|
|
42
42
|
};
|
|
43
|
+
/** What the session spent so far, every model call OpenCode made for it (the title too). */
|
|
44
|
+
cost?: number;
|
|
45
|
+
tokens?: OpenCodeTokens;
|
|
43
46
|
}>;
|
|
44
47
|
/** Resolves when the session goes idle. Optional: a safety net for a missed terminal event. */
|
|
45
48
|
wait?(args: {
|
|
@@ -204,6 +207,16 @@ interface OpenCodeFormField {
|
|
|
204
207
|
/** The user may type their own answer instead of picking one of `options`. */
|
|
205
208
|
custom?: boolean;
|
|
206
209
|
}
|
|
210
|
+
/** A token count as OpenCode reports it: `input` is the uncached input, the cache beside it. */
|
|
211
|
+
interface OpenCodeTokens {
|
|
212
|
+
input: number;
|
|
213
|
+
output: number;
|
|
214
|
+
reasoning?: number;
|
|
215
|
+
cache?: {
|
|
216
|
+
read: number;
|
|
217
|
+
write: number;
|
|
218
|
+
};
|
|
219
|
+
}
|
|
207
220
|
|
|
208
221
|
/** How an OpenCode execution ended. */
|
|
209
222
|
type TurnOutcome = {
|
|
@@ -284,6 +297,15 @@ interface OpenCodeTurnArgs {
|
|
|
284
297
|
store: AgentStore;
|
|
285
298
|
/** What an action needs before it runs (the module's `ApprovalPolicy`). */
|
|
286
299
|
approvalFor: (action: string) => Promise<ApprovalRequirement>;
|
|
300
|
+
/**
|
|
301
|
+
* The app tool a permission `action` names, with its presentation: its `confirm` templates (filled
|
|
302
|
+
* from the call's arguments) word the approval card. `undefined` (or omitted) → an OpenCode
|
|
303
|
+
* built-in, worded from its name.
|
|
304
|
+
*/
|
|
305
|
+
approvalPresentation?: (action: string) => {
|
|
306
|
+
name: string;
|
|
307
|
+
presentation?: ToolPresentation;
|
|
308
|
+
} | undefined;
|
|
287
309
|
/** The model label usage is recorded under until OpenCode names the step's model. */
|
|
288
310
|
modelLabel: string;
|
|
289
311
|
/**
|
|
@@ -344,6 +366,10 @@ declare class OpenCodeTurn {
|
|
|
344
366
|
private reported;
|
|
345
367
|
private priced;
|
|
346
368
|
private model;
|
|
369
|
+
/** What the session had spent when this turn prompted it; what OpenCode reported since. */
|
|
370
|
+
private baseline;
|
|
371
|
+
private readonly seen;
|
|
372
|
+
private compacted;
|
|
347
373
|
private stepOpen;
|
|
348
374
|
private sawText;
|
|
349
375
|
private separator;
|
|
@@ -352,6 +378,16 @@ declare class OpenCodeTurn {
|
|
|
352
378
|
private ended;
|
|
353
379
|
private stepError;
|
|
354
380
|
constructor(a: OpenCodeTurnArgs);
|
|
381
|
+
/**
|
|
382
|
+
* What the session had spent before this turn prompted it (`session.get`). When the execution
|
|
383
|
+
* ends, what the session spent since that this turn's events did not report — the title OpenCode
|
|
384
|
+
* generates, a compaction: OpenCode records those calls on the session without streaming them — is
|
|
385
|
+
* recorded as a usage row of its own.
|
|
386
|
+
*/
|
|
387
|
+
setBaseline(info: {
|
|
388
|
+
cost?: number;
|
|
389
|
+
tokens?: OpenCodeTokens;
|
|
390
|
+
} | undefined): void;
|
|
355
391
|
/** What the run spent so far: carried from before this turn object, plus what it saw. */
|
|
356
392
|
totalUsage(): OpenCodeUsage;
|
|
357
393
|
get sessionId(): string;
|
|
@@ -418,6 +454,8 @@ declare class OpenCodeTurn {
|
|
|
418
454
|
private onForm;
|
|
419
455
|
private answered;
|
|
420
456
|
private addResult;
|
|
457
|
+
/** What the session spent since the baseline that no event of this turn reported. */
|
|
458
|
+
private unreported;
|
|
421
459
|
private end;
|
|
422
460
|
}
|
|
423
461
|
/**
|
|
@@ -790,6 +828,11 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
|
|
|
790
828
|
}): Promise<StepCost>;
|
|
791
829
|
/** The tools server's name in OpenCode. */
|
|
792
830
|
get toolsServer(): string;
|
|
831
|
+
/** The app tool an OpenCode permission `action` names, and its presentation — `undefined` for a built-in. */
|
|
832
|
+
presentationOf(action: string): {
|
|
833
|
+
name: string;
|
|
834
|
+
presentation?: ToolPresentation;
|
|
835
|
+
} | undefined;
|
|
793
836
|
/** The roles policy a turn's tools are gated by (the agent's own). */
|
|
794
837
|
rolesPolicyFor(input: Pick<AgentRunInput, 'agentName'>): RolesPolicy;
|
|
795
838
|
/** The tools a turn may reach: the agent's allow-list, narrowed by its persona's. */
|
|
@@ -889,6 +932,8 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
|
|
|
889
932
|
*/
|
|
890
933
|
reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply, spent?: OpenCodeUsage): Promise<SessionHandle>;
|
|
891
934
|
private continueAfterRestart;
|
|
935
|
+
/** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
|
|
936
|
+
private sessionUsage;
|
|
892
937
|
/** Interrupt the thread's session (cancel). */
|
|
893
938
|
interrupt(input: AgentRunInput): Promise<void>;
|
|
894
939
|
private ensureLive;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentDepsFactory, ChatQueueService, AgentEngine } from '@dudousxd/nestjs-agent';
|
|
2
2
|
import { ConflictException, Logger, OnApplicationShutdown, Type, InjectionToken, Provider } from '@nestjs/common';
|
|
3
|
-
import { ElicitationRequest, CostSource, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, MessageUsage, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, AgentPricingStore, PriceCatalogOptions, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
|
|
3
|
+
import { ElicitationRequest, CostSource, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, ToolPresentation, MessageUsage, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, AgentPricingStore, PriceCatalogOptions, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
|
|
4
4
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
5
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
6
|
|
|
@@ -40,6 +40,9 @@ interface OpenCodeClient {
|
|
|
40
40
|
metadata?: {
|
|
41
41
|
readonly [key: string]: OpenCodeJson;
|
|
42
42
|
};
|
|
43
|
+
/** What the session spent so far, every model call OpenCode made for it (the title too). */
|
|
44
|
+
cost?: number;
|
|
45
|
+
tokens?: OpenCodeTokens;
|
|
43
46
|
}>;
|
|
44
47
|
/** Resolves when the session goes idle. Optional: a safety net for a missed terminal event. */
|
|
45
48
|
wait?(args: {
|
|
@@ -204,6 +207,16 @@ interface OpenCodeFormField {
|
|
|
204
207
|
/** The user may type their own answer instead of picking one of `options`. */
|
|
205
208
|
custom?: boolean;
|
|
206
209
|
}
|
|
210
|
+
/** A token count as OpenCode reports it: `input` is the uncached input, the cache beside it. */
|
|
211
|
+
interface OpenCodeTokens {
|
|
212
|
+
input: number;
|
|
213
|
+
output: number;
|
|
214
|
+
reasoning?: number;
|
|
215
|
+
cache?: {
|
|
216
|
+
read: number;
|
|
217
|
+
write: number;
|
|
218
|
+
};
|
|
219
|
+
}
|
|
207
220
|
|
|
208
221
|
/** How an OpenCode execution ended. */
|
|
209
222
|
type TurnOutcome = {
|
|
@@ -284,6 +297,15 @@ interface OpenCodeTurnArgs {
|
|
|
284
297
|
store: AgentStore;
|
|
285
298
|
/** What an action needs before it runs (the module's `ApprovalPolicy`). */
|
|
286
299
|
approvalFor: (action: string) => Promise<ApprovalRequirement>;
|
|
300
|
+
/**
|
|
301
|
+
* The app tool a permission `action` names, with its presentation: its `confirm` templates (filled
|
|
302
|
+
* from the call's arguments) word the approval card. `undefined` (or omitted) → an OpenCode
|
|
303
|
+
* built-in, worded from its name.
|
|
304
|
+
*/
|
|
305
|
+
approvalPresentation?: (action: string) => {
|
|
306
|
+
name: string;
|
|
307
|
+
presentation?: ToolPresentation;
|
|
308
|
+
} | undefined;
|
|
287
309
|
/** The model label usage is recorded under until OpenCode names the step's model. */
|
|
288
310
|
modelLabel: string;
|
|
289
311
|
/**
|
|
@@ -344,6 +366,10 @@ declare class OpenCodeTurn {
|
|
|
344
366
|
private reported;
|
|
345
367
|
private priced;
|
|
346
368
|
private model;
|
|
369
|
+
/** What the session had spent when this turn prompted it; what OpenCode reported since. */
|
|
370
|
+
private baseline;
|
|
371
|
+
private readonly seen;
|
|
372
|
+
private compacted;
|
|
347
373
|
private stepOpen;
|
|
348
374
|
private sawText;
|
|
349
375
|
private separator;
|
|
@@ -352,6 +378,16 @@ declare class OpenCodeTurn {
|
|
|
352
378
|
private ended;
|
|
353
379
|
private stepError;
|
|
354
380
|
constructor(a: OpenCodeTurnArgs);
|
|
381
|
+
/**
|
|
382
|
+
* What the session had spent before this turn prompted it (`session.get`). When the execution
|
|
383
|
+
* ends, what the session spent since that this turn's events did not report — the title OpenCode
|
|
384
|
+
* generates, a compaction: OpenCode records those calls on the session without streaming them — is
|
|
385
|
+
* recorded as a usage row of its own.
|
|
386
|
+
*/
|
|
387
|
+
setBaseline(info: {
|
|
388
|
+
cost?: number;
|
|
389
|
+
tokens?: OpenCodeTokens;
|
|
390
|
+
} | undefined): void;
|
|
355
391
|
/** What the run spent so far: carried from before this turn object, plus what it saw. */
|
|
356
392
|
totalUsage(): OpenCodeUsage;
|
|
357
393
|
get sessionId(): string;
|
|
@@ -418,6 +454,8 @@ declare class OpenCodeTurn {
|
|
|
418
454
|
private onForm;
|
|
419
455
|
private answered;
|
|
420
456
|
private addResult;
|
|
457
|
+
/** What the session spent since the baseline that no event of this turn reported. */
|
|
458
|
+
private unreported;
|
|
421
459
|
private end;
|
|
422
460
|
}
|
|
423
461
|
/**
|
|
@@ -790,6 +828,11 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
|
|
|
790
828
|
}): Promise<StepCost>;
|
|
791
829
|
/** The tools server's name in OpenCode. */
|
|
792
830
|
get toolsServer(): string;
|
|
831
|
+
/** The app tool an OpenCode permission `action` names, and its presentation — `undefined` for a built-in. */
|
|
832
|
+
presentationOf(action: string): {
|
|
833
|
+
name: string;
|
|
834
|
+
presentation?: ToolPresentation;
|
|
835
|
+
} | undefined;
|
|
793
836
|
/** The roles policy a turn's tools are gated by (the agent's own). */
|
|
794
837
|
rolesPolicyFor(input: Pick<AgentRunInput, 'agentName'>): RolesPolicy;
|
|
795
838
|
/** The tools a turn may reach: the agent's allow-list, narrowed by its persona's. */
|
|
@@ -889,6 +932,8 @@ declare class OpenCodeTurns implements OnApplicationShutdown {
|
|
|
889
932
|
*/
|
|
890
933
|
reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply, spent?: OpenCodeUsage): Promise<SessionHandle>;
|
|
891
934
|
private continueAfterRestart;
|
|
935
|
+
/** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
|
|
936
|
+
private sessionUsage;
|
|
892
937
|
/** Interrupt the thread's session (cancel). */
|
|
893
938
|
interrupt(input: AgentRunInput): Promise<void>;
|
|
894
939
|
private ensureLive;
|
package/dist/index.cjs
CHANGED
|
@@ -395,6 +395,17 @@ var OpenCodeTurn = class {
|
|
|
395
395
|
reported = emptyUsage();
|
|
396
396
|
priced = false;
|
|
397
397
|
model;
|
|
398
|
+
/** What the session had spent when this turn prompted it; what OpenCode reported since. */
|
|
399
|
+
baseline;
|
|
400
|
+
seen = {
|
|
401
|
+
cost: 0,
|
|
402
|
+
input: 0,
|
|
403
|
+
output: 0,
|
|
404
|
+
reasoning: 0,
|
|
405
|
+
read: 0,
|
|
406
|
+
write: 0
|
|
407
|
+
};
|
|
408
|
+
compacted = false;
|
|
398
409
|
stepOpen = false;
|
|
399
410
|
sawText = false;
|
|
400
411
|
separator = false;
|
|
@@ -406,6 +417,19 @@ var OpenCodeTurn = class {
|
|
|
406
417
|
this.a = a;
|
|
407
418
|
this.model = modelOfLabel(a.input.model);
|
|
408
419
|
}
|
|
420
|
+
/**
|
|
421
|
+
* What the session had spent before this turn prompted it (`session.get`). When the execution
|
|
422
|
+
* ends, what the session spent since that this turn's events did not report — the title OpenCode
|
|
423
|
+
* generates, a compaction: OpenCode records those calls on the session without streaming them — is
|
|
424
|
+
* recorded as a usage row of its own.
|
|
425
|
+
*/
|
|
426
|
+
setBaseline(info) {
|
|
427
|
+
if (info?.tokens === void 0) return;
|
|
428
|
+
this.baseline = {
|
|
429
|
+
cost: Number(info.cost) || 0,
|
|
430
|
+
tokens: info.tokens
|
|
431
|
+
};
|
|
432
|
+
}
|
|
409
433
|
/** What the run spent so far: carried from before this turn object, plus what it saw. */
|
|
410
434
|
totalUsage() {
|
|
411
435
|
return addUsage(this.a.carried, this.spent);
|
|
@@ -662,6 +686,10 @@ var OpenCodeTurn = class {
|
|
|
662
686
|
}
|
|
663
687
|
if (this.ended) return;
|
|
664
688
|
switch (e.type) {
|
|
689
|
+
case "session.compaction.started":
|
|
690
|
+
case "session.compaction.ended":
|
|
691
|
+
this.compacted = true;
|
|
692
|
+
return;
|
|
665
693
|
case "session.step.started":
|
|
666
694
|
case "session.model.selected": {
|
|
667
695
|
const model = d.model;
|
|
@@ -831,6 +859,13 @@ ${delta}` : delta;
|
|
|
831
859
|
async spend(d, purpose) {
|
|
832
860
|
const usage = usageOf(d.tokens);
|
|
833
861
|
const reportedCostUsd = typeof d.cost === "number" && Number.isFinite(d.cost) && d.cost >= 0 ? d.cost : void 0;
|
|
862
|
+
const n = /* @__PURE__ */ __name((v) => typeof v === "number" && Number.isFinite(v) && v > 0 ? v : 0, "n");
|
|
863
|
+
this.seen.cost += reportedCostUsd ?? 0;
|
|
864
|
+
this.seen.input += n(d.tokens?.input);
|
|
865
|
+
this.seen.output += n(d.tokens?.output);
|
|
866
|
+
this.seen.reasoning += n(d.tokens?.reasoning);
|
|
867
|
+
this.seen.read += n(d.tokens?.cache?.read);
|
|
868
|
+
this.seen.write += n(d.tokens?.cache?.write);
|
|
834
869
|
const fallback = reportedCostUsd !== void 0 ? {
|
|
835
870
|
costUsd: reportedCostUsd,
|
|
836
871
|
costSource: "provider"
|
|
@@ -1036,6 +1071,8 @@ ${delta}` : delta;
|
|
|
1036
1071
|
}
|
|
1037
1072
|
const ttlMs = requirement.ttlMs !== void 0 && Number.isFinite(requirement.ttlMs) && requirement.ttlMs > 0 ? Math.floor(requirement.ttlMs) : void 0;
|
|
1038
1073
|
const expiresAt = ttlMs !== void 0 ? new Date(Date.now() + ttlMs).toISOString() : void 0;
|
|
1074
|
+
const tool = this.a.approvalPresentation?.(action);
|
|
1075
|
+
const confirmation = tool !== void 0 ? (0, import_nestjs_agent_core2.resolveToolConfirmation)(tool.name, args, tool.presentation) : void 0;
|
|
1039
1076
|
this.segment.calls.push({
|
|
1040
1077
|
id: req.id,
|
|
1041
1078
|
name: action,
|
|
@@ -1050,6 +1087,9 @@ ${delta}` : delta;
|
|
|
1050
1087
|
toolType: "action",
|
|
1051
1088
|
input: args,
|
|
1052
1089
|
status: "pending_approval",
|
|
1090
|
+
...confirmation !== void 0 ? {
|
|
1091
|
+
confirmation
|
|
1092
|
+
} : {},
|
|
1053
1093
|
runId: this.a.runId,
|
|
1054
1094
|
approver: requirement.approver,
|
|
1055
1095
|
...expiresAt !== void 0 ? {
|
|
@@ -1067,6 +1107,9 @@ ${delta}` : delta;
|
|
|
1067
1107
|
kind: "approval-requested",
|
|
1068
1108
|
id: req.id,
|
|
1069
1109
|
approver: requirement.approver,
|
|
1110
|
+
...confirmation !== void 0 ? {
|
|
1111
|
+
confirmation
|
|
1112
|
+
} : {},
|
|
1070
1113
|
...expiresAt !== void 0 ? {
|
|
1071
1114
|
expiresAt
|
|
1072
1115
|
} : {}
|
|
@@ -1298,9 +1341,39 @@ ${delta}` : delta;
|
|
|
1298
1341
|
this.messageResults.set(messageId, results);
|
|
1299
1342
|
await this.a.store.setMessageToolResults(messageId, results);
|
|
1300
1343
|
}
|
|
1344
|
+
/** What the session spent since the baseline that no event of this turn reported. */
|
|
1345
|
+
async unreported() {
|
|
1346
|
+
const baseline = this.baseline;
|
|
1347
|
+
if (baseline === void 0) return;
|
|
1348
|
+
this.baseline = void 0;
|
|
1349
|
+
const info = await this.a.client.session.get?.({
|
|
1350
|
+
sessionID: this.a.sessionId
|
|
1351
|
+
}).catch(() => void 0);
|
|
1352
|
+
const after = info?.tokens;
|
|
1353
|
+
if (after === void 0) return;
|
|
1354
|
+
const before = baseline.tokens;
|
|
1355
|
+
const left = /* @__PURE__ */ __name((now, then, seen) => Math.max(0, (Number(now) || 0) - (Number(then) || 0) - seen), "left");
|
|
1356
|
+
const tokens = {
|
|
1357
|
+
input: left(after.input, before.input, this.seen.input),
|
|
1358
|
+
output: left(after.output, before.output, this.seen.output),
|
|
1359
|
+
reasoning: left(after.reasoning, before.reasoning, this.seen.reasoning),
|
|
1360
|
+
cache: {
|
|
1361
|
+
read: left(after.cache?.read, before.cache?.read, this.seen.read),
|
|
1362
|
+
write: left(after.cache?.write, before.cache?.write, this.seen.write)
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
const total = tokens.input + tokens.output + tokens.reasoning + tokens.cache.read + tokens.cache.write;
|
|
1366
|
+
if (total === 0) return;
|
|
1367
|
+
const cost = left(info?.cost, baseline.cost, this.seen.cost);
|
|
1368
|
+
await this.spend({
|
|
1369
|
+
tokens,
|
|
1370
|
+
cost: cost < 1e-9 ? 0 : cost
|
|
1371
|
+
}, this.compacted ? "history_summary" : "title");
|
|
1372
|
+
}
|
|
1301
1373
|
async end(outcome) {
|
|
1302
1374
|
if (this.ended) return;
|
|
1303
1375
|
this.ended = true;
|
|
1376
|
+
await this.unreported().catch((error) => this.a.logger.warn(`run ${this.a.runId}: ${errorText(error, "could not read the session usage")}`));
|
|
1304
1377
|
if (outcome.status !== "failed" || this.segment.text || this.segment.calls.length > 0 || this.segment.ui.length > 0) {
|
|
1305
1378
|
await this.flush(true);
|
|
1306
1379
|
}
|
|
@@ -1817,6 +1890,25 @@ var OpenCodeTurns = class {
|
|
|
1817
1890
|
get toolsServer() {
|
|
1818
1891
|
return this.settings.tools?.server ?? "aviary";
|
|
1819
1892
|
}
|
|
1893
|
+
/** The app tool an OpenCode permission `action` names, and its presentation — `undefined` for a built-in. */
|
|
1894
|
+
presentationOf(action) {
|
|
1895
|
+
if (this.settings.tools === void 0) return void 0;
|
|
1896
|
+
const server = this.toolsServer;
|
|
1897
|
+
for (const prefix of [
|
|
1898
|
+
`${server}.`,
|
|
1899
|
+
`${server}_`
|
|
1900
|
+
]) {
|
|
1901
|
+
if (!action.startsWith(prefix)) continue;
|
|
1902
|
+
const spec = this.registry.spec(action.slice(prefix.length));
|
|
1903
|
+
if (spec !== void 0) return {
|
|
1904
|
+
name: spec.name,
|
|
1905
|
+
...spec.presentation !== void 0 ? {
|
|
1906
|
+
presentation: spec.presentation
|
|
1907
|
+
} : {}
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
return void 0;
|
|
1911
|
+
}
|
|
1820
1912
|
/** The roles policy a turn's tools are gated by (the agent's own). */
|
|
1821
1913
|
rolesPolicyFor(input) {
|
|
1822
1914
|
return this.deps.forAgent(input.agentName).rolesPolicy;
|
|
@@ -2417,6 +2509,7 @@ ${body}
|
|
|
2417
2509
|
})
|
|
2418
2510
|
} : {}
|
|
2419
2511
|
};
|
|
2512
|
+
live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
|
|
2420
2513
|
await live.client.session.prompt({
|
|
2421
2514
|
sessionID: handle.sessionId,
|
|
2422
2515
|
text: prompt.text,
|
|
@@ -2496,12 +2589,23 @@ ${body}
|
|
|
2496
2589
|
await live.turn.decide(ask, reply, {
|
|
2497
2590
|
tellOpenCode: false
|
|
2498
2591
|
});
|
|
2592
|
+
live.turn.setBaseline(await this.sessionUsage(live.client, handle.sessionId));
|
|
2499
2593
|
await live.client.session.prompt({
|
|
2500
2594
|
sessionID: handle.sessionId,
|
|
2501
2595
|
text: note
|
|
2502
2596
|
});
|
|
2503
2597
|
return handle;
|
|
2504
2598
|
}
|
|
2599
|
+
/** What a session spent so far (`session.get`), or nothing when OpenCode does not say. */
|
|
2600
|
+
async sessionUsage(client, sessionId) {
|
|
2601
|
+
try {
|
|
2602
|
+
return await client.session.get?.({
|
|
2603
|
+
sessionID: sessionId
|
|
2604
|
+
});
|
|
2605
|
+
} catch {
|
|
2606
|
+
return void 0;
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2505
2609
|
/** Interrupt the thread's session (cancel). */
|
|
2506
2610
|
async interrupt(input) {
|
|
2507
2611
|
const known = await this.sessions.get(input.threadId);
|
|
@@ -2537,6 +2641,9 @@ ${body}
|
|
|
2537
2641
|
agentName: input.agentName
|
|
2538
2642
|
} : {}
|
|
2539
2643
|
}), "approvalFor"),
|
|
2644
|
+
// An app tool reaches OpenCode as `<server>.<name>` / `<server>_<name>`: its own presentation
|
|
2645
|
+
// words the approval card. OpenCode's built-ins (`bash`, `edit`) have none.
|
|
2646
|
+
approvalPresentation: /* @__PURE__ */ __name((action) => this.presentationOf(action), "approvalPresentation"),
|
|
2540
2647
|
modelLabel: input.model ?? "opencode",
|
|
2541
2648
|
priceStep: /* @__PURE__ */ __name((step) => this.priceStep(step), "priceStep"),
|
|
2542
2649
|
...spent !== void 0 ? {
|