@nifrajs/agent 2.14.1 → 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 +36 -0
- package/dist/deployment.d.ts +157 -0
- package/dist/deployment.d.ts.map +1 -0
- package/dist/deployment.js +374 -0
- package/dist/deployment.js.map +1 -0
- package/dist/events.d.ts +41 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +75 -0
- package/dist/events.js.map +1 -1
- package/dist/execution-policy.d.ts.map +1 -1
- package/dist/execution-policy.js +11 -7
- package/dist/execution-policy.js.map +1 -1
- package/dist/gateway.d.ts +153 -0
- package/dist/gateway.d.ts.map +1 -0
- package/dist/gateway.js +409 -0
- package/dist/gateway.js.map +1 -0
- package/dist/index.d.ts +86 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +163 -1
- package/dist/index.js.map +1 -1
- package/dist/mount.d.ts +59 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +247 -0
- package/dist/mount.js.map +1 -0
- package/dist/registry.d.ts +112 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +326 -0
- package/dist/registry.js.map +1 -0
- package/package.json +12 -2
- package/src/deployment.ts +621 -0
- package/src/events.ts +125 -0
- package/src/execution-policy.ts +9 -5
- package/src/gateway.ts +607 -0
- package/src/index.ts +303 -1
- package/src/mount.ts +344 -0
- package/src/registry.ts +444 -0
package/src/events.ts
CHANGED
|
@@ -83,3 +83,128 @@ export function createAgentEvidenceStream(
|
|
|
83
83
|
}
|
|
84
84
|
return stream
|
|
85
85
|
}
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Resumable evidence log
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
/** One turn's replayable evidence view - see {@link AgentEvidenceLog.replay}. */
|
|
92
|
+
export interface AgentEvidenceReplay {
|
|
93
|
+
/** Evidence already recorded with `seq` greater than the requested cursor, in order. */
|
|
94
|
+
readonly evidence: readonly AgentStepEvidence[]
|
|
95
|
+
/** Live continuation while the turn is still running; `undefined` once it has finished. */
|
|
96
|
+
readonly live: AgentEvidenceStream | undefined
|
|
97
|
+
/** Resolves with the value passed to {@link AgentEvidenceLog.finish} when the turn ends. */
|
|
98
|
+
readonly result: Promise<unknown>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Seam for resumable evidence streams. An HTTP seam records one turn's step evidence through
|
|
103
|
+
* `open`, stores the terminal wire frame through `finish`, and serves SSE reconnects through
|
|
104
|
+
* `replay` (`Last-Event-ID` maps to evidence `seq`). The stored result value is opaque to the log -
|
|
105
|
+
* each seam stores whatever it needs to re-emit its own terminal frames.
|
|
106
|
+
*
|
|
107
|
+
* {@link createMemoryAgentEvidenceLog} is the single-process dev/test reference; a durable,
|
|
108
|
+
* multi-process log is an adapter concern behind the same interface.
|
|
109
|
+
*/
|
|
110
|
+
export interface AgentEvidenceLog {
|
|
111
|
+
/**
|
|
112
|
+
* Telemetry port recording one turn's evidence - combine it into `ports.telemetry` with
|
|
113
|
+
* `combineAgentTelemetry`. Opening a finished turn reopens it (a resume of a suspended run):
|
|
114
|
+
* evidence keeps appending and late replays await the new result.
|
|
115
|
+
*/
|
|
116
|
+
open(turnId: string): AgentTelemetryPort
|
|
117
|
+
/** Mark the turn finished: live replays complete and `result` is stored for late reconnects. */
|
|
118
|
+
finish(turnId: string, result: unknown): void | Promise<void>
|
|
119
|
+
/** The turn's evidence after `afterSeq`, or `undefined` for an unknown turn. */
|
|
120
|
+
replay(
|
|
121
|
+
turnId: string,
|
|
122
|
+
afterSeq: number,
|
|
123
|
+
): AgentEvidenceReplay | undefined | Promise<AgentEvidenceReplay | undefined>
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface MemoryAgentEvidenceLogOptions {
|
|
127
|
+
/** Maximum retained turns; the oldest turn is evicted when a new one opens. Default 256. */
|
|
128
|
+
readonly maxTurns?: number
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
interface MemoryTurnRecord {
|
|
132
|
+
readonly evidence: AgentStepEvidence[]
|
|
133
|
+
finished: boolean
|
|
134
|
+
resultPromise: Promise<unknown>
|
|
135
|
+
resolveResult: (value: unknown) => void
|
|
136
|
+
readonly subscribers: Set<AgentEvidenceStream>
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* In-memory {@link AgentEvidenceLog} reference for local development and tests. Single-process by
|
|
141
|
+
* construction: replay only sees runs recorded by this instance. Retention is bounded by
|
|
142
|
+
* `maxTurns` with oldest-first eviction, so a reconnect to an evicted turn reports
|
|
143
|
+
* replay-unavailable rather than growing memory without bound.
|
|
144
|
+
*/
|
|
145
|
+
export function createMemoryAgentEvidenceLog(
|
|
146
|
+
options: MemoryAgentEvidenceLogOptions = {},
|
|
147
|
+
): AgentEvidenceLog {
|
|
148
|
+
const maxTurns = options.maxTurns ?? 256
|
|
149
|
+
if (!Number.isSafeInteger(maxTurns) || maxTurns < 1)
|
|
150
|
+
throw new RangeError("agent evidence log: maxTurns must be a positive safe integer")
|
|
151
|
+
const turns = new Map<string, MemoryTurnRecord>()
|
|
152
|
+
|
|
153
|
+
const deferred = (record: MemoryTurnRecord): void => {
|
|
154
|
+
record.resultPromise = new Promise<unknown>((resolve) => {
|
|
155
|
+
record.resolveResult = resolve
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
open(turnId) {
|
|
161
|
+
let turn = turns.get(turnId)
|
|
162
|
+
if (turn === undefined) {
|
|
163
|
+
while (turns.size >= maxTurns) {
|
|
164
|
+
const oldest = turns.keys().next().value
|
|
165
|
+
if (oldest === undefined) break
|
|
166
|
+
turns.delete(oldest)
|
|
167
|
+
}
|
|
168
|
+
turn = {
|
|
169
|
+
evidence: [],
|
|
170
|
+
finished: false,
|
|
171
|
+
resultPromise: Promise.resolve(undefined),
|
|
172
|
+
resolveResult: () => {},
|
|
173
|
+
subscribers: new Set(),
|
|
174
|
+
}
|
|
175
|
+
deferred(turn)
|
|
176
|
+
turns.set(turnId, turn)
|
|
177
|
+
} else if (turn.finished) {
|
|
178
|
+
// A resume reopens the turn: the next finish() resolves a fresh result for new replays.
|
|
179
|
+
turn.finished = false
|
|
180
|
+
deferred(turn)
|
|
181
|
+
}
|
|
182
|
+
const opened = turn
|
|
183
|
+
return {
|
|
184
|
+
step(evidence) {
|
|
185
|
+
opened.evidence.push(evidence)
|
|
186
|
+
for (const subscriber of opened.subscribers) subscriber.step(evidence)
|
|
187
|
+
},
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
finish(turnId, result) {
|
|
191
|
+
const turn = turns.get(turnId)
|
|
192
|
+
if (turn === undefined || turn.finished) return
|
|
193
|
+
turn.finished = true
|
|
194
|
+
turn.resolveResult(result)
|
|
195
|
+
for (const subscriber of turn.subscribers) subscriber.complete()
|
|
196
|
+
turn.subscribers.clear()
|
|
197
|
+
},
|
|
198
|
+
replay(turnId, afterSeq) {
|
|
199
|
+
const turn = turns.get(turnId)
|
|
200
|
+
if (turn === undefined) return undefined
|
|
201
|
+
const evidence = turn.evidence.filter((item) => item.seq > afterSeq)
|
|
202
|
+
if (turn.finished) return { evidence, live: undefined, result: turn.resultPromise }
|
|
203
|
+
// Snapshot and subscribe in the same synchronous step, so no evidence item can land between
|
|
204
|
+
// the filtered history and the live stream.
|
|
205
|
+
const live = createAgentEvidenceStream()
|
|
206
|
+
turn.subscribers.add(live)
|
|
207
|
+
return { evidence, live, result: turn.resultPromise }
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
}
|
package/src/execution-policy.ts
CHANGED
|
@@ -199,19 +199,21 @@ function spawnProcess(input: SpawnInput): Promise<LocalProcessResult> {
|
|
|
199
199
|
cancelled = true
|
|
200
200
|
terminate()
|
|
201
201
|
}
|
|
202
|
-
|
|
202
|
+
const cleanup = (): void => {
|
|
203
|
+
clearTimeout(timer)
|
|
204
|
+
if (killTimer !== undefined) clearTimeout(killTimer)
|
|
205
|
+
input.signal?.removeEventListener("abort", cancel)
|
|
206
|
+
}
|
|
203
207
|
child.once("error", () => {
|
|
204
208
|
if (settled) return
|
|
205
209
|
settled = true
|
|
206
|
-
|
|
207
|
-
if (killTimer !== undefined) clearTimeout(killTimer)
|
|
210
|
+
cleanup()
|
|
208
211
|
reject(new LocalProcessPolicyError("invalid_request"))
|
|
209
212
|
})
|
|
210
213
|
child.once("close", (exitCode, signal) => {
|
|
211
214
|
if (settled) return
|
|
212
215
|
settled = true
|
|
213
|
-
|
|
214
|
-
if (killTimer !== undefined) clearTimeout(killTimer)
|
|
216
|
+
cleanup()
|
|
215
217
|
resolve({
|
|
216
218
|
ok: !timedOut && !cancelled && exitCode === 0,
|
|
217
219
|
exitCode,
|
|
@@ -223,6 +225,8 @@ function spawnProcess(input: SpawnInput): Promise<LocalProcessResult> {
|
|
|
223
225
|
limitations: input.limitations,
|
|
224
226
|
})
|
|
225
227
|
})
|
|
228
|
+
input.signal?.addEventListener("abort", cancel, { once: true })
|
|
229
|
+
if (input.signal?.aborted === true) cancel() // close the precheck/listener-registration race
|
|
226
230
|
})
|
|
227
231
|
}
|
|
228
232
|
|