@qilin-agent/experimental-agent-team 0.0.0-stage → 3.1.4

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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.i18n.yaml +103 -0
  3. package/README.md +225 -2
  4. package/README.zh.md +226 -0
  5. package/lib/index.js +1782 -0
  6. package/lib/typert.host.d.ts +3 -0
  7. package/lib/typert.host.js +875 -0
  8. package/lib/typert.remote-client.d.ts +30 -0
  9. package/lib/typert.remote-client.js +141 -0
  10. package/lib/types/activity.d.ts +23 -0
  11. package/lib/types/activity.js +85 -0
  12. package/lib/types/client.d.ts +8 -0
  13. package/lib/types/client.js +3 -0
  14. package/lib/types/error.d.ts +13 -0
  15. package/lib/types/error.js +23 -0
  16. package/lib/types/index.d.ts +148 -0
  17. package/lib/types/index.js +340 -0
  18. package/lib/types/journal.d.ts +39 -0
  19. package/lib/types/journal.js +63 -0
  20. package/lib/types/lifecycle.d.ts +45 -0
  21. package/lib/types/lifecycle.js +103 -0
  22. package/lib/types/persisted.d.ts +20 -0
  23. package/lib/types/persisted.js +20 -0
  24. package/lib/types/projection.d.ts +66 -0
  25. package/lib/types/projection.js +327 -0
  26. package/lib/types/remote.d.ts +31 -0
  27. package/lib/types/remote.js +22 -0
  28. package/lib/types/roster.d.ts +107 -0
  29. package/lib/types/roster.js +441 -0
  30. package/lib/types/session-message.d.ts +11 -0
  31. package/lib/types/session-message.js +23 -0
  32. package/lib/types/task-board.d.ts +53 -0
  33. package/lib/types/task-board.js +235 -0
  34. package/lib/types/task-graph.d.ts +21 -0
  35. package/lib/types/task-graph.js +62 -0
  36. package/lib/types/task-view.d.ts +21 -0
  37. package/lib/types/task-view.js +57 -0
  38. package/lib/types/types.d.ts +212 -0
  39. package/lib/types/types.js +26 -0
  40. package/lib/types/validation.d.ts +16 -0
  41. package/lib/types/validation.js +33 -0
  42. package/locale/en.json +6 -0
  43. package/locale/zh.json +6 -0
  44. package/package.json +83 -3
@@ -0,0 +1,340 @@
1
+ /** Agent Teams service façade over roster, messaging, task, and runtime lifecycle owners. */
2
+ var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
3
+ var useValue = arguments.length > 2;
4
+ for (var i = 0; i < initializers.length; i++) {
5
+ value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
6
+ }
7
+ return useValue ? value : void 0;
8
+ };
9
+ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
10
+ function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
11
+ var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
12
+ var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
13
+ var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
14
+ var _, done = false;
15
+ for (var i = decorators.length - 1; i >= 0; i--) {
16
+ var context = {};
17
+ for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
18
+ for (var p in contextIn.access) context.access[p] = contextIn.access[p];
19
+ context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
20
+ var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
21
+ if (kind === "accessor") {
22
+ if (result === void 0) continue;
23
+ if (result === null || typeof result !== "object") throw new TypeError("Object expected");
24
+ if (_ = accept(result.get)) descriptor.get = _;
25
+ if (_ = accept(result.set)) descriptor.set = _;
26
+ if (_ = accept(result.init)) initializers.unshift(_);
27
+ }
28
+ else if (_ = accept(result)) {
29
+ if (kind === "field") initializers.unshift(_);
30
+ else descriptor[key] = _;
31
+ }
32
+ }
33
+ if (target) Object.defineProperty(target, contextIn.name, descriptor);
34
+ done = true;
35
+ };
36
+ import { randomUUID } from 'node:crypto';
37
+ import z from '@qilin-agent/schemastery';
38
+ import { createUserMessage } from '@qilin-agent/llm';
39
+ import { steerHostSubagentPrompt } from '@qilin-agent/subagent/internal';
40
+ import { Remote, TypertRemoteService } from '@qilin-agent/typert-protocol';
41
+ import { TeamActivity } from "./activity.js";
42
+ import { errorMessage, TeamError } from "./error.js";
43
+ import { TeamJournal } from "./journal.js";
44
+ import { TeamRuntimeLifecycle } from "./lifecycle.js";
45
+ import { teamProjectionDefinition } from "./projection.js";
46
+ import { teamWireError } from "./remote.js";
47
+ import { resolveActiveMember, TeamRoster } from "./roster.js";
48
+ import { TeamTaskBoard } from "./task-board.js";
49
+ import { TeamId, TeamMessageId } from "./types.js";
50
+ export { TeamId, TeamMessageId, TeamTaskId } from "./types.js";
51
+ export { TeamError } from "./error.js";
52
+ const DEFAULT_MAX_MEMBERS = 16;
53
+ const DEFAULT_MAX_TASKS = 256;
54
+ const DEFAULT_MAX_MESSAGE_BYTES = 65_536;
55
+ const DEFAULT_DISPOSAL_TIMEOUT_MS = 5_000;
56
+ /** Validate one positive safe-integer deployment limit. */
57
+ function positiveLimit(name, value) {
58
+ if (!Number.isSafeInteger(value) || value < 1) {
59
+ throw new TeamError(`${name} must be a positive safe integer`, 'TEAM_INVALID_CONFIG');
60
+ }
61
+ return value;
62
+ }
63
+ /**
64
+ * Agent Teams service backed by the exact live Lead Session log. `createTask`
65
+ * and `updateTask` also carry the browser write bridge through the `agentTeams`
66
+ * Typert Remote namespace; the wire faces map domain failures onto the shared
67
+ * Remote failure vocabulary without changing the domain methods' contracts.
68
+ */
69
+ let TeamService = (() => {
70
+ let _classSuper = TypertRemoteService;
71
+ let _instanceExtraInitializers = [];
72
+ let _remoteCreateTask_decorators;
73
+ let _remoteUpdateTask_decorators;
74
+ return class TeamService extends _classSuper {
75
+ static {
76
+ const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
77
+ _remoteCreateTask_decorators = [Remote('createTask')];
78
+ _remoteUpdateTask_decorators = [Remote('updateTask')];
79
+ __esDecorate(this, null, _remoteCreateTask_decorators, { kind: "method", name: "remoteCreateTask", static: false, private: false, access: { has: obj => "remoteCreateTask" in obj, get: obj => obj.remoteCreateTask }, metadata: _metadata }, null, _instanceExtraInitializers);
80
+ __esDecorate(this, null, _remoteUpdateTask_decorators, { kind: "method", name: "remoteUpdateTask", static: false, private: false, access: { has: obj => "remoteUpdateTask" in obj, get: obj => obj.remoteUpdateTask }, metadata: _metadata }, null, _instanceExtraInitializers);
81
+ if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
82
+ }
83
+ static inject = ['agents', 'sessions', 'sessionPersistence', 'sessionProjections', 'subagents'];
84
+ static Config = z.object({
85
+ maxMembers: z.number().step(1).min(1).default(DEFAULT_MAX_MEMBERS),
86
+ maxTasks: z.number().step(1).min(1).default(DEFAULT_MAX_TASKS),
87
+ maxMessageBytes: z.number().step(1).min(1).default(DEFAULT_MAX_MESSAGE_BYTES),
88
+ disposalTimeoutMs: z.number().step(1).min(1).default(DEFAULT_DISPOSAL_TIMEOUT_MS),
89
+ });
90
+ /** Validated deployment limits used by every Team operation. */
91
+ config = __runInitializers(this, _instanceExtraInitializers);
92
+ activity;
93
+ lifecycle;
94
+ journal;
95
+ roster;
96
+ tasks;
97
+ constructor(ctx, config = {}) {
98
+ super(ctx, 'agentTeams');
99
+ this.config = {
100
+ maxMembers: positiveLimit('maxMembers', config.maxMembers ?? DEFAULT_MAX_MEMBERS),
101
+ maxTasks: positiveLimit('maxTasks', config.maxTasks ?? DEFAULT_MAX_TASKS),
102
+ maxMessageBytes: positiveLimit('maxMessageBytes', config.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES),
103
+ disposalTimeoutMs: positiveLimit('disposalTimeoutMs', config.disposalTimeoutMs ?? DEFAULT_DISPOSAL_TIMEOUT_MS),
104
+ };
105
+ this.activity = new TeamActivity();
106
+ this.lifecycle = new TeamRuntimeLifecycle(this.config.disposalTimeoutMs);
107
+ this.journal = new TeamJournal(ctx, (root) => { this.activity.notify(TeamId(root.id)); });
108
+ this.roster = new TeamRoster(ctx, this.journal, this.lifecycle, this.config.maxMembers);
109
+ this.tasks = new TeamTaskBoard(this.journal, this.config.maxTasks);
110
+ ctx.on('agent/created', ({ agent }) => { this.scheduleRecovery(agent); });
111
+ ctx.on('agent/status', ({ agent }) => {
112
+ const membership = this.roster.tryMembership(agent);
113
+ if (membership !== undefined)
114
+ this.activity.notify(membership.id);
115
+ });
116
+ ctx.effect(() => {
117
+ const disposeProjection = ctx.root.sessionProjections.register(teamProjectionDefinition);
118
+ return async () => {
119
+ try {
120
+ await this.disposeRuntime();
121
+ }
122
+ finally {
123
+ disposeProjection();
124
+ }
125
+ };
126
+ }, 'agentTeams.runtimeLifecycle()');
127
+ for (const agent of ctx.agents.list())
128
+ this.scheduleRecovery(agent);
129
+ }
130
+ /**
131
+ * Resolve one exact live Agent's Team role.
132
+ * @param agent - exact live Agent used as the authority credential.
133
+ * @returns its root, Team identity, role, and model-facing name.
134
+ */
135
+ membership(agent) {
136
+ return this.roster.membership(agent);
137
+ }
138
+ /**
139
+ * List the runtime-enriched roster visible to one Team member.
140
+ * @param agent - exact live Team member.
141
+ * @returns Lead and teammate rows in creation order.
142
+ */
143
+ listMembers(agent) {
144
+ return this.roster.list(this.roster.membership(agent));
145
+ }
146
+ /**
147
+ * Create one named, continuable direct child of the Team Lead.
148
+ * @param caller - exact live Lead Agent.
149
+ * @param request - immutable name, description, prompt, context mode, provider, and cancellation.
150
+ * @returns the active roster row.
151
+ */
152
+ async spawnTeammate(caller, request) {
153
+ return await this.roster.spawn(caller, request);
154
+ }
155
+ /**
156
+ * Steer one peer message into the target inbox or reject the attempt.
157
+ * @param caller - exact live sending Team member.
158
+ * @param request - target name, content, and cancellation before acceptance.
159
+ * @returns accepted inbox identity; acceptance follows normal Agent persistence and does not await model processing.
160
+ */
161
+ async sendMessage(caller, request) {
162
+ if (this.lifecycle.disposed)
163
+ throw new TeamError('Agent Teams service is disposing', 'TEAM_DISPOSED');
164
+ return await this.lifecycle.track(this.sendAdmitted(caller, {
165
+ ...request,
166
+ signal: AbortSignal.any([request.signal, this.lifecycle.signal]),
167
+ }));
168
+ }
169
+ /**
170
+ * Admit one peer message and steer it into the target's inbox.
171
+ * @param caller - exact live sending Team member.
172
+ * @param request - target name, content, and signal already joined with runtime cancellation.
173
+ * @returns the durable message identity carried by the delivered source.
174
+ */
175
+ async sendAdmitted(caller, request) {
176
+ const membership = this.roster.membership(caller);
177
+ request.signal.throwIfAborted();
178
+ const { root } = membership;
179
+ const target = resolveActiveMember(root, this.journal.state(root), request.target);
180
+ if (target.id === caller.id)
181
+ throw new TeamError('a Team member cannot message itself', 'TEAM_SELF_MESSAGE');
182
+ const messageId = TeamMessageId(`team-message-${randomUUID()}`);
183
+ const source = {
184
+ kind: 'team-message',
185
+ teamId: TeamId(root.id),
186
+ messageId,
187
+ senderId: caller.id,
188
+ senderName: membership.name,
189
+ };
190
+ const content = [
191
+ { type: 'text', text: `Team message ${messageId} from ${membership.name}:` },
192
+ ...structuredClone(request.content),
193
+ ];
194
+ if (Buffer.byteLength(JSON.stringify(content), 'utf8') > this.config.maxMessageBytes) {
195
+ throw new TeamError(`team message exceeds ${this.config.maxMessageBytes} bytes`, 'TEAM_MESSAGE_TOO_LARGE');
196
+ }
197
+ if (target.id === root.id)
198
+ root.steer(createUserMessage({ content, source }));
199
+ else
200
+ await steerHostSubagentPrompt(this.ctx.subagents, root, target.id, content, source, request.signal);
201
+ this.activity.notify(membership.id);
202
+ return { messageId };
203
+ }
204
+ /**
205
+ * Create one unowned pending task in the Team Lead log.
206
+ * @param caller - exact live Team member creating the task.
207
+ * @param request - task text, blockers, and advisory write scopes.
208
+ * @returns the revision-one task view.
209
+ */
210
+ async createTask(caller, request) {
211
+ return await this.tasks.create(this.roster.membership(caller), request);
212
+ }
213
+ /**
214
+ * Return one task, including a deleted tombstone.
215
+ * @param caller - exact live Team member reading the task.
216
+ * @param id - Team-local task identity.
217
+ * @returns the latest task value and derived readiness diagnostics.
218
+ */
219
+ getTask(caller, id) {
220
+ return this.tasks.get(this.roster.membership(caller), id);
221
+ }
222
+ /**
223
+ * List current non-deleted tasks in numeric creation order.
224
+ * @param caller - exact live Team member reading the board.
225
+ * @returns detached current task views.
226
+ */
227
+ listTasks(caller) {
228
+ return this.tasks.list(this.roster.membership(caller));
229
+ }
230
+ /**
231
+ * Compare-and-set one authorized task transition.
232
+ * @param caller - exact live Team member authorizing the mutation.
233
+ * @param request - task identity, expected revision, action, and action fields.
234
+ * @returns the committed next task revision.
235
+ */
236
+ async updateTask(caller, request) {
237
+ return await this.tasks.update(caller, this.roster.membership(caller), request);
238
+ }
239
+ /**
240
+ * Wire face of {@link TeamService.createTask}: create one shared task on a
241
+ * person's behalf through the browser write bridge.
242
+ * @param agent - exact live Team member resolved from the wire identity.
243
+ * @param request - subject, description, optional blockers, and optional write scopes.
244
+ * @returns the revision-one task view.
245
+ * @throws {RemoteError} `agent-team/not-a-member` when the identity is not a Team member, and
246
+ * `agent-team/rejected` when the domain refuses the request.
247
+ */
248
+ async remoteCreateTask(agent, request) {
249
+ try {
250
+ return await this.createTask(agent, request);
251
+ }
252
+ catch (error) {
253
+ throw teamWireError(error);
254
+ }
255
+ }
256
+ /**
257
+ * Wire face of {@link TeamService.updateTask}: commit one compare-and-set
258
+ * task transition on a person's behalf. A committed mutation is durable and
259
+ * never undone by a later wire cancellation.
260
+ * @param agent - exact live Team member resolved from the wire identity.
261
+ * @param request - task identity, expected revision, action, and action fields.
262
+ * @returns the committed next task view.
263
+ * @throws {RemoteError} `agent-team/not-a-member` when the identity is not a Team member,
264
+ * `agent-team/stale-revision` when the expected revision no longer matches, and
265
+ * `agent-team/rejected` when the domain refuses the transition.
266
+ */
267
+ async remoteUpdateTask(agent, request) {
268
+ try {
269
+ return await this.updateTask(agent, request);
270
+ }
271
+ catch (error) {
272
+ throw teamWireError(error);
273
+ }
274
+ }
275
+ /**
276
+ * Wait for the next Team-domain or member-status change.
277
+ * @param caller - exact live Team member waiting for activity.
278
+ * @param timeoutMs - bounded wait duration from ten seconds through one hour.
279
+ * @param signal - caller cancellation for the wait only.
280
+ * @returns one observed change or a timeout result.
281
+ */
282
+ async waitForChange(caller, timeoutMs, signal) {
283
+ const membership = this.roster.membership(caller);
284
+ return await this.activity.wait(membership.id, timeoutMs, signal);
285
+ }
286
+ /**
287
+ * Interrupt one live teammate turn without clearing its pending inbox.
288
+ * @param caller - exact live Lead Agent.
289
+ * @param targetName - durable teammate name.
290
+ * @returns the target status sampled before cancellation.
291
+ */
292
+ interrupt(caller, targetName) {
293
+ return this.roster.interrupt(caller, targetName);
294
+ }
295
+ /**
296
+ * Resolve a caller without throwing, used by scoped-tool installation and observers.
297
+ * @param agent - candidate exact live Agent.
298
+ * @returns Team membership, or undefined for non-Team subagents and stale identities.
299
+ */
300
+ tryMembership(agent) {
301
+ return this.roster.tryMembership(agent);
302
+ }
303
+ /** Queue one contained recovery pass after publication has unwound. */
304
+ scheduleRecovery(agent) {
305
+ queueMicrotask(() => {
306
+ if (this.lifecycle.disposed)
307
+ return;
308
+ void this.recoverFor(agent).catch((error) => {
309
+ if (this.lifecycle.disposed)
310
+ return;
311
+ this.ctx.logger.warn(`Agent Teams recovery for "${agent.id}" failed: ${errorMessage(error)}`);
312
+ });
313
+ });
314
+ }
315
+ /** Reconcile roster provisioning when one Team member Session starts. */
316
+ async recoverFor(agent) {
317
+ await this.roster.recoverFor(agent, this.lifecycle.signal);
318
+ }
319
+ /** Stop Team-owned live branches and release every waiter before service disposal completes. */
320
+ async disposeRuntime() {
321
+ this.lifecycle.close();
322
+ this.activity.close();
323
+ const failures = [];
324
+ await this.lifecycle.settle(this.lifecycle.pending(), failures);
325
+ for (const [root, childIds] of this.roster.liveChildrenByRoot()) {
326
+ try {
327
+ await this.roster.stopTeammates(root, childIds);
328
+ }
329
+ catch (error) {
330
+ failures.push(error);
331
+ }
332
+ }
333
+ if (failures.length > 0)
334
+ throw new AggregateError(failures, 'Agent Teams runtime disposal failed');
335
+ }
336
+ };
337
+ })();
338
+ export { TeamService };
339
+ export default TeamService;
340
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,39 @@
1
+ /** Serialized Team transactions over the exact live Lead Session log. */
2
+ import type { Agent } from '@qilin-agent/agent';
3
+ import type { Context } from '@qilin-agent/kylin';
4
+ import type { SessionEventMap, SessionId } from '@qilin-agent/session';
5
+ import type { TeamState } from './projection.ts';
6
+ type MutableTeamEventType = 'team/member' | 'team/task' | 'team/message/queued' | 'team/message/delivered';
7
+ /** Owns per-Lead transaction order and committed Team event publication. */
8
+ export declare class TeamJournal {
9
+ private readonly ctx;
10
+ private readonly onCommit;
11
+ private readonly tails;
12
+ /**
13
+ * @param ctx - Team service context with the injected Session service.
14
+ * @param onCommit - synchronous notification after the Team event flush succeeds.
15
+ */
16
+ constructor(ctx: Context, onCommit: (root: Agent) => void);
17
+ /**
18
+ * Read authoritative Team state for one exact live Lead.
19
+ * @param root - exact live Team Lead.
20
+ * @returns current projected state selected by the Lead Team id.
21
+ */
22
+ state(root: Agent): TeamState;
23
+ /**
24
+ * Serialize one Lead's asynchronous mutation operation.
25
+ * @param rootId - Lead Session identity selecting the transaction queue.
26
+ * @param operation - complete read-check-append operation.
27
+ * @returns the operation result.
28
+ */
29
+ transact<T>(rootId: SessionId, operation: () => Promise<T>): Promise<T>;
30
+ /**
31
+ * Append and checkpoint one root-owned Team event before publication.
32
+ * @param root - exact live Lead whose Session owns the event.
33
+ * @param type - Team event discriminant.
34
+ * @param data - payload correlated with the event type.
35
+ */
36
+ appendAndFlush<T extends MutableTeamEventType>(root: Agent, type: T, data: SessionEventMap[T]): Promise<void>;
37
+ }
38
+ export {};
39
+ //# sourceMappingURL=journal.d.ts.map
@@ -0,0 +1,63 @@
1
+ /** Serialized Team transactions over the exact live Lead Session log. */
2
+ /** Owns per-Lead transaction order and committed Team event publication. */
3
+ export class TeamJournal {
4
+ ctx;
5
+ onCommit;
6
+ tails = new Map();
7
+ /**
8
+ * @param ctx - Team service context with the injected Session service.
9
+ * @param onCommit - synchronous notification after the Team event flush succeeds.
10
+ */
11
+ constructor(ctx, onCommit) {
12
+ this.ctx = ctx;
13
+ this.onCommit = onCommit;
14
+ }
15
+ /**
16
+ * Read authoritative Team state for one exact live Lead.
17
+ * @param root - exact live Team Lead.
18
+ * @returns current projected state selected by the Lead Team id.
19
+ */
20
+ state(root) {
21
+ const projection = this.ctx.sessionProjections.stateOf(root.session, 'agentTeam');
22
+ if (projection === undefined)
23
+ throw new Error('Agent Teams projection is not registered');
24
+ if (projection.failure !== undefined)
25
+ throw new Error(projection.failure);
26
+ return projection;
27
+ }
28
+ /**
29
+ * Serialize one Lead's asynchronous mutation operation.
30
+ * @param rootId - Lead Session identity selecting the transaction queue.
31
+ * @param operation - complete read-check-append operation.
32
+ * @returns the operation result.
33
+ */
34
+ async transact(rootId, operation) {
35
+ const prior = this.tails.get(rootId) ?? Promise.resolve();
36
+ const run = prior.then(operation, operation);
37
+ const tail = run.then(() => undefined, () => undefined);
38
+ this.tails.set(rootId, tail);
39
+ try {
40
+ return await run;
41
+ }
42
+ finally {
43
+ if (this.tails.get(rootId) === tail)
44
+ this.tails.delete(rootId);
45
+ }
46
+ }
47
+ /**
48
+ * Append and checkpoint one root-owned Team event before publication.
49
+ * @param root - exact live Lead whose Session owns the event.
50
+ * @param type - Team event discriminant.
51
+ * @param data - payload correlated with the event type.
52
+ */
53
+ async appendAndFlush(root, type, data) {
54
+ // Team events never enter the conversation surface. This narrower local
55
+ // capability removes Session.append's conditional surface argument while
56
+ // preserving the event-key/payload correlation.
57
+ const append = root.session.append.bind(root.session);
58
+ append(type, data);
59
+ await this.ctx.sessions.flush(root.session);
60
+ this.onCommit(root);
61
+ }
62
+ }
63
+ //# sourceMappingURL=journal.js.map
@@ -0,0 +1,45 @@
1
+ /** Shared admission cutoff and bounded settlement for the Team runtime. */
2
+ /** Owns the single Team runtime cancellation fact and disposal timeout. */
3
+ export declare class TeamRuntimeLifecycle {
4
+ private readonly disposalTimeoutMs;
5
+ private readonly operations;
6
+ private readonly controller;
7
+ /**
8
+ * @param disposalTimeoutMs - maximum wait for one disposal settlement operation.
9
+ */
10
+ constructor(disposalTimeoutMs: number);
11
+ /** Signal aborted exactly when Team runtime admission closes. */
12
+ get signal(): AbortSignal;
13
+ /** Whether Team runtime admission is closed. */
14
+ get disposed(): boolean;
15
+ /** The exact cancellation reason used to distinguish expected disposal rejection. */
16
+ get reason(): unknown;
17
+ /** Whether a rejection is the runtime cancellation, directly or through an Error cause chain. */
18
+ private isCancellation;
19
+ /** Close Team runtime admission and cancel admitted interruptible work. */
20
+ close(): void;
21
+ /**
22
+ * Track an admitted send or complete creation transaction until it settles.
23
+ * @param operation - asynchronous work owned by this runtime.
24
+ * @returns the same operation.
25
+ */
26
+ track<T>(operation: Promise<T>): Promise<T>;
27
+ /**
28
+ * Capture outstanding operations after closing admission.
29
+ * @returns admitted work still awaiting settlement.
30
+ */
31
+ pending(): readonly Promise<unknown>[];
32
+ /**
33
+ * Await admitted operations and retain failures other than runtime cancellation.
34
+ * @param operations - admitted operations captured after the admission cutoff.
35
+ * @param failures - aggregate destination for unexpected rejection or timeout.
36
+ */
37
+ settle(operations: readonly Promise<unknown>[], failures: unknown[]): Promise<void>;
38
+ /**
39
+ * Bound one runtime settlement operation.
40
+ * @param operation - settlement that may otherwise block HMR or process shutdown.
41
+ * @returns the operation result.
42
+ */
43
+ withTimeout<T>(operation: Promise<T>): Promise<T>;
44
+ }
45
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1,103 @@
1
+ /** Shared admission cutoff and bounded settlement for the Team runtime. */
2
+ import { TeamError } from "./error.js";
3
+ /** Owns the single Team runtime cancellation fact and disposal timeout. */
4
+ export class TeamRuntimeLifecycle {
5
+ disposalTimeoutMs;
6
+ operations = new Set();
7
+ controller = new AbortController();
8
+ /**
9
+ * @param disposalTimeoutMs - maximum wait for one disposal settlement operation.
10
+ */
11
+ constructor(disposalTimeoutMs) {
12
+ this.disposalTimeoutMs = disposalTimeoutMs;
13
+ }
14
+ /** Signal aborted exactly when Team runtime admission closes. */
15
+ get signal() {
16
+ return this.controller.signal;
17
+ }
18
+ /** Whether Team runtime admission is closed. */
19
+ get disposed() {
20
+ return this.signal.aborted;
21
+ }
22
+ /** The exact cancellation reason used to distinguish expected disposal rejection. */
23
+ get reason() {
24
+ const reason = this.signal.reason;
25
+ return reason;
26
+ }
27
+ /** Whether a rejection is the runtime cancellation, directly or through an Error cause chain. */
28
+ isCancellation(reason) {
29
+ const seen = new Set();
30
+ let current = reason;
31
+ while (!seen.has(current)) {
32
+ if (this.disposed && current === this.reason)
33
+ return true;
34
+ if (this.disposed && current instanceof TeamError && current.code === 'TEAM_DISPOSED')
35
+ return true;
36
+ if (!(current instanceof Error))
37
+ return false;
38
+ seen.add(current);
39
+ current = current.cause;
40
+ }
41
+ return false;
42
+ }
43
+ /** Close Team runtime admission and cancel admitted interruptible work. */
44
+ close() {
45
+ this.controller.abort(new TeamError('Agent Teams service disposed', 'TEAM_DISPOSED'));
46
+ }
47
+ /**
48
+ * Track an admitted send or complete creation transaction until it settles.
49
+ * @param operation - asynchronous work owned by this runtime.
50
+ * @returns the same operation.
51
+ */
52
+ track(operation) {
53
+ this.operations.add(operation);
54
+ void operation.then(() => { this.operations.delete(operation); }, () => { this.operations.delete(operation); });
55
+ return operation;
56
+ }
57
+ /**
58
+ * Capture outstanding operations after closing admission.
59
+ * @returns admitted work still awaiting settlement.
60
+ */
61
+ pending() {
62
+ return [...this.operations];
63
+ }
64
+ /**
65
+ * Await admitted operations and retain failures other than runtime cancellation.
66
+ * @param operations - admitted operations captured after the admission cutoff.
67
+ * @param failures - aggregate destination for unexpected rejection or timeout.
68
+ */
69
+ async settle(operations, failures) {
70
+ if (operations.length === 0)
71
+ return;
72
+ try {
73
+ const outcomes = await this.withTimeout(Promise.allSettled(operations));
74
+ for (const outcome of outcomes) {
75
+ if (outcome.status === 'rejected' && !this.isCancellation(outcome.reason))
76
+ failures.push(outcome.reason);
77
+ }
78
+ }
79
+ catch (error) {
80
+ failures.push(error);
81
+ }
82
+ }
83
+ /**
84
+ * Bound one runtime settlement operation.
85
+ * @param operation - settlement that may otherwise block HMR or process shutdown.
86
+ * @returns the operation result.
87
+ */
88
+ async withTimeout(operation) {
89
+ let timer;
90
+ const timeout = new Promise((_resolve, reject) => {
91
+ timer = setTimeout(() => {
92
+ reject(new TeamError(`Agent Teams runtime disposal exceeded ${this.disposalTimeoutMs}ms`, 'TEAM_DISPOSAL_TIMEOUT'));
93
+ }, this.disposalTimeoutMs);
94
+ });
95
+ try {
96
+ return await Promise.race([operation, timeout]);
97
+ }
98
+ finally {
99
+ clearTimeout(timer);
100
+ }
101
+ }
102
+ }
103
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1,20 @@
1
+ /** Short-lived read-handle access to persisted Team member Sessions. */
2
+ import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset } from '@qilin-agent/session';
3
+ import type { SessionPersistence } from '@qilin-agent/session-persistence';
4
+ /** One persisted Session's detached header and complete committed event log. */
5
+ export interface PersistedSessionView {
6
+ /** Exact fork-inherited event count paired with `header`. */
7
+ readonly inheritedEventCount: SessionLogOffset;
8
+ readonly header: SessionHeader;
9
+ readonly events: readonly SessionEvent[];
10
+ }
11
+ /**
12
+ * Read one stored session's header and complete event log through a
13
+ * short-lived read handle, closing the handle before returning.
14
+ * @param persistence - the durable session store.
15
+ * @param id - the stored session to read.
16
+ * @param signal - cancellation observed by open and read.
17
+ * @returns the stored header and every committed event.
18
+ */
19
+ export declare function readPersistedSession(persistence: SessionPersistence, id: SessionId, signal: AbortSignal): Promise<PersistedSessionView>;
20
+ //# sourceMappingURL=persisted.d.ts.map
@@ -0,0 +1,20 @@
1
+ /** Short-lived read-handle access to persisted Team member Sessions. */
2
+ /**
3
+ * Read one stored session's header and complete event log through a
4
+ * short-lived read handle, closing the handle before returning.
5
+ * @param persistence - the durable session store.
6
+ * @param id - the stored session to read.
7
+ * @param signal - cancellation observed by open and read.
8
+ * @returns the stored header and every committed event.
9
+ */
10
+ export async function readPersistedSession(persistence, id, signal) {
11
+ const handle = await persistence.open(id, 'read', { signal });
12
+ try {
13
+ const { events } = await handle.read(0, undefined, { signal });
14
+ return { header: handle.header, inheritedEventCount: handle.inheritedEventCount, events };
15
+ }
16
+ finally {
17
+ await handle.close();
18
+ }
19
+ }
20
+ //# sourceMappingURL=persisted.js.map