@qilin-agent/agent 0.0.0-stage → 3.1.3

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,379 @@
1
+ /**
2
+ * Agent service: live registry, factory delegation, and process-local
3
+ * initiator scope. Concrete creation and driving belong to the loop.
4
+ *
5
+ * @module @qilin-agent/agent
6
+ */
7
+ import { Context, Service } from '@qilin-agent/kylin';
8
+ import type { SessionEvent, SessionId, SessionLogOffset } from '@qilin-agent/session';
9
+ import type { Agent } from './types.ts';
10
+ import type { AgentOptions, SessionStartSource } from './runtime-types.ts';
11
+ export * from './runtime-types.ts';
12
+ export * from './types.ts';
13
+ export type * from './projection.ts';
14
+ export * from './consumed-work.ts';
15
+ export * from './model-selection.ts';
16
+ export { agentCarrier, agentEvents, assembleContextFor, emitAgentEvent } from './dispatch.ts';
17
+ export type { AgentEventDispatch, AgentSubjectEvent } from './dispatch.ts';
18
+ declare module '@qilin-agent/kylin' {
19
+ interface Context {
20
+ agents: AgentRegistry;
21
+ }
22
+ }
23
+ /**
24
+ * Synchronous finalizer returned by unpublished Agent setup when its
25
+ * contributions need validation at the exact publication commit point.
26
+ */
27
+ export interface AgentSetupCommit {
28
+ /**
29
+ * Validate and commit the prepared setup immediately before publication.
30
+ * @throws when publication must roll the unpublished Agent back.
31
+ */
32
+ commit(): void;
33
+ }
34
+ /**
35
+ * Compose an unpublished Agent scope and optionally return its publication commit.
36
+ * @param agentCtx - unpublished Agent scope.
37
+ * @param agent - unpublished Agent being composed.
38
+ * @returns an optional synchronous commit invoked after setup awaits settle and immediately before publication.
39
+ */
40
+ export type AgentSetup = (agentCtx: Context, agent: Agent) => AgentSetupCommit | Promise<AgentSetupCommit | void> | void;
41
+ /**
42
+ * Options for programmatically creating an agent through the registry factory
43
+ * ({@link AgentRegistry.create}). The caller supplies the single live
44
+ * `sessionId` shared by the agent registry and session log (e.g. an
45
+ * ACP-generated id), plus optional session metadata (the validated `cwd`, fork
46
+ * lineage); the factory creates the session and agent under that identity.
47
+ */
48
+ export interface CreateAgentOptions {
49
+ /** The live agent/session identity. */
50
+ readonly sessionId: SessionId;
51
+ /** Live parent Agent for runtime ownership; omit for a root Agent. */
52
+ readonly parentAgent?: Agent;
53
+ /**
54
+ * Session creation metadata: validated absolute `cwd`, `parentSession`
55
+ * fork lineage, the `isSeeded` fork marker, the coarse `origin`
56
+ * classification, and the `delegationDepth` recursion budget. Mirrors the
57
+ * `cwd`/`parentSession`/`isSeeded`/`origin`/`delegationDepth` fields of
58
+ * {@link CreateSessionOptions.meta} in qilin-session (the internal-only
59
+ * `createdAt`, used when reconstructing a persisted session, is deliberately
60
+ * excluded — a factory caller never sets it). This is durable session data,
61
+ * so the session boundary validates and snapshots it before asynchronous
62
+ * setup begins.
63
+ */
64
+ readonly meta?: {
65
+ readonly cwd?: string;
66
+ readonly parentSession?: SessionId;
67
+ readonly isSeeded?: boolean;
68
+ readonly origin?: 'subagent';
69
+ readonly delegationDepth?: number;
70
+ readonly agentPreset?: string;
71
+ };
72
+ /** Exact fork-inherited prefix length when the session metadata sets `isSeeded`. */
73
+ readonly inheritedEventCount?: SessionLogOffset;
74
+ /**
75
+ * Initial replay/fork history, contiguous from seq 0 with lossless-JSON data.
76
+ * A fork supplies an exact parent prefix, its inherited marker, and closers
77
+ * for the open tail. Previously closed steps and turns remain unchanged.
78
+ * The factory validates and snapshots the seed before publication.
79
+ */
80
+ readonly seed?: readonly SessionEvent[];
81
+ /** Per-agent options (model, …). */
82
+ readonly agentOptions?: AgentOptions;
83
+ /** Optional creation-only cancellation signal; detached before the returned handle becomes visible. */
84
+ readonly signal?: AbortSignal;
85
+ /**
86
+ * Creation-time composition of the agent's scoped world. The factory awaits
87
+ * setup after minting `agentCtx` but BEFORE inserting or announcing either
88
+ * the session or agent, so observers can never see a partially configured
89
+ * world. Setup may return an {@link AgentSetupCommit}; the factory invokes its
90
+ * synchronous `commit()` after every setup await settles and immediately
91
+ * before registry publication. This lets mutable provisioning revalidate at
92
+ * the exact publication boundary. Everything registered through `agentCtx`
93
+ * (scoped tools, prompt sections/variables, `restrict()`, listeners, awaited
94
+ * child plugins) exists before `session/created`, `agent/created`,
95
+ * and the first prompt assembly. A setup
96
+ * throw/rejection, commit throw, or owner disposal rolls the scope back
97
+ * without publishing either id.
98
+ *
99
+ * **Setup composes, it never drives**: the callback is trusted same-process
100
+ * code and receives the full scoped context, so this is a contract rather
101
+ * than a runtime restriction. Drive the agent only after creation resolves.
102
+ */
103
+ readonly setup?: AgentSetup;
104
+ }
105
+ /**
106
+ * Options for resuming an agent on a persisted session
107
+ * ({@link AgentRegistry.resume}).
108
+ */
109
+ export interface ResumeAgentOptions {
110
+ /** The persisted session id to load and use as the live agent/session identity. */
111
+ readonly resumeSessionId: SessionId;
112
+ /** Live parent Agent for runtime ownership; omit for a root Agent. */
113
+ readonly parentAgent?: Agent;
114
+ /** Per-agent options (model, …). */
115
+ readonly agentOptions?: AgentOptions;
116
+ /** Optional creation-only cancellation signal for persistence load/setup; detached before return. */
117
+ readonly signal?: AbortSignal;
118
+ /**
119
+ * Resume-time composition of the agent's fresh scoped world. Persistence is
120
+ * loaded first; the factory then mints `agentCtx` and awaits setup while the
121
+ * reconstructed session and agent remain unpublished. The callback has the
122
+ * same trusted composition-only contract and optional synchronous
123
+ * publication commit as {@link CreateAgentOptions.setup}: all registrations
124
+ * exist before either creation announcement, and rejection, commit failure,
125
+ * or owner disposal rolls the transaction back without publishing either id.
126
+ */
127
+ readonly setup?: AgentSetup;
128
+ }
129
+ /**
130
+ * An owned agent plus its disposer, returned by {@link AgentRegistry.create} /
131
+ * {@link AgentRegistry.resume}. The disposer is a CAPABILITY: among consumers,
132
+ * only the holder can tear this agent down. The registered factory provider is
133
+ * also a structural owner because the scoped agent depends on that provider's
134
+ * service API; provider unload stops and drains every live handle it made.
135
+ * `dispose()` stops the loop, awaits its exit, unregisters the agent, removes
136
+ * its session from the store, and finally unwinds its scoped world.
137
+ *
138
+ * `ctx.agents.get(id)` still returns a bare {@link Agent} — the handle is
139
+ * exposed only to the consumer owner that created it; the structural provider
140
+ * reaches the same teardown internally. Config-created agents (the loop's own
141
+ * startup) are owned by the loop fiber and never need a handle.
142
+ */
143
+ export interface AgentHandle {
144
+ agent: Agent;
145
+ dispose(): Promise<void>;
146
+ }
147
+ /**
148
+ * The agent-creation factory the loop implementation provides to the registry
149
+ * via {@link AgentRegistry.setFactory}. Kept on the `qilin-agent` interface so
150
+ * consumers (e.g. the ACP bridge) program against `ctx.agents` without
151
+ * depending on the concrete `qilin-agent-loop` package.
152
+ */
153
+ export interface AgentFactory {
154
+ /**
155
+ * Create a new agent on a caller-supplied session id. Async because creation
156
+ * awaits unpublished setup, invokes its optional synchronous commit, inserts
157
+ * both session and agent, announces session creation, and awaits serial
158
+ * `agent/created` listeners before releasing queued work. The sequence is
159
+ * rollback-covered, but notifications delivered before a later listener
160
+ * failure remain observable; every agent or session creation announcement
161
+ * that began is paired by `agent/disposed` or `session/disposed` during
162
+ * rollback. The owner disposes the resolved handle to stop/drain,
163
+ * unregister, remove the session, and unwind the scope.
164
+ * The registry passes a context carrying the `create()` caller's fiber and
165
+ * scope as `ownerCtx`. The implementation attaches the unpublished
166
+ * transaction and resulting lifecycle to that owner; it must not infer
167
+ * ownership from the factory object's registration context.
168
+ * @param ownerCtx - caller-bound context that owns the transaction and live handle.
169
+ * @param options - agent/session identity, configuration, optional live parent, and setup.
170
+ * @returns the owned handle after setup and both creation announcements complete.
171
+ */
172
+ createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>;
173
+ /**
174
+ * Resume an agent on a persisted session. Async because it opens the
175
+ * persisted session for write, reads and repairs the log, publishes it, and
176
+ * awaits the optional unpublished setup transaction; must be called after
177
+ * `ctx.sessionPersistence` exists (consumers inject `sessionPersistence`).
178
+ * Publication follows the same setup-commit and ordered boundary as
179
+ * {@link createAgent}.
180
+ * @param ownerCtx - caller-bound context that owns load, setup, and the live handle.
181
+ * @param options - persisted identity, configuration, optional live parent, and setup.
182
+ * @returns the owned handle after setup, both announcements, and loop start complete.
183
+ */
184
+ resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>;
185
+ }
186
+ /**
187
+ * Agent service (`ctx.agents`): tracks live agents and carries the initiating
188
+ * Agent through one process-local asynchronous driver chain. Agent *creation*
189
+ * is provided by whichever plugin implements the {@link AgentFactory}
190
+ * (`@qilin-agent/agent-loop`), registered via {@link setFactory}.
191
+ *
192
+ * Initiator methods provide same-process causal attribution only. Ambient
193
+ * presence is neither liveness proof nor authorization; subjects and owners
194
+ * remain explicit, as does identity at worker, process, persistence, and wire
195
+ * boundaries. Returned Promise boundaries drain during teardown, except a
196
+ * nested lineage that starts an owning-fiber unload is excluded from its own drain.
197
+ */
198
+ export declare class AgentRegistry extends Service {
199
+ private store;
200
+ private factory;
201
+ private readonly initiators;
202
+ private readonly initiatorRuns;
203
+ private initiatorState;
204
+ private activeInitiatorRuns;
205
+ private initiatorDrain;
206
+ private initiatorDisposal;
207
+ constructor(ctx: Context);
208
+ /**
209
+ * Read the Agent that initiated the inherited asynchronous driver chain.
210
+ * Use this optional form for logging, tracing, metrics, or host attribution
211
+ * that also supports agentless calls. When a parent creates a child, setup
212
+ * reports the causal parent while the setup callback's Agent parameter
213
+ * identifies the child.
214
+ * @returns the inherited Agent, or `undefined` outside an initiator boundary
215
+ * and inside an explicit clearing boundary.
216
+ * @throws when this service instance has been disposed.
217
+ */
218
+ currentInitiator(): Agent | undefined;
219
+ /**
220
+ * Read the initiating Agent and fail when no initiator boundary is active.
221
+ * Use this for private helpers contractually below a driver, or for a
222
+ * deployment-owned outbound request whose contract forbids agentless calls.
223
+ * Generic or direct-call paths use optional lookup or explicit request fields.
224
+ * @returns the inherited Agent.
225
+ * @throws when no initiator is active or this service instance has been disposed.
226
+ */
227
+ requireInitiator(): Agent;
228
+ /**
229
+ * Run an operation with one exact Agent as its process-local initiator. The
230
+ * exact synchronous value or Promise returned by the operation is preserved.
231
+ * Custom drivers and test harnesses wrap their complete returned foreground
232
+ * lifetime.
233
+ * A queue or wire receiver may establish this boundary only after validating
234
+ * explicit identity and resolving the exact live Agent; this method does neither.
235
+ * Detached work remains owned by the subsystem that starts it.
236
+ * @param agent - initiating Agent to inherit; presence is neither liveness proof nor authorization.
237
+ * @param operation - synchronous or asynchronous operation to invoke.
238
+ * @returns the exact value returned by `operation`.
239
+ * @throws when the initiator scope is closing/disposed, or when `operation` throws.
240
+ */
241
+ withInitiator<T>(agent: Agent, operation: () => T): T;
242
+ /**
243
+ * Run an operation inside a boundary that hides any inherited initiating
244
+ * Agent. The exact synchronous value or Promise is preserved.
245
+ * Use this while creating lazy shared timers, queue pumps, pool maintenance,
246
+ * watchers, or exporters so they do not inherit the first Agent that happens
247
+ * to initialize them. It clears only initiator attribution, not explicit
248
+ * fields, and does not own or drain detached resources.
249
+ * @param operation - synchronous or asynchronous operation to invoke without an initiator.
250
+ * @returns the exact value returned by `operation`.
251
+ * @throws when the initiator scope is closing/disposed, or when `operation` throws.
252
+ */
253
+ withoutInitiator<T>(operation: () => T): T;
254
+ /**
255
+ * Register the agent-creation factory (the loop calls this on construction,
256
+ * effect-scoped). A traced Cordis service is canonicalized to its concrete
257
+ * target; each create/resume call is then traced through that caller's
258
+ * context so ownership follows the caller without stacking proxy layers.
259
+ * Throws if a factory is already registered. Returns the disposer; on
260
+ * dispose the factory slot is cleared.
261
+ * @param factory - the loop-owned factory {@link create}/{@link resume} delegate to.
262
+ * @returns the disposer that clears the factory slot. The exact
263
+ * Cordis effect disposer (single-shot): composite (generator) effects may
264
+ * yield it directly — exact identity nests the teardown in order.
265
+ */
266
+ setFactory(factory: AgentFactory): () => void;
267
+ /** Return the active creation factory. */
268
+ private requireFactory;
269
+ /**
270
+ * Create and publish a new agent through the registered factory.
271
+ * Distinct from {@link register} (which records an already-constructed
272
+ * agent): this constructs the agent and its session. Rejects if no factory is
273
+ * registered or creation/setup fails. The resolved {@link AgentHandle} lets
274
+ * the owner tear down exactly this agent.
275
+ * @param options - shared identity, optional live parent, session seed/metadata, and agent options.
276
+ * @returns the handle after setup, rollback-covered publication, and loop start complete.
277
+ */
278
+ create(options: CreateAgentOptions): Promise<AgentHandle>;
279
+ /**
280
+ * Load a persisted session and resume an agent on it through the registered
281
+ * factory. Rejects if no factory is registered; the factory rejects if
282
+ * session persistence is not configured or persistence/setup fails.
283
+ * @param options - persisted identity, optional live parent, configuration, and setup.
284
+ * @returns the handle after setup, rollback-covered publication, and loop start complete.
285
+ */
286
+ resume(options: ResumeAgentOptions): Promise<AgentHandle>;
287
+ /**
288
+ * Register a live agent with source `startup`. Rejects if the id is already registered or a
289
+ * serial `agent/created` listener fails. Emits `agent/disposed`
290
+ * when the calling fiber is disposed — both with the agent's scope carrier
291
+ * (`scopeTarget(agent, agent)`): the subject is the agent in hand, so the
292
+ * emits are scope-filtered regardless of which context invoked `register`
293
+ * (calling through `agent.ctx` scopes EFFECTS; dispatch scoping always
294
+ * requires passing the carrier). The entry is a runtime root; factory-backed
295
+ * creation uses `options.parentAgent` for child ownership. Await the registration before using the agent.
296
+ * @param agent - the already-constructed agent to record in the store.
297
+ * @returns the awaitable Cordis effect disposer (single-shot; a repeat call
298
+ * returns undefined without awaiting an in-flight teardown). Exact
299
+ * identity is load-bearing: a composite (generator) effect that owns a
300
+ * teardown ORDER — the agent factory's lifecycle chain — must yield THIS
301
+ * function so Cordis nests the unregistration at that yield position;
302
+ * yielding a wrapper would leave it disposing as a concurrent sibling on
303
+ * owner unload, unregistering the agent (and emitting `agent/disposed`)
304
+ * while its final turn is still draining.
305
+ */
306
+ register(agent: Agent): ReturnType<Context['effect']>;
307
+ /**
308
+ * Insert an already-constructed agent without announcing it. This is the
309
+ * advanced ordered-lifecycle primitive used by the async agent factory: it
310
+ * first completes setup while the agent is unpublished, then assigns the
311
+ * returned detach closure into its pre-installed composite teardown before
312
+ * calling {@link announce}. Ordinary callers use {@link register}.
313
+ * @param agent - the prepared, unpublished agent.
314
+ * @param owner - explicitly supplied live runtime owner, or
315
+ * undefined for a top-level runtime root. This is runtime ownership, not
316
+ * the resumed session's durable parent lineage.
317
+ * @returns an idempotent closure that removes this exact entry and emits
318
+ * `agent/disposed` with listener failures contained. When called from a
319
+ * `agent/created` listener, removal and disposal wait until the serial
320
+ * creation dispatch settles.
321
+ */
322
+ enter(agent: Agent, owner: Agent | undefined): () => void;
323
+ /** Remove one exact entered agent and emit its paired disposal when announced. */
324
+ private detachEntered;
325
+ /** Emit the paired disposal edge through the entry's stable carrier. */
326
+ private emitDisposed;
327
+ /**
328
+ * Announce an agent previously inserted with {@link enter}.
329
+ * @param agent - the live inserted agent to announce.
330
+ * @param source - fresh creation, resume, clear, or compaction source.
331
+ * @param signal - optional factory initialization cancellation signal passed to listeners.
332
+ * @returns completion of the serial creation listeners; a listener failure rejects.
333
+ * @throws if `agent` is not the exact live registry entry for its id, or its
334
+ * creation announcement already began (including a reentrant call from a
335
+ * creation listener).
336
+ */
337
+ announce(agent: Agent, source: SessionStartSource, signal?: AbortSignal): Promise<void>;
338
+ /**
339
+ * Look up a live agent.
340
+ * @param id - the shared agent/session id to look up.
341
+ * @returns the agent, or undefined when no live agent has that id.
342
+ */
343
+ get(id: SessionId): Agent | undefined;
344
+ /**
345
+ * Test whether a live agent was created through one exact parent agent's
346
+ * scoped context. Runtime ownership is independent of durable session
347
+ * lineage and remains unambiguous when unrelated providers reuse an id.
348
+ * @param id - the candidate child agent's shared agent/session id.
349
+ * @param owner - the expected runtime creator agent.
350
+ * @returns true only while the exact child entry is live under that owner.
351
+ */
352
+ isOwnedBy(id: SessionId, owner: Agent): boolean;
353
+ /**
354
+ * All live agents, in registration order.
355
+ * @returns a fresh array; mutating it does not affect the registry.
356
+ */
357
+ list(): Agent[];
358
+ /**
359
+ * All live top-level agents in registration order. A top-level agent was
360
+ * created without an owning agent context; durable session lineage does not
361
+ * affect this runtime relation, so a resumed fork may still be a root.
362
+ * @returns a fresh array; mutating it does not affect the registry.
363
+ */
364
+ roots(): Agent[];
365
+ /** Reject new initiator boundaries while inherited continuations drain. */
366
+ private closeInitiators;
367
+ /** Wait for returned-Promise boundaries, then invalidate retained references. */
368
+ private disposeInitiators;
369
+ /** Establish one tracked initiator or clearing boundary. */
370
+ private runWithInitiator;
371
+ /** Whether one unloading fiber owns this service's lifecycle. */
372
+ private hasLifecycleAncestor;
373
+ private assertInitiatorsReadable;
374
+ /** Exclude the boundary chain that initiated this teardown from its own drain. */
375
+ private releaseReentrantInitiatorRuns;
376
+ private releaseInitiatorRun;
377
+ }
378
+ export default AgentRegistry;
379
+ //# sourceMappingURL=index.d.ts.map