@deepseek-ai/dsh-experimental-agent-team 0.1.5-alpha.2
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/LICENSE +21 -0
- package/README.i18n.yaml +6 -0
- package/README.md +220 -0
- package/README.zh.md +220 -0
- package/lib/index.js +1894 -0
- package/lib/invariant.js +361 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +867 -0
- package/lib/typert.remote-client.d.ts +32 -0
- package/lib/typert.remote-client.js +210 -0
- package/lib/types/activity.d.ts +23 -0
- package/lib/types/activity.js +85 -0
- package/lib/types/client.d.ts +3 -0
- package/lib/types/client.js +3 -0
- package/lib/types/error.d.ts +13 -0
- package/lib/types/error.js +23 -0
- package/lib/types/index.d.ts +137 -0
- package/lib/types/index.js +313 -0
- package/lib/types/invariant.d.ts +9 -0
- package/lib/types/invariant.js +26 -0
- package/lib/types/journal.d.ts +39 -0
- package/lib/types/journal.js +63 -0
- package/lib/types/lifecycle.d.ts +33 -0
- package/lib/types/lifecycle.js +85 -0
- package/lib/types/mailbox.d.ts +78 -0
- package/lib/types/mailbox.js +292 -0
- package/lib/types/persisted.d.ts +20 -0
- package/lib/types/persisted.js +20 -0
- package/lib/types/projection.d.ts +48 -0
- package/lib/types/projection.js +260 -0
- package/lib/types/roster.d.ts +113 -0
- package/lib/types/roster.js +446 -0
- package/lib/types/session-message.d.ts +11 -0
- package/lib/types/session-message.js +23 -0
- package/lib/types/task-board.d.ts +62 -0
- package/lib/types/task-board.js +275 -0
- package/lib/types/task-graph.d.ts +21 -0
- package/lib/types/task-graph.js +62 -0
- package/lib/types/types.d.ts +206 -0
- package/lib/types/types.js +26 -0
- package/lib/types/validation.d.ts +16 -0
- package/lib/types/validation.js +33 -0
- package/package.json +88 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/** Agent Teams service façade over roster, mailbox, 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 z from '@deepseek-ai/schemastery';
|
|
37
|
+
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
38
|
+
import { TeamActivity } from "./activity.js";
|
|
39
|
+
import { errorMessage, TeamError } from "./error.js";
|
|
40
|
+
import { TeamJournal } from "./journal.js";
|
|
41
|
+
import { TeamRuntimeLifecycle } from "./lifecycle.js";
|
|
42
|
+
import { TeamMailbox } from "./mailbox.js";
|
|
43
|
+
import { teamProjectionDefinition } from "./projection.js";
|
|
44
|
+
import { TeamRoster } from "./roster.js";
|
|
45
|
+
import { TeamTaskBoard } from "./task-board.js";
|
|
46
|
+
import { TeamId } from "./types.js";
|
|
47
|
+
export { TeamId, TeamMessageId, TeamTaskId } from "./types.js";
|
|
48
|
+
export { TeamError } from "./error.js";
|
|
49
|
+
const DEFAULT_MAX_MEMBERS = 8;
|
|
50
|
+
const DEFAULT_MAX_TASKS = 256;
|
|
51
|
+
const DEFAULT_MAX_PENDING_MESSAGES = 64;
|
|
52
|
+
const DEFAULT_MAX_MESSAGE_BYTES = 65_536;
|
|
53
|
+
const DEFAULT_DISPOSAL_TIMEOUT_MS = 5_000;
|
|
54
|
+
/** Validate one positive safe-integer deployment limit. */
|
|
55
|
+
function positiveLimit(name, value) {
|
|
56
|
+
if (!Number.isSafeInteger(value) || value < 1) {
|
|
57
|
+
throw new TeamError(`${name} must be a positive safe integer`, 'TEAM_INVALID_CONFIG');
|
|
58
|
+
}
|
|
59
|
+
return value;
|
|
60
|
+
}
|
|
61
|
+
/** Agent Teams service backed by the exact live Lead Session log. */
|
|
62
|
+
let TeamService = (() => {
|
|
63
|
+
let _classSuper = TypertRemoteService;
|
|
64
|
+
let _instanceExtraInitializers = [];
|
|
65
|
+
let _remoteView_decorators;
|
|
66
|
+
let _remoteCreateTask_decorators;
|
|
67
|
+
let _remoteUpdateTask_decorators;
|
|
68
|
+
return class TeamService extends _classSuper {
|
|
69
|
+
static {
|
|
70
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
|
|
71
|
+
_remoteView_decorators = [Remote('view')];
|
|
72
|
+
_remoteCreateTask_decorators = [Remote('createTask')];
|
|
73
|
+
_remoteUpdateTask_decorators = [Remote('updateTask')];
|
|
74
|
+
__esDecorate(this, null, _remoteView_decorators, { kind: "method", name: "remoteView", static: false, private: false, access: { has: obj => "remoteView" in obj, get: obj => obj.remoteView }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
75
|
+
__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);
|
|
76
|
+
__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);
|
|
77
|
+
if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
78
|
+
}
|
|
79
|
+
static inject = ['agents', 'sessions', 'sessionPersistence', 'sessionProjections', 'subagents'];
|
|
80
|
+
static Config = z.object({
|
|
81
|
+
maxMembers: z.number().step(1).min(1).default(DEFAULT_MAX_MEMBERS),
|
|
82
|
+
maxTasks: z.number().step(1).min(1).default(DEFAULT_MAX_TASKS),
|
|
83
|
+
maxPendingMessagesPerMember: z.number().step(1).min(1).default(DEFAULT_MAX_PENDING_MESSAGES),
|
|
84
|
+
maxMessageBytes: z.number().step(1).min(1).default(DEFAULT_MAX_MESSAGE_BYTES),
|
|
85
|
+
disposalTimeoutMs: z.number().step(1).min(1).default(DEFAULT_DISPOSAL_TIMEOUT_MS),
|
|
86
|
+
});
|
|
87
|
+
/** Validated deployment limits used by every Team operation. */
|
|
88
|
+
config = __runInitializers(this, _instanceExtraInitializers);
|
|
89
|
+
activity;
|
|
90
|
+
lifecycle;
|
|
91
|
+
journal;
|
|
92
|
+
roster;
|
|
93
|
+
mailbox;
|
|
94
|
+
tasks;
|
|
95
|
+
constructor(ctx, config = {}) {
|
|
96
|
+
super(ctx, 'agentTeams');
|
|
97
|
+
this.config = {
|
|
98
|
+
maxMembers: positiveLimit('maxMembers', config.maxMembers ?? DEFAULT_MAX_MEMBERS),
|
|
99
|
+
maxTasks: positiveLimit('maxTasks', config.maxTasks ?? DEFAULT_MAX_TASKS),
|
|
100
|
+
maxPendingMessagesPerMember: positiveLimit('maxPendingMessagesPerMember', config.maxPendingMessagesPerMember ?? DEFAULT_MAX_PENDING_MESSAGES),
|
|
101
|
+
maxMessageBytes: positiveLimit('maxMessageBytes', config.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES),
|
|
102
|
+
disposalTimeoutMs: positiveLimit('disposalTimeoutMs', config.disposalTimeoutMs ?? DEFAULT_DISPOSAL_TIMEOUT_MS),
|
|
103
|
+
};
|
|
104
|
+
this.activity = new TeamActivity();
|
|
105
|
+
this.lifecycle = new TeamRuntimeLifecycle(this.config.disposalTimeoutMs);
|
|
106
|
+
this.journal = new TeamJournal(ctx, (root) => { this.activity.notify(TeamId(root.id)); });
|
|
107
|
+
this.roster = new TeamRoster(ctx, this.journal, this.lifecycle, this.config.maxMembers);
|
|
108
|
+
this.mailbox = new TeamMailbox(ctx, this.journal, this.roster, this.lifecycle, this.config.maxPendingMessagesPerMember, this.config.maxMessageBytes);
|
|
109
|
+
this.tasks = new TeamTaskBoard(this.journal, this.config.maxTasks);
|
|
110
|
+
ctx.on('session/event', (session, event) => { this.mailbox.observeSessionEvent(session, event); });
|
|
111
|
+
ctx.on('agent/session-start', ({ agent }) => { this.scheduleRecovery(agent); });
|
|
112
|
+
ctx.on('agent/status', ({ agent }) => {
|
|
113
|
+
const membership = this.roster.tryMembership(agent);
|
|
114
|
+
if (membership !== undefined)
|
|
115
|
+
this.activity.notify(membership.id);
|
|
116
|
+
});
|
|
117
|
+
ctx.effect(() => {
|
|
118
|
+
const disposeProjection = ctx.root.sessionProjections.register(teamProjectionDefinition);
|
|
119
|
+
return async () => {
|
|
120
|
+
try {
|
|
121
|
+
await this.disposeRuntime();
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
disposeProjection();
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}, 'agentTeams.runtimeLifecycle()');
|
|
128
|
+
for (const agent of ctx.agents.list())
|
|
129
|
+
this.scheduleRecovery(agent);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolve one exact live Agent's Team role.
|
|
133
|
+
* @param agent - exact live Agent used as the authority credential.
|
|
134
|
+
* @returns its root, Team identity, role, and model-facing name.
|
|
135
|
+
*/
|
|
136
|
+
membership(agent) {
|
|
137
|
+
return this.roster.membership(agent);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* List the runtime-enriched roster visible to one Team member.
|
|
141
|
+
* @param agent - exact live Team member.
|
|
142
|
+
* @returns Lead and teammate rows in creation order.
|
|
143
|
+
*/
|
|
144
|
+
listMembers(agent) {
|
|
145
|
+
return this.roster.list(this.roster.membership(agent));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Create one named, continuable direct child of the Team Lead.
|
|
149
|
+
* @param caller - exact live Lead Agent.
|
|
150
|
+
* @param request - immutable name, description, prompt, context mode, provider, and cancellation.
|
|
151
|
+
* @returns the active roster row.
|
|
152
|
+
*/
|
|
153
|
+
async spawnTeammate(caller, request) {
|
|
154
|
+
return await this.roster.spawn(caller, request);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Queue one durable peer message, then attempt immediate delivery.
|
|
158
|
+
* @param caller - exact live sending Team member.
|
|
159
|
+
* @param request - target name, content, and pre-queue cancellation.
|
|
160
|
+
* @returns durable message identity and immediate-delivery observation.
|
|
161
|
+
*/
|
|
162
|
+
async sendMessage(caller, request) {
|
|
163
|
+
return await this.mailbox.send(caller, request);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create one unowned pending task in the Team Lead log.
|
|
167
|
+
* @param caller - exact live Team member creating the task.
|
|
168
|
+
* @param request - task text, blockers, and advisory write scopes.
|
|
169
|
+
* @returns the revision-one task view.
|
|
170
|
+
*/
|
|
171
|
+
async createTask(caller, request) {
|
|
172
|
+
return await this.tasks.create(this.roster.membership(caller), request);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Return one task, including a deleted tombstone.
|
|
176
|
+
* @param caller - exact live Team member reading the task.
|
|
177
|
+
* @param id - Team-local task identity.
|
|
178
|
+
* @returns the latest task value and derived readiness diagnostics.
|
|
179
|
+
*/
|
|
180
|
+
getTask(caller, id) {
|
|
181
|
+
return this.tasks.get(this.roster.membership(caller), id);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* List current non-deleted tasks in numeric creation order.
|
|
185
|
+
* @param caller - exact live Team member reading the board.
|
|
186
|
+
* @returns detached current task views.
|
|
187
|
+
*/
|
|
188
|
+
listTasks(caller) {
|
|
189
|
+
return this.tasks.list(this.roster.membership(caller));
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Compare-and-set one authorized task transition.
|
|
193
|
+
* @param caller - exact live Team member authorizing the mutation.
|
|
194
|
+
* @param request - task identity, expected revision, action, and action fields.
|
|
195
|
+
* @returns the committed next task revision.
|
|
196
|
+
*/
|
|
197
|
+
async updateTask(caller, request) {
|
|
198
|
+
return await this.tasks.update(caller, this.roster.membership(caller), request);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Wait for the next Team-domain or member-status change.
|
|
202
|
+
* @param caller - exact live Team member waiting for activity.
|
|
203
|
+
* @param timeoutMs - bounded wait duration from ten seconds through one hour.
|
|
204
|
+
* @param signal - caller cancellation for the wait only.
|
|
205
|
+
* @returns one observed change or a timeout result.
|
|
206
|
+
*/
|
|
207
|
+
async waitForChange(caller, timeoutMs, signal) {
|
|
208
|
+
const membership = this.roster.membership(caller);
|
|
209
|
+
return await this.activity.wait(membership.id, timeoutMs, signal);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Interrupt one live teammate turn without clearing its pending inbox.
|
|
213
|
+
* @param caller - exact live Lead Agent.
|
|
214
|
+
* @param targetName - durable teammate name.
|
|
215
|
+
* @returns the target status sampled before cancellation.
|
|
216
|
+
*/
|
|
217
|
+
interrupt(caller, targetName) {
|
|
218
|
+
return this.roster.interrupt(caller, targetName);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Resolve a caller without throwing, used by scoped-tool installation and observers.
|
|
222
|
+
* @param agent - candidate exact live Agent.
|
|
223
|
+
* @returns Team membership, or undefined for non-Team subagents and stale identities.
|
|
224
|
+
*/
|
|
225
|
+
tryMembership(agent) {
|
|
226
|
+
return this.roster.tryMembership(agent);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Read the current roster and non-deleted task board through the generated Remote API.
|
|
230
|
+
* @param agent - exact live Team member used as the authority credential.
|
|
231
|
+
* @returns detached current roster and task views.
|
|
232
|
+
*/
|
|
233
|
+
remoteView(agent) {
|
|
234
|
+
return {
|
|
235
|
+
members: this.listMembers(agent),
|
|
236
|
+
tasks: this.listTasks(agent),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Create one shared task through the generated Remote API.
|
|
241
|
+
* @param agent - exact live Team member creating the task.
|
|
242
|
+
* @param request - task text, blockers, and advisory write scopes.
|
|
243
|
+
* @returns the revision-one task or a typed Team rejection.
|
|
244
|
+
*/
|
|
245
|
+
remoteCreateTask(agent, request) {
|
|
246
|
+
return this.taskMutationResult(this.createTask(agent, request));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Apply one task mutation and preserve Team rejections as business results.
|
|
250
|
+
* @param agent - exact live Team member authorizing the mutation.
|
|
251
|
+
* @param request - task identity, expected revision, action, and action fields.
|
|
252
|
+
* @returns the committed task or a typed Team rejection.
|
|
253
|
+
*/
|
|
254
|
+
remoteUpdateTask(agent, request) {
|
|
255
|
+
return this.taskMutationResult(this.updateTask(agent, request));
|
|
256
|
+
}
|
|
257
|
+
/** Preserve Team task rejections while allowing unexpected failures to reject the Remote call. */
|
|
258
|
+
async taskMutationResult(operation) {
|
|
259
|
+
try {
|
|
260
|
+
return { ok: true, value: await operation };
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
if (!(error instanceof TeamError))
|
|
264
|
+
throw error;
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
error: {
|
|
268
|
+
code: error.code === 'TEAM_TASK_STALE_REVISION' ? 'team-task-conflict' : 'team-rejected',
|
|
269
|
+
message: error.message,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/** Queue one contained recovery pass after publication has unwound. */
|
|
275
|
+
scheduleRecovery(agent) {
|
|
276
|
+
queueMicrotask(() => {
|
|
277
|
+
if (this.lifecycle.disposed)
|
|
278
|
+
return;
|
|
279
|
+
void this.recoverFor(agent).catch((error) => {
|
|
280
|
+
if (this.lifecycle.disposed)
|
|
281
|
+
return;
|
|
282
|
+
this.ctx.logger.warn(`Agent Teams recovery for "${agent.id}" failed: ${errorMessage(error)}`);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/** Reconcile roster provisioning before retrying that member's pending mailbox. */
|
|
287
|
+
async recoverFor(agent) {
|
|
288
|
+
await this.roster.recoverFor(agent, this.lifecycle.signal);
|
|
289
|
+
await this.mailbox.recoverFor(agent, this.lifecycle.signal);
|
|
290
|
+
}
|
|
291
|
+
/** Stop Team-owned live branches and release every waiter before service disposal completes. */
|
|
292
|
+
async disposeRuntime() {
|
|
293
|
+
this.lifecycle.close();
|
|
294
|
+
this.activity.close();
|
|
295
|
+
const failures = [];
|
|
296
|
+
await this.lifecycle.settle(this.roster.pendingCreations(), failures);
|
|
297
|
+
await this.lifecycle.settle(this.mailbox.pendingDispatches(), failures);
|
|
298
|
+
for (const [root, childIds] of this.roster.liveChildrenByRoot()) {
|
|
299
|
+
try {
|
|
300
|
+
await this.roster.stopTeammates(root, childIds);
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
failures.push(error);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (failures.length > 0)
|
|
307
|
+
throw new AggregateError(failures, 'Agent Teams runtime disposal failed');
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
})();
|
|
311
|
+
export { TeamService };
|
|
312
|
+
export default TeamService;
|
|
313
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Agent Teams runtime invariant companion. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
/** Cordis companion plugin name. */
|
|
4
|
+
export declare const name = "team-invariant";
|
|
5
|
+
/** Invariant registry required by the companion. */
|
|
6
|
+
export declare const inject: string[];
|
|
7
|
+
/** Register the package invariant companion. */
|
|
8
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
9
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Agent Teams runtime invariant companion. */
|
|
2
|
+
import { isTeamEvent, teamProjectionDefinition, } from "./projection.js";
|
|
3
|
+
const PACKAGE_NAME = '@deepseek-ai/dsh-experimental-agent-team';
|
|
4
|
+
/** Cordis companion plugin name. */
|
|
5
|
+
export const name = 'team-invariant';
|
|
6
|
+
/** Invariant registry required by the companion. */
|
|
7
|
+
export const inject = ['invariants'];
|
|
8
|
+
/** Validate candidate Team events against the projected committed prefix. */
|
|
9
|
+
const install = Object.assign((ctx, fail) => {
|
|
10
|
+
ctx.on('internal/dispatch', (_mode, eventName, args) => {
|
|
11
|
+
if (eventName !== 'session/event')
|
|
12
|
+
return;
|
|
13
|
+
const [session, event] = args;
|
|
14
|
+
/* v8 ignore next -- non-Team Session events have no Agent Teams invariant. */
|
|
15
|
+
if (!isTeamEvent(event))
|
|
16
|
+
return;
|
|
17
|
+
const state = ctx.sessionProjections.stateOf(session, 'agentTeam');
|
|
18
|
+
const candidate = teamProjectionDefinition.apply(structuredClone(state), event);
|
|
19
|
+
if (candidate.failure !== undefined) {
|
|
20
|
+
fail(`session event ${event.seq} violates the Agent Teams stream: ${candidate.failure}`);
|
|
21
|
+
}
|
|
22
|
+
}, { global: true });
|
|
23
|
+
}, { inject: ['sessionProjections'] });
|
|
24
|
+
/** Register the package invariant companion. */
|
|
25
|
+
export const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
26
|
+
//# sourceMappingURL=invariant.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** Serialized Team transactions over the exact live Lead Session log. */
|
|
2
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
3
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
4
|
+
import type { SessionEventMap, SessionId } from '@deepseek-ai/dsh-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,33 @@
|
|
|
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 controller;
|
|
6
|
+
/**
|
|
7
|
+
* @param disposalTimeoutMs - maximum wait for one disposal settlement operation.
|
|
8
|
+
*/
|
|
9
|
+
constructor(disposalTimeoutMs: number);
|
|
10
|
+
/** Signal aborted exactly when Team runtime admission closes. */
|
|
11
|
+
get signal(): AbortSignal;
|
|
12
|
+
/** Whether Team runtime admission is closed. */
|
|
13
|
+
get disposed(): boolean;
|
|
14
|
+
/** The exact cancellation reason used to distinguish expected disposal rejection. */
|
|
15
|
+
get reason(): unknown;
|
|
16
|
+
/** Whether a rejection is the runtime cancellation, directly or through an Error cause chain. */
|
|
17
|
+
private isCancellation;
|
|
18
|
+
/** Close Team runtime admission and cancel admitted interruptible work. */
|
|
19
|
+
close(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Await admitted operations and retain failures other than runtime cancellation.
|
|
22
|
+
* @param operations - admitted operations captured after the admission cutoff.
|
|
23
|
+
* @param failures - aggregate destination for unexpected rejection or timeout.
|
|
24
|
+
*/
|
|
25
|
+
settle(operations: readonly Promise<unknown>[], failures: unknown[]): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Bound one runtime settlement operation.
|
|
28
|
+
* @param operation - settlement that may otherwise block HMR or process shutdown.
|
|
29
|
+
* @returns the operation result.
|
|
30
|
+
*/
|
|
31
|
+
withTimeout<T>(operation: Promise<T>): Promise<T>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
controller = new AbortController();
|
|
7
|
+
/**
|
|
8
|
+
* @param disposalTimeoutMs - maximum wait for one disposal settlement operation.
|
|
9
|
+
*/
|
|
10
|
+
constructor(disposalTimeoutMs) {
|
|
11
|
+
this.disposalTimeoutMs = disposalTimeoutMs;
|
|
12
|
+
}
|
|
13
|
+
/** Signal aborted exactly when Team runtime admission closes. */
|
|
14
|
+
get signal() {
|
|
15
|
+
return this.controller.signal;
|
|
16
|
+
}
|
|
17
|
+
/** Whether Team runtime admission is closed. */
|
|
18
|
+
get disposed() {
|
|
19
|
+
return this.signal.aborted;
|
|
20
|
+
}
|
|
21
|
+
/** The exact cancellation reason used to distinguish expected disposal rejection. */
|
|
22
|
+
get reason() {
|
|
23
|
+
const reason = this.signal.reason;
|
|
24
|
+
return reason;
|
|
25
|
+
}
|
|
26
|
+
/** Whether a rejection is the runtime cancellation, directly or through an Error cause chain. */
|
|
27
|
+
isCancellation(reason) {
|
|
28
|
+
const seen = new Set();
|
|
29
|
+
let current = reason;
|
|
30
|
+
while (!seen.has(current)) {
|
|
31
|
+
if (this.disposed && current === this.reason)
|
|
32
|
+
return true;
|
|
33
|
+
if (this.disposed && current instanceof TeamError && current.code === 'TEAM_DISPOSED')
|
|
34
|
+
return true;
|
|
35
|
+
if (!(current instanceof Error))
|
|
36
|
+
return false;
|
|
37
|
+
seen.add(current);
|
|
38
|
+
current = current.cause;
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
/** Close Team runtime admission and cancel admitted interruptible work. */
|
|
43
|
+
close() {
|
|
44
|
+
this.controller.abort(new TeamError('Agent Teams service disposed', 'TEAM_DISPOSED'));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Await admitted operations and retain failures other than runtime cancellation.
|
|
48
|
+
* @param operations - admitted operations captured after the admission cutoff.
|
|
49
|
+
* @param failures - aggregate destination for unexpected rejection or timeout.
|
|
50
|
+
*/
|
|
51
|
+
async settle(operations, failures) {
|
|
52
|
+
if (operations.length === 0)
|
|
53
|
+
return;
|
|
54
|
+
try {
|
|
55
|
+
const outcomes = await this.withTimeout(Promise.allSettled(operations));
|
|
56
|
+
for (const outcome of outcomes) {
|
|
57
|
+
if (outcome.status === 'rejected' && !this.isCancellation(outcome.reason))
|
|
58
|
+
failures.push(outcome.reason);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
failures.push(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Bound one runtime settlement operation.
|
|
67
|
+
* @param operation - settlement that may otherwise block HMR or process shutdown.
|
|
68
|
+
* @returns the operation result.
|
|
69
|
+
*/
|
|
70
|
+
async withTimeout(operation) {
|
|
71
|
+
let timer;
|
|
72
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
73
|
+
timer = setTimeout(() => {
|
|
74
|
+
reject(new TeamError(`Agent Teams runtime disposal exceeded ${this.disposalTimeoutMs}ms`, 'TEAM_DISPOSAL_TIMEOUT'));
|
|
75
|
+
}, this.disposalTimeoutMs);
|
|
76
|
+
});
|
|
77
|
+
try {
|
|
78
|
+
return await Promise.race([operation, timeout]);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=lifecycle.js.map
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** Durable Team mailbox admission, target-local dispatch, acknowledgement, and recovery. */
|
|
2
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
3
|
+
import type { Agent } from '@deepseek-ai/dsh-agent';
|
|
4
|
+
import type { Session, SessionEvent } from '@deepseek-ai/dsh-session';
|
|
5
|
+
import type { TeamJournal } from './journal.ts';
|
|
6
|
+
import type { TeamRuntimeLifecycle } from './lifecycle.ts';
|
|
7
|
+
import type { TeamRoster } from './roster.ts';
|
|
8
|
+
import type { SendTeamMessageRequest, SendTeamMessageResult } from './types.ts';
|
|
9
|
+
/** Owns every process-local state transition for the durable Team mailbox. */
|
|
10
|
+
export declare class TeamMailbox {
|
|
11
|
+
private readonly ctx;
|
|
12
|
+
private readonly journal;
|
|
13
|
+
private readonly roster;
|
|
14
|
+
private readonly lifecycle;
|
|
15
|
+
private readonly maxPendingMessagesPerMember;
|
|
16
|
+
private readonly maxMessageBytes;
|
|
17
|
+
private readonly dispatchTails;
|
|
18
|
+
private readonly inFlightMessages;
|
|
19
|
+
private readonly inFlightDispatches;
|
|
20
|
+
/**
|
|
21
|
+
* @param ctx - Team service context with Agent, Session, persistence, and subagent services.
|
|
22
|
+
* @param journal - authoritative Lead-log transaction owner.
|
|
23
|
+
* @param roster - Team membership and member-name resolver.
|
|
24
|
+
* @param lifecycle - shared Team runtime admission cutoff.
|
|
25
|
+
* @param maxPendingMessagesPerMember - per-target queued-minus-delivered limit.
|
|
26
|
+
* @param maxMessageBytes - maximum complete sender-framed delivery size.
|
|
27
|
+
*/
|
|
28
|
+
constructor(ctx: Context, journal: TeamJournal, roster: TeamRoster, lifecycle: TeamRuntimeLifecycle, maxPendingMessagesPerMember: number, maxMessageBytes: number);
|
|
29
|
+
/**
|
|
30
|
+
* Queue one durable peer message, then attempt immediate delivery.
|
|
31
|
+
* @param caller - exact live sending Team member.
|
|
32
|
+
* @param request - target name, content, and pre-queue cancellation.
|
|
33
|
+
* @returns durable message identity and immediate-delivery observation.
|
|
34
|
+
*/
|
|
35
|
+
send(caller: Agent, request: SendTeamMessageRequest): Promise<SendTeamMessageResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Observe target-side durable receipts and checkpoint their Lead-log acknowledgement.
|
|
38
|
+
* @param session - exact target Session receiving the event.
|
|
39
|
+
* @param event - newly appended Session event.
|
|
40
|
+
*/
|
|
41
|
+
observeSessionEvent(session: Session, event: SessionEvent): void;
|
|
42
|
+
/**
|
|
43
|
+
* Retry durable pending messages relevant to one started Team member.
|
|
44
|
+
* @param agent - newly started exact live Agent.
|
|
45
|
+
* @param signal - shared runtime cancellation.
|
|
46
|
+
*/
|
|
47
|
+
recoverFor(agent: Agent, signal: AbortSignal): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Return admitted dispatch and acknowledgement operations captured for disposal.
|
|
50
|
+
* @returns detached snapshot ordered only by Set insertion.
|
|
51
|
+
*/
|
|
52
|
+
pendingDispatches(): readonly Promise<unknown>[];
|
|
53
|
+
/** Queue and dispatch one mailbox item admitted before the disposal cutoff. */
|
|
54
|
+
private sendAdmitted;
|
|
55
|
+
/** Attempt one queued message exactly once in this process at a time. */
|
|
56
|
+
private tryDispatch;
|
|
57
|
+
/** Track one dispatch transaction through delivery admission or contained failure. */
|
|
58
|
+
private trackDispatch;
|
|
59
|
+
/** Attempt one queued message admitted before the service lifecycle cutoff. */
|
|
60
|
+
private tryDispatchAdmitted;
|
|
61
|
+
/** Serialize delivery admission for one durable target in queued order. */
|
|
62
|
+
private serializeDispatch;
|
|
63
|
+
/** Deliver every pending target message through `message` in durable queue order. */
|
|
64
|
+
private dispatchThrough;
|
|
65
|
+
/** Attempt one queued delivery after target-local ordering admits it. */
|
|
66
|
+
private dispatchOnce;
|
|
67
|
+
/** Flush one live target receipt before the Lead records its delivered edge. */
|
|
68
|
+
private checkpointDelivered;
|
|
69
|
+
/** Record delivery unless the acknowledgement already exists. */
|
|
70
|
+
private markDelivered;
|
|
71
|
+
/** Whether a target Session already contains the durable message identity. */
|
|
72
|
+
private targetRecorded;
|
|
73
|
+
/** Frame peer content with stable sender and message identity for the receiving model. */
|
|
74
|
+
private deliveryContent;
|
|
75
|
+
/** Read an inactive target's durable log before cold resume; uncertainty keeps the mailbox queued. */
|
|
76
|
+
private persistedTargetRecorded;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=mailbox.d.ts.map
|