@dudousxd/nestjs-agent-opencode 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +169 -2
- package/dist/durable/index.cjs +3138 -0
- package/dist/durable/index.cjs.map +1 -0
- package/dist/durable/index.d.cts +75 -0
- package/dist/durable/index.d.ts +75 -0
- package/dist/durable/index.js +3109 -0
- package/dist/durable/index.js.map +1 -0
- package/dist/engine-Bij6AdoF.d.cts +849 -0
- package/dist/engine-Bij6AdoF.d.ts +849 -0
- package/dist/index.cjs +2938 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +124 -0
- package/dist/index.d.ts +124 -0
- package/dist/index.js +2889 -0
- package/dist/index.js.map +1 -0
- package/package.json +93 -3
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
import { AgentDepsFactory, ChatQueueService, AgentEngine } from '@dudousxd/nestjs-agent';
|
|
2
|
+
import { ConflictException, Logger, OnApplicationShutdown, Type, InjectionToken, Provider } from '@nestjs/common';
|
|
3
|
+
import { ElicitationRequest, AgentRunInput, SinkWriter, AgentStore, ApprovalRequirement, AgentUiComponent, HumanReply, Actor, StoredMessage, ToolRegistry, TokenStreamSink, SkillsConfig, MemoryConfig, RolesPolicy, RememberToolInput, AiToolCtx } from '@dudousxd/nestjs-agent-core';
|
|
4
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
5
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The part of OpenCode 2's server API (`@opencode/client`, the v2 routes `/api/session`,
|
|
9
|
+
* `/api/event`, …) this engine calls — structural, so the engine has no runtime dependency on the
|
|
10
|
+
* client package and a test can hand it a fake. A real `@opencode/client` client satisfies it.
|
|
11
|
+
*/
|
|
12
|
+
interface OpenCodeClient {
|
|
13
|
+
session: {
|
|
14
|
+
create(args: OpenCodeSessionCreate): Promise<{
|
|
15
|
+
id: string;
|
|
16
|
+
}>;
|
|
17
|
+
prompt(args: {
|
|
18
|
+
sessionID: string;
|
|
19
|
+
text: string;
|
|
20
|
+
files?: OpenCodePromptFile[];
|
|
21
|
+
}): Promise<unknown>;
|
|
22
|
+
/** Stops the running execution; OpenCode answers with `session.execution.interrupted`. */
|
|
23
|
+
interrupt(args: {
|
|
24
|
+
sessionID: string;
|
|
25
|
+
}): Promise<unknown>;
|
|
26
|
+
instructions: {
|
|
27
|
+
entry: {
|
|
28
|
+
put(args: {
|
|
29
|
+
sessionID: string;
|
|
30
|
+
key: string;
|
|
31
|
+
value: string;
|
|
32
|
+
}): Promise<unknown>;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/** The session's info. Optional: finds the thread of a session (its `metadata.threadId`). */
|
|
36
|
+
get?(args: {
|
|
37
|
+
sessionID: string;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
id: string;
|
|
40
|
+
metadata?: {
|
|
41
|
+
readonly [key: string]: OpenCodeJson;
|
|
42
|
+
};
|
|
43
|
+
}>;
|
|
44
|
+
/** Resolves when the session goes idle. Optional: a safety net for a missed terminal event. */
|
|
45
|
+
wait?(args: {
|
|
46
|
+
sessionID: string;
|
|
47
|
+
}): Promise<unknown>;
|
|
48
|
+
/** Rewind the session to before a message (regenerate). Optional. */
|
|
49
|
+
revert?: {
|
|
50
|
+
stage(args: {
|
|
51
|
+
sessionID: string;
|
|
52
|
+
messageID: string;
|
|
53
|
+
files?: boolean;
|
|
54
|
+
}): Promise<unknown>;
|
|
55
|
+
commit(args: {
|
|
56
|
+
sessionID: string;
|
|
57
|
+
}): Promise<unknown>;
|
|
58
|
+
};
|
|
59
|
+
form: {
|
|
60
|
+
/** The session's open forms. Optional: lets a resumed turn find questions asked while nobody listened. */
|
|
61
|
+
list?(args: {
|
|
62
|
+
sessionID: string;
|
|
63
|
+
}): Promise<OpenCodeForm[]>;
|
|
64
|
+
reply(args: {
|
|
65
|
+
sessionID: string;
|
|
66
|
+
formID: string;
|
|
67
|
+
answer: Record<string, OpenCodeFormValue>;
|
|
68
|
+
}): Promise<unknown>;
|
|
69
|
+
cancel(args: {
|
|
70
|
+
sessionID: string;
|
|
71
|
+
formID: string;
|
|
72
|
+
}): Promise<unknown>;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
permission: {
|
|
76
|
+
/** The session's open permission requests. Optional, like `session.form.list`. */
|
|
77
|
+
list?(args: {
|
|
78
|
+
sessionID: string;
|
|
79
|
+
}): Promise<OpenCodePermissionRequest[]>;
|
|
80
|
+
reply(args: {
|
|
81
|
+
sessionID: string;
|
|
82
|
+
requestID: string;
|
|
83
|
+
decision: 'once' | 'reject';
|
|
84
|
+
message?: string;
|
|
85
|
+
}): Promise<unknown>;
|
|
86
|
+
};
|
|
87
|
+
/** The session's messages, newest first with `order: 'desc'`. Optional (regenerate). */
|
|
88
|
+
message?: {
|
|
89
|
+
list(args: {
|
|
90
|
+
sessionID: string;
|
|
91
|
+
order?: 'asc' | 'desc';
|
|
92
|
+
limit?: number;
|
|
93
|
+
}): Promise<{
|
|
94
|
+
data: Array<{
|
|
95
|
+
id: string;
|
|
96
|
+
type: string;
|
|
97
|
+
}>;
|
|
98
|
+
}>;
|
|
99
|
+
};
|
|
100
|
+
/** Register an MCP server at a location. Optional (exposing the module's tools). */
|
|
101
|
+
mcp?: {
|
|
102
|
+
add(args: {
|
|
103
|
+
server: string;
|
|
104
|
+
location?: {
|
|
105
|
+
directory: string;
|
|
106
|
+
};
|
|
107
|
+
config: {
|
|
108
|
+
type: 'remote';
|
|
109
|
+
url: string;
|
|
110
|
+
headers?: Record<string, string>;
|
|
111
|
+
/** `false`: never start OAuth for this server (the headers authenticate it). */
|
|
112
|
+
oauth?: false;
|
|
113
|
+
};
|
|
114
|
+
}): Promise<unknown>;
|
|
115
|
+
};
|
|
116
|
+
/** Write a file on the server. Optional (writing the module's skills where OpenCode finds them). */
|
|
117
|
+
file?: {
|
|
118
|
+
write(args: {
|
|
119
|
+
location?: {
|
|
120
|
+
directory: string;
|
|
121
|
+
};
|
|
122
|
+
path: string;
|
|
123
|
+
payload: Uint8Array;
|
|
124
|
+
}): Promise<unknown>;
|
|
125
|
+
};
|
|
126
|
+
event: {
|
|
127
|
+
/** Every event of the server, for every session, until `signal` aborts. */
|
|
128
|
+
subscribe(args: {
|
|
129
|
+
signal: AbortSignal;
|
|
130
|
+
}): AsyncIterable<OpenCodeEvent>;
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** A file attached to a prompt: a URI OpenCode can read (`file://…`, `data:…`, `https://…`). */
|
|
134
|
+
interface OpenCodePromptFile {
|
|
135
|
+
uri: string;
|
|
136
|
+
name?: string;
|
|
137
|
+
description?: string;
|
|
138
|
+
}
|
|
139
|
+
/** A JSON value — what OpenCode stores as metadata. */
|
|
140
|
+
type OpenCodeJson = string | number | boolean | null | OpenCodeJson[] | {
|
|
141
|
+
readonly [key: string]: OpenCodeJson;
|
|
142
|
+
};
|
|
143
|
+
interface OpenCodeSessionCreate {
|
|
144
|
+
/** A named OpenCode agent (`.opencode/agents/<name>`), when the location defines one. */
|
|
145
|
+
agent?: string;
|
|
146
|
+
model?: OpenCodeModelRef;
|
|
147
|
+
location?: {
|
|
148
|
+
directory: string;
|
|
149
|
+
};
|
|
150
|
+
/** OpenCode permission rules (`{ action, resource, effect: 'allow' | 'deny' | 'ask' }`). */
|
|
151
|
+
permissions?: OpenCodePermissionRule[];
|
|
152
|
+
metadata?: {
|
|
153
|
+
readonly [key: string]: OpenCodeJson;
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
interface OpenCodeModelRef {
|
|
157
|
+
providerID: string;
|
|
158
|
+
id: string;
|
|
159
|
+
variant?: string;
|
|
160
|
+
}
|
|
161
|
+
interface OpenCodePermissionRule {
|
|
162
|
+
action: string;
|
|
163
|
+
resource: string;
|
|
164
|
+
effect: 'allow' | 'deny' | 'ask';
|
|
165
|
+
}
|
|
166
|
+
/** What a form reply takes for one field. */
|
|
167
|
+
type OpenCodeFormValue = string | number | boolean | string[];
|
|
168
|
+
/** One server event. `data.sessionID` (or `data.form.sessionID` on form events) names its session. */
|
|
169
|
+
interface OpenCodeEvent {
|
|
170
|
+
type: string;
|
|
171
|
+
data?: any;
|
|
172
|
+
}
|
|
173
|
+
/** A permission OpenCode is waiting on (`permission.asked`): an `ask` rule matched a tool call. */
|
|
174
|
+
interface OpenCodePermissionRequest {
|
|
175
|
+
id: string;
|
|
176
|
+
sessionID: string;
|
|
177
|
+
/** The rule's action, e.g. `company.send_email` or `webfetch`. */
|
|
178
|
+
action: string;
|
|
179
|
+
resources?: string[];
|
|
180
|
+
/**
|
|
181
|
+
* About the call. A built-in tool's arguments sit straight here (`webfetch` → `{ url, format }`);
|
|
182
|
+
* calls that wrap others nest them under `input` or `args`.
|
|
183
|
+
*/
|
|
184
|
+
metadata?: Record<string, unknown>;
|
|
185
|
+
}
|
|
186
|
+
/** A form the model put to the user through OpenCode's question tool (`form.created`). */
|
|
187
|
+
interface OpenCodeForm {
|
|
188
|
+
id: string;
|
|
189
|
+
sessionID: string;
|
|
190
|
+
title?: string;
|
|
191
|
+
fields?: OpenCodeFormField[];
|
|
192
|
+
}
|
|
193
|
+
interface OpenCodeFormField {
|
|
194
|
+
key: string;
|
|
195
|
+
type?: string;
|
|
196
|
+
title?: string;
|
|
197
|
+
description?: string;
|
|
198
|
+
required?: boolean;
|
|
199
|
+
options?: Array<{
|
|
200
|
+
value: string;
|
|
201
|
+
label?: string;
|
|
202
|
+
description?: string;
|
|
203
|
+
}>;
|
|
204
|
+
/** The user may type their own answer instead of picking one of `options`. */
|
|
205
|
+
custom?: boolean;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** How an OpenCode execution ended. */
|
|
209
|
+
type TurnOutcome = {
|
|
210
|
+
status: 'succeeded';
|
|
211
|
+
} | {
|
|
212
|
+
status: 'failed';
|
|
213
|
+
error: string;
|
|
214
|
+
} | {
|
|
215
|
+
status: 'interrupted';
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Something the turn put to a person, with everything needed to hand the answer back to OpenCode
|
|
219
|
+
* later — serializable, so a durable run journals it and replies from another process.
|
|
220
|
+
*/
|
|
221
|
+
type PendingAsk = {
|
|
222
|
+
kind: 'approval';
|
|
223
|
+
/** The OpenCode permission request id — also the tool-call id the decision is signalled under. */
|
|
224
|
+
id: string;
|
|
225
|
+
action: string;
|
|
226
|
+
approver: string;
|
|
227
|
+
expiresAt?: string;
|
|
228
|
+
/** The assistant message the call hangs on (absent for a request recovered after a restart). */
|
|
229
|
+
messageId?: string;
|
|
230
|
+
} | {
|
|
231
|
+
kind: 'form';
|
|
232
|
+
id: string;
|
|
233
|
+
form: OpenCodeForm;
|
|
234
|
+
request: ElicitationRequest;
|
|
235
|
+
messageId?: string;
|
|
236
|
+
};
|
|
237
|
+
/** Where a turn stands: it needs a person, or it is over. */
|
|
238
|
+
type Milestone = {
|
|
239
|
+
kind: 'ask';
|
|
240
|
+
ask: PendingAsk;
|
|
241
|
+
timeoutMs?: number;
|
|
242
|
+
} | {
|
|
243
|
+
kind: 'finished';
|
|
244
|
+
outcome: TurnOutcome;
|
|
245
|
+
};
|
|
246
|
+
interface OpenCodeTurnArgs {
|
|
247
|
+
runId: string;
|
|
248
|
+
input: AgentRunInput;
|
|
249
|
+
client: OpenCodeClient;
|
|
250
|
+
sessionId: string;
|
|
251
|
+
writer: SinkWriter;
|
|
252
|
+
store: AgentStore;
|
|
253
|
+
/** What an action needs before it runs (the module's `ApprovalPolicy`). */
|
|
254
|
+
approvalFor: (action: string) => Promise<ApprovalRequirement>;
|
|
255
|
+
/** The model label usage is recorded under. */
|
|
256
|
+
modelLabel: string;
|
|
257
|
+
logger: Logger;
|
|
258
|
+
/**
|
|
259
|
+
* An action OpenCode was allowed to run (a person approved it, or the policy did): the approval
|
|
260
|
+
* the tools endpoint spends when OpenCode calls it. Called BEFORE OpenCode is told yes.
|
|
261
|
+
*/
|
|
262
|
+
onGranted?: (action: string, permissionId: string) => void;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Answers (a form reply, a skip) sent to a call waiting for an approve/reject. They say nothing
|
|
266
|
+
* about whether the action should run — and treating them as a "no" would record a rejection the
|
|
267
|
+
* person never made — so the reply is refused (409) and the call keeps waiting.
|
|
268
|
+
*/
|
|
269
|
+
declare class OpenCodeReplyMismatchError extends ConflictException {
|
|
270
|
+
readonly runId: string;
|
|
271
|
+
readonly toolCallId: string;
|
|
272
|
+
constructor(runId: string, toolCallId: string);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* One turn of an OpenCode session, as the library's stream and store see it. Fed the session's
|
|
276
|
+
* events in order, it writes the protocol's frames (`docs/stream-protocol.md`) and persists what the
|
|
277
|
+
* loop would have — the assistant messages with their tool calls and results, the calls put to a
|
|
278
|
+
* person (so the approve/answer routes find their run), usage per model step — and reports
|
|
279
|
+
* {@link Milestone}s: a person was asked something, or the execution ended.
|
|
280
|
+
*
|
|
281
|
+
* The turn does not wait for people itself. Whoever drives it (`OpenCodeTurns`, under an in-memory
|
|
282
|
+
* or a durable runner) takes the `ask` milestone, waits however it waits, and hands the answer back
|
|
283
|
+
* with {@link decide} — which is what lets a durable run wait across a restart and reply from
|
|
284
|
+
* another process, with a fresh turn object fed the journaled {@link PendingAsk}.
|
|
285
|
+
*/
|
|
286
|
+
declare class OpenCodeTurn {
|
|
287
|
+
private readonly a;
|
|
288
|
+
/** Serializes the handling of events, so frames and rows land in the order OpenCode sent them. */
|
|
289
|
+
private chain;
|
|
290
|
+
private segment;
|
|
291
|
+
private readonly tools;
|
|
292
|
+
/** Results already persisted per message, so a late settlement adds to them rather than replacing. */
|
|
293
|
+
private readonly messageResults;
|
|
294
|
+
private readonly asked;
|
|
295
|
+
/** Calls of {@link HIDDEN_TOOLS}, by id. */
|
|
296
|
+
private readonly hidden;
|
|
297
|
+
private readonly milestones;
|
|
298
|
+
private waiting;
|
|
299
|
+
private readonly usage;
|
|
300
|
+
private costUsd;
|
|
301
|
+
private stepOpen;
|
|
302
|
+
private sawText;
|
|
303
|
+
private separator;
|
|
304
|
+
private reasoningSince;
|
|
305
|
+
private wroteMessage;
|
|
306
|
+
private ended;
|
|
307
|
+
private stepError;
|
|
308
|
+
constructor(a: OpenCodeTurnArgs);
|
|
309
|
+
get sessionId(): string;
|
|
310
|
+
get finished(): boolean;
|
|
311
|
+
/** Feed one of the session's events. */
|
|
312
|
+
handle(event: OpenCodeEvent): void;
|
|
313
|
+
/** End the turn as failed without an event (the prompt was refused, the stream is gone). */
|
|
314
|
+
fail(error: string): void;
|
|
315
|
+
/**
|
|
316
|
+
* A component a tool pushed (`ctx.emitUi`, through the MCP surface): streamed as a `ui` frame and
|
|
317
|
+
* persisted on the message being written, in the order it arrived. Pushing the same id again
|
|
318
|
+
* replaces it.
|
|
319
|
+
*/
|
|
320
|
+
pushUi(component: AgentUiComponent): Promise<void>;
|
|
321
|
+
/** The next milestone: one already reached, or the next one to come. */
|
|
322
|
+
next(): Promise<Milestone>;
|
|
323
|
+
/** Whether a milestone is waiting to be taken. */
|
|
324
|
+
hasMilestone(): boolean;
|
|
325
|
+
/**
|
|
326
|
+
* Requests OpenCode raised while nobody listened (this process started after them, or the event
|
|
327
|
+
* stream dropped): its open permissions and forms, handled as if their events had just arrived.
|
|
328
|
+
* One already recorded in the store is reported without being recorded or streamed again.
|
|
329
|
+
*/
|
|
330
|
+
catchUp(): Promise<void>;
|
|
331
|
+
/**
|
|
332
|
+
* Hand a person's answer to what the turn asked, and let OpenCode go on. `tellOpenCode: false`
|
|
333
|
+
* only settles the call (stream and store): the request died with a restarted server, and the
|
|
334
|
+
* new session is told the answer in its prompt instead.
|
|
335
|
+
*/
|
|
336
|
+
decide(ask: PendingAsk, reply: HumanReply, opts?: {
|
|
337
|
+
tellOpenCode?: boolean;
|
|
338
|
+
}): Promise<void>;
|
|
339
|
+
private reach;
|
|
340
|
+
private enqueue;
|
|
341
|
+
private write;
|
|
342
|
+
private step;
|
|
343
|
+
private endReasoning;
|
|
344
|
+
private tool;
|
|
345
|
+
private announce;
|
|
346
|
+
private makeAvailable;
|
|
347
|
+
private setInput;
|
|
348
|
+
private innerCalls;
|
|
349
|
+
private onEvent;
|
|
350
|
+
private settleTool;
|
|
351
|
+
private stepEnded;
|
|
352
|
+
/**
|
|
353
|
+
* Persist what the turn produced since the last flush as one assistant message — the message a
|
|
354
|
+
* call put to a person hangs on, or the turn's last one. Returns its id.
|
|
355
|
+
*/
|
|
356
|
+
private flush;
|
|
357
|
+
/** A request recorded by an earlier process (a turn resumed after a restart). */
|
|
358
|
+
private alreadyRecorded;
|
|
359
|
+
/** The persisted assistant message carrying call `id`, so its result lands on the same message. */
|
|
360
|
+
private messageOf;
|
|
361
|
+
private onPermission;
|
|
362
|
+
private decided;
|
|
363
|
+
private onForm;
|
|
364
|
+
private answered;
|
|
365
|
+
private addResult;
|
|
366
|
+
private end;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Where a turn runs and how its OpenCode session is set up — the part only the host knows. The
|
|
371
|
+
* engine owns everything between the library and OpenCode (sessions per thread, the event stream,
|
|
372
|
+
* the turn's frames, approvals and questions, cancel); the host answers these questions.
|
|
373
|
+
*/
|
|
374
|
+
interface OpenCodeHost {
|
|
375
|
+
/**
|
|
376
|
+
* The OpenCode server this actor's turns run on — one per tenant in a sandbox, one for the whole
|
|
377
|
+
* deployment, anything. Asked once per turn, so a server that restarted is picked up.
|
|
378
|
+
*/
|
|
379
|
+
server(actor: Actor): Promise<OpenCodeServer>;
|
|
380
|
+
/**
|
|
381
|
+
* How to create a session for a thread: location, model, OpenCode agent, permission rules.
|
|
382
|
+
* Asked only when the thread has no live session on this server (its first turn, or the server
|
|
383
|
+
* restarted since). `input.model` is the model the caller picked for this turn, when it picked one.
|
|
384
|
+
*/
|
|
385
|
+
session(context: OpenCodeTurnContext): Promise<OpenCodeSessionCreate>;
|
|
386
|
+
/**
|
|
387
|
+
* Instructions entries refreshed on every turn (key → text), on top of the agent's own prompt
|
|
388
|
+
* (`@SystemPrompt` and the contributors, under `aviary.system`). Profile, memories, the project
|
|
389
|
+
* a chat is about… An entry that disappears is not cleared: put an empty value to drop it.
|
|
390
|
+
*/
|
|
391
|
+
instructions?(context: OpenCodeTurnContext & {
|
|
392
|
+
sessionId: string;
|
|
393
|
+
}): Promise<Record<string, string>>;
|
|
394
|
+
/**
|
|
395
|
+
* What the session is prompted with, when it is more than the user message: documents read into
|
|
396
|
+
* the text, images as files the model can see. Omit → the user message, and `files`.
|
|
397
|
+
*/
|
|
398
|
+
promptFor?(context: OpenCodeTurnContext & {
|
|
399
|
+
sessionId: string;
|
|
400
|
+
}): Promise<{
|
|
401
|
+
text: string;
|
|
402
|
+
files?: OpenCodePromptFile[];
|
|
403
|
+
}>;
|
|
404
|
+
/**
|
|
405
|
+
* The user message's attachments (`input.attachments`) in the form `session.prompt` takes them.
|
|
406
|
+
* Omit → attachments are not sent to OpenCode. Not asked when `promptFor` answers.
|
|
407
|
+
*/
|
|
408
|
+
files?(context: OpenCodeTurnContext): Promise<OpenCodePromptFile[]>;
|
|
409
|
+
/**
|
|
410
|
+
* Anything else to do on a NEW session before its first prompt — register MCP servers
|
|
411
|
+
* (`client.mcp.add`), write skill files… Runs right after `session` and before `instructions`.
|
|
412
|
+
*/
|
|
413
|
+
prepare?(context: OpenCodeTurnContext & {
|
|
414
|
+
sessionId: string;
|
|
415
|
+
client: OpenCodeClient;
|
|
416
|
+
}): Promise<void>;
|
|
417
|
+
/**
|
|
418
|
+
* `openCodeDurable()` only: the turn's workflow start options (tags, search attributes, a
|
|
419
|
+
* concurrency quota) — like `settings.durable.start`, from a host that has its services in DI.
|
|
420
|
+
*/
|
|
421
|
+
startOptions?(input: AgentRunInput, runId: string): Promise<Record<string, unknown>>;
|
|
422
|
+
/** `openCodeDurable()` only: what a refused start becomes (e.g. a concurrency limit → a 429). */
|
|
423
|
+
startError?(error: unknown, input: AgentRunInput): unknown;
|
|
424
|
+
/**
|
|
425
|
+
* May this turn keep the thread's session? Asked when the session is still on its server; `false`
|
|
426
|
+
* opens a new one (told the conversation so far) — e.g. the person the session's tools act for
|
|
427
|
+
* changed. Omit → always reuse.
|
|
428
|
+
*/
|
|
429
|
+
reuse?(context: OpenCodeTurnContext & {
|
|
430
|
+
session: OpenCodeSessionRef;
|
|
431
|
+
}): Promise<boolean>;
|
|
432
|
+
/**
|
|
433
|
+
* Every turn, once the session is known (`created`: opened for this turn) and before the prompt:
|
|
434
|
+
* bring it up to date — the turn's model, permission rules that changed, tools, skills.
|
|
435
|
+
*/
|
|
436
|
+
beforePrompt?(context: OpenCodeTurnContext & {
|
|
437
|
+
sessionId: string;
|
|
438
|
+
client: OpenCodeClient;
|
|
439
|
+
created: boolean;
|
|
440
|
+
}): Promise<void>;
|
|
441
|
+
/**
|
|
442
|
+
* A person was asked something (an approval or a question form): post it where else they are —
|
|
443
|
+
* a Slack thread, a Teams chat — or wake whoever waits on the run. May run again for the same ask
|
|
444
|
+
* after a restart: make it idempotent.
|
|
445
|
+
*/
|
|
446
|
+
onAsk?(context: OpenCodeTurnContext & {
|
|
447
|
+
sessionId: string;
|
|
448
|
+
ask: PendingAsk;
|
|
449
|
+
}): Promise<void>;
|
|
450
|
+
/** A tool pushed a component into the run (`ctx.emitUi` over MCP). */
|
|
451
|
+
onUi?(context: OpenCodeTurnContext & {
|
|
452
|
+
component: AgentUiComponent;
|
|
453
|
+
}): Promise<void>;
|
|
454
|
+
/**
|
|
455
|
+
* The run is over and about to settle: the last word on the answer — components appended to it
|
|
456
|
+
* (a guardrail notice), and for a failed run the error the person reads instead of OpenCode's.
|
|
457
|
+
*/
|
|
458
|
+
beforeSettle?(result: OpenCodeRunResult): Promise<OpenCodeAmendment | undefined>;
|
|
459
|
+
/**
|
|
460
|
+
* The run settled (the stream ended, the thread moved on): deliver the answer elsewhere, record
|
|
461
|
+
* spend and telemetry. Errors are logged, never the run's.
|
|
462
|
+
*/
|
|
463
|
+
onSettled?(result: OpenCodeRunResult): Promise<void>;
|
|
464
|
+
}
|
|
465
|
+
/** What a run produced, as the host's settle hooks see it. */
|
|
466
|
+
interface OpenCodeRunResult {
|
|
467
|
+
runId: string;
|
|
468
|
+
input: AgentRunInput;
|
|
469
|
+
outcome: TurnOutcome;
|
|
470
|
+
/** The run's answer: its assistant messages' text, in order. */
|
|
471
|
+
text: string;
|
|
472
|
+
/** The run's assistant messages, as stored. */
|
|
473
|
+
messages: StoredMessage[];
|
|
474
|
+
usage: {
|
|
475
|
+
inputTokens: number;
|
|
476
|
+
outputTokens: number;
|
|
477
|
+
costUsd: number;
|
|
478
|
+
};
|
|
479
|
+
durationMs: number;
|
|
480
|
+
}
|
|
481
|
+
/** See {@link OpenCodeHost.beforeSettle}. */
|
|
482
|
+
interface OpenCodeAmendment {
|
|
483
|
+
ui?: AgentUiComponent[];
|
|
484
|
+
/** For a failed run: what the person reads. */
|
|
485
|
+
error?: string;
|
|
486
|
+
}
|
|
487
|
+
interface OpenCodeServer {
|
|
488
|
+
client: OpenCodeClient;
|
|
489
|
+
/** Identifies the server across turns; sessions are only reused on the server that holds them. */
|
|
490
|
+
key: string;
|
|
491
|
+
/**
|
|
492
|
+
* Changes whenever the server restarts and loses its sessions (a sandbox boot id). Omit for a
|
|
493
|
+
* server whose sessions outlive restarts.
|
|
494
|
+
*/
|
|
495
|
+
bootId?: string;
|
|
496
|
+
}
|
|
497
|
+
interface OpenCodeTurnContext {
|
|
498
|
+
input: AgentRunInput;
|
|
499
|
+
runId: string;
|
|
500
|
+
}
|
|
501
|
+
/** The session a thread talks to, and where it lives. */
|
|
502
|
+
interface OpenCodeSessionRef {
|
|
503
|
+
sessionId: string;
|
|
504
|
+
serverKey: string;
|
|
505
|
+
bootId?: string;
|
|
506
|
+
/** The session's directory (`location.directory`), when it has one. */
|
|
507
|
+
directory?: string;
|
|
508
|
+
/**
|
|
509
|
+
* The agent (and persona id) the thread's latest turn ran as — what the tools endpoint checks a
|
|
510
|
+
* call against when it lands on a process that is not following the turn.
|
|
511
|
+
*/
|
|
512
|
+
agentName?: string;
|
|
513
|
+
persona?: string;
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Which OpenCode session each thread uses. The default keeps it in memory, which is enough for one
|
|
517
|
+
* process; a deployment with several replicas (or that wants sessions to survive a restart of its
|
|
518
|
+
* own) persists it — on the thread row, typically.
|
|
519
|
+
*/
|
|
520
|
+
interface OpenCodeSessionStore {
|
|
521
|
+
get(threadId: string): Promise<OpenCodeSessionRef | null>;
|
|
522
|
+
set(threadId: string, ref: OpenCodeSessionRef): Promise<void>;
|
|
523
|
+
}
|
|
524
|
+
declare class InMemoryOpenCodeSessionStore implements OpenCodeSessionStore {
|
|
525
|
+
private readonly refs;
|
|
526
|
+
get(threadId: string): Promise<OpenCodeSessionRef | null>;
|
|
527
|
+
set(threadId: string, ref: OpenCodeSessionRef): Promise<void>;
|
|
528
|
+
}
|
|
529
|
+
/** The two calls of a key-value store a session store needs — an ioredis or node-redis client fits. */
|
|
530
|
+
interface OpenCodeKeyValue {
|
|
531
|
+
get(key: string): Promise<string | null | undefined>;
|
|
532
|
+
set(key: string, value: string): Promise<unknown>;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Sessions kept in a shared key-value store (Redis…), so every process of a deployment finds the
|
|
536
|
+
* session a thread already has — what running more than one process needs, alongside a
|
|
537
|
+
* cross-process `TokenStreamSink`. Keys are `<prefix><threadId>`.
|
|
538
|
+
*/
|
|
539
|
+
declare function keyValueOpenCodeSessionStore(kv: OpenCodeKeyValue, prefix?: string): OpenCodeSessionStore;
|
|
540
|
+
|
|
541
|
+
/** What a tools token says: whose turns it may serve, on which OpenCode server, until when. */
|
|
542
|
+
interface OpenCodeToolsClaims {
|
|
543
|
+
v: 1;
|
|
544
|
+
actor: Actor;
|
|
545
|
+
/** The OpenCode server key (`OpenCodeServer.key`) the token was registered on. */
|
|
546
|
+
server: string;
|
|
547
|
+
/** Epoch ms after which the token is refused. */
|
|
548
|
+
exp: number;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Signs and checks the bearer tokens the engine registers its tools endpoint with (`mcp.add`). A
|
|
552
|
+
* token names an actor and an OpenCode server, and expires; it is HMAC-SHA256 over its claims with
|
|
553
|
+
* a key derived from `tools.secret`.
|
|
554
|
+
*
|
|
555
|
+
* A token on its own runs nothing: the endpoint also needs the call to come from a session that is
|
|
556
|
+
* running a turn of that actor right now (see `OpenCodeTurns.callContext`), and an `action` tool
|
|
557
|
+
* needs an approval the turn granted (see `OpenCodeTurns.spendApproval`).
|
|
558
|
+
*/
|
|
559
|
+
declare class OpenCodeToolsTokens {
|
|
560
|
+
readonly ttlMs: number;
|
|
561
|
+
private readonly key;
|
|
562
|
+
constructor(secret: string | undefined, ttlMs: number);
|
|
563
|
+
mint(actor: Actor, server: string, now?: number): string;
|
|
564
|
+
verify(token: string, now?: number): OpenCodeToolsClaims | null;
|
|
565
|
+
private sign;
|
|
566
|
+
}
|
|
567
|
+
/** A call this endpoint will not run, said to the model as the tool's error. */
|
|
568
|
+
declare class OpenCodeToolRefusedError extends Error {
|
|
569
|
+
readonly name = "OpenCodeToolRefusedError";
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* The MCP endpoint OpenCode sessions reach the module's tools through — `POST <agent path>/opencode/mcp`,
|
|
573
|
+
* mounted by the engine when `tools` is set. Stateless Streamable HTTP (one server per request), so
|
|
574
|
+
* any process answers.
|
|
575
|
+
*
|
|
576
|
+
* - `tools/list`: the registry's tools the token's actor may reach (roles, `enabled`, `canUse`), the
|
|
577
|
+
* kinds only the loop serves left out; with `_meta` naming a running turn, its agent's (and
|
|
578
|
+
* persona's) allow-list too. Plus `remember`, when the memory provider writes — served HERE only,
|
|
579
|
+
* never registered in the module's shared registry.
|
|
580
|
+
* - `tools/call`: only for a call `_meta` ties to a turn of the token's actor running on that
|
|
581
|
+
* session, on the token's server; the turn's allow-list and the registry's own checks apply, an
|
|
582
|
+
* `action` runs only against an approval the turn granted (one call per approval), and the tool's
|
|
583
|
+
* `ctx` is the turn's (thread, run, `emitUi` into its stream and message).
|
|
584
|
+
*/
|
|
585
|
+
declare class OpenCodeMcpEndpoint {
|
|
586
|
+
private readonly turns;
|
|
587
|
+
private readonly tokens;
|
|
588
|
+
constructor(turns: OpenCodeTurns, tokens: OpenCodeToolsTokens);
|
|
589
|
+
/** The claims of a request's bearer token, or `null`. */
|
|
590
|
+
authenticate(authorization: string | string[] | undefined): OpenCodeToolsClaims | null;
|
|
591
|
+
handle(req: IncomingMessage, res: ServerResponse, body: unknown): Promise<void>;
|
|
592
|
+
/** One MCP server for one request, answering as the token's actor. */
|
|
593
|
+
server(claims: OpenCodeToolsClaims): Server;
|
|
594
|
+
private invoke;
|
|
595
|
+
/** `remember`: one fact, at the actor's own scope only — as the loop serves it. */
|
|
596
|
+
private remember;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* The module's `@AiTool`s, served to sessions over the engine's own MCP endpoint
|
|
601
|
+
* (`POST <agent path>/opencode/mcp`, mounted by the engine — not `AgentMcpServerModule`).
|
|
602
|
+
*/
|
|
603
|
+
interface OpenCodeToolsOptions {
|
|
604
|
+
/**
|
|
605
|
+
* The URL the OpenCode server calls that endpoint at, passed to it verbatim (no default, nothing
|
|
606
|
+
* derived). It is resolved from where OpenCode runs, not from the app: e.g.
|
|
607
|
+
* `http://127.0.0.1:3000/agent/opencode/mcp` on the same machine, the app's Compose service or
|
|
608
|
+
* Kubernetes Service name otherwise. `/agent` is `AgentModule`'s `path`; it must reach a process
|
|
609
|
+
* that mounts controllers (not `surface: 'engine'`).
|
|
610
|
+
*/
|
|
611
|
+
url: string;
|
|
612
|
+
/** The MCP server's name in OpenCode; its tools are `<server>.<tool>` / `<server>_<tool>`. Default `'aviary'`. */
|
|
613
|
+
server?: string;
|
|
614
|
+
/**
|
|
615
|
+
* The secret the endpoint's bearer tokens are signed with — the same in every process that serves
|
|
616
|
+
* the agent. Omit → a random per-process secret (one process only; a warning says so).
|
|
617
|
+
*/
|
|
618
|
+
secret?: string;
|
|
619
|
+
/**
|
|
620
|
+
* How long a token OpenCode is given stays valid. A token only ever reaches a turn of its own
|
|
621
|
+
* actor that is running on the session that calls, and it is re-issued on a kept session's turns
|
|
622
|
+
* once it is half-way through. Default 7 days.
|
|
623
|
+
*/
|
|
624
|
+
ttlMs?: number;
|
|
625
|
+
}
|
|
626
|
+
interface OpenCodeEngineSettings {
|
|
627
|
+
/** The id a run gets (e.g. a tenant prefix your durable store partitions by). Default: a random UUID. */
|
|
628
|
+
runId?: (input: AgentRunInput) => string;
|
|
629
|
+
/**
|
|
630
|
+
* `openCodeDurable()` only: how each turn's workflow run starts — tags, search attributes, a
|
|
631
|
+
* concurrency quota (`WorkflowService.start` options) — and what a refused start becomes (e.g. a
|
|
632
|
+
* concurrency limit → a 429 for the person sending).
|
|
633
|
+
*/
|
|
634
|
+
durable?: {
|
|
635
|
+
start?: (input: AgentRunInput, runId: string) => Record<string, unknown> | Promise<Record<string, unknown>>;
|
|
636
|
+
startError?: (error: unknown, input: AgentRunInput) => unknown;
|
|
637
|
+
};
|
|
638
|
+
/** How many earlier messages a NEW session is told about (a thread whose session was lost). Default 20. */
|
|
639
|
+
historyMessages?: number;
|
|
640
|
+
/** The instructions key the agent's own prompt is put under. Default `'aviary.system'`. */
|
|
641
|
+
systemKey?: string;
|
|
642
|
+
/** Serve the module's `@AiTool`s to the sessions. Omit → the session has the host's tools only. */
|
|
643
|
+
tools?: OpenCodeToolsOptions;
|
|
644
|
+
/** Where skills are written, relative to the session's directory. Default `'.opencode/skills'`. */
|
|
645
|
+
skillsDir?: string;
|
|
646
|
+
/**
|
|
647
|
+
* How long `observe` waits for a milestone before failing the turn. Default 30 minutes. A turn
|
|
648
|
+
* parked on a person is not observing: this bounds one stretch of OpenCode working on its own.
|
|
649
|
+
*/
|
|
650
|
+
turnTimeoutMs?: number;
|
|
651
|
+
}
|
|
652
|
+
/** A thread's session, as the steps of a turn pass it along (and a durable run journals it). */
|
|
653
|
+
interface SessionHandle {
|
|
654
|
+
sessionId: string;
|
|
655
|
+
serverKey: string;
|
|
656
|
+
bootId?: string;
|
|
657
|
+
directory?: string;
|
|
658
|
+
}
|
|
659
|
+
/** What the engine's MCP endpoint tells the turns about the call it is serving. */
|
|
660
|
+
interface OpenCodeToolCall {
|
|
661
|
+
actor: Actor;
|
|
662
|
+
/** The OpenCode server the endpoint's token was issued for. */
|
|
663
|
+
serverKey: string;
|
|
664
|
+
/** The MCP request id, so two calls' unnamed components stay apart. */
|
|
665
|
+
requestId?: string;
|
|
666
|
+
/** The call's `_meta` — OpenCode names its session there (`ai.opencode/sessionID`). */
|
|
667
|
+
meta: Readonly<Record<string, unknown>> | undefined;
|
|
668
|
+
}
|
|
669
|
+
/** The turn a tool call over MCP belongs to — see {@link OpenCodeTurns.callContext}. */
|
|
670
|
+
interface OpenCodeCallContext {
|
|
671
|
+
input: AgentRunInput;
|
|
672
|
+
runId: string;
|
|
673
|
+
ctx: Pick<AiToolCtx, 'threadId' | 'runId' | 'emitUi'>;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* The steps of an OpenCode turn — `begin` (thread, session, instructions), `prompt`, `observe`
|
|
677
|
+
* (until a person is asked something or the execution ends), `reply`, `settle` — so an in-memory
|
|
678
|
+
* runner can run them in a row and a durable one can checkpoint each. A step may run in a process
|
|
679
|
+
* that did not run the one before (a durable run resumed elsewhere): the live turn is rebuilt from
|
|
680
|
+
* the session, catching up on what OpenCode asked while nobody listened.
|
|
681
|
+
*/
|
|
682
|
+
declare class OpenCodeTurns implements OnApplicationShutdown {
|
|
683
|
+
private readonly host;
|
|
684
|
+
private readonly sessions;
|
|
685
|
+
private readonly settings;
|
|
686
|
+
private readonly store;
|
|
687
|
+
private readonly sink;
|
|
688
|
+
private readonly deps;
|
|
689
|
+
readonly registry: ToolRegistry;
|
|
690
|
+
private readonly skills?;
|
|
691
|
+
private readonly memory?;
|
|
692
|
+
private readonly queue?;
|
|
693
|
+
private readonly toolsTokens?;
|
|
694
|
+
private readonly logger;
|
|
695
|
+
private readonly events;
|
|
696
|
+
private readonly live;
|
|
697
|
+
/** Actions approved per run, and the approvals the MCP endpoint already spent. */
|
|
698
|
+
private readonly grants;
|
|
699
|
+
private readonly spent;
|
|
700
|
+
/** When the tools endpoint was last registered for a location (`server|boot|directory|actor`). */
|
|
701
|
+
private readonly toolsIssued;
|
|
702
|
+
constructor(host: OpenCodeHost, sessions: OpenCodeSessionStore, settings: OpenCodeEngineSettings, store: AgentStore, sink: TokenStreamSink, deps: AgentDepsFactory, registry: ToolRegistry, skills?: SkillsConfig | undefined, memory?: MemoryConfig | undefined, queue?: ChatQueueService | undefined, toolsTokens?: (OpenCodeToolsTokens | null) | undefined);
|
|
703
|
+
/** The tools server's name in OpenCode. */
|
|
704
|
+
get toolsServer(): string;
|
|
705
|
+
/** The roles policy a turn's tools are gated by (the agent's own). */
|
|
706
|
+
rolesPolicyFor(input: Pick<AgentRunInput, 'agentName'>): RolesPolicy;
|
|
707
|
+
/** The tools a turn may reach: the agent's allow-list, narrowed by its persona's. */
|
|
708
|
+
allowedTools(input: Pick<AgentRunInput, 'agentName' | 'persona'>): string[] | undefined;
|
|
709
|
+
/**
|
|
710
|
+
* Whether the tools endpoint serves `remember`: the module's tools are served and the memory
|
|
711
|
+
* provider writes. Served by the engine's own endpoint only — never put in the module's shared
|
|
712
|
+
* registry, where every MCP client and the `/tools` catalog would see it.
|
|
713
|
+
*/
|
|
714
|
+
memoryWritable(): boolean;
|
|
715
|
+
/** `remember` for a turn: one fact, at the actor's own scope only — as the loop serves it. */
|
|
716
|
+
remember(input: RememberToolInput, turn: OpenCodeCallContext): Promise<string>;
|
|
717
|
+
/**
|
|
718
|
+
* The turn a tool call over the engine's MCP endpoint belongs to, or `undefined` when it belongs
|
|
719
|
+
* to none — which the endpoint refuses: it exists only to serve turns. OpenCode names its session in
|
|
720
|
+
* every call's `_meta` (`ai.opencode/sessionID`); the call is that session's running turn's, and
|
|
721
|
+
* only when the endpoint's caller is the person that turn runs for, on the server the token was
|
|
722
|
+
* issued for. A session this process is not following is found through OpenCode (`session.get` →
|
|
723
|
+
* the thread it was created for → the thread's running turn); its components still reach the
|
|
724
|
+
* stream, as a message of their own.
|
|
725
|
+
*/
|
|
726
|
+
callContext(call: OpenCodeToolCall): Promise<OpenCodeCallContext | undefined>;
|
|
727
|
+
/**
|
|
728
|
+
* Spend one approval of `tool` in `runId` — what the MCP endpoint asks before it runs an `action`.
|
|
729
|
+
* OpenCode asks the person (or the policy answers) before it calls an action tool; the endpoint
|
|
730
|
+
* runs it only against an approval this engine saw granted, so a caller that skipped OpenCode's
|
|
731
|
+
* permission rules (a model in code mode that read the endpoint's headers, anything else that got
|
|
732
|
+
* hold of the token) cannot run an action unapproved. One approval, one call. The approval is the
|
|
733
|
+
* run's own: live in this process, else read off the run's persisted calls (spent marks are kept
|
|
734
|
+
* per process).
|
|
735
|
+
*/
|
|
736
|
+
spendApproval(runId: string, tool: string, input: Pick<AgentRunInput, 'threadId'>): Promise<boolean>;
|
|
737
|
+
private activeRun;
|
|
738
|
+
private uiPushed;
|
|
739
|
+
/**
|
|
740
|
+
* Push a component into the run an OpenCode session is serving, for the host's own trusted
|
|
741
|
+
* callers (no caller check — {@link callContext} is the one for MCP requests). `false` when no run
|
|
742
|
+
* is live on that session.
|
|
743
|
+
*/
|
|
744
|
+
pushToSession(sessionId: string, component: AgentUiComponent): Promise<boolean>;
|
|
745
|
+
/** The runs this process is following now (an updater drains on it). */
|
|
746
|
+
liveRuns(): string[];
|
|
747
|
+
private threadOfSession;
|
|
748
|
+
onApplicationShutdown(): void;
|
|
749
|
+
/**
|
|
750
|
+
* Persist the user message (or rewind the thread for a regenerate), find or create the thread's
|
|
751
|
+
* session, and refresh what the session is told this turn. Safe to run again for the same run (a
|
|
752
|
+
* durable `begin` re-run after a crash): the run's user message is written once.
|
|
753
|
+
*/
|
|
754
|
+
begin(runId: string, input: AgentRunInput): Promise<SessionHandle>;
|
|
755
|
+
private createSession;
|
|
756
|
+
/** The user message, as the loop persists it (or, on a regenerate, the thread rewound to it). */
|
|
757
|
+
private prepareThread;
|
|
758
|
+
/** Rewind the session to before its last user message, so the regenerated answer replaces it. */
|
|
759
|
+
private revertLastExchange;
|
|
760
|
+
/** The agent's prompt, the memory block and the host's entries — refreshed every turn. */
|
|
761
|
+
private refreshInstructions;
|
|
762
|
+
/** The agent's base prompt (`@Agent({ systemPrompt })` / `@SystemPrompt()`) and the contributors. */
|
|
763
|
+
private systemPrompt;
|
|
764
|
+
private scopeContext;
|
|
765
|
+
/** What is on file about the actor (`forRoot({ memory })`); writable when `remember` is served. */
|
|
766
|
+
private memoryBlock;
|
|
767
|
+
/**
|
|
768
|
+
* The module's skills (`forRoot({ skills })` / `@Skill`) as `SKILL.md` files under the session's
|
|
769
|
+
* directory, where OpenCode's own `skill` tool finds them. Returns the names written.
|
|
770
|
+
*/
|
|
771
|
+
private writeSkills;
|
|
772
|
+
/**
|
|
773
|
+
* Permission rules for the module's tools: the MCP server allowed as a whole (OpenCode offers a
|
|
774
|
+
* server's tools only when the server itself is allowed), then each `action` tool asked — the last
|
|
775
|
+
* matching rule wins, so an action still lands on an approval card. Tools outside the agent's
|
|
776
|
+
* (and persona's) allow-list, and the kinds only the loop serves, are denied. The endpoint enforces
|
|
777
|
+
* all of it again on every call: these rules are what OpenCode offers, not what keeps a call out.
|
|
778
|
+
*/
|
|
779
|
+
private toolRules;
|
|
780
|
+
/**
|
|
781
|
+
* Register the tools endpoint at the session's location, with a bearer token for the turn's actor
|
|
782
|
+
* on this server. On a new session always; on a kept one when this process has not registered it
|
|
783
|
+
* yet or its token is half-way through its life.
|
|
784
|
+
*/
|
|
785
|
+
private addTools;
|
|
786
|
+
/** Start listening to the session (so nothing it says is missed), then send the user message. */
|
|
787
|
+
prompt(runId: string, input: AgentRunInput, handle: SessionHandle): Promise<void>;
|
|
788
|
+
/**
|
|
789
|
+
* Wait for the turn's next milestone. In a process that was not following the turn (a resumed
|
|
790
|
+
* durable run), the turn is rebuilt and first catches up on requests OpenCode raised meanwhile;
|
|
791
|
+
* `session.wait` is the safety net for a terminal event that was missed.
|
|
792
|
+
*/
|
|
793
|
+
observe(runId: string, input: AgentRunInput, handle: SessionHandle): Promise<Milestone>;
|
|
794
|
+
/**
|
|
795
|
+
* Hand a person's answer back to OpenCode. When the server restarted while the turn waited, the
|
|
796
|
+
* request is gone with it: a new session is opened, told what the person decided, and prompted
|
|
797
|
+
* to go on.
|
|
798
|
+
*/
|
|
799
|
+
reply(runId: string, input: AgentRunInput, handle: SessionHandle, ask: PendingAsk, reply: HumanReply): Promise<SessionHandle>;
|
|
800
|
+
private continueAfterRestart;
|
|
801
|
+
/** Interrupt the thread's session (cancel). */
|
|
802
|
+
interrupt(input: AgentRunInput): Promise<void>;
|
|
803
|
+
private ensureLive;
|
|
804
|
+
/** Stop following a run in this process. */
|
|
805
|
+
drop(runId: string): void;
|
|
806
|
+
/** Stop following a run and drop the approvals it was granted: it is over. */
|
|
807
|
+
private forget;
|
|
808
|
+
/** A person was asked something: tell the host (cards in other channels, wake-ups). */
|
|
809
|
+
private reached;
|
|
810
|
+
/** What the run produced, as the host's settle hooks see it. */
|
|
811
|
+
private runResult;
|
|
812
|
+
/**
|
|
813
|
+
* The host's last word on the answer before the stream ends: components to append (a guardrail
|
|
814
|
+
* notice), or, for a failed run, the error the person reads instead of OpenCode's.
|
|
815
|
+
*/
|
|
816
|
+
private amend;
|
|
817
|
+
/** Delivery, telemetry, spend: after the run settled. Errors are the host's, never the run's. */
|
|
818
|
+
private settled;
|
|
819
|
+
/** End the run's stream and bookkeeping for how its execution ended. */
|
|
820
|
+
settle(runId: string, input: AgentRunInput, outcome: TurnOutcome, durationMs: number): Promise<void>;
|
|
821
|
+
settleFailed(runId: string, input: AgentRunInput, error: string, durationMs?: number): Promise<void>;
|
|
822
|
+
/** Starts the next queued message of the thread; set by the runner (it owns `start`). */
|
|
823
|
+
startNext: ((next: AgentRunInput, runId: string) => Promise<unknown>) | undefined;
|
|
824
|
+
/** As `InlineAgentRunner`: hand the thread to its next queued message, and say so on the stream. */
|
|
825
|
+
private handoff;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
interface OpenCodeEngineOptions extends OpenCodeEngineSettings {
|
|
829
|
+
/**
|
|
830
|
+
* The host: an {@link OpenCodeHost} instance, or a provider class / token resolved from DI (so it
|
|
831
|
+
* can inject the services that know where the server is and how to set sessions up). A class is
|
|
832
|
+
* registered as a provider; a token must be provided by a module the agent module can see.
|
|
833
|
+
*/
|
|
834
|
+
host: OpenCodeHost | Type<OpenCodeHost> | InjectionToken;
|
|
835
|
+
/** Where each thread's session is kept. Same forms as `host`. Omit → in memory. */
|
|
836
|
+
sessions?: OpenCodeSessionStore | Type<OpenCodeSessionStore> | InjectionToken;
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Run the agent's turns on OpenCode 2 — `AgentModule.forRoot({ engine: openCode({ host }) })`.
|
|
840
|
+
* The routes, threads, stream protocol, approvals and queue stay the library's; OpenCode runs the
|
|
841
|
+
* model, the tools, skills and the context, on the server and sessions the host provides.
|
|
842
|
+
*/
|
|
843
|
+
declare function openCode(options: OpenCodeEngineOptions): AgentEngine;
|
|
844
|
+
/** The engine's own controllers: the tools endpoint, when `tools` is set. */
|
|
845
|
+
declare function openCodeControllers(options: OpenCodeEngineSettings): Type<object>[];
|
|
846
|
+
/** The providers every OpenCode engine needs: host, session store, settings and the turn steps. */
|
|
847
|
+
declare function openCodeProviders(options: OpenCodeEngineOptions): Provider[];
|
|
848
|
+
|
|
849
|
+
export { OpenCodeTurn as A, type OpenCodeTurnContext as B, keyValueOpenCodeSessionStore as C, openCode as D, openCodeControllers as E, openCodeProviders as F, InMemoryOpenCodeSessionStore as I, type Milestone as M, OpenCodeTurns as O, type PendingAsk as P, type SessionHandle as S, type TurnOutcome as T, type OpenCodeEngineSettings as a, type OpenCodeHost as b, type OpenCodeEngineOptions as c, type OpenCodeClient as d, type OpenCodeEvent as e, type OpenCodeForm as f, type OpenCodeFormValue as g, type OpenCodeFormField as h, OpenCodeMcpEndpoint as i, type OpenCodeAmendment as j, type OpenCodeCallContext as k, type OpenCodeKeyValue as l, type OpenCodeModelRef as m, type OpenCodePermissionRequest as n, type OpenCodePermissionRule as o, OpenCodeReplyMismatchError as p, type OpenCodeRunResult as q, type OpenCodeServer as r, type OpenCodeSessionCreate as s, type OpenCodeSessionRef as t, type OpenCodeSessionStore as u, type OpenCodeToolCall as v, OpenCodeToolRefusedError as w, type OpenCodeToolsClaims as x, type OpenCodeToolsOptions as y, OpenCodeToolsTokens as z };
|