@nifrajs/coding-agent 4.0.2 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +22 -2
- package/dist/approvals.d.ts +30 -2
- package/dist/approvals.d.ts.map +1 -1
- package/dist/approvals.js +43 -12
- package/dist/approvals.js.map +1 -1
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +120 -100
- package/dist/cli.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +9 -21
- package/dist/context.js.map +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +8 -21
- package/dist/diff.js.map +1 -1
- package/dist/extensions.js +4 -2
- package/dist/extensions.js.map +1 -1
- package/dist/handoffs.d.ts +5 -0
- package/dist/handoffs.d.ts.map +1 -1
- package/dist/handoffs.js +27 -0
- package/dist/handoffs.js.map +1 -1
- package/dist/host.d.ts +15 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js +152 -37
- package/dist/host.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/isolated.js +3 -2
- package/dist/isolated.js.map +1 -1
- package/dist/native.d.ts +29 -2
- package/dist/native.d.ts.map +1 -1
- package/dist/native.js +86 -26
- package/dist/native.js.map +1 -1
- package/dist/orchestration/dispatch.d.ts +5 -1
- package/dist/orchestration/dispatch.d.ts.map +1 -1
- package/dist/orchestration/dispatch.js +1 -1
- package/dist/orchestration/dispatch.js.map +1 -1
- package/dist/orchestration/evidence-store.d.ts.map +1 -1
- package/dist/orchestration/evidence-store.js +5 -6
- package/dist/orchestration/evidence-store.js.map +1 -1
- package/dist/orchestration/host.d.ts +14 -0
- package/dist/orchestration/host.d.ts.map +1 -1
- package/dist/orchestration/host.js +26 -0
- package/dist/orchestration/host.js.map +1 -1
- package/dist/orchestration/index.d.ts +1 -1
- package/dist/orchestration/index.d.ts.map +1 -1
- package/dist/orchestration/index.js.map +1 -1
- package/dist/orchestration/recovery.d.ts +5 -1
- package/dist/orchestration/recovery.d.ts.map +1 -1
- package/dist/orchestration/recovery.js +5 -3
- package/dist/orchestration/recovery.js.map +1 -1
- package/dist/process.d.ts +22 -1
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +75 -1
- package/dist/process.js.map +1 -1
- package/dist/rpc.d.ts +29 -0
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +205 -46
- package/dist/rpc.js.map +1 -1
- package/dist/session-migration.d.ts.map +1 -1
- package/dist/session-migration.js +30 -6
- package/dist/session-migration.js.map +1 -1
- package/dist/sessions.d.ts +13 -2
- package/dist/sessions.d.ts.map +1 -1
- package/dist/sessions.js +104 -24
- package/dist/sessions.js.map +1 -1
- package/dist/subagents.d.ts.map +1 -1
- package/dist/subagents.js +13 -2
- package/dist/subagents.js.map +1 -1
- package/dist/turn-replay.d.ts +40 -0
- package/dist/turn-replay.d.ts.map +1 -0
- package/dist/turn-replay.js +136 -0
- package/dist/turn-replay.js.map +1 -0
- package/dist/ui.js +84 -30
- package/dist/ui.js.map +1 -1
- package/dist/verification.d.ts +7 -0
- package/dist/verification.d.ts.map +1 -1
- package/dist/verification.js +66 -63
- package/dist/verification.js.map +1 -1
- package/dist/workflows.d.ts +5 -0
- package/dist/workflows.d.ts.map +1 -1
- package/dist/workflows.js +2 -1
- package/dist/workflows.js.map +1 -1
- package/package.json +6 -6
- package/src/approvals.ts +70 -16
- package/src/cli.ts +125 -93
- package/src/context.ts +12 -20
- package/src/diff.ts +11 -23
- package/src/extensions.ts +6 -2
- package/src/handoffs.ts +27 -0
- package/src/host.ts +165 -34
- package/src/index.ts +3 -0
- package/src/isolated.ts +3 -2
- package/src/native.ts +137 -34
- package/src/orchestration/dispatch.ts +6 -2
- package/src/orchestration/evidence-store.ts +5 -6
- package/src/orchestration/host.ts +44 -0
- package/src/orchestration/index.ts +1 -0
- package/src/orchestration/recovery.ts +14 -8
- package/src/process.ts +89 -0
- package/src/rpc.ts +268 -49
- package/src/session-migration.ts +29 -5
- package/src/sessions.ts +115 -29
- package/src/subagents.ts +11 -2
- package/src/turn-replay.ts +151 -0
- package/src/ui.ts +90 -32
- package/src/verification.ts +74 -60
- package/src/workflows.ts +7 -1
package/dist/workflows.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../src/workflows.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nifrajs/coding-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.1.0",
|
|
4
4
|
"description": "Standalone, extensible Nifra coding-agent host and CLI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bun",
|
|
@@ -70,11 +70,11 @@
|
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"@nifrajs/agent": "
|
|
74
|
-
"@nifrajs/agent-review": "
|
|
75
|
-
"@nifrajs/agent-protocol": "
|
|
76
|
-
"@nifrajs/jobs": "
|
|
77
|
-
"@nifrajs/pi": "
|
|
73
|
+
"@nifrajs/agent": "5.1.0",
|
|
74
|
+
"@nifrajs/agent-review": "5.1.0",
|
|
75
|
+
"@nifrajs/agent-protocol": "5.1.0",
|
|
76
|
+
"@nifrajs/jobs": "5.1.0",
|
|
77
|
+
"@nifrajs/pi": "5.1.0"
|
|
78
78
|
},
|
|
79
79
|
"repository": {
|
|
80
80
|
"type": "git",
|
package/src/approvals.ts
CHANGED
|
@@ -21,9 +21,44 @@ export interface ApprovalRequest {
|
|
|
21
21
|
*/
|
|
22
22
|
readonly coordinate?: DecisionCoordinate
|
|
23
23
|
readonly createdAt: number
|
|
24
|
+
/**
|
|
25
|
+
* Absolute epoch-ms after which the approval is denied as timed out. An approval opened with a
|
|
26
|
+
* `"never"` timeout reports {@link APPROVAL_NEVER_EXPIRES_AT}, so `now < expiresAt` keeps treating
|
|
27
|
+
* it as live.
|
|
28
|
+
*/
|
|
24
29
|
readonly expiresAt: number
|
|
25
30
|
}
|
|
26
31
|
|
|
32
|
+
/**
|
|
33
|
+
* How long an approval may wait: whole milliseconds from 1 ms to 24 h, or `"never"` for no timer. A
|
|
34
|
+
* `"never"` approval still settles on an answer, a cancel, the end of its turn, and close.
|
|
35
|
+
*/
|
|
36
|
+
export type ApprovalTimeout = number | "never"
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The `expiresAt` of an approval that never times out: the largest epoch-ms a `Date` holds
|
|
40
|
+
* (+275760-09-13). It survives JSON, unlike `Infinity`, and `new Date(expiresAt)` stays valid.
|
|
41
|
+
*/
|
|
42
|
+
export const APPROVAL_NEVER_EXPIRES_AT = 8_640_000_000_000_000
|
|
43
|
+
|
|
44
|
+
const DEFAULT_APPROVAL_TIMEOUT_MS = 5 * 60_000
|
|
45
|
+
const MAX_APPROVAL_TIMEOUT_MS = 24 * 60 * 60_000
|
|
46
|
+
|
|
47
|
+
/** True for `"never"` or a whole number of milliseconds from 1 ms to 24 h. */
|
|
48
|
+
export function isApprovalTimeout(value: unknown): value is ApprovalTimeout {
|
|
49
|
+
return (
|
|
50
|
+
value === "never" ||
|
|
51
|
+
(typeof value === "number" &&
|
|
52
|
+
Number.isSafeInteger(value) &&
|
|
53
|
+
value >= 1 &&
|
|
54
|
+
value <= MAX_APPROVAL_TIMEOUT_MS)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function approvalExpiresAt(createdAt: number, timeoutMs: ApprovalTimeout): number {
|
|
59
|
+
return timeoutMs === "never" ? APPROVAL_NEVER_EXPIRES_AT : createdAt + timeoutMs
|
|
60
|
+
}
|
|
61
|
+
|
|
27
62
|
/** Stable, content-free reasons a coordinate-matched resolution is refused. */
|
|
28
63
|
export type ApprovalMatchRejection =
|
|
29
64
|
| "unknown_boundary"
|
|
@@ -45,7 +80,11 @@ export interface ApprovalDecision {
|
|
|
45
80
|
|
|
46
81
|
export interface ApprovalManagerOptions {
|
|
47
82
|
readonly maxPending?: number
|
|
48
|
-
|
|
83
|
+
/**
|
|
84
|
+
* How long an approval waits before it is denied as timed out (default 5 minutes, at most 24 h).
|
|
85
|
+
* `"never"` removes the timer; `maxPending` still bounds how many approvals can wait.
|
|
86
|
+
*/
|
|
87
|
+
readonly timeoutMs?: ApprovalTimeout
|
|
49
88
|
readonly onRequired?: (request: ApprovalRequest) => void | PromiseLike<void>
|
|
50
89
|
readonly onResolved?: (decision: ApprovalDecision) => void | PromiseLike<void>
|
|
51
90
|
}
|
|
@@ -55,7 +94,7 @@ interface PendingApproval {
|
|
|
55
94
|
/** Settles with the decision; it exists before `onRequired` runs, so no decision can miss it. */
|
|
56
95
|
readonly decided: Promise<boolean>
|
|
57
96
|
readonly settle: (approved: boolean) => void
|
|
58
|
-
readonly timer: ReturnType<typeof setTimeout>
|
|
97
|
+
readonly timer: ReturnType<typeof setTimeout> | undefined
|
|
59
98
|
}
|
|
60
99
|
|
|
61
100
|
/** The agent protocol's token alphabet, so every protocol-valid id is accepted here too. */
|
|
@@ -77,16 +116,17 @@ export class ApprovalManager {
|
|
|
77
116
|
this.options = {
|
|
78
117
|
...options,
|
|
79
118
|
maxPending: options.maxPending ?? 32,
|
|
80
|
-
timeoutMs: options.timeoutMs ??
|
|
119
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_APPROVAL_TIMEOUT_MS,
|
|
81
120
|
}
|
|
82
121
|
if (!Number.isSafeInteger(this.options.maxPending) || this.options.maxPending < 1)
|
|
83
122
|
throw new RangeError("approvals: maxPending must be positive")
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
123
|
+
if (!isApprovalTimeout(this.options.timeoutMs))
|
|
124
|
+
throw new RangeError('approvals: timeoutMs must be between 1ms and 24h, or "never"')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** The configured timeout, for {@link approvalRequestFromEvent}. */
|
|
128
|
+
get timeoutMs(): ApprovalTimeout {
|
|
129
|
+
return this.options.timeoutMs
|
|
90
130
|
}
|
|
91
131
|
|
|
92
132
|
get pending(): readonly ApprovalRequest[] {
|
|
@@ -219,13 +259,17 @@ export class ApprovalManager {
|
|
|
219
259
|
...(input.reason === undefined ? {} : { reason: input.reason.slice(0, 512) }),
|
|
220
260
|
...(input.coordinate === undefined ? {} : { coordinate: input.coordinate }),
|
|
221
261
|
createdAt,
|
|
222
|
-
expiresAt: createdAt
|
|
262
|
+
expiresAt: approvalExpiresAt(createdAt, this.options.timeoutMs),
|
|
223
263
|
})
|
|
224
264
|
const { promise: decided, resolve: settle } = Promise.withResolvers<boolean>()
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
265
|
+
const timeoutMs = this.options.timeoutMs
|
|
266
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
267
|
+
if (timeoutMs !== "never") {
|
|
268
|
+
timer = setTimeout(() => {
|
|
269
|
+
this.settle(request.id, false, "approval timed out")
|
|
270
|
+
}, timeoutMs)
|
|
271
|
+
;(timer as unknown as { unref?: () => void }).unref?.()
|
|
272
|
+
}
|
|
229
273
|
const pending: PendingApproval = { request, decided, settle, timer }
|
|
230
274
|
this.pendingApprovals.set(request.id, pending)
|
|
231
275
|
await this.options.onRequired?.(request)
|
|
@@ -233,7 +277,17 @@ export class ApprovalManager {
|
|
|
233
277
|
}
|
|
234
278
|
}
|
|
235
279
|
|
|
236
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Project a backend approval event into a request. Pass the broker's timeout
|
|
282
|
+
* (`ApprovalManager.timeoutMs`) so `expiresAt` matches when it will actually be denied; the default
|
|
283
|
+
* is the broker's default of 5 minutes.
|
|
284
|
+
*/
|
|
285
|
+
export function approvalRequestFromEvent(
|
|
286
|
+
event: AgentApprovalRequiredEvent,
|
|
287
|
+
timeoutMs: ApprovalTimeout = DEFAULT_APPROVAL_TIMEOUT_MS,
|
|
288
|
+
): ApprovalRequest {
|
|
289
|
+
if (!isApprovalTimeout(timeoutMs))
|
|
290
|
+
throw new RangeError('approvals: timeoutMs must be between 1ms and 24h, or "never"')
|
|
237
291
|
return Object.freeze({
|
|
238
292
|
id: event.approvalId,
|
|
239
293
|
sessionId: event.sessionId,
|
|
@@ -242,7 +296,7 @@ export function approvalRequestFromEvent(event: AgentApprovalRequiredEvent): App
|
|
|
242
296
|
capability: event.capability,
|
|
243
297
|
...(event.reason === undefined ? {} : { reason: event.reason }),
|
|
244
298
|
createdAt: event.at,
|
|
245
|
-
expiresAt: event.at
|
|
299
|
+
expiresAt: approvalExpiresAt(event.at, timeoutMs),
|
|
246
300
|
})
|
|
247
301
|
}
|
|
248
302
|
|
package/src/cli.ts
CHANGED
|
@@ -179,6 +179,35 @@ function parseVerificationNames(value: string): readonly ("check" | "assure" | "
|
|
|
179
179
|
return Object.freeze([...new Set(names)] as ("check" | "assure" | "test")[])
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
export interface LineHandlers {
|
|
183
|
+
/** Answers an approval command and returns true; returns false for any other line. */
|
|
184
|
+
readonly answerApproval: (message: string) => Promise<boolean>
|
|
185
|
+
readonly run: (message: string) => Promise<void>
|
|
186
|
+
readonly stopReading: () => void
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Route interactive input. A running turn can be waiting on an approval, so approval commands answer
|
|
191
|
+
* at once; every other line runs in order behind the turn in flight. Reading ends at `/quit`, `/exit`
|
|
192
|
+
* or end of input, and settles once the queued lines have run. The first failure stops reading and
|
|
193
|
+
* is rethrown.
|
|
194
|
+
*/
|
|
195
|
+
export async function dispatchLines(
|
|
196
|
+
lines: AsyncIterable<string>,
|
|
197
|
+
handlers: LineHandlers,
|
|
198
|
+
): Promise<void> {
|
|
199
|
+
let queue: Promise<void> = Promise.resolve()
|
|
200
|
+
for await (const line of lines) {
|
|
201
|
+
const message = line.trim()
|
|
202
|
+
if (message.length === 0) continue
|
|
203
|
+
if (message === "/quit" || message === "/exit") break
|
|
204
|
+
if (await handlers.answerApproval(message)) continue
|
|
205
|
+
queue = queue.then(() => handlers.run(message))
|
|
206
|
+
queue.catch(handlers.stopReading)
|
|
207
|
+
}
|
|
208
|
+
await queue
|
|
209
|
+
}
|
|
210
|
+
|
|
182
211
|
function printEvent(event: AgentEvent, json: boolean): void {
|
|
183
212
|
if (json) {
|
|
184
213
|
console.log(JSON.stringify(event))
|
|
@@ -304,102 +333,105 @@ async function main(): Promise<void> {
|
|
|
304
333
|
input: process.stdin,
|
|
305
334
|
output: options.json ? undefined : process.stdout,
|
|
306
335
|
})
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
const
|
|
310
|
-
if (
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
|
|
340
|
-
|
|
336
|
+
const run = async (message: string): Promise<void> => {
|
|
337
|
+
if (message === "/reload") {
|
|
338
|
+
const result = await host.reload()
|
|
339
|
+
if (options.json) console.log(JSON.stringify({ type: "extension.reloaded", ...result }))
|
|
340
|
+
else console.error(`[reload] ${result.rolledBack ? "rolled back" : result.revision}`)
|
|
341
|
+
return
|
|
342
|
+
}
|
|
343
|
+
if (message === "/compact") {
|
|
344
|
+
const result = await host.compact("manual")
|
|
345
|
+
if (options.json) console.log(JSON.stringify({ type: "memory.compacted", ...result }))
|
|
346
|
+
else console.error(`[memory] compacted ${result.before} -> ${result.after}`)
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
if (message === "/checkpoint") {
|
|
350
|
+
await host.checkpoint()
|
|
351
|
+
if (options.json) console.log(JSON.stringify({ type: "session.checkpoint", ok: true }))
|
|
352
|
+
else console.error("[session] checkpoint saved")
|
|
353
|
+
return
|
|
354
|
+
}
|
|
355
|
+
const forkCommand = /^\/fork(?:\s+([^\s]+))?$/.exec(message)
|
|
356
|
+
if (forkCommand !== null) {
|
|
357
|
+
const result = await host.fork(forkCommand[1])
|
|
358
|
+
if (options.json) console.log(JSON.stringify({ type: "session.forked", ...result }))
|
|
359
|
+
else console.log(`[session] forked ${result.sessionId}`)
|
|
360
|
+
return
|
|
361
|
+
}
|
|
362
|
+
const historyCommand = /^\/history(?:\s+(\d+))?$/.exec(message)
|
|
363
|
+
if (historyCommand !== null) {
|
|
364
|
+
const result = await host.history(
|
|
365
|
+
historyCommand[1] === undefined ? 20 : Number(historyCommand[1]),
|
|
366
|
+
)
|
|
367
|
+
if (options.json) console.log(JSON.stringify({ type: "session.history", entries: result }))
|
|
368
|
+
else for (const entry of result) console.log(`${entry.seq} · ${entry.type}`)
|
|
369
|
+
return
|
|
370
|
+
}
|
|
371
|
+
if (message === "/check") {
|
|
372
|
+
const result = await runNifraVerification("check", { cwd: options.cwd })
|
|
373
|
+
if (options.json) console.log(JSON.stringify({ type: "verification.completed", ...result }))
|
|
374
|
+
else
|
|
375
|
+
console.error(
|
|
376
|
+
`[check] ${result.ok ? "passed" : "failed"}${result.status === null ? "" : ` (exit ${result.status})`}`,
|
|
341
377
|
)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}
|
|
346
|
-
if (
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
355
|
-
if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
378
|
+
return
|
|
379
|
+
}
|
|
380
|
+
if (message === "/assure") {
|
|
381
|
+
const result = await runNifraVerification("assure", { cwd: options.cwd })
|
|
382
|
+
if (options.json) console.log(JSON.stringify({ type: "verification.completed", ...result }))
|
|
383
|
+
else
|
|
384
|
+
console.error(
|
|
385
|
+
`[assure] ${result.ok ? "passed" : "failed"}${result.status === null ? "" : ` (exit ${result.status})`}`,
|
|
386
|
+
)
|
|
387
|
+
return
|
|
388
|
+
}
|
|
389
|
+
if (message === "/context") {
|
|
390
|
+
const result = await runNifraContext({ cwd: options.cwd })
|
|
391
|
+
if (options.json) console.log(JSON.stringify({ type: "context.completed", ...result }))
|
|
392
|
+
else console.log(result.output ?? JSON.stringify(result.report ?? result))
|
|
393
|
+
return
|
|
394
|
+
}
|
|
395
|
+
await send(message)
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const answerApproval = async (message: string): Promise<boolean> => {
|
|
399
|
+
if (message === "/approvals") {
|
|
400
|
+
const result = host.pendingApprovals
|
|
401
|
+
if (options.json) console.log(JSON.stringify({ type: "approval.list", pending: result }))
|
|
402
|
+
else if (result.length === 0) console.log("No pending approvals")
|
|
403
|
+
else
|
|
404
|
+
for (const approval of result)
|
|
405
|
+
console.log(`${approval.id} · ${approval.capability} · ${approval.action}`)
|
|
406
|
+
return true
|
|
407
|
+
}
|
|
408
|
+
const approvalCommand = /^(\/approve|\/deny)\s+([^\s]+)$/.exec(message)
|
|
409
|
+
if (approvalCommand !== null) {
|
|
410
|
+
const result = await host.resolveApproval(
|
|
411
|
+
approvalCommand[2]!,
|
|
412
|
+
approvalCommand[1] === "/approve",
|
|
413
|
+
"CLI decision",
|
|
414
|
+
)
|
|
415
|
+
if (options.json)
|
|
416
|
+
console.log(
|
|
417
|
+
JSON.stringify({
|
|
418
|
+
type: "approval.resolved",
|
|
419
|
+
...(result ?? { error: "approval not found" }),
|
|
420
|
+
}),
|
|
421
|
+
)
|
|
422
|
+
else
|
|
423
|
+
console.log(
|
|
424
|
+
result === undefined
|
|
425
|
+
? "Approval not found"
|
|
426
|
+
: `${result.approved ? "Approved" : "Denied"} ${result.approvalId}`,
|
|
385
427
|
)
|
|
386
|
-
|
|
387
|
-
console.log(
|
|
388
|
-
JSON.stringify({
|
|
389
|
-
type: "approval.resolved",
|
|
390
|
-
...(result ?? { error: "approval not found" }),
|
|
391
|
-
}),
|
|
392
|
-
)
|
|
393
|
-
else
|
|
394
|
-
console.log(
|
|
395
|
-
result === undefined
|
|
396
|
-
? "Approval not found"
|
|
397
|
-
: `${result.approved ? "Approved" : "Denied"} ${result.approvalId}`,
|
|
398
|
-
)
|
|
399
|
-
continue
|
|
400
|
-
}
|
|
401
|
-
await send(message)
|
|
428
|
+
return true
|
|
402
429
|
}
|
|
430
|
+
return false
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
await dispatchLines(readline, { answerApproval, run, stopReading: () => readline.close() })
|
|
403
435
|
} finally {
|
|
404
436
|
readline.close()
|
|
405
437
|
await stop()
|
package/src/context.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runBoundedProcess } from "./process.ts"
|
|
2
2
|
|
|
3
3
|
export interface NifraContextOptions {
|
|
4
4
|
readonly cwd: string
|
|
@@ -18,25 +18,19 @@ export interface NifraContextResult {
|
|
|
18
18
|
|
|
19
19
|
/** Project discovery kept outside the framework runtime; it is only spawned when an agent asks for it. */
|
|
20
20
|
export async function runNifraContext(options: NifraContextOptions): Promise<NifraContextResult> {
|
|
21
|
-
const proc = Bun.spawn([options.command ?? "nifra", "context"], {
|
|
22
|
-
cwd: options.cwd,
|
|
23
|
-
stdin: "ignore",
|
|
24
|
-
stdout: "pipe",
|
|
25
|
-
stderr: "pipe",
|
|
26
|
-
env: safeEnv(options.env),
|
|
27
|
-
})
|
|
28
21
|
const timeoutMs = options.timeoutMs ?? 30_000
|
|
29
|
-
let timedOut = false
|
|
30
|
-
const timer = setTimeout(() => {
|
|
31
|
-
timedOut = true
|
|
32
|
-
proc.kill()
|
|
33
|
-
}, timeoutMs)
|
|
34
22
|
try {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
23
|
+
const maxOutputBytes = options.maxOutputBytes ?? 1_048_576
|
|
24
|
+
const { stdout, stderr, status, timedOut } = await runBoundedProcess(
|
|
25
|
+
[options.command ?? "nifra", "context"],
|
|
26
|
+
{
|
|
27
|
+
cwd: options.cwd,
|
|
28
|
+
env: safeEnv(options.env),
|
|
29
|
+
timeoutMs,
|
|
30
|
+
maxStdoutBytes: maxOutputBytes,
|
|
31
|
+
maxStderrBytes: maxOutputBytes,
|
|
32
|
+
},
|
|
33
|
+
)
|
|
40
34
|
let report: unknown
|
|
41
35
|
try {
|
|
42
36
|
report = JSON.parse(stdout.text)
|
|
@@ -56,8 +50,6 @@ export async function runNifraContext(options: NifraContextOptions): Promise<Nif
|
|
|
56
50
|
status: null,
|
|
57
51
|
error: error instanceof Error ? error.message : String(error),
|
|
58
52
|
}
|
|
59
|
-
} finally {
|
|
60
|
-
clearTimeout(timer)
|
|
61
53
|
}
|
|
62
54
|
}
|
|
63
55
|
|
package/src/diff.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { publicErrorDetails } from "./errors.ts"
|
|
2
|
-
import {
|
|
2
|
+
import { runBoundedProcess } from "./process.ts"
|
|
3
3
|
|
|
4
4
|
export interface ProjectDiffOptions {
|
|
5
5
|
readonly cwd: string
|
|
@@ -29,27 +29,17 @@ export async function readProjectDiff(options: ProjectDiffOptions): Promise<Proj
|
|
|
29
29
|
maxOutputBytes > 16 * 1024 * 1024
|
|
30
30
|
)
|
|
31
31
|
throw new RangeError("project diff: maxOutputBytes is invalid")
|
|
32
|
-
const proc = Bun.spawn(
|
|
33
|
-
["git", "--no-pager", "diff", "HEAD", "--no-ext-diff", "--unified=3", "--"],
|
|
34
|
-
{
|
|
35
|
-
cwd: options.cwd,
|
|
36
|
-
stdin: "ignore",
|
|
37
|
-
stdout: "pipe",
|
|
38
|
-
stderr: "pipe",
|
|
39
|
-
env: safeEnv(),
|
|
40
|
-
},
|
|
41
|
-
)
|
|
42
|
-
let timedOut = false
|
|
43
|
-
const timer = setTimeout(() => {
|
|
44
|
-
timedOut = true
|
|
45
|
-
proc.kill()
|
|
46
|
-
}, timeoutMs)
|
|
47
32
|
try {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
33
|
+
const { stdout, stderr, status, timedOut } = await runBoundedProcess(
|
|
34
|
+
["git", "--no-pager", "diff", "HEAD", "--no-ext-diff", "--unified=3", "--"],
|
|
35
|
+
{
|
|
36
|
+
cwd: options.cwd,
|
|
37
|
+
env: safeEnv(),
|
|
38
|
+
timeoutMs,
|
|
39
|
+
maxStdoutBytes: maxOutputBytes,
|
|
40
|
+
maxStderrBytes: Math.min(maxOutputBytes, 64 * 1024),
|
|
41
|
+
},
|
|
42
|
+
)
|
|
53
43
|
return {
|
|
54
44
|
ok: !timedOut && status === 0,
|
|
55
45
|
status,
|
|
@@ -65,8 +55,6 @@ export async function readProjectDiff(options: ProjectDiffOptions): Promise<Proj
|
|
|
65
55
|
error: details.message,
|
|
66
56
|
...(details.stack === undefined ? {} : { stack: details.stack }),
|
|
67
57
|
}
|
|
68
|
-
} finally {
|
|
69
|
-
clearTimeout(timer)
|
|
70
58
|
}
|
|
71
59
|
}
|
|
72
60
|
|
package/src/extensions.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, realpathSync } from "node:fs"
|
|
2
|
-
import { join, relative, resolve } from "node:path"
|
|
2
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path"
|
|
3
3
|
import {
|
|
4
4
|
type WorkflowEvent,
|
|
5
5
|
type WorkflowResult,
|
|
@@ -398,5 +398,9 @@ function isWithin(root: string, candidate: string): boolean {
|
|
|
398
398
|
}
|
|
399
399
|
}
|
|
400
400
|
const relativePath = relative(canonical(root), canonical(candidate))
|
|
401
|
-
|
|
401
|
+
// On Windows a path on another drive has no relative form, so `relative` returns it absolute.
|
|
402
|
+
return (
|
|
403
|
+
relativePath === "" ||
|
|
404
|
+
(relativePath !== ".." && !relativePath.startsWith(`..${sep}`) && !isAbsolute(relativePath))
|
|
405
|
+
)
|
|
402
406
|
}
|
package/src/handoffs.ts
CHANGED
|
@@ -93,6 +93,8 @@ export interface HandoffCoordinatorOptions {
|
|
|
93
93
|
readonly approvals?: ApprovalManager
|
|
94
94
|
readonly defaultTtlMs?: number
|
|
95
95
|
readonly maxPending?: number
|
|
96
|
+
/** Settled boundaries kept for `list`/`inspect`; the oldest go first. Default 256. */
|
|
97
|
+
readonly maxRetained?: number
|
|
96
98
|
/** Injectable clock (epoch ms). Defaults to {@link Date.now}. */
|
|
97
99
|
readonly now?: () => number
|
|
98
100
|
}
|
|
@@ -108,12 +110,14 @@ interface HandoffRecord {
|
|
|
108
110
|
|
|
109
111
|
const DEFAULT_TTL_MS = 5 * 60_000
|
|
110
112
|
const DEFAULT_MAX_PENDING = 64
|
|
113
|
+
const DEFAULT_MAX_RETAINED = 256
|
|
111
114
|
|
|
112
115
|
export class HandoffCoordinator {
|
|
113
116
|
private readonly vectors: ChildVectorTracker
|
|
114
117
|
private readonly approvals: ApprovalManager | undefined
|
|
115
118
|
private readonly defaultTtlMs: number
|
|
116
119
|
private readonly maxPending: number
|
|
120
|
+
private readonly maxRetained: number
|
|
117
121
|
private readonly now: () => number
|
|
118
122
|
private readonly records = new Map<string, HandoffRecord>()
|
|
119
123
|
|
|
@@ -122,11 +126,14 @@ export class HandoffCoordinator {
|
|
|
122
126
|
this.approvals = options.approvals
|
|
123
127
|
this.defaultTtlMs = options.defaultTtlMs ?? DEFAULT_TTL_MS
|
|
124
128
|
this.maxPending = options.maxPending ?? DEFAULT_MAX_PENDING
|
|
129
|
+
this.maxRetained = options.maxRetained ?? DEFAULT_MAX_RETAINED
|
|
125
130
|
this.now = options.now ?? Date.now
|
|
126
131
|
if (!Number.isSafeInteger(this.defaultTtlMs) || this.defaultTtlMs < 1)
|
|
127
132
|
throw new RangeError("handoff: defaultTtlMs must be positive")
|
|
128
133
|
if (!Number.isSafeInteger(this.maxPending) || this.maxPending < 1)
|
|
129
134
|
throw new RangeError("handoff: maxPending must be positive")
|
|
135
|
+
if (!Number.isSafeInteger(this.maxRetained) || this.maxRetained < 0)
|
|
136
|
+
throw new RangeError("handoff: maxRetained must be a non-negative integer")
|
|
130
137
|
}
|
|
131
138
|
|
|
132
139
|
/** Open a pending handoff. Allocates the run's next monotonic child vector. */
|
|
@@ -142,6 +149,7 @@ export class HandoffCoordinator {
|
|
|
142
149
|
(input.sessionId !== undefined && !SESSION_TOKEN.test(input.sessionId))
|
|
143
150
|
)
|
|
144
151
|
throw new HandoffError("invalid_handoff")
|
|
152
|
+
this.sweep()
|
|
145
153
|
if (this.records.has(input.requestId)) throw new HandoffError("duplicate")
|
|
146
154
|
if (this.activeCount() >= this.maxPending) throw new HandoffError("invalid_handoff")
|
|
147
155
|
|
|
@@ -182,6 +190,7 @@ export class HandoffCoordinator {
|
|
|
182
190
|
|
|
183
191
|
/** Every boundary, or - with `activeOnly` - just those still awaiting a decision. */
|
|
184
192
|
list(activeOnly = false): readonly HandoffView[] {
|
|
193
|
+
this.sweep()
|
|
185
194
|
const views: HandoffView[] = []
|
|
186
195
|
for (const record of this.records.values()) {
|
|
187
196
|
if (activeOnly && this.isTerminal(record.state)) continue
|
|
@@ -192,6 +201,7 @@ export class HandoffCoordinator {
|
|
|
192
201
|
|
|
193
202
|
/** Inspect one boundary by request id, or `undefined` when unknown. */
|
|
194
203
|
inspect(requestId: string): HandoffView | undefined {
|
|
204
|
+
this.sweep()
|
|
195
205
|
const record = this.records.get(requestId)
|
|
196
206
|
return record === undefined ? undefined : this.view(record)
|
|
197
207
|
}
|
|
@@ -265,6 +275,23 @@ export class HandoffCoordinator {
|
|
|
265
275
|
return this.view(record)
|
|
266
276
|
}
|
|
267
277
|
|
|
278
|
+
/** Expire every boundary past its deadline, then drop the oldest settled ones over `maxRetained`. */
|
|
279
|
+
private sweep(): void {
|
|
280
|
+
const now = this.now()
|
|
281
|
+
let settled = 0
|
|
282
|
+
for (const record of this.records.values()) {
|
|
283
|
+
if (!this.isTerminal(record.state) && !coordinateIsFresh(record.coordinate, now))
|
|
284
|
+
this.settle(record, "expired")
|
|
285
|
+
if (this.isTerminal(record.state)) settled++
|
|
286
|
+
}
|
|
287
|
+
for (const [requestId, record] of this.records) {
|
|
288
|
+
if (settled <= this.maxRetained) break
|
|
289
|
+
if (!this.isTerminal(record.state)) continue
|
|
290
|
+
this.records.delete(requestId)
|
|
291
|
+
settled--
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
268
295
|
private settle(record: HandoffRecord, state: HandoffLifecycleState): void {
|
|
269
296
|
record.state = state
|
|
270
297
|
this.settleApproval(record, "expire")
|