@nifrajs/agent-protocol 3.2.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 +33 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +112 -0
- package/dist/index.js.map +1 -0
- package/dist/orchestration.d.ts +150 -0
- package/dist/orchestration.d.ts.map +1 -0
- package/dist/orchestration.js +278 -0
- package/dist/orchestration.js.map +1 -0
- package/dist/run-lifecycle.d.ts +190 -0
- package/dist/run-lifecycle.d.ts.map +1 -0
- package/dist/run-lifecycle.js +302 -0
- package/dist/run-lifecycle.js.map +1 -0
- package/package.json +45 -0
- package/src/index.ts +405 -0
- package/src/orchestration.ts +447 -0
- package/src/run-lifecycle.ts +516 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend-neutral protocol for local coding-agent hosts.
|
|
3
|
+
*
|
|
4
|
+
* This package deliberately has no runtime dependencies. A Pi adapter, a Nifra-native backend,
|
|
5
|
+
* the CLI, and the Workbench can all implement or consume these contracts without pulling any
|
|
6
|
+
* framework, model provider, or UI code into an application.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const AGENT_PROTOCOL_VERSION = 1 as const
|
|
10
|
+
|
|
11
|
+
export type AgentSessionStatus = "idle" | "running" | "waiting" | "completed" | "failed" | "stopped"
|
|
12
|
+
|
|
13
|
+
export type AgentEventType =
|
|
14
|
+
| "session.started"
|
|
15
|
+
| "session.updated"
|
|
16
|
+
| "turn.started"
|
|
17
|
+
| "assistant.delta"
|
|
18
|
+
| "assistant.message"
|
|
19
|
+
| "tool.started"
|
|
20
|
+
| "tool.delta"
|
|
21
|
+
| "tool.completed"
|
|
22
|
+
| "approval.required"
|
|
23
|
+
| "approval.resolved"
|
|
24
|
+
| "repair.required"
|
|
25
|
+
| "verification.completed"
|
|
26
|
+
| "memory.compacted"
|
|
27
|
+
| "extension.reloaded"
|
|
28
|
+
| "session.completed"
|
|
29
|
+
| "session.failed"
|
|
30
|
+
| "session.stopped"
|
|
31
|
+
|
|
32
|
+
export interface AgentSessionSnapshot {
|
|
33
|
+
readonly version: typeof AGENT_PROTOCOL_VERSION
|
|
34
|
+
readonly id: string
|
|
35
|
+
readonly backend: string
|
|
36
|
+
readonly cwd: string
|
|
37
|
+
readonly status: AgentSessionStatus
|
|
38
|
+
readonly createdAt: number
|
|
39
|
+
readonly updatedAt: number
|
|
40
|
+
readonly lastSeq: number
|
|
41
|
+
readonly activeTurnId?: string
|
|
42
|
+
readonly extensionRevision?: string
|
|
43
|
+
readonly capabilities: readonly string[]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AgentBackendInfo {
|
|
47
|
+
readonly name: string
|
|
48
|
+
readonly version?: string
|
|
49
|
+
readonly capabilities: readonly string[]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AgentSessionEventBase {
|
|
53
|
+
readonly version: typeof AGENT_PROTOCOL_VERSION
|
|
54
|
+
readonly sessionId: string
|
|
55
|
+
readonly seq: number
|
|
56
|
+
readonly at: number
|
|
57
|
+
readonly type: AgentEventType
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface AgentSessionStartedEvent extends AgentSessionEventBase {
|
|
61
|
+
readonly type: "session.started"
|
|
62
|
+
readonly snapshot: AgentSessionSnapshot
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface AgentSessionUpdatedEvent extends AgentSessionEventBase {
|
|
66
|
+
readonly type: "session.updated"
|
|
67
|
+
readonly snapshot: AgentSessionSnapshot
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface AgentTurnStartedEvent extends AgentSessionEventBase {
|
|
71
|
+
readonly type: "turn.started"
|
|
72
|
+
readonly turnId: string
|
|
73
|
+
readonly prompt: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface AgentAssistantDeltaEvent extends AgentSessionEventBase {
|
|
77
|
+
readonly type: "assistant.delta"
|
|
78
|
+
readonly turnId: string
|
|
79
|
+
readonly text: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface AgentAssistantMessageEvent extends AgentSessionEventBase {
|
|
83
|
+
readonly type: "assistant.message"
|
|
84
|
+
readonly turnId: string
|
|
85
|
+
readonly text: string
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface AgentToolStartedEvent extends AgentSessionEventBase {
|
|
89
|
+
readonly type: "tool.started"
|
|
90
|
+
readonly turnId: string
|
|
91
|
+
readonly callId: string
|
|
92
|
+
readonly name: string
|
|
93
|
+
readonly input?: unknown
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface AgentToolDeltaEvent extends AgentSessionEventBase {
|
|
97
|
+
readonly type: "tool.delta"
|
|
98
|
+
readonly turnId: string
|
|
99
|
+
readonly callId: string
|
|
100
|
+
readonly text: string
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface AgentToolCompletedEvent extends AgentSessionEventBase {
|
|
104
|
+
readonly type: "tool.completed"
|
|
105
|
+
readonly turnId: string
|
|
106
|
+
readonly callId: string
|
|
107
|
+
readonly name: string
|
|
108
|
+
readonly ok: boolean
|
|
109
|
+
readonly output?: unknown
|
|
110
|
+
readonly error?: AgentError
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface AgentApprovalRequiredEvent extends AgentSessionEventBase {
|
|
114
|
+
readonly type: "approval.required"
|
|
115
|
+
readonly turnId: string
|
|
116
|
+
readonly approvalId: string
|
|
117
|
+
readonly action: string
|
|
118
|
+
readonly capability: string
|
|
119
|
+
readonly reason?: string
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface AgentApprovalResolvedEvent extends AgentSessionEventBase {
|
|
123
|
+
readonly type: "approval.resolved"
|
|
124
|
+
readonly turnId?: string
|
|
125
|
+
readonly approvalId: string
|
|
126
|
+
readonly approved: boolean
|
|
127
|
+
readonly reason?: string
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface AgentRepairTask {
|
|
131
|
+
readonly id: string
|
|
132
|
+
readonly verification: "check" | "assure" | "test"
|
|
133
|
+
readonly cwd: string
|
|
134
|
+
readonly reason: string
|
|
135
|
+
readonly capabilities: readonly string[]
|
|
136
|
+
readonly output?: string
|
|
137
|
+
readonly report?: unknown
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export interface AgentRepairRequiredEvent extends AgentSessionEventBase {
|
|
141
|
+
readonly type: "repair.required"
|
|
142
|
+
readonly turnId?: string
|
|
143
|
+
readonly task: AgentRepairTask
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export interface AgentVerificationCompletedEvent extends AgentSessionEventBase {
|
|
147
|
+
readonly type: "verification.completed"
|
|
148
|
+
readonly name: string
|
|
149
|
+
readonly ok: boolean
|
|
150
|
+
readonly report?: unknown
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface AgentMemoryCompactedEvent extends AgentSessionEventBase {
|
|
154
|
+
readonly type: "memory.compacted"
|
|
155
|
+
readonly before: number
|
|
156
|
+
readonly after: number
|
|
157
|
+
readonly reason: "manual" | "threshold" | "overflow" | "workflow"
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface AgentExtensionReloadedEvent extends AgentSessionEventBase {
|
|
161
|
+
readonly type: "extension.reloaded"
|
|
162
|
+
readonly revision: string
|
|
163
|
+
readonly loaded: readonly string[]
|
|
164
|
+
readonly disabled: readonly string[]
|
|
165
|
+
readonly rolledBack: boolean
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export interface AgentSessionCompletedEvent extends AgentSessionEventBase {
|
|
169
|
+
readonly type: "session.completed"
|
|
170
|
+
readonly snapshot: AgentSessionSnapshot
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface AgentSessionFailedEvent extends AgentSessionEventBase {
|
|
174
|
+
readonly type: "session.failed"
|
|
175
|
+
readonly error: AgentError
|
|
176
|
+
readonly recoverable: boolean
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface AgentSessionStoppedEvent extends AgentSessionEventBase {
|
|
180
|
+
readonly type: "session.stopped"
|
|
181
|
+
readonly reason?: string
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type AgentEvent =
|
|
185
|
+
| AgentSessionStartedEvent
|
|
186
|
+
| AgentSessionUpdatedEvent
|
|
187
|
+
| AgentTurnStartedEvent
|
|
188
|
+
| AgentAssistantDeltaEvent
|
|
189
|
+
| AgentAssistantMessageEvent
|
|
190
|
+
| AgentToolStartedEvent
|
|
191
|
+
| AgentToolDeltaEvent
|
|
192
|
+
| AgentToolCompletedEvent
|
|
193
|
+
| AgentApprovalRequiredEvent
|
|
194
|
+
| AgentApprovalResolvedEvent
|
|
195
|
+
| AgentRepairRequiredEvent
|
|
196
|
+
| AgentVerificationCompletedEvent
|
|
197
|
+
| AgentMemoryCompactedEvent
|
|
198
|
+
| AgentExtensionReloadedEvent
|
|
199
|
+
| AgentSessionCompletedEvent
|
|
200
|
+
| AgentSessionFailedEvent
|
|
201
|
+
| AgentSessionStoppedEvent
|
|
202
|
+
|
|
203
|
+
/** Event payload before the transport adds protocol version, session id, sequence, and timestamp. */
|
|
204
|
+
export type AgentEventPayload =
|
|
205
|
+
| Omit<AgentSessionStartedEvent, "version" | "sessionId" | "seq" | "at">
|
|
206
|
+
| Omit<AgentSessionUpdatedEvent, "version" | "sessionId" | "seq" | "at">
|
|
207
|
+
| Omit<AgentTurnStartedEvent, "version" | "sessionId" | "seq" | "at">
|
|
208
|
+
| Omit<AgentAssistantDeltaEvent, "version" | "sessionId" | "seq" | "at">
|
|
209
|
+
| Omit<AgentAssistantMessageEvent, "version" | "sessionId" | "seq" | "at">
|
|
210
|
+
| Omit<AgentToolStartedEvent, "version" | "sessionId" | "seq" | "at">
|
|
211
|
+
| Omit<AgentToolDeltaEvent, "version" | "sessionId" | "seq" | "at">
|
|
212
|
+
| Omit<AgentToolCompletedEvent, "version" | "sessionId" | "seq" | "at">
|
|
213
|
+
| Omit<AgentApprovalRequiredEvent, "version" | "sessionId" | "seq" | "at">
|
|
214
|
+
| Omit<AgentApprovalResolvedEvent, "version" | "sessionId" | "seq" | "at">
|
|
215
|
+
| Omit<AgentRepairRequiredEvent, "version" | "sessionId" | "seq" | "at">
|
|
216
|
+
| Omit<AgentVerificationCompletedEvent, "version" | "sessionId" | "seq" | "at">
|
|
217
|
+
| Omit<AgentMemoryCompactedEvent, "version" | "sessionId" | "seq" | "at">
|
|
218
|
+
| Omit<AgentExtensionReloadedEvent, "version" | "sessionId" | "seq" | "at">
|
|
219
|
+
| Omit<AgentSessionCompletedEvent, "version" | "sessionId" | "seq" | "at">
|
|
220
|
+
| Omit<AgentSessionFailedEvent, "version" | "sessionId" | "seq" | "at">
|
|
221
|
+
| Omit<AgentSessionStoppedEvent, "version" | "sessionId" | "seq" | "at">
|
|
222
|
+
|
|
223
|
+
export interface AgentError {
|
|
224
|
+
readonly code: string
|
|
225
|
+
readonly message: string
|
|
226
|
+
readonly details?: unknown
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface CreateSessionInput {
|
|
230
|
+
readonly cwd: string
|
|
231
|
+
readonly backend?: string
|
|
232
|
+
readonly capabilities?: readonly string[]
|
|
233
|
+
readonly sessionId?: string
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export interface SendMessageInput {
|
|
237
|
+
readonly sessionId: string
|
|
238
|
+
readonly message: string
|
|
239
|
+
readonly signal?: AbortSignal
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export interface ForkSessionInput {
|
|
243
|
+
readonly sessionId: string
|
|
244
|
+
readonly targetSessionId?: string
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export interface ForkSessionResult {
|
|
248
|
+
readonly sessionId: string
|
|
249
|
+
readonly parentSessionId: string
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export interface AgentSessionCheckpoint {
|
|
253
|
+
readonly version: 1
|
|
254
|
+
readonly sessionId: string
|
|
255
|
+
readonly at: number
|
|
256
|
+
readonly payload?: unknown
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface ReloadResult {
|
|
260
|
+
readonly revision: string
|
|
261
|
+
readonly loaded: readonly string[]
|
|
262
|
+
readonly disabled: readonly string[]
|
|
263
|
+
readonly rolledBack: boolean
|
|
264
|
+
readonly error?: AgentError
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export interface AgentBackend {
|
|
268
|
+
readonly info: AgentBackendInfo
|
|
269
|
+
createSession(input: CreateSessionInput): Promise<AgentSessionSnapshot>
|
|
270
|
+
send(input: SendMessageInput): AsyncIterable<AgentEvent>
|
|
271
|
+
cancel(sessionId: string, reason?: string): Promise<void>
|
|
272
|
+
snapshot(sessionId: string): Promise<AgentSessionSnapshot>
|
|
273
|
+
/** Backends may provide native branching; the host can fall back to its session store. */
|
|
274
|
+
fork?(input: ForkSessionInput): Promise<ForkSessionResult>
|
|
275
|
+
checkpoint?(sessionId: string, payload?: unknown): Promise<AgentSessionCheckpoint>
|
|
276
|
+
reload(sessionId: string): Promise<ReloadResult>
|
|
277
|
+
/** Resolve an approval surfaced by a backend UI/tool, when that backend supports interactive approvals. */
|
|
278
|
+
resolveApproval?(
|
|
279
|
+
sessionId: string,
|
|
280
|
+
approvalId: string,
|
|
281
|
+
approved: boolean,
|
|
282
|
+
reason?: string,
|
|
283
|
+
): Promise<boolean | undefined>
|
|
284
|
+
close(sessionId: string): Promise<void>
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export interface AgentEventSink {
|
|
288
|
+
push(event: AgentEvent): void
|
|
289
|
+
complete(): void
|
|
290
|
+
fail(error: unknown): void
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface AgentEventStream extends AgentEventSink, AsyncIterableIterator<AgentEvent> {
|
|
294
|
+
readonly dropped: number
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Small bounded event stream for RPC clients and UIs. The authoritative event history belongs to
|
|
299
|
+
* the backend/session store; this live view may drop old transient events if a consumer falls behind.
|
|
300
|
+
*/
|
|
301
|
+
export function createAgentEventStream(maxQueueSize = 256): AgentEventStream {
|
|
302
|
+
if (!Number.isSafeInteger(maxQueueSize) || maxQueueSize < 1)
|
|
303
|
+
throw new RangeError("agent event stream: maxQueueSize must be a positive safe integer")
|
|
304
|
+
|
|
305
|
+
const queue: AgentEvent[] = []
|
|
306
|
+
const waiters: Array<{
|
|
307
|
+
readonly resolve: (result: IteratorResult<AgentEvent>) => void
|
|
308
|
+
readonly reject: (reason?: unknown) => void
|
|
309
|
+
}> = []
|
|
310
|
+
let completed = false
|
|
311
|
+
let failure: unknown
|
|
312
|
+
let failed = false
|
|
313
|
+
let dropped = 0
|
|
314
|
+
|
|
315
|
+
const finish = (): void => {
|
|
316
|
+
if (!completed || queue.length > 0) return
|
|
317
|
+
while (waiters.length > 0) {
|
|
318
|
+
const waiter = waiters.shift()
|
|
319
|
+
if (failed) waiter?.reject(failure)
|
|
320
|
+
else waiter?.resolve({ done: true, value: undefined })
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const stream: AgentEventStream = {
|
|
325
|
+
get dropped() {
|
|
326
|
+
return dropped
|
|
327
|
+
},
|
|
328
|
+
push(event) {
|
|
329
|
+
if (completed) return
|
|
330
|
+
const waiter = waiters.shift()
|
|
331
|
+
if (waiter !== undefined) {
|
|
332
|
+
waiter.resolve({ done: false, value: event })
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
if (queue.length >= maxQueueSize) {
|
|
336
|
+
queue.shift()
|
|
337
|
+
dropped += 1
|
|
338
|
+
}
|
|
339
|
+
queue.push(event)
|
|
340
|
+
},
|
|
341
|
+
complete() {
|
|
342
|
+
completed = true
|
|
343
|
+
finish()
|
|
344
|
+
},
|
|
345
|
+
fail(error) {
|
|
346
|
+
if (completed) return
|
|
347
|
+
failure = error
|
|
348
|
+
failed = true
|
|
349
|
+
completed = true
|
|
350
|
+
finish()
|
|
351
|
+
},
|
|
352
|
+
next() {
|
|
353
|
+
const event = queue.shift()
|
|
354
|
+
if (event !== undefined) return Promise.resolve({ done: false, value: event })
|
|
355
|
+
if (completed) {
|
|
356
|
+
if (failed) return Promise.reject(failure)
|
|
357
|
+
return Promise.resolve({ done: true, value: undefined })
|
|
358
|
+
}
|
|
359
|
+
return new Promise<IteratorResult<AgentEvent>>((resolve, reject) =>
|
|
360
|
+
waiters.push({ resolve, reject }),
|
|
361
|
+
)
|
|
362
|
+
},
|
|
363
|
+
return() {
|
|
364
|
+
queue.length = 0
|
|
365
|
+
completed = true
|
|
366
|
+
finish()
|
|
367
|
+
return Promise.resolve({ done: true, value: undefined })
|
|
368
|
+
},
|
|
369
|
+
[Symbol.asyncIterator]() {
|
|
370
|
+
return this
|
|
371
|
+
},
|
|
372
|
+
}
|
|
373
|
+
return stream
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
export function agentError(code: string, message: string, details?: unknown): AgentError {
|
|
377
|
+
return details === undefined ? { code, message } : { code, message, details }
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Orchestration value contracts (run plans, content-free evidence, artifact refs, effect keys).
|
|
382
|
+
* Additive to the session protocol; carries no behavior and no payload content.
|
|
383
|
+
*/
|
|
384
|
+
export * from "./orchestration.ts"
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Additive run-lifecycle contracts (run snapshots, evidence-event envelopes, handoff snapshots,
|
|
388
|
+
* feature negotiation, cursor resume). Additive to the session protocol; carries no payload content.
|
|
389
|
+
*/
|
|
390
|
+
export * from "./run-lifecycle.ts"
|
|
391
|
+
|
|
392
|
+
export function isAgentEvent(value: unknown): value is AgentEvent {
|
|
393
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false
|
|
394
|
+
const record = value as Record<string, unknown>
|
|
395
|
+
return (
|
|
396
|
+
record.version === AGENT_PROTOCOL_VERSION &&
|
|
397
|
+
typeof record.sessionId === "string" &&
|
|
398
|
+
typeof record.seq === "number" &&
|
|
399
|
+
Number.isSafeInteger(record.seq) &&
|
|
400
|
+
typeof record.at === "number" &&
|
|
401
|
+
Number.isFinite(record.at) &&
|
|
402
|
+
typeof record.type === "string" &&
|
|
403
|
+
record.type.includes(".")
|
|
404
|
+
)
|
|
405
|
+
}
|