@nightowlsdev/engine-ai-sdk 0.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.
@@ -0,0 +1,210 @@
1
+ import { EngineCapabilities, Engine, AssembledEngineOpts, RunInput, SwarmContext, SwarmEvent, SwarmMessage, ThreadSummary, ScratchpadEntry, ActiveRun, AgentSummary } from '@nightowlsdev/core';
2
+
3
+ /**
4
+ * The AI-SDK-native engine's static capability descriptor (P3 v0). Tier 3 declared with
5
+ * `swarm.handoff` absent from `emits` — handoff is delegation-only (this engine's `delegation` is
6
+ * `false`); `emits` is authoritative over the declared tier for which events actually stream.
7
+ *
8
+ * Kept in its OWN module (not `index.ts`) so `src/engine.ts` (Task 5+) can import it without a
9
+ * circular dependency once `index.ts`'s `aiSdkEngine()` factory constructs `AiSdkEngine` directly
10
+ * (Task 10) — `index.ts` re-exports this same constant, so the public export path
11
+ * (`@nightowlsdev/engine-ai-sdk`'s `AI_SDK_ENGINE_CAPABILITIES`) is unchanged.
12
+ */
13
+ declare const AI_SDK_ENGINE_CAPABILITIES: EngineCapabilities;
14
+
15
+ /**
16
+ * The AI-SDK-native engine (P3) — a governed `ai@^6` `streamText` turn loop behind the `Engine` seam, single-
17
+ * agent v1 (no delegation/workflows). `run()` carries the full pre-loop + lifecycle scaffolding (rows, floor
18
+ * serialization, the preGeneration reserve, the persisted-not-yielded user message, telemetry spans); `resume()`
19
+ * recovers a parked run's snapshot, turns the human/approval/client answer into the tool-call's result, and
20
+ * re-enters the SAME governed loop (`drive()`) from there. Both share `drive()`: tool assembly (Task 4),
21
+ * system-prompt assembly (Task 3), the `streamText` multi-step loop, per-generation metering + cost caps,
22
+ * mid-stream cancellation, completion supervision (nudge-on-incomplete), and suspend/resume parking (ask /
23
+ * native tool approvals / client actions).
24
+ */
25
+ declare class AiSdkEngine implements Engine {
26
+ private readonly opts;
27
+ readonly capabilities: EngineCapabilities;
28
+ private readonly hooks;
29
+ private readonly floor;
30
+ private readonly telemetry;
31
+ constructor(opts: AssembledEngineOpts, extra: {
32
+ durable: boolean;
33
+ });
34
+ /**
35
+ * SP2 — the preGeneration DECISION seam, awaited immediately before every model launch. Verbatim mirror of
36
+ * the reference engine's private `guardGeneration` (`packages/core/src/engine.ts:543-553`): the dispatcher is
37
+ * fail-closed (a throwing hook ⇒ deny), so this only ever sees a clean allow/deny; a deny THROWS
38
+ * `ReserveDenied` (re-exported from core, not redefined here) so the caller's model launch never happens and
39
+ * the run/resume catch-all maps it to a terminal `run_failed` stage `"reserve"` (never the generic
40
+ * `"exception"`).
41
+ */
42
+ private guardGeneration;
43
+ /**
44
+ * SP5 truth-fix — resolve whether a tool WILL require approval, for the `swarm.tool_call` event's advisory
45
+ * `needsApproval` field. Verbatim mirror of the reference engine's private `gatesApproval`
46
+ * (`packages/core/src/engine.ts:528-534`): the tool's resolved `needsApproval` (its own flag, defaulting by
47
+ * origin) run through the dispatcher's SYNC `policyDecision` (`ask` ⇒ true). The async `preToolCall` hook can
48
+ * still escalate a specific call at execute time (that's what actually drives the tool's native pause — see
49
+ * `tools.ts`'s `buildServerTool`); this is only the truthful baseline the event carries.
50
+ */
51
+ private gatesApproval;
52
+ /**
53
+ * T9 (T8-review fix) — best-effort recall of the thread's request text for the completion verifier's
54
+ * `request` param on RESUME (the engine doesn't hold the original prompt there — `resume()` only carries the
55
+ * suspended tool's answer). Mirrors the reference engine's `recallRequest`, but deliberately the LAST user
56
+ * turn (not the first): a resume continues whichever prompt MOST RECENTLY triggered this run, not the
57
+ * thread's opening message. Fail-safe: gated behind `verifyCompletion` (a no-verifier config never pays for
58
+ * the read) and never throws — any failure yields `""`, matching the reference's own fallback.
59
+ */
60
+ private recallRequest;
61
+ /** Run the completion supervisor (`EngineOpts.verifyCompletion`), FAIL-OPEN: no verifier, or a throwing one,
62
+ * yields `{ complete: true }` so a missing/broken judge never traps a run in a verify loop. Mirrors the
63
+ * reference engine's private `safeVerify`. */
64
+ private safeVerify;
65
+ /** FR-024 — resolve the host's per-run advisory system-context block. FAIL-SAFE: a throwing/rejecting resolver
66
+ * is swallowed (logged) and yields no block, so a broken context source never breaks the run. Mirrors the
67
+ * reference engine's private `safeSystemContext`. */
68
+ private safeSystemContext;
69
+ /** FR-027 — resolve the host's opt-in per-request instruction-only (dynamic/imported) skills, by name.
70
+ * FAIL-SAFE: a throwing/rejecting resolver yields no dynamic skills (never breaks the run). No memoization —
71
+ * v1 has no delegation, so there is only ever ONE resolution per segment (no sub-agent to share it with);
72
+ * re-resolved fresh on every `resume()` segment too (never carried over the suspend boundary). */
73
+ private safeDynamicSkills;
74
+ /**
75
+ * Resolve the CONCRETE model instance for one generation launch: tier routing (a sentinel `modelId` routes to
76
+ * the configured tier; a concrete pin passes verbatim), THEN the allow-list `opts.model.resolve` (a deny
77
+ * throws — this is the security-critical re-validation the plan calls out: EVERY resolved id, tier-routed or
78
+ * not, goes through the allow-list, never just the stored one), THEN `opts.modelFactory`. Mirrors the
79
+ * reference engine's private `modelFor` (`packages/core/src/mastra-map.ts:284-292`). Called once for the
80
+ * `streamText` call's outer `model` field AND again from `prepareStep` for EVERY step (including the first) —
81
+ * so a revoked/rotated allow-list entry is caught mid-run, not just at the top of the segment.
82
+ */
83
+ private resolveModel;
84
+ /** Build this segment's `ToolAssemblyOpts` — identical wiring for `run()` and `resume()` (both rebuild tools
85
+ * fresh from the CURRENT agent row; only `runCtx` differs). */
86
+ private toolAssemblyOpts;
87
+ /**
88
+ * The shared governed `streamText` drive — Task 6's loop, generalized so both `run()` (segmentIndex 0) and
89
+ * `resume()` (segmentIndex = the snapshot's genIndex) call the SAME implementation: per-step metering + cost
90
+ * caps, mid-stream cancellation, suspend-parking (ask / native tool approvals / client actions), and
91
+ * completion-supervisor nudging. Returns the segment's terminal outcome as the generator's return value (the
92
+ * caller captures it via `const outcome = yield* this.drive(...)`); every terminal event (`done`/`run_failed`/
93
+ * `run_cancelled`/the park's `waiting`+`question`/`client_action`) is yielded from INSIDE this loop — the
94
+ * caller only needs the returned outcome for its own `onRunEnd` reporting.
95
+ */
96
+ private drive;
97
+ run(input: RunInput, ctx: SwarmContext, signal?: AbortSignal): AsyncIterable<SwarmEvent>;
98
+ /**
99
+ * Resume a parked run: `markFollowupAnswered` EARLY (CAS, replay-safe) → `loadSnapshot` → reload the agent
100
+ * row fresh (same as `run()`) → re-acquire the floor → emit `swarm.answer` → preGeneration re-fire (kind
101
+ * 'resume', at the snapshot's genIndex) → per-kind append the answer to the snapshot's messages → re-enter
102
+ * the SHARED `drive()` with rebuilt tools (`approvedToolCallIds` pre-populated on an approval-approve, BEFORE
103
+ * `buildTools`/`streamText` — Task 4's blocker-fix contract) + restored messages. A re-park persists
104
+ * `genIndex = (this segment's own segmentIndex) + 1` — the same bookkeeping `drive()` already does for
105
+ * `run()`'s park, generalized.
106
+ *
107
+ * ⚠ `onRunStart` deliberately does NOT fire here — this is a CONTINUATION of the run `onRunStart` already
108
+ * announced (in `run()`), not a new one. Mirrors the reference (Mastra) engine exactly: `packages/core/src/
109
+ * engine.ts`'s own `resume()` never calls `this.opts.onRunStart` either (only `run()` does — see that file's
110
+ * `onRunStart` doc comment: "fires on `run()` only"). A host that seeds per-run state in `onRunStart` gets it
111
+ * back via `snap.state` → `resumedState` below, so nothing is lost across the suspend/resume boundary.
112
+ */
113
+ resume(args: {
114
+ runId: string;
115
+ toolCallId: string;
116
+ followupId: string;
117
+ answer: unknown;
118
+ context?: Record<string, unknown>;
119
+ }, ctx: SwarmContext, signal?: AbortSignal): AsyncIterable<SwarmEvent>;
120
+ /**
121
+ * T9 — events-derived thread history (no Mastra memory in this engine: the events log IS the store).
122
+ * `container(threadId)` resolves the root container; `events.listForContainer` (optional on `StorageAdapter`
123
+ * — absent ⇒ `[]`, matching the reference's "no memory ⇒ []" degradation) returns the FULL container log
124
+ * (root + any lane sub-threads), each event tagged with its OWN run's `threadId`. Filtered LANE-EXACT to the
125
+ * REQUESTED `threadId` (so a root read never leaks a lane's side-chat and vice versa), then folded into
126
+ * `SwarmMessage[]` by `messagesFromEvents` (structural attribution; decode-only legacy prefix stripping — see
127
+ * its doc comment). Hard-capped to the most recent `limit` (default 200, mirrors the reference).
128
+ */
129
+ history(threadId: string, ctx: SwarmContext, opts?: {
130
+ limit?: number;
131
+ }): Promise<SwarmMessage[]>;
132
+ /** T9 helper — one container's `ThreadSummary`: title = its first (oldest) user message's text, falling back
133
+ * to the bare container id (an empty/title-less container, matching the reference's "no Mastra row" fallback
134
+ * shape: `{ threadId: container, title: container, lastActivityAt: 0 }`). `lastActivityAt` = the max `seq`
135
+ * seen on the container's BARE (non-lane) events (T9-review fix): `seq` is the storage-assigned, GLOBALLY
136
+ * monotonic append counter, so a newer run's events always rank above an older run's — whereas `ts` is a
137
+ * per-segment ordinal (restarts at 0/1000 every segment, see `emit.ts`) and is meaningless across runs. Same
138
+ * seq-over-ts preference the react timeline established (react/timeline.ts). NOT a wall-clock epoch (no
139
+ * adapter in the `StorageAdapter` contract exposes one for runs/events) — a recency-ORDERING proxy only.
140
+ */
141
+ private threadSummaryFor;
142
+ /**
143
+ * T9 — R14 participation-based thread listing (mirrors the reference `listThreads`, minus the Mastra memory
144
+ * dependency): `runs.listUserContainers` (optional) gives the containers this user has a run in,
145
+ * newest-active first; each is summarized via `threadSummaryFor` (concurrently, order preserved).
146
+ * FALLBACK (an adapter without `listUserContainers`): the reference degrades to the CURRENT container under
147
+ * multi-user (no cross-container Mastra listing to fall back on here either) — a single-entry list for
148
+ * `ctx.threadId`'s own container.
149
+ */
150
+ listThreads(ctx: SwarmContext, opts?: {
151
+ limit?: number;
152
+ }): Promise<ThreadSummary[]>;
153
+ /** The PUBLIC entries of a conversation's scratchpad (empty array when the feature is off or unset).
154
+ * Mirrors the reference engine's `scratchpadPublic` verbatim (storage-backed, engine-neutral). */
155
+ scratchpadPublic(container: string, ctx: SwarmContext): Promise<ScratchpadEntry[]>;
156
+ /** In-flight runs (running|suspended) for a container + its lanes. Mirrors the reference engine's
157
+ * `activeRuns` verbatim (storage-backed, engine-neutral). */
158
+ activeRuns(container: string, ctx: SwarmContext): Promise<ActiveRun[]>;
159
+ /** The full, globally-ordered event log for a thread's CONTAINER (all its runs + lane sub-threads) — unlike
160
+ * `history()`, NOT lane-filtered: this is the rich raw timeline (tool calls + status, not just messages).
161
+ * Mirrors the reference engine's `threadEvents` verbatim (storage-backed, engine-neutral). Returns `[]` when
162
+ * the store has no `events.listForContainer`. */
163
+ threadEvents(threadId: string, ctx: SwarmContext): Promise<SwarmEvent[]>;
164
+ /**
165
+ * T9 (REQUIRED on `Engine`) — the tenant's agent roster as wall-safe `AgentSummary[]`: `agents.listSlugs` →
166
+ * `head` per slug (dropping any slug whose head resolves to `null`, a race with a mid-listing deletion).
167
+ * `delegateSlugs` is ALWAYS `[]` — a deliberate divergence from the reference engine (which surfaces the
168
+ * stored `AgentVersion.delegateSlugs`): this engine's `capabilities.delegation` is `false` (v1, single-agent),
169
+ * so there is no addressable delegate graph to report regardless of what a row happens to store.
170
+ */
171
+ listAgents(ctx: SwarmContext): Promise<AgentSummary[]>;
172
+ }
173
+
174
+ /**
175
+ * Engine factory for `defineSwarm({ engine: aiSdkEngine() })` — constructs the wired `AiSdkEngine` (a
176
+ * governed `ai@^6` `streamText` loop, single-agent v1; see `src/engine.ts`). Mirrors the reference
177
+ * (Mastra) engine's own `durable` ctor param: `durableResume` is a per-INSTANCE capability flip
178
+ * (`AI_SDK_ENGINE_CAPABILITIES.hitl.durableResume`), never inferred from the injected `StorageAdapter`.
179
+ * Pass `aiSdkEngine({ durable: true })` only when that storage persists suspend/resume snapshots
180
+ * CROSS-PROCESS (e.g. `@nightowlsdev/storage-supabase`) — otherwise leave the default `false` so
181
+ * consumers (mcp-server/runner-background) keep treating `ask`/approval resumes as non-durable.
182
+ */
183
+ declare function aiSdkEngine({ durable }?: {
184
+ durable?: boolean;
185
+ }): (opts: AssembledEngineOpts) => Engine;
186
+ /**
187
+ * The declarative Night Owls plugin manifest consumed by `@nightowlsdev/cli`. STRUCTURALLY matches the CLI's
188
+ * `NightOwlsPlugin` type but does NOT import it (no dependency cycle): all codegen lives in `@nightowlsdev/cli`;
189
+ * this adapter stays data-only.
190
+ */
191
+ declare const nightOwlsPlugin: {
192
+ readonly name: "engine-ai-sdk";
193
+ readonly version: "0.0.0";
194
+ readonly kind: "engine";
195
+ readonly pkg: "@nightowlsdev/engine-ai-sdk";
196
+ readonly description: "The AI-SDK-native engine as a named engine package (single-agent v1, governed ai@6 streamText loop).";
197
+ readonly env: readonly [];
198
+ readonly config: {
199
+ readonly import: "import { aiSdkEngine } from \"@nightowlsdev/engine-ai-sdk\";";
200
+ readonly snippet: "engine = aiSdkEngine();";
201
+ readonly marker: "engine";
202
+ };
203
+ readonly init: (ctx: {
204
+ cwd: string;
205
+ log: (m: string) => void;
206
+ }) => void;
207
+ readonly commands: readonly [];
208
+ };
209
+
210
+ export { AI_SDK_ENGINE_CAPABILITIES, AiSdkEngine, aiSdkEngine, nightOwlsPlugin };
@@ -0,0 +1,210 @@
1
+ import { EngineCapabilities, Engine, AssembledEngineOpts, RunInput, SwarmContext, SwarmEvent, SwarmMessage, ThreadSummary, ScratchpadEntry, ActiveRun, AgentSummary } from '@nightowlsdev/core';
2
+
3
+ /**
4
+ * The AI-SDK-native engine's static capability descriptor (P3 v0). Tier 3 declared with
5
+ * `swarm.handoff` absent from `emits` — handoff is delegation-only (this engine's `delegation` is
6
+ * `false`); `emits` is authoritative over the declared tier for which events actually stream.
7
+ *
8
+ * Kept in its OWN module (not `index.ts`) so `src/engine.ts` (Task 5+) can import it without a
9
+ * circular dependency once `index.ts`'s `aiSdkEngine()` factory constructs `AiSdkEngine` directly
10
+ * (Task 10) — `index.ts` re-exports this same constant, so the public export path
11
+ * (`@nightowlsdev/engine-ai-sdk`'s `AI_SDK_ENGINE_CAPABILITIES`) is unchanged.
12
+ */
13
+ declare const AI_SDK_ENGINE_CAPABILITIES: EngineCapabilities;
14
+
15
+ /**
16
+ * The AI-SDK-native engine (P3) — a governed `ai@^6` `streamText` turn loop behind the `Engine` seam, single-
17
+ * agent v1 (no delegation/workflows). `run()` carries the full pre-loop + lifecycle scaffolding (rows, floor
18
+ * serialization, the preGeneration reserve, the persisted-not-yielded user message, telemetry spans); `resume()`
19
+ * recovers a parked run's snapshot, turns the human/approval/client answer into the tool-call's result, and
20
+ * re-enters the SAME governed loop (`drive()`) from there. Both share `drive()`: tool assembly (Task 4),
21
+ * system-prompt assembly (Task 3), the `streamText` multi-step loop, per-generation metering + cost caps,
22
+ * mid-stream cancellation, completion supervision (nudge-on-incomplete), and suspend/resume parking (ask /
23
+ * native tool approvals / client actions).
24
+ */
25
+ declare class AiSdkEngine implements Engine {
26
+ private readonly opts;
27
+ readonly capabilities: EngineCapabilities;
28
+ private readonly hooks;
29
+ private readonly floor;
30
+ private readonly telemetry;
31
+ constructor(opts: AssembledEngineOpts, extra: {
32
+ durable: boolean;
33
+ });
34
+ /**
35
+ * SP2 — the preGeneration DECISION seam, awaited immediately before every model launch. Verbatim mirror of
36
+ * the reference engine's private `guardGeneration` (`packages/core/src/engine.ts:543-553`): the dispatcher is
37
+ * fail-closed (a throwing hook ⇒ deny), so this only ever sees a clean allow/deny; a deny THROWS
38
+ * `ReserveDenied` (re-exported from core, not redefined here) so the caller's model launch never happens and
39
+ * the run/resume catch-all maps it to a terminal `run_failed` stage `"reserve"` (never the generic
40
+ * `"exception"`).
41
+ */
42
+ private guardGeneration;
43
+ /**
44
+ * SP5 truth-fix — resolve whether a tool WILL require approval, for the `swarm.tool_call` event's advisory
45
+ * `needsApproval` field. Verbatim mirror of the reference engine's private `gatesApproval`
46
+ * (`packages/core/src/engine.ts:528-534`): the tool's resolved `needsApproval` (its own flag, defaulting by
47
+ * origin) run through the dispatcher's SYNC `policyDecision` (`ask` ⇒ true). The async `preToolCall` hook can
48
+ * still escalate a specific call at execute time (that's what actually drives the tool's native pause — see
49
+ * `tools.ts`'s `buildServerTool`); this is only the truthful baseline the event carries.
50
+ */
51
+ private gatesApproval;
52
+ /**
53
+ * T9 (T8-review fix) — best-effort recall of the thread's request text for the completion verifier's
54
+ * `request` param on RESUME (the engine doesn't hold the original prompt there — `resume()` only carries the
55
+ * suspended tool's answer). Mirrors the reference engine's `recallRequest`, but deliberately the LAST user
56
+ * turn (not the first): a resume continues whichever prompt MOST RECENTLY triggered this run, not the
57
+ * thread's opening message. Fail-safe: gated behind `verifyCompletion` (a no-verifier config never pays for
58
+ * the read) and never throws — any failure yields `""`, matching the reference's own fallback.
59
+ */
60
+ private recallRequest;
61
+ /** Run the completion supervisor (`EngineOpts.verifyCompletion`), FAIL-OPEN: no verifier, or a throwing one,
62
+ * yields `{ complete: true }` so a missing/broken judge never traps a run in a verify loop. Mirrors the
63
+ * reference engine's private `safeVerify`. */
64
+ private safeVerify;
65
+ /** FR-024 — resolve the host's per-run advisory system-context block. FAIL-SAFE: a throwing/rejecting resolver
66
+ * is swallowed (logged) and yields no block, so a broken context source never breaks the run. Mirrors the
67
+ * reference engine's private `safeSystemContext`. */
68
+ private safeSystemContext;
69
+ /** FR-027 — resolve the host's opt-in per-request instruction-only (dynamic/imported) skills, by name.
70
+ * FAIL-SAFE: a throwing/rejecting resolver yields no dynamic skills (never breaks the run). No memoization —
71
+ * v1 has no delegation, so there is only ever ONE resolution per segment (no sub-agent to share it with);
72
+ * re-resolved fresh on every `resume()` segment too (never carried over the suspend boundary). */
73
+ private safeDynamicSkills;
74
+ /**
75
+ * Resolve the CONCRETE model instance for one generation launch: tier routing (a sentinel `modelId` routes to
76
+ * the configured tier; a concrete pin passes verbatim), THEN the allow-list `opts.model.resolve` (a deny
77
+ * throws — this is the security-critical re-validation the plan calls out: EVERY resolved id, tier-routed or
78
+ * not, goes through the allow-list, never just the stored one), THEN `opts.modelFactory`. Mirrors the
79
+ * reference engine's private `modelFor` (`packages/core/src/mastra-map.ts:284-292`). Called once for the
80
+ * `streamText` call's outer `model` field AND again from `prepareStep` for EVERY step (including the first) —
81
+ * so a revoked/rotated allow-list entry is caught mid-run, not just at the top of the segment.
82
+ */
83
+ private resolveModel;
84
+ /** Build this segment's `ToolAssemblyOpts` — identical wiring for `run()` and `resume()` (both rebuild tools
85
+ * fresh from the CURRENT agent row; only `runCtx` differs). */
86
+ private toolAssemblyOpts;
87
+ /**
88
+ * The shared governed `streamText` drive — Task 6's loop, generalized so both `run()` (segmentIndex 0) and
89
+ * `resume()` (segmentIndex = the snapshot's genIndex) call the SAME implementation: per-step metering + cost
90
+ * caps, mid-stream cancellation, suspend-parking (ask / native tool approvals / client actions), and
91
+ * completion-supervisor nudging. Returns the segment's terminal outcome as the generator's return value (the
92
+ * caller captures it via `const outcome = yield* this.drive(...)`); every terminal event (`done`/`run_failed`/
93
+ * `run_cancelled`/the park's `waiting`+`question`/`client_action`) is yielded from INSIDE this loop — the
94
+ * caller only needs the returned outcome for its own `onRunEnd` reporting.
95
+ */
96
+ private drive;
97
+ run(input: RunInput, ctx: SwarmContext, signal?: AbortSignal): AsyncIterable<SwarmEvent>;
98
+ /**
99
+ * Resume a parked run: `markFollowupAnswered` EARLY (CAS, replay-safe) → `loadSnapshot` → reload the agent
100
+ * row fresh (same as `run()`) → re-acquire the floor → emit `swarm.answer` → preGeneration re-fire (kind
101
+ * 'resume', at the snapshot's genIndex) → per-kind append the answer to the snapshot's messages → re-enter
102
+ * the SHARED `drive()` with rebuilt tools (`approvedToolCallIds` pre-populated on an approval-approve, BEFORE
103
+ * `buildTools`/`streamText` — Task 4's blocker-fix contract) + restored messages. A re-park persists
104
+ * `genIndex = (this segment's own segmentIndex) + 1` — the same bookkeeping `drive()` already does for
105
+ * `run()`'s park, generalized.
106
+ *
107
+ * ⚠ `onRunStart` deliberately does NOT fire here — this is a CONTINUATION of the run `onRunStart` already
108
+ * announced (in `run()`), not a new one. Mirrors the reference (Mastra) engine exactly: `packages/core/src/
109
+ * engine.ts`'s own `resume()` never calls `this.opts.onRunStart` either (only `run()` does — see that file's
110
+ * `onRunStart` doc comment: "fires on `run()` only"). A host that seeds per-run state in `onRunStart` gets it
111
+ * back via `snap.state` → `resumedState` below, so nothing is lost across the suspend/resume boundary.
112
+ */
113
+ resume(args: {
114
+ runId: string;
115
+ toolCallId: string;
116
+ followupId: string;
117
+ answer: unknown;
118
+ context?: Record<string, unknown>;
119
+ }, ctx: SwarmContext, signal?: AbortSignal): AsyncIterable<SwarmEvent>;
120
+ /**
121
+ * T9 — events-derived thread history (no Mastra memory in this engine: the events log IS the store).
122
+ * `container(threadId)` resolves the root container; `events.listForContainer` (optional on `StorageAdapter`
123
+ * — absent ⇒ `[]`, matching the reference's "no memory ⇒ []" degradation) returns the FULL container log
124
+ * (root + any lane sub-threads), each event tagged with its OWN run's `threadId`. Filtered LANE-EXACT to the
125
+ * REQUESTED `threadId` (so a root read never leaks a lane's side-chat and vice versa), then folded into
126
+ * `SwarmMessage[]` by `messagesFromEvents` (structural attribution; decode-only legacy prefix stripping — see
127
+ * its doc comment). Hard-capped to the most recent `limit` (default 200, mirrors the reference).
128
+ */
129
+ history(threadId: string, ctx: SwarmContext, opts?: {
130
+ limit?: number;
131
+ }): Promise<SwarmMessage[]>;
132
+ /** T9 helper — one container's `ThreadSummary`: title = its first (oldest) user message's text, falling back
133
+ * to the bare container id (an empty/title-less container, matching the reference's "no Mastra row" fallback
134
+ * shape: `{ threadId: container, title: container, lastActivityAt: 0 }`). `lastActivityAt` = the max `seq`
135
+ * seen on the container's BARE (non-lane) events (T9-review fix): `seq` is the storage-assigned, GLOBALLY
136
+ * monotonic append counter, so a newer run's events always rank above an older run's — whereas `ts` is a
137
+ * per-segment ordinal (restarts at 0/1000 every segment, see `emit.ts`) and is meaningless across runs. Same
138
+ * seq-over-ts preference the react timeline established (react/timeline.ts). NOT a wall-clock epoch (no
139
+ * adapter in the `StorageAdapter` contract exposes one for runs/events) — a recency-ORDERING proxy only.
140
+ */
141
+ private threadSummaryFor;
142
+ /**
143
+ * T9 — R14 participation-based thread listing (mirrors the reference `listThreads`, minus the Mastra memory
144
+ * dependency): `runs.listUserContainers` (optional) gives the containers this user has a run in,
145
+ * newest-active first; each is summarized via `threadSummaryFor` (concurrently, order preserved).
146
+ * FALLBACK (an adapter without `listUserContainers`): the reference degrades to the CURRENT container under
147
+ * multi-user (no cross-container Mastra listing to fall back on here either) — a single-entry list for
148
+ * `ctx.threadId`'s own container.
149
+ */
150
+ listThreads(ctx: SwarmContext, opts?: {
151
+ limit?: number;
152
+ }): Promise<ThreadSummary[]>;
153
+ /** The PUBLIC entries of a conversation's scratchpad (empty array when the feature is off or unset).
154
+ * Mirrors the reference engine's `scratchpadPublic` verbatim (storage-backed, engine-neutral). */
155
+ scratchpadPublic(container: string, ctx: SwarmContext): Promise<ScratchpadEntry[]>;
156
+ /** In-flight runs (running|suspended) for a container + its lanes. Mirrors the reference engine's
157
+ * `activeRuns` verbatim (storage-backed, engine-neutral). */
158
+ activeRuns(container: string, ctx: SwarmContext): Promise<ActiveRun[]>;
159
+ /** The full, globally-ordered event log for a thread's CONTAINER (all its runs + lane sub-threads) — unlike
160
+ * `history()`, NOT lane-filtered: this is the rich raw timeline (tool calls + status, not just messages).
161
+ * Mirrors the reference engine's `threadEvents` verbatim (storage-backed, engine-neutral). Returns `[]` when
162
+ * the store has no `events.listForContainer`. */
163
+ threadEvents(threadId: string, ctx: SwarmContext): Promise<SwarmEvent[]>;
164
+ /**
165
+ * T9 (REQUIRED on `Engine`) — the tenant's agent roster as wall-safe `AgentSummary[]`: `agents.listSlugs` →
166
+ * `head` per slug (dropping any slug whose head resolves to `null`, a race with a mid-listing deletion).
167
+ * `delegateSlugs` is ALWAYS `[]` — a deliberate divergence from the reference engine (which surfaces the
168
+ * stored `AgentVersion.delegateSlugs`): this engine's `capabilities.delegation` is `false` (v1, single-agent),
169
+ * so there is no addressable delegate graph to report regardless of what a row happens to store.
170
+ */
171
+ listAgents(ctx: SwarmContext): Promise<AgentSummary[]>;
172
+ }
173
+
174
+ /**
175
+ * Engine factory for `defineSwarm({ engine: aiSdkEngine() })` — constructs the wired `AiSdkEngine` (a
176
+ * governed `ai@^6` `streamText` loop, single-agent v1; see `src/engine.ts`). Mirrors the reference
177
+ * (Mastra) engine's own `durable` ctor param: `durableResume` is a per-INSTANCE capability flip
178
+ * (`AI_SDK_ENGINE_CAPABILITIES.hitl.durableResume`), never inferred from the injected `StorageAdapter`.
179
+ * Pass `aiSdkEngine({ durable: true })` only when that storage persists suspend/resume snapshots
180
+ * CROSS-PROCESS (e.g. `@nightowlsdev/storage-supabase`) — otherwise leave the default `false` so
181
+ * consumers (mcp-server/runner-background) keep treating `ask`/approval resumes as non-durable.
182
+ */
183
+ declare function aiSdkEngine({ durable }?: {
184
+ durable?: boolean;
185
+ }): (opts: AssembledEngineOpts) => Engine;
186
+ /**
187
+ * The declarative Night Owls plugin manifest consumed by `@nightowlsdev/cli`. STRUCTURALLY matches the CLI's
188
+ * `NightOwlsPlugin` type but does NOT import it (no dependency cycle): all codegen lives in `@nightowlsdev/cli`;
189
+ * this adapter stays data-only.
190
+ */
191
+ declare const nightOwlsPlugin: {
192
+ readonly name: "engine-ai-sdk";
193
+ readonly version: "0.0.0";
194
+ readonly kind: "engine";
195
+ readonly pkg: "@nightowlsdev/engine-ai-sdk";
196
+ readonly description: "The AI-SDK-native engine as a named engine package (single-agent v1, governed ai@6 streamText loop).";
197
+ readonly env: readonly [];
198
+ readonly config: {
199
+ readonly import: "import { aiSdkEngine } from \"@nightowlsdev/engine-ai-sdk\";";
200
+ readonly snippet: "engine = aiSdkEngine();";
201
+ readonly marker: "engine";
202
+ };
203
+ readonly init: (ctx: {
204
+ cwd: string;
205
+ log: (m: string) => void;
206
+ }) => void;
207
+ readonly commands: readonly [];
208
+ };
209
+
210
+ export { AI_SDK_ENGINE_CAPABILITIES, AiSdkEngine, aiSdkEngine, nightOwlsPlugin };