@nifrajs/coding-agent 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/EXTENSIONS.md +25 -0
- package/PERFORMANCE.md +19 -0
- package/README.md +69 -0
- package/REGISTRY.md +15 -0
- package/SECURITY.md +32 -0
- package/dist/approvals.d.ts +70 -0
- package/dist/approvals.d.ts.map +1 -0
- package/dist/approvals.js +174 -0
- package/dist/approvals.js.map +1 -0
- package/dist/capabilities.d.ts +20 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +49 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/cli.d.ts +24 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +380 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +17 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +66 -0
- package/dist/context.js.map +1 -0
- package/dist/deployment-adapters.d.ts +40 -0
- package/dist/deployment-adapters.d.ts.map +1 -0
- package/dist/deployment-adapters.js +147 -0
- package/dist/deployment-adapters.js.map +1 -0
- package/dist/diff.d.ts +17 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +61 -0
- package/dist/diff.js.map +1 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/errors.js.map +1 -0
- package/dist/extensions.d.ts +104 -0
- package/dist/extensions.d.ts.map +1 -0
- package/dist/extensions.js +284 -0
- package/dist/extensions.js.map +1 -0
- package/dist/handoffs.d.ts +97 -0
- package/dist/handoffs.d.ts.map +1 -0
- package/dist/handoffs.js +199 -0
- package/dist/handoffs.js.map +1 -0
- package/dist/healing.d.ts +56 -0
- package/dist/healing.d.ts.map +1 -0
- package/dist/healing.js +54 -0
- package/dist/healing.js.map +1 -0
- package/dist/host.d.ts +61 -0
- package/dist/host.d.ts.map +1 -0
- package/dist/host.js +369 -0
- package/dist/host.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/isolated-worker.d.ts +2 -0
- package/dist/isolated-worker.d.ts.map +1 -0
- package/dist/isolated-worker.js +155 -0
- package/dist/isolated-worker.js.map +1 -0
- package/dist/isolated.d.ts +52 -0
- package/dist/isolated.d.ts.map +1 -0
- package/dist/isolated.js +293 -0
- package/dist/isolated.js.map +1 -0
- package/dist/native.d.ts +92 -0
- package/dist/native.d.ts.map +1 -0
- package/dist/native.js +339 -0
- package/dist/native.js.map +1 -0
- package/dist/orchestration/artifact-port.d.ts +30 -0
- package/dist/orchestration/artifact-port.d.ts.map +1 -0
- package/dist/orchestration/artifact-port.js +59 -0
- package/dist/orchestration/artifact-port.js.map +1 -0
- package/dist/orchestration/catalog.d.ts +71 -0
- package/dist/orchestration/catalog.d.ts.map +1 -0
- package/dist/orchestration/catalog.js +46 -0
- package/dist/orchestration/catalog.js.map +1 -0
- package/dist/orchestration/compile.d.ts +59 -0
- package/dist/orchestration/compile.d.ts.map +1 -0
- package/dist/orchestration/compile.js +268 -0
- package/dist/orchestration/compile.js.map +1 -0
- package/dist/orchestration/dispatch.d.ts +115 -0
- package/dist/orchestration/dispatch.d.ts.map +1 -0
- package/dist/orchestration/dispatch.js +220 -0
- package/dist/orchestration/dispatch.js.map +1 -0
- package/dist/orchestration/durable-jobs.d.ts +35 -0
- package/dist/orchestration/durable-jobs.d.ts.map +1 -0
- package/dist/orchestration/durable-jobs.js +273 -0
- package/dist/orchestration/durable-jobs.js.map +1 -0
- package/dist/orchestration/effect-key.d.ts +23 -0
- package/dist/orchestration/effect-key.d.ts.map +1 -0
- package/dist/orchestration/effect-key.js +24 -0
- package/dist/orchestration/effect-key.js.map +1 -0
- package/dist/orchestration/evidence-store.d.ts +75 -0
- package/dist/orchestration/evidence-store.d.ts.map +1 -0
- package/dist/orchestration/evidence-store.js +183 -0
- package/dist/orchestration/evidence-store.js.map +1 -0
- package/dist/orchestration/hash.d.ts +12 -0
- package/dist/orchestration/hash.d.ts.map +1 -0
- package/dist/orchestration/hash.js +43 -0
- package/dist/orchestration/hash.js.map +1 -0
- package/dist/orchestration/host.d.ts +121 -0
- package/dist/orchestration/host.d.ts.map +1 -0
- package/dist/orchestration/host.js +334 -0
- package/dist/orchestration/host.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +19 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/policy.d.ts +63 -0
- package/dist/orchestration/policy.d.ts.map +1 -0
- package/dist/orchestration/policy.js +80 -0
- package/dist/orchestration/policy.js.map +1 -0
- package/dist/orchestration/recovery.d.ts +87 -0
- package/dist/orchestration/recovery.d.ts.map +1 -0
- package/dist/orchestration/recovery.js +197 -0
- package/dist/orchestration/recovery.js.map +1 -0
- package/dist/orchestration/tracer.d.ts +31 -0
- package/dist/orchestration/tracer.d.ts.map +1 -0
- package/dist/orchestration/tracer.js +141 -0
- package/dist/orchestration/tracer.js.map +1 -0
- package/dist/plans.d.ts +35 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +63 -0
- package/dist/plans.js.map +1 -0
- package/dist/presets.d.ts +10 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +70 -0
- package/dist/presets.js.map +1 -0
- package/dist/process.d.ts +6 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +42 -0
- package/dist/process.js.map +1 -0
- package/dist/registry.d.ts +37 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +51 -0
- package/dist/registry.js.map +1 -0
- package/dist/replay.d.ts +24 -0
- package/dist/replay.d.ts.map +1 -0
- package/dist/replay.js +154 -0
- package/dist/replay.js.map +1 -0
- package/dist/rpc.d.ts +51 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +412 -0
- package/dist/rpc.js.map +1 -0
- package/dist/session-migration.d.ts +59 -0
- package/dist/session-migration.d.ts.map +1 -0
- package/dist/session-migration.js +315 -0
- package/dist/session-migration.js.map +1 -0
- package/dist/sessions.d.ts +74 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +222 -0
- package/dist/sessions.js.map +1 -0
- package/dist/subagents.d.ts +51 -0
- package/dist/subagents.d.ts.map +1 -0
- package/dist/subagents.js +141 -0
- package/dist/subagents.js.map +1 -0
- package/dist/tools.d.ts +13 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +37 -0
- package/dist/tools.js.map +1 -0
- package/dist/ui.d.ts +42 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +93 -0
- package/dist/ui.js.map +1 -0
- package/dist/verification.d.ts +34 -0
- package/dist/verification.d.ts.map +1 -0
- package/dist/verification.js +91 -0
- package/dist/verification.js.map +1 -0
- package/dist/workflows.d.ts +98 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +174 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +84 -0
- package/src/approvals.ts +249 -0
- package/src/capabilities.ts +74 -0
- package/src/cli.ts +405 -0
- package/src/context.ts +81 -0
- package/src/deployment-adapters.ts +181 -0
- package/src/diff.ts +80 -0
- package/src/errors.ts +46 -0
- package/src/extensions.ts +402 -0
- package/src/handoffs.ts +299 -0
- package/src/healing.ts +93 -0
- package/src/host.ts +465 -0
- package/src/index.ts +165 -0
- package/src/isolated-worker.ts +177 -0
- package/src/isolated.ts +344 -0
- package/src/native.ts +478 -0
- package/src/orchestration/artifact-port.ts +75 -0
- package/src/orchestration/catalog.ts +102 -0
- package/src/orchestration/compile.ts +373 -0
- package/src/orchestration/dispatch.ts +383 -0
- package/src/orchestration/durable-jobs.ts +374 -0
- package/src/orchestration/effect-key.ts +35 -0
- package/src/orchestration/evidence-store.ts +233 -0
- package/src/orchestration/hash.ts +43 -0
- package/src/orchestration/host.ts +445 -0
- package/src/orchestration/index.ts +131 -0
- package/src/orchestration/policy.ts +116 -0
- package/src/orchestration/recovery.ts +271 -0
- package/src/orchestration/tracer.ts +177 -0
- package/src/plans.ts +105 -0
- package/src/presets.ts +90 -0
- package/src/process.ts +47 -0
- package/src/registry.ts +80 -0
- package/src/replay.ts +190 -0
- package/src/rpc.ts +598 -0
- package/src/session-migration.ts +401 -0
- package/src/sessions.ts +303 -0
- package/src/subagents.ts +210 -0
- package/src/tools.ts +52 -0
- package/src/ui.ts +137 -0
- package/src/verification.ts +133 -0
- package/src/workflows.ts +279 -0
package/src/registry.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capability-descriptor adapter for coding-agent extensions.
|
|
3
|
+
*
|
|
4
|
+
* An extension is host-supplied code that runs with granted capabilities, so its descriptor is
|
|
5
|
+
* projected under the `extension` kind and, unlike a typed tool, admits an opaque input. The adapter
|
|
6
|
+
* fails closed on capability escalation: an extension must declare its capabilities explicitly (an
|
|
7
|
+
* empty array asserts "none"), and every declared capability must appear in the host's trusted
|
|
8
|
+
* allowlist. An omitted declaration or any capability outside the allowlist raises
|
|
9
|
+
* `capability_escalation` rather than silently narrowing or widening the grant.
|
|
10
|
+
*
|
|
11
|
+
* This module is the only place `@nifrajs/coding-agent` reaches `@nifrajs/agent`; the edge is
|
|
12
|
+
* `coding-agent -> agent`, never the reverse, so the agent package stays free of coding-agent code.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
type ApprovalClass,
|
|
17
|
+
type CapabilityDescriptor,
|
|
18
|
+
composeDescriptor,
|
|
19
|
+
type IsolationClass,
|
|
20
|
+
RegistryError,
|
|
21
|
+
} from "@nifrajs/agent/registry"
|
|
22
|
+
|
|
23
|
+
/** Opaque extension input. Extensions do not publish a typed schema, so identity rests on name and grant. */
|
|
24
|
+
const EXTENSION_INPUT_SCHEMA: Record<string, unknown> = Object.freeze({
|
|
25
|
+
type: "object",
|
|
26
|
+
additionalProperties: true,
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
/** The minimal shape an extension exposes for projection: an identity and an explicit capability grant. */
|
|
30
|
+
export interface ExtensionDescriptorSource {
|
|
31
|
+
readonly name: string
|
|
32
|
+
readonly capabilities?: readonly string[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ExtensionDescriptorOptions {
|
|
36
|
+
/** The host's trusted capability allowlist. A declared capability outside it fails closed. */
|
|
37
|
+
readonly trustedCapabilities: readonly string[]
|
|
38
|
+
readonly version?: string
|
|
39
|
+
readonly approval?: ApprovalClass
|
|
40
|
+
readonly isolation?: IsolationClass
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Project one extension into an `extension` capability descriptor, enforcing the grant. The declared
|
|
45
|
+
* capabilities become the descriptor's required capabilities only after every one is confirmed
|
|
46
|
+
* trusted; an undeclared grant or an escalated capability throws before any descriptor is built.
|
|
47
|
+
* Approval defaults to required and isolation to process, matching how untyped host code is admitted.
|
|
48
|
+
*/
|
|
49
|
+
export function extensionDescriptor(
|
|
50
|
+
extension: ExtensionDescriptorSource,
|
|
51
|
+
options: ExtensionDescriptorOptions,
|
|
52
|
+
): Promise<CapabilityDescriptor> {
|
|
53
|
+
const requested = extension.capabilities
|
|
54
|
+
if (requested === undefined) throw new RegistryError("capability_escalation")
|
|
55
|
+
const trusted = new Set(options.trustedCapabilities)
|
|
56
|
+
for (const capability of requested) {
|
|
57
|
+
if (!trusted.has(capability)) throw new RegistryError("capability_escalation")
|
|
58
|
+
}
|
|
59
|
+
return composeDescriptor({
|
|
60
|
+
kind: "extension",
|
|
61
|
+
name: extension.name,
|
|
62
|
+
...(options.version === undefined ? {} : { version: options.version }),
|
|
63
|
+
inputSchema: EXTENSION_INPUT_SCHEMA,
|
|
64
|
+
requiredCapabilities: requested,
|
|
65
|
+
approval: options.approval ?? { kind: "required" },
|
|
66
|
+
retry: "none",
|
|
67
|
+
idempotency: "none",
|
|
68
|
+
isolation: options.isolation ?? "process",
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Project a set of extensions under a single trusted allowlist, preserving order. */
|
|
73
|
+
export function extensionDescriptors(
|
|
74
|
+
extensions: readonly ExtensionDescriptorSource[],
|
|
75
|
+
options: ExtensionDescriptorOptions,
|
|
76
|
+
): Promise<readonly CapabilityDescriptor[]> {
|
|
77
|
+
return Promise.all(extensions.map((extension) => extensionDescriptor(extension, options)))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type { CapabilityDescriptor }
|
package/src/replay.ts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AgentBackend,
|
|
3
|
+
type AgentBackendInfo,
|
|
4
|
+
type AgentEvent,
|
|
5
|
+
type AgentEventStream,
|
|
6
|
+
type AgentSessionSnapshot,
|
|
7
|
+
agentError,
|
|
8
|
+
type CreateSessionInput,
|
|
9
|
+
createAgentEventStream,
|
|
10
|
+
type ReloadResult,
|
|
11
|
+
type SendMessageInput,
|
|
12
|
+
} from "@nifrajs/agent-protocol"
|
|
13
|
+
|
|
14
|
+
export interface ReplayBackendOptions {
|
|
15
|
+
readonly events: readonly AgentEvent[]
|
|
16
|
+
readonly delayMs?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface ReplaySession {
|
|
20
|
+
readonly id: string
|
|
21
|
+
snapshot: AgentSessionSnapshot
|
|
22
|
+
active: AgentEventStream | undefined
|
|
23
|
+
closed: boolean
|
|
24
|
+
seq: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Deterministic protocol backend for demos, CI, and UI regression tests. */
|
|
28
|
+
export class ReplayBackend implements AgentBackend {
|
|
29
|
+
readonly info: AgentBackendInfo = Object.freeze({
|
|
30
|
+
name: "replay",
|
|
31
|
+
capabilities: Object.freeze(["sessions", "streaming", "replay"]),
|
|
32
|
+
})
|
|
33
|
+
private readonly events: readonly AgentEvent[]
|
|
34
|
+
private readonly delayMs: number
|
|
35
|
+
private readonly sessions = new Map<string, ReplaySession>()
|
|
36
|
+
|
|
37
|
+
constructor(options: ReplayBackendOptions) {
|
|
38
|
+
this.events = Object.freeze(options.events.map((event) => Object.freeze({ ...event })))
|
|
39
|
+
this.delayMs = options.delayMs ?? 0
|
|
40
|
+
if (!Number.isSafeInteger(this.delayMs) || this.delayMs < 0 || this.delayMs > 60_000)
|
|
41
|
+
throw new RangeError("replay backend: delayMs must be between 0 and 60000")
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async createSession(input: CreateSessionInput): Promise<AgentSessionSnapshot> {
|
|
45
|
+
const id = input.sessionId ?? crypto.randomUUID()
|
|
46
|
+
if (!/^[a-zA-Z0-9._:-]{1,128}$/.test(id))
|
|
47
|
+
throw new TypeError("replay backend: invalid sessionId")
|
|
48
|
+
if (this.sessions.has(id)) throw new Error(`replay backend: session already exists: ${id}`)
|
|
49
|
+
const now = Date.now()
|
|
50
|
+
const snapshot: AgentSessionSnapshot = {
|
|
51
|
+
version: 1,
|
|
52
|
+
id,
|
|
53
|
+
backend: this.info.name,
|
|
54
|
+
cwd: input.cwd,
|
|
55
|
+
status: "idle",
|
|
56
|
+
createdAt: now,
|
|
57
|
+
updatedAt: now,
|
|
58
|
+
lastSeq: 0,
|
|
59
|
+
capabilities: Object.freeze([...(input.capabilities ?? this.info.capabilities)]),
|
|
60
|
+
}
|
|
61
|
+
this.sessions.set(id, { id, snapshot, active: undefined, closed: false, seq: 0 })
|
|
62
|
+
return snapshot
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
send(input: SendMessageInput): AsyncIterable<AgentEvent> {
|
|
66
|
+
const session = this.requireSession(input.sessionId)
|
|
67
|
+
if (session.closed)
|
|
68
|
+
return failedStream(agentError("SESSION_CLOSED", "replay session is closed"))
|
|
69
|
+
if (session.active !== undefined)
|
|
70
|
+
return failedStream(agentError("SESSION_BUSY", "replay session is busy"))
|
|
71
|
+
const stream = createAgentEventStream()
|
|
72
|
+
session.active = stream
|
|
73
|
+
void this.replay(session, stream)
|
|
74
|
+
return stream
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async cancel(sessionId: string, reason = "cancelled"): Promise<void> {
|
|
78
|
+
const session = this.requireSession(sessionId)
|
|
79
|
+
session.active?.complete()
|
|
80
|
+
session.active = undefined
|
|
81
|
+
session.snapshot = Object.freeze({
|
|
82
|
+
...session.snapshot,
|
|
83
|
+
status: "stopped",
|
|
84
|
+
updatedAt: Date.now(),
|
|
85
|
+
})
|
|
86
|
+
if (session.seq === 0) this.push(session, { type: "session.stopped", reason })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async snapshot(sessionId: string): Promise<AgentSessionSnapshot> {
|
|
90
|
+
return this.requireSession(sessionId).snapshot
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async reload(sessionId: string): Promise<ReloadResult> {
|
|
94
|
+
const session = this.requireSession(sessionId)
|
|
95
|
+
const revision = `replay:${this.events.length}`
|
|
96
|
+
session.snapshot = Object.freeze({
|
|
97
|
+
...session.snapshot,
|
|
98
|
+
extensionRevision: revision,
|
|
99
|
+
updatedAt: Date.now(),
|
|
100
|
+
})
|
|
101
|
+
return { revision, loaded: [], disabled: [], rolledBack: false }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async close(sessionId: string): Promise<void> {
|
|
105
|
+
const session = this.requireSession(sessionId)
|
|
106
|
+
session.closed = true
|
|
107
|
+
session.active?.complete()
|
|
108
|
+
session.active = undefined
|
|
109
|
+
this.sessions.delete(sessionId)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private async replay(session: ReplaySession, stream: AgentEventStream): Promise<void> {
|
|
113
|
+
session.snapshot = Object.freeze({
|
|
114
|
+
...session.snapshot,
|
|
115
|
+
status: "running",
|
|
116
|
+
updatedAt: Date.now(),
|
|
117
|
+
})
|
|
118
|
+
try {
|
|
119
|
+
for (const source of this.events) {
|
|
120
|
+
if (this.delayMs > 0) await new Promise((resolve) => setTimeout(resolve, this.delayMs))
|
|
121
|
+
this.push(session, remap(source, session.id, session.seq))
|
|
122
|
+
}
|
|
123
|
+
session.snapshot = Object.freeze({
|
|
124
|
+
...session.snapshot,
|
|
125
|
+
status: "idle",
|
|
126
|
+
updatedAt: Date.now(),
|
|
127
|
+
})
|
|
128
|
+
this.push(session, { type: "session.completed", snapshot: session.snapshot })
|
|
129
|
+
} catch (error) {
|
|
130
|
+
this.push(session, {
|
|
131
|
+
type: "session.failed",
|
|
132
|
+
error: agentError("REPLAY_FAILED", error instanceof Error ? error.message : String(error)),
|
|
133
|
+
recoverable: true,
|
|
134
|
+
})
|
|
135
|
+
} finally {
|
|
136
|
+
stream.complete()
|
|
137
|
+
session.active = undefined
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private push(
|
|
142
|
+
session: ReplaySession,
|
|
143
|
+
event: AgentEvent | import("@nifrajs/agent-protocol").AgentEventPayload,
|
|
144
|
+
): void {
|
|
145
|
+
const next =
|
|
146
|
+
"version" in event && "sessionId" in event
|
|
147
|
+
? event
|
|
148
|
+
: { version: 1 as const, sessionId: session.id, seq: session.seq, at: Date.now(), ...event }
|
|
149
|
+
session.seq = next.seq + 1
|
|
150
|
+
session.snapshot = Object.freeze({ ...session.snapshot, lastSeq: next.seq, updatedAt: next.at })
|
|
151
|
+
session.active?.push(next as AgentEvent)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private requireSession(id: string): ReplaySession {
|
|
155
|
+
const session = this.sessions.get(id)
|
|
156
|
+
if (session === undefined) throw new Error(`replay backend: unknown session: ${id}`)
|
|
157
|
+
return session
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function readReplayEvents(path: string): Promise<readonly AgentEvent[]> {
|
|
162
|
+
const text = await Bun.file(path).text()
|
|
163
|
+
const events: AgentEvent[] = []
|
|
164
|
+
for (const line of text.split("\n")) {
|
|
165
|
+
if (line.trim().length === 0) continue
|
|
166
|
+
const value: unknown = JSON.parse(line)
|
|
167
|
+
if (!isAgentEvent(value)) throw new Error(`replay: invalid event in ${path}`)
|
|
168
|
+
events.push(value)
|
|
169
|
+
}
|
|
170
|
+
return Object.freeze(events)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function remap(event: AgentEvent, sessionId: string, seq: number): AgentEvent {
|
|
174
|
+
return Object.freeze({ ...event, sessionId, seq, at: Date.now() })
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function isAgentEvent(value: unknown): value is AgentEvent {
|
|
178
|
+
return (
|
|
179
|
+
value !== null &&
|
|
180
|
+
typeof value === "object" &&
|
|
181
|
+
(value as { version?: unknown }).version === 1 &&
|
|
182
|
+
typeof (value as { type?: unknown }).type === "string"
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function failedStream(error: unknown): AgentEventStream {
|
|
187
|
+
const stream = createAgentEventStream()
|
|
188
|
+
stream.fail(error)
|
|
189
|
+
return stream
|
|
190
|
+
}
|