@rebon/cli-darwin-arm64 0.17.2 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/compose-runtime/package.json +17 -0
- package/bin/compose-runtime/payload/compose/credentials-runtime.js +38 -0
- package/bin/compose-runtime/payload/compose/llm-runtime.js +330 -0
- package/bin/compose-runtime/payload/compose/loop-assembly.js +149 -0
- package/bin/compose-runtime/payload/compose/serve-dispatch.js +82 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-anonymous-user-id.js +10 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-credentials.js +13 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-launch-environment.js +21 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-llm.js +313 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-settings.js +43 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-tools.js +20 -0
- package/bin/compose-runtime/payload/compose/shims/dsh-web.js +10 -0
- package/bin/compose-runtime/payload/compose/shims/llm-adapter.js +32 -0
- package/bin/compose-runtime/payload/compose/shims/zod-lite.js +49 -0
- package/bin/compose-runtime/payload/compose/systemprompt-runtime.js +74 -0
- package/bin/compose-runtime/payload/compose/tools-runtime.js +228 -0
- package/bin/compose-runtime/payload/compose/web-runtime.js +204 -0
- package/bin/compose-runtime/payload/vendor/cordis/index.js +1530 -0
- package/bin/compose-runtime/payload/vendor/cosmokit/LICENSE +21 -0
- package/bin/compose-runtime/payload/vendor/cosmokit/index.mjs +357 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-agent-loop +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-core +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-llm-deepseek +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-logger-console +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-scope +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-session +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-system-prompt +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timeout +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-timer +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-todo +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tool-web +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-tools-schema +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/LICENSE-web-search-exa +21 -0
- package/bin/compose-runtime/payload/vendor/dsh/agent-loop.js +1193 -0
- package/bin/compose-runtime/payload/vendor/dsh/agent.js +714 -0
- package/bin/compose-runtime/payload/vendor/dsh/llm-core.js +269 -0
- package/bin/compose-runtime/payload/vendor/dsh/llm-deepseek.js +672 -0
- package/bin/compose-runtime/payload/vendor/dsh/logger-console.js +83 -0
- package/bin/compose-runtime/payload/vendor/dsh/scope.js +287 -0
- package/bin/compose-runtime/payload/vendor/dsh/session.js +1668 -0
- package/bin/compose-runtime/payload/vendor/dsh/system-prompt.js +309 -0
- package/bin/compose-runtime/payload/vendor/dsh/timeout.js +100 -0
- package/bin/compose-runtime/payload/vendor/dsh/timer.js +128 -0
- package/bin/compose-runtime/payload/vendor/dsh/tool-todo.js +143 -0
- package/bin/compose-runtime/payload/vendor/dsh/tool-web.js +1527 -0
- package/bin/compose-runtime/payload/vendor/dsh/tools-schema.js +849 -0
- package/bin/compose-runtime/payload/vendor/dsh/web-search-exa.js +122 -0
- package/bin/compose-runtime/payload/vendor/eventsource-parser/LICENSE +21 -0
- package/bin/compose-runtime/payload/vendor/eventsource-parser/index.js +177 -0
- package/bin/compose-runtime/payload/vendor/eventsource-parser/stream.js +48 -0
- package/bin/compose-runtime/payload/vendor/schemastery/index.mjs +656 -0
- package/bin/compose-runtime/payload-manifests.json +48 -0
- package/bin/compose-runtime/src/bridge.mjs +142 -0
- package/bin/compose-runtime/src/credentials-runtime.mjs +50 -0
- package/bin/compose-runtime/src/eventsource-stream.mjs +48 -0
- package/bin/compose-runtime/src/index.mjs +7 -0
- package/bin/compose-runtime/src/llm-runtime.mjs +326 -0
- package/bin/compose-runtime/src/loader.mjs +78 -0
- package/bin/compose-runtime/src/loop-assembly.mjs +237 -0
- package/bin/compose-runtime/src/payload.mjs +49 -0
- package/bin/compose-runtime/src/plugin.mjs +59 -0
- package/bin/compose-runtime/src/realm.mjs +223 -0
- package/bin/compose-runtime/src/registry.mjs +191 -0
- package/bin/compose-runtime/src/resolve.mjs +115 -0
- package/bin/compose-runtime/src/systemprompt-runtime.mjs +67 -0
- package/bin/compose-runtime/src/tools-runtime.mjs +239 -0
- package/bin/compose-runtime/src/web-runtime.mjs +209 -0
- package/bin/plugin-host/package.json +11 -0
- package/bin/plugin-host/src/bridge.mjs +64 -0
- package/bin/plugin-host/src/cli.mjs +105 -0
- package/bin/plugin-host/src/framing.mjs +113 -0
- package/bin/plugin-host/src/host.mjs +624 -0
- package/bin/plugin-host/src/json.mjs +196 -0
- package/bin/plugin-host/src/lifecycle.mjs +114 -0
- package/bin/plugin-host/src/loader.mjs +142 -0
- package/bin/plugin-host/src/methods.mjs +256 -0
- package/bin/plugin-host/src/protocol.mjs +129 -0
- package/bin/plugin-host/src/sdk.mjs +7 -0
- package/bin/rebon +0 -0
- package/bin/rebon-boa-helper +0 -0
- package/package.json +4 -1
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
// packages/core/agent/src/index.ts
|
|
2
|
+
import { FiberState, getTraceable, Service, symbols } from "@deepseek-ai/cordis";
|
|
3
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
+
import { isPromise } from "node:util/types";
|
|
5
|
+
import { scopeTarget as scopeTarget2 } from "@deepseek-ai/dsh-scope";
|
|
6
|
+
|
|
7
|
+
// packages/core/agent/src/inbox.ts
|
|
8
|
+
var Inbox = class {
|
|
9
|
+
constructor(session, notifications) {
|
|
10
|
+
this.session = session;
|
|
11
|
+
this.notifications = notifications;
|
|
12
|
+
for (const event of session.events.slice(session.header.seedLength ?? 0)) {
|
|
13
|
+
if (event.type !== "agent/inbox/spliced") continue;
|
|
14
|
+
try {
|
|
15
|
+
this.apply(event.data);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw new Error(`invalid persisted inbox splice at session seq ${event.seq}`, { cause: error });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
session;
|
|
22
|
+
notifications;
|
|
23
|
+
state = { "next-turn": [], "next-step": [] };
|
|
24
|
+
/** Prompts awaiting individual turns. */
|
|
25
|
+
get nextTurn() {
|
|
26
|
+
return this.state["next-turn"];
|
|
27
|
+
}
|
|
28
|
+
/** Input awaiting the next step boundary. */
|
|
29
|
+
get nextStep() {
|
|
30
|
+
return this.state["next-step"];
|
|
31
|
+
}
|
|
32
|
+
/** Whether either pending-message list contains work. */
|
|
33
|
+
get hasPending() {
|
|
34
|
+
return this.nextTurn.length > 0 || this.nextStep.length > 0;
|
|
35
|
+
}
|
|
36
|
+
/** Durably cancel all pending input, clearing next-step before next-turn. */
|
|
37
|
+
clear() {
|
|
38
|
+
this.splice("next-step", 0, this.nextStep.length, []);
|
|
39
|
+
this.splice("next-turn", 0, this.nextTurn.length, []);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Remove and return the complete batch proposed for one step, publishing
|
|
43
|
+
* each claimed message. The durable splices are pure deletions.
|
|
44
|
+
* @param target - whether this boundary also consumes one queued turn.
|
|
45
|
+
* @param turn - turn that will own the claimed batch.
|
|
46
|
+
* @returns next-step input followed by the queued turn, when requested.
|
|
47
|
+
* @internal - The agent loop's step-boundary operation, not a plugin extension point.
|
|
48
|
+
*/
|
|
49
|
+
claim(target, turn) {
|
|
50
|
+
const claimed = this.mutate("next-step", 0, this.nextStep.length, [], false);
|
|
51
|
+
if (target === "next-turn") {
|
|
52
|
+
claimed.push(...this.mutate("next-turn", 0, 1, [], false));
|
|
53
|
+
}
|
|
54
|
+
for (const message of claimed) this.notifications.claimed(message, turn);
|
|
55
|
+
return claimed;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Append one message to a pending list and durably record the insertion.
|
|
59
|
+
* @param target - pending list to extend.
|
|
60
|
+
* @param message - message to append.
|
|
61
|
+
* @throws if the message identity is already pending.
|
|
62
|
+
*/
|
|
63
|
+
append(target, message) {
|
|
64
|
+
this.splice(target, this.state[target].length, 0, [message]);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Prepend one message to a pending list and durably record the insertion.
|
|
68
|
+
* @param target - pending list to extend.
|
|
69
|
+
* @param message - message to prepend.
|
|
70
|
+
* @throws if the message identity is already pending.
|
|
71
|
+
*/
|
|
72
|
+
prepend(target, message) {
|
|
73
|
+
this.splice(target, 0, 0, [message]);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Replace one pending message in place, possibly changing its identity. A
|
|
77
|
+
* successful replacement publishes the old message as discarded and the new
|
|
78
|
+
* message as inserted.
|
|
79
|
+
* @param messageId - identity of the pending message to replace.
|
|
80
|
+
* @param newMessage - replacement message.
|
|
81
|
+
* @returns whether the message was still pending.
|
|
82
|
+
* @throws if the replacement duplicates another pending message identity.
|
|
83
|
+
*/
|
|
84
|
+
replace(messageId, newMessage) {
|
|
85
|
+
const location = this.locate(messageId);
|
|
86
|
+
if (location === void 0) return false;
|
|
87
|
+
this.splice(location.target, location.index, 1, [newMessage]);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Remove one pending message and durably record its cancellation.
|
|
92
|
+
* @param messageId - identity of the pending message to remove.
|
|
93
|
+
* @returns whether the message was still pending.
|
|
94
|
+
*/
|
|
95
|
+
remove(messageId) {
|
|
96
|
+
const location = this.locate(messageId);
|
|
97
|
+
if (location === void 0) return false;
|
|
98
|
+
this.splice(location.target, location.index, 1, []);
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Apply standard splice semantics and durably record the normalized result.
|
|
103
|
+
* The durable event commits before the live projection mutates, so synchronous
|
|
104
|
+
* `session/event` observers see the pre-splice lists and can reconstruct the
|
|
105
|
+
* removed messages from the normalized coordinates.
|
|
106
|
+
* @param target - pending list to mutate.
|
|
107
|
+
* @param start - splice position.
|
|
108
|
+
* @param deleteCount - maximum number of messages to remove.
|
|
109
|
+
* @param inserted - messages to insert at the resolved position.
|
|
110
|
+
* @returns messages removed by the splice.
|
|
111
|
+
*/
|
|
112
|
+
splice(target, start, deleteCount, inserted) {
|
|
113
|
+
return this.mutate(target, start, deleteCount, inserted, true);
|
|
114
|
+
}
|
|
115
|
+
/** Locate one pending identity across both owned lists. */
|
|
116
|
+
locate(messageId) {
|
|
117
|
+
for (const target of ["next-turn", "next-step"]) {
|
|
118
|
+
const index = this.state[target].findIndex((message) => message.id === messageId);
|
|
119
|
+
if (index >= 0) return { target, index };
|
|
120
|
+
}
|
|
121
|
+
return void 0;
|
|
122
|
+
}
|
|
123
|
+
/** Commit one normalized mutation and publish its live notifications. */
|
|
124
|
+
mutate(target, start, deleteCount, inserted, discardRemoved) {
|
|
125
|
+
const inbox = this.state[target];
|
|
126
|
+
const truncatedStart = Math.trunc(start);
|
|
127
|
+
const offset = Number.isNaN(truncatedStart) ? 0 : truncatedStart;
|
|
128
|
+
const actualStart = offset < 0 ? Math.max(inbox.length + offset, 0) : Math.min(offset, inbox.length);
|
|
129
|
+
const truncatedDeleteCount = Math.trunc(deleteCount);
|
|
130
|
+
const actualDeleteCount = Math.min(
|
|
131
|
+
Math.max(Number.isNaN(truncatedDeleteCount) ? 0 : truncatedDeleteCount, 0),
|
|
132
|
+
inbox.length - actualStart
|
|
133
|
+
);
|
|
134
|
+
if (actualDeleteCount === 0 && inserted.length === 0) return [];
|
|
135
|
+
const outcome = discardRemoved && actualDeleteCount > 0 ? "canceled" : void 0;
|
|
136
|
+
const splice = {
|
|
137
|
+
target,
|
|
138
|
+
start: actualStart,
|
|
139
|
+
...actualDeleteCount === 0 ? {} : { removedCount: actualDeleteCount },
|
|
140
|
+
inserted,
|
|
141
|
+
...outcome === void 0 ? {} : { outcome }
|
|
142
|
+
};
|
|
143
|
+
this.validate(splice);
|
|
144
|
+
const event = this.session.append("agent/inbox/spliced", splice);
|
|
145
|
+
const removed = inbox.splice(actualStart, actualDeleteCount, ...event.data.inserted);
|
|
146
|
+
if (discardRemoved) {
|
|
147
|
+
for (const message of removed) this.notifications.discarded(message);
|
|
148
|
+
}
|
|
149
|
+
for (const message of event.data.inserted) this.notifications.inserted(message);
|
|
150
|
+
return removed;
|
|
151
|
+
}
|
|
152
|
+
/** Apply one normalized durable splice to the projection. */
|
|
153
|
+
apply(splice) {
|
|
154
|
+
this.validate(splice);
|
|
155
|
+
const inbox = this.state[splice.target];
|
|
156
|
+
return inbox.splice(splice.start, splice.removedCount ?? 0, ...splice.inserted);
|
|
157
|
+
}
|
|
158
|
+
/** Validate one normalized splice against the current projection. */
|
|
159
|
+
validate(splice) {
|
|
160
|
+
const inbox = this.state[splice.target];
|
|
161
|
+
const removedCount = splice.removedCount ?? 0;
|
|
162
|
+
if (!Number.isSafeInteger(splice.start) || splice.start < 0 || splice.start > inbox.length || !Number.isSafeInteger(removedCount) || removedCount < 0 || splice.start + removedCount > inbox.length) {
|
|
163
|
+
throw new Error("invalid inbox splice");
|
|
164
|
+
}
|
|
165
|
+
const candidate = inbox.toSpliced(splice.start, removedCount, ...splice.inserted);
|
|
166
|
+
const ids = /* @__PURE__ */ new Set();
|
|
167
|
+
for (const message of splice.target === "next-turn" ? [...candidate, ...this.nextStep] : [...this.nextTurn, ...candidate]) {
|
|
168
|
+
if (ids.has(message.id)) throw new Error(`message "${message.id}" is already pending`);
|
|
169
|
+
ids.add(message.id);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
// packages/core/agent/src/consumed-work.ts
|
|
175
|
+
function accountsForClaim(reason) {
|
|
176
|
+
switch (reason.kind) {
|
|
177
|
+
case "completed":
|
|
178
|
+
return false;
|
|
179
|
+
case "blocked":
|
|
180
|
+
case "aborted":
|
|
181
|
+
case "interrupted":
|
|
182
|
+
case "error":
|
|
183
|
+
return true;
|
|
184
|
+
/* v8 ignore next 4 -- unreachable: the one unnamed built-in, `max-tokens`, requires a step,
|
|
185
|
+
* so its turn short-circuits as stepped before this call, and `TurnEndReasonMap` is
|
|
186
|
+
* merge-extensible, so a backend-added variant cannot be listed; an unnameable ending over
|
|
187
|
+
* consumed input must not read as success. */
|
|
188
|
+
default:
|
|
189
|
+
return true;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function foldConsumedWork(events) {
|
|
193
|
+
const stepped = /* @__PURE__ */ new Set();
|
|
194
|
+
const claimed = /* @__PURE__ */ new Set();
|
|
195
|
+
let open;
|
|
196
|
+
let end;
|
|
197
|
+
let droppedUnrun = false;
|
|
198
|
+
for (const event of events) {
|
|
199
|
+
switch (event.type) {
|
|
200
|
+
case "turn/start":
|
|
201
|
+
open = event.data.turn;
|
|
202
|
+
break;
|
|
203
|
+
case "step/start":
|
|
204
|
+
stepped.add(event.data.turn);
|
|
205
|
+
break;
|
|
206
|
+
case "agent/inbox/spliced": {
|
|
207
|
+
const { removedCount, outcome, inserted } = event.data;
|
|
208
|
+
if (removedCount === void 0) break;
|
|
209
|
+
if (outcome === "canceled") droppedUnrun ||= inserted.length === 0;
|
|
210
|
+
else if (open !== void 0) claimed.add(open);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case "turn/end": {
|
|
214
|
+
const { turn, reason } = event.data;
|
|
215
|
+
open = void 0;
|
|
216
|
+
if (stepped.delete(turn) || claimed.delete(turn) && accountsForClaim(reason)) {
|
|
217
|
+
end = event;
|
|
218
|
+
droppedUnrun = false;
|
|
219
|
+
}
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
default:
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { ...end === void 0 ? {} : { end }, droppedUnrun };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// packages/core/agent/src/model-selection.ts
|
|
230
|
+
function installModelSelection(agentCtx, selection) {
|
|
231
|
+
const disposeAssembly = agentCtx.on("system-prompt/assemble", async (_assembly, _context, next) => {
|
|
232
|
+
const selected = selection.current;
|
|
233
|
+
const assembled = await next();
|
|
234
|
+
selection.assembled = selected;
|
|
235
|
+
if (selected === void 0) return assembled;
|
|
236
|
+
return {
|
|
237
|
+
...assembled,
|
|
238
|
+
variables: {
|
|
239
|
+
...assembled.variables,
|
|
240
|
+
provider: selected.provider,
|
|
241
|
+
model: selected.model
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
const disposeRequest = agentCtx.on(
|
|
246
|
+
"agent/request",
|
|
247
|
+
async (_payload, next) => {
|
|
248
|
+
const resolved = await next();
|
|
249
|
+
const selected = selection.assembled;
|
|
250
|
+
if (selected === void 0) return resolved;
|
|
251
|
+
const { reasoningEffort: _inheritedEffort, ...withoutInheritedEffort } = resolved;
|
|
252
|
+
return {
|
|
253
|
+
...withoutInheritedEffort,
|
|
254
|
+
provider: selected.provider,
|
|
255
|
+
model: selected.model,
|
|
256
|
+
...selected.reasoningEffort === void 0 ? {} : { reasoningEffort: selected.reasoningEffort }
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
);
|
|
260
|
+
return () => {
|
|
261
|
+
disposeAssembly();
|
|
262
|
+
disposeRequest();
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// packages/core/agent/src/dispatch.ts
|
|
267
|
+
import { scopeTarget } from "@deepseek-ai/dsh-scope";
|
|
268
|
+
function agentCarrier(agent) {
|
|
269
|
+
return scopeTarget(agent, agent);
|
|
270
|
+
}
|
|
271
|
+
function agentEvents(ctx, agent, carrier = agentCarrier(agent)) {
|
|
272
|
+
const fused = (payload) => (
|
|
273
|
+
// The dispatcher owns the subject injection; callers pass PayloadRest, so
|
|
274
|
+
// the fused record is exactly the declared payload. The spread comes
|
|
275
|
+
// first, so a structurally acceptable payload that happens to carry an
|
|
276
|
+
// `agent` field can never override the injected subject.
|
|
277
|
+
{ ...payload, agent }
|
|
278
|
+
);
|
|
279
|
+
return {
|
|
280
|
+
emit(name, payload) {
|
|
281
|
+
const args = [carrier, name, fused(payload)];
|
|
282
|
+
const callbacks = ctx.events.dispatch("emit", args);
|
|
283
|
+
for (const callback of callbacks) {
|
|
284
|
+
try {
|
|
285
|
+
const returned = callback(...args);
|
|
286
|
+
void Promise.resolve(returned).catch((error) => {
|
|
287
|
+
ctx.logger.warn(`agent event "${name}" listener rejected: ${String(error)}`);
|
|
288
|
+
});
|
|
289
|
+
} catch (error) {
|
|
290
|
+
ctx.logger.warn(`agent event "${name}" listener threw: ${String(error)}`);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
async serial(name, payload) {
|
|
295
|
+
const serial = ctx.serial;
|
|
296
|
+
return await serial(carrier, name, fused(payload));
|
|
297
|
+
},
|
|
298
|
+
waterfall(name, payload, ...rest) {
|
|
299
|
+
const waterfall = ctx.waterfall;
|
|
300
|
+
return waterfall(carrier, name, fused(payload), ...rest);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
function emitAgentEvent(ctx, agent, name, payload) {
|
|
305
|
+
agentEvents(ctx, agent).emit(name, payload);
|
|
306
|
+
}
|
|
307
|
+
function assembleContextFor(agent, signal) {
|
|
308
|
+
return { agent, scope: agent, ...signal === void 0 ? {} : { signal } };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// packages/core/agent/src/index.ts
|
|
312
|
+
var NO_FACTORY_MESSAGE = "no agent factory registered (load an agent-loop plugin)";
|
|
313
|
+
var NO_INITIATOR_MESSAGE = "no initiating agent is active";
|
|
314
|
+
var DISPOSED_INITIATOR_MESSAGE = "agent initiator scope is disposed";
|
|
315
|
+
var AgentRegistry = class extends Service {
|
|
316
|
+
store = /* @__PURE__ */ new Map();
|
|
317
|
+
factory;
|
|
318
|
+
initiators = new AsyncLocalStorage();
|
|
319
|
+
initiatorRuns = new AsyncLocalStorage();
|
|
320
|
+
initiatorState = "active";
|
|
321
|
+
activeInitiatorRuns = 0;
|
|
322
|
+
initiatorDrain;
|
|
323
|
+
initiatorDisposal;
|
|
324
|
+
constructor(ctx) {
|
|
325
|
+
super(ctx, "agents");
|
|
326
|
+
ctx.inject(["typert"], (typeCtx) => {
|
|
327
|
+
typeCtx.typert.lookups.register("agent", {
|
|
328
|
+
parameter: "agent",
|
|
329
|
+
wire: "agentId",
|
|
330
|
+
hostTypeSymbol: "@deepseek-ai/dsh-agent#Agent",
|
|
331
|
+
wireTypeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
|
|
332
|
+
resolve: (sessionId) => this.get(sessionId)
|
|
333
|
+
});
|
|
334
|
+
typeCtx.typert.contexts.registerHost("agent", {
|
|
335
|
+
wire: "agentId",
|
|
336
|
+
wireTypeSymbol: "@deepseek-ai/dsh-session/types#SessionId",
|
|
337
|
+
resolve: (sessionId) => this.get(sessionId)?.ctx
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
ctx.accessor("agent", { get: () => void 0 });
|
|
341
|
+
ctx.on("internal/status", (fiber) => {
|
|
342
|
+
if (fiber.state === FiberState.UNLOADING && this.hasLifecycleAncestor(fiber)) {
|
|
343
|
+
this.closeInitiators();
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
ctx.effect(function* () {
|
|
347
|
+
yield () => this.disposeInitiators();
|
|
348
|
+
yield () => {
|
|
349
|
+
this.closeInitiators();
|
|
350
|
+
};
|
|
351
|
+
}.bind(this), "agents.initiatorLifecycle()");
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Read the Agent that initiated the inherited asynchronous driver chain.
|
|
355
|
+
* Use this optional form for logging, tracing, metrics, or host attribution
|
|
356
|
+
* that also supports agentless calls. When a parent creates a child, setup
|
|
357
|
+
* reports the causal parent while `agentCtx.agent` identifies the child.
|
|
358
|
+
* @returns the inherited Agent, or `undefined` outside an initiator boundary
|
|
359
|
+
* and inside an explicit clearing boundary.
|
|
360
|
+
* @throws when this service instance has been disposed.
|
|
361
|
+
*/
|
|
362
|
+
currentInitiator() {
|
|
363
|
+
this.assertInitiatorsReadable();
|
|
364
|
+
return this.initiators.getStore();
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Read the initiating Agent and fail when no initiator boundary is active.
|
|
368
|
+
* Use this for private helpers contractually below a driver, or for a
|
|
369
|
+
* deployment-owned outbound request whose contract forbids agentless calls.
|
|
370
|
+
* Generic or direct-call paths use optional lookup or explicit request fields.
|
|
371
|
+
* @returns the inherited Agent.
|
|
372
|
+
* @throws when no initiator is active or this service instance has been disposed.
|
|
373
|
+
*/
|
|
374
|
+
requireInitiator() {
|
|
375
|
+
const agent = this.currentInitiator();
|
|
376
|
+
if (agent === void 0) throw new Error(NO_INITIATOR_MESSAGE);
|
|
377
|
+
return agent;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Run an operation with one exact Agent as its process-local initiator. The
|
|
381
|
+
* exact synchronous value or Promise returned by the operation is preserved.
|
|
382
|
+
* Custom drivers and test harnesses wrap their complete returned foreground
|
|
383
|
+
* lifetime.
|
|
384
|
+
* A queue or wire receiver may establish this boundary only after validating
|
|
385
|
+
* explicit identity and resolving the exact live Agent; this method does neither.
|
|
386
|
+
* Detached work remains owned by the subsystem that starts it.
|
|
387
|
+
* @param agent - initiating Agent to inherit; presence is neither liveness proof nor authorization.
|
|
388
|
+
* @param operation - synchronous or asynchronous operation to invoke.
|
|
389
|
+
* @returns the exact value returned by `operation`.
|
|
390
|
+
* @throws when the initiator scope is closing/disposed, or when `operation` throws.
|
|
391
|
+
*/
|
|
392
|
+
withInitiator(agent, operation) {
|
|
393
|
+
return this.runWithInitiator(agent, operation);
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Run an operation inside a boundary that hides any inherited initiating
|
|
397
|
+
* Agent. The exact synchronous value or Promise is preserved.
|
|
398
|
+
* Use this while creating lazy shared timers, queue pumps, pool maintenance,
|
|
399
|
+
* watchers, or exporters so they do not inherit the first Agent that happens
|
|
400
|
+
* to initialize them. It clears only initiator attribution, not explicit
|
|
401
|
+
* fields, and does not own or drain detached resources.
|
|
402
|
+
* @param operation - synchronous or asynchronous operation to invoke without an initiator.
|
|
403
|
+
* @returns the exact value returned by `operation`.
|
|
404
|
+
* @throws when the initiator scope is closing/disposed, or when `operation` throws.
|
|
405
|
+
*/
|
|
406
|
+
withoutInitiator(operation) {
|
|
407
|
+
return this.runWithInitiator(void 0, operation);
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Register the agent-creation factory (the loop calls this on construction,
|
|
411
|
+
* effect-scoped). A traced Cordis service is canonicalized to its concrete
|
|
412
|
+
* target; each create/resume call is then traced through that caller's
|
|
413
|
+
* context so ownership follows the caller without stacking proxy layers.
|
|
414
|
+
* Throws if a factory is already registered. Returns the disposer; on
|
|
415
|
+
* dispose the factory slot is cleared.
|
|
416
|
+
* @param factory - the loop-owned factory {@link create}/{@link resume} delegate to.
|
|
417
|
+
* @returns the disposer that clears the factory slot. The exact
|
|
418
|
+
* Cordis effect disposer (single-shot): composite (generator) effects may
|
|
419
|
+
* yield it directly — exact identity nests the teardown in order.
|
|
420
|
+
*/
|
|
421
|
+
setFactory(factory) {
|
|
422
|
+
const dispose = this.ctx.effect(() => {
|
|
423
|
+
if (this.factory !== void 0) throw new Error("an agent factory is already registered");
|
|
424
|
+
const target = factory[symbols.original] ?? factory;
|
|
425
|
+
this.factory = { target };
|
|
426
|
+
return () => {
|
|
427
|
+
this.factory = void 0;
|
|
428
|
+
};
|
|
429
|
+
}, "agents.setFactory()");
|
|
430
|
+
return dispose;
|
|
431
|
+
}
|
|
432
|
+
/** Return the active creation factory. */
|
|
433
|
+
requireFactory() {
|
|
434
|
+
if (this.factory === void 0) throw new Error(NO_FACTORY_MESSAGE);
|
|
435
|
+
return this.factory;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Create and publish a new agent through the registered factory.
|
|
439
|
+
* Distinct from {@link register} (which records an already-constructed
|
|
440
|
+
* agent): this constructs the agent and its session. Rejects if no factory is
|
|
441
|
+
* registered or creation/setup fails. The resolved {@link AgentHandle} lets
|
|
442
|
+
* the owner tear down exactly this agent.
|
|
443
|
+
* @param options - shared identity, session seed/metadata, and agent options.
|
|
444
|
+
* @returns the handle after setup, rollback-covered publication, and loop start complete.
|
|
445
|
+
*/
|
|
446
|
+
async create(options) {
|
|
447
|
+
const ownerCtx = this.ctx;
|
|
448
|
+
const { target } = this.requireFactory();
|
|
449
|
+
const receiver = getTraceable(ownerCtx, target);
|
|
450
|
+
return Reflect.apply(target.createAgent, receiver, [ownerCtx, options]);
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Load a persisted session and resume an agent on it through the registered
|
|
454
|
+
* factory. Rejects if no factory is registered; the factory rejects if
|
|
455
|
+
* session persistence is not configured or persistence/setup fails.
|
|
456
|
+
* @param options - persisted identity, configuration, and optional setup.
|
|
457
|
+
* @returns the handle after setup, rollback-covered publication, and loop start complete.
|
|
458
|
+
*/
|
|
459
|
+
async resume(options) {
|
|
460
|
+
const ownerCtx = this.ctx;
|
|
461
|
+
const { target } = this.requireFactory();
|
|
462
|
+
const receiver = getTraceable(ownerCtx, target);
|
|
463
|
+
return Reflect.apply(target.resume, receiver, [ownerCtx, options]);
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Register a live agent. Throws if an agent with the same id is already
|
|
467
|
+
* registered. Emits `agent/created` on registration and `agent/disposed`
|
|
468
|
+
* when the calling fiber is disposed — both with the agent's scope carrier
|
|
469
|
+
* (`scopeTarget(agent, agent)`): the subject is the agent in hand, so the
|
|
470
|
+
* emits are scope-filtered regardless of which context invoked `register`
|
|
471
|
+
* (calling through `agent.ctx` scopes EFFECTS; dispatch scoping always
|
|
472
|
+
* requires passing the carrier). Returns the disposer.
|
|
473
|
+
* @param agent - the already-constructed agent to record in the store.
|
|
474
|
+
* @returns the EXACT Cordis effect disposer (single-shot; a repeat call
|
|
475
|
+
* returns undefined without awaiting an in-flight teardown). Exact
|
|
476
|
+
* identity is load-bearing: a composite (generator) effect that owns a
|
|
477
|
+
* teardown ORDER — the agent factory's lifecycle chain — must yield THIS
|
|
478
|
+
* function so Cordis nests the unregistration at that yield position;
|
|
479
|
+
* yielding a wrapper would leave it disposing as a concurrent sibling on
|
|
480
|
+
* owner unload, unregistering the agent (and emitting `agent/disposed`)
|
|
481
|
+
* while its final turn is still draining.
|
|
482
|
+
*/
|
|
483
|
+
register(agent) {
|
|
484
|
+
const dispose = this.ctx.effect(function* () {
|
|
485
|
+
yield this.enter(agent, this.ctx.agent);
|
|
486
|
+
this.announce(agent);
|
|
487
|
+
}.bind(this), "agents.register()");
|
|
488
|
+
return dispose;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Insert an already-constructed agent without announcing it. This is the
|
|
492
|
+
* advanced ordered-lifecycle primitive used by the async agent factory: it
|
|
493
|
+
* first completes setup while the agent is unpublished, then assigns the
|
|
494
|
+
* returned detach closure into its pre-installed composite teardown before
|
|
495
|
+
* calling {@link announce}. Ordinary callers use {@link register}.
|
|
496
|
+
* @param agent - the prepared, unpublished agent.
|
|
497
|
+
* @param owner - live agent whose scoped context created this agent, or
|
|
498
|
+
* undefined for a top-level runtime root. This is runtime ownership, not
|
|
499
|
+
* the resumed session's durable parent lineage.
|
|
500
|
+
* @returns an idempotent closure that removes this exact entry and emits
|
|
501
|
+
* `agent/disposed` with listener failures contained. When called from a
|
|
502
|
+
* synchronous `agent/created` listener, removal and disposal wait until
|
|
503
|
+
* that creation dispatch unwinds.
|
|
504
|
+
*/
|
|
505
|
+
enter(agent, owner) {
|
|
506
|
+
const id = agent.id;
|
|
507
|
+
if (id !== agent.session.id) {
|
|
508
|
+
throw new Error(`agent id "${id}" does not match session id "${agent.session.id}"`);
|
|
509
|
+
}
|
|
510
|
+
const carrier = scopeTarget2(agent, agent);
|
|
511
|
+
if (this.store.has(id)) throw new Error(`agent "${id}" is already registered`);
|
|
512
|
+
const entry = {
|
|
513
|
+
id,
|
|
514
|
+
agent,
|
|
515
|
+
owner,
|
|
516
|
+
carrier,
|
|
517
|
+
announced: false,
|
|
518
|
+
announcing: false,
|
|
519
|
+
detachRequested: false
|
|
520
|
+
};
|
|
521
|
+
this.store.set(id, entry);
|
|
522
|
+
let entered = true;
|
|
523
|
+
const detach = () => {
|
|
524
|
+
if (!entered) return;
|
|
525
|
+
entered = false;
|
|
526
|
+
if (entry.announcing) {
|
|
527
|
+
entry.detachRequested = true;
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
this.detachEntered(entry);
|
|
531
|
+
};
|
|
532
|
+
return detach;
|
|
533
|
+
}
|
|
534
|
+
/** Remove one exact entered agent and emit its paired disposal when announced. */
|
|
535
|
+
detachEntered(entry) {
|
|
536
|
+
entry.detachRequested = false;
|
|
537
|
+
if (this.store.get(entry.id) !== entry) return;
|
|
538
|
+
this.store.delete(entry.id);
|
|
539
|
+
if (!entry.announced) return;
|
|
540
|
+
this.emitDisposed(entry);
|
|
541
|
+
}
|
|
542
|
+
/** Emit the paired disposal edge through the entry's stable carrier. */
|
|
543
|
+
emitDisposed(entry) {
|
|
544
|
+
const args = [entry.carrier, "agent/disposed", { agent: entry.agent }];
|
|
545
|
+
for (const callback of this.ctx.events.dispatch("emit", args)) {
|
|
546
|
+
try {
|
|
547
|
+
const returned = callback(...args);
|
|
548
|
+
void Promise.resolve(returned).catch((error) => {
|
|
549
|
+
this.ctx.logger.warn(`agent "${entry.id}": agent/disposed listener rejected: ${String(error)}`);
|
|
550
|
+
});
|
|
551
|
+
} catch (error) {
|
|
552
|
+
this.ctx.logger.warn(`agent "${entry.id}": agent/disposed listener threw: ${String(error)}`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
/**
|
|
557
|
+
* Announce an agent previously inserted with {@link enter}.
|
|
558
|
+
* @param agent - the live inserted agent to announce.
|
|
559
|
+
* @throws if `agent` is not the exact live registry entry for its id, or its
|
|
560
|
+
* creation announcement already began (including a reentrant call from a
|
|
561
|
+
* creation listener).
|
|
562
|
+
*/
|
|
563
|
+
announce(agent) {
|
|
564
|
+
const entry = this.store.get(agent.id);
|
|
565
|
+
if (entry === void 0 || entry.agent !== agent) {
|
|
566
|
+
throw new Error(`agent "${agent.id}" is not live in this registry`);
|
|
567
|
+
}
|
|
568
|
+
if (entry.announced || entry.announcing) {
|
|
569
|
+
throw new Error(`agent "${entry.id}" was already announced`);
|
|
570
|
+
}
|
|
571
|
+
entry.announcing = true;
|
|
572
|
+
entry.announced = true;
|
|
573
|
+
const args = [entry.carrier, "agent/created", { agent: entry.agent }];
|
|
574
|
+
try {
|
|
575
|
+
for (const callback of this.ctx.events.dispatch("emit", args)) {
|
|
576
|
+
const returned = callback(...args);
|
|
577
|
+
void Promise.resolve(returned).catch((error) => {
|
|
578
|
+
this.ctx.logger.warn(`agent "${entry.id}": agent/created listener rejected: ${String(error)}`);
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
} finally {
|
|
582
|
+
entry.announcing = false;
|
|
583
|
+
if (entry.detachRequested) this.detachEntered(entry);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Look up a live agent.
|
|
588
|
+
* @param id - the shared agent/session id to look up.
|
|
589
|
+
* @returns the agent, or undefined when no live agent has that id.
|
|
590
|
+
*/
|
|
591
|
+
get(id) {
|
|
592
|
+
return this.store.get(id)?.agent;
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Test whether a live agent was created through one exact parent agent's
|
|
596
|
+
* scoped context. Runtime ownership is independent of durable session
|
|
597
|
+
* lineage and remains unambiguous when unrelated providers reuse an id.
|
|
598
|
+
* @param id - the candidate child agent's shared agent/session id.
|
|
599
|
+
* @param owner - the expected runtime creator agent.
|
|
600
|
+
* @returns true only while the exact child entry is live under that owner.
|
|
601
|
+
*/
|
|
602
|
+
isOwnedBy(id, owner) {
|
|
603
|
+
return this.store.get(id)?.owner === owner;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* All live agents, in registration order.
|
|
607
|
+
* @returns a fresh array; mutating it does not affect the registry.
|
|
608
|
+
*/
|
|
609
|
+
list() {
|
|
610
|
+
return [...this.store.values()].map((entry) => entry.agent);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* All live top-level agents in registration order. A top-level agent was
|
|
614
|
+
* created without an owning agent context; durable session lineage does not
|
|
615
|
+
* affect this runtime relation, so a resumed fork may still be a root.
|
|
616
|
+
* @returns a fresh array; mutating it does not affect the registry.
|
|
617
|
+
*/
|
|
618
|
+
roots() {
|
|
619
|
+
return [...this.store.values()].filter((entry) => entry.owner === void 0).map((entry) => entry.agent);
|
|
620
|
+
}
|
|
621
|
+
/** Reject new initiator boundaries while inherited continuations drain. */
|
|
622
|
+
closeInitiators() {
|
|
623
|
+
if (this.initiatorState === "active") this.initiatorState = "closing";
|
|
624
|
+
}
|
|
625
|
+
/** Wait for returned-Promise boundaries, then invalidate retained references. */
|
|
626
|
+
disposeInitiators() {
|
|
627
|
+
return this.initiatorDisposal ??= (async () => {
|
|
628
|
+
this.closeInitiators();
|
|
629
|
+
this.releaseReentrantInitiatorRuns();
|
|
630
|
+
if (this.activeInitiatorRuns !== 0) {
|
|
631
|
+
this.initiatorDrain ??= Promise.withResolvers();
|
|
632
|
+
await this.initiatorDrain.promise;
|
|
633
|
+
}
|
|
634
|
+
this.initiatorState = "disposed";
|
|
635
|
+
this.initiators.disable();
|
|
636
|
+
this.initiatorRuns.disable();
|
|
637
|
+
})();
|
|
638
|
+
}
|
|
639
|
+
/** Establish one tracked initiator or clearing boundary. */
|
|
640
|
+
runWithInitiator(agent, operation) {
|
|
641
|
+
if (this.initiatorState !== "active") throw new Error(DISPOSED_INITIATOR_MESSAGE);
|
|
642
|
+
const run = {
|
|
643
|
+
active: true,
|
|
644
|
+
parent: this.initiatorRuns.getStore()
|
|
645
|
+
};
|
|
646
|
+
this.activeInitiatorRuns += 1;
|
|
647
|
+
let result;
|
|
648
|
+
try {
|
|
649
|
+
result = this.initiatorRuns.run(run, () => this.initiators.run(agent, operation));
|
|
650
|
+
} catch (error) {
|
|
651
|
+
this.releaseInitiatorRun(run);
|
|
652
|
+
throw error;
|
|
653
|
+
}
|
|
654
|
+
if (isPromise(result)) {
|
|
655
|
+
try {
|
|
656
|
+
void Promise.prototype.then.call(
|
|
657
|
+
result,
|
|
658
|
+
() => {
|
|
659
|
+
this.releaseInitiatorRun(run);
|
|
660
|
+
},
|
|
661
|
+
() => {
|
|
662
|
+
this.releaseInitiatorRun(run);
|
|
663
|
+
}
|
|
664
|
+
);
|
|
665
|
+
} catch {
|
|
666
|
+
this.releaseInitiatorRun(run);
|
|
667
|
+
}
|
|
668
|
+
} else {
|
|
669
|
+
this.releaseInitiatorRun(run);
|
|
670
|
+
}
|
|
671
|
+
return result;
|
|
672
|
+
}
|
|
673
|
+
/** Whether one unloading fiber owns this service's lifecycle. */
|
|
674
|
+
hasLifecycleAncestor(candidate) {
|
|
675
|
+
let fiber = this.ctx.fiber;
|
|
676
|
+
while (true) {
|
|
677
|
+
if (fiber === candidate) return true;
|
|
678
|
+
const parent = fiber.parent.fiber;
|
|
679
|
+
if (parent === fiber) return false;
|
|
680
|
+
fiber = parent;
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
assertInitiatorsReadable() {
|
|
684
|
+
if (this.initiatorState === "disposed") throw new Error(DISPOSED_INITIATOR_MESSAGE);
|
|
685
|
+
}
|
|
686
|
+
/** Exclude the boundary chain that initiated this teardown from its own drain. */
|
|
687
|
+
releaseReentrantInitiatorRuns() {
|
|
688
|
+
let run = this.initiatorRuns.getStore();
|
|
689
|
+
while (run !== void 0) {
|
|
690
|
+
this.releaseInitiatorRun(run);
|
|
691
|
+
run = run.parent;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
releaseInitiatorRun(run) {
|
|
695
|
+
if (!run.active) return;
|
|
696
|
+
run.active = false;
|
|
697
|
+
this.activeInitiatorRuns -= 1;
|
|
698
|
+
if (this.activeInitiatorRuns !== 0) return;
|
|
699
|
+
this.initiatorDrain?.resolve();
|
|
700
|
+
this.initiatorDrain = void 0;
|
|
701
|
+
}
|
|
702
|
+
};
|
|
703
|
+
var index_default = AgentRegistry;
|
|
704
|
+
export {
|
|
705
|
+
AgentRegistry,
|
|
706
|
+
Inbox,
|
|
707
|
+
agentCarrier,
|
|
708
|
+
agentEvents,
|
|
709
|
+
assembleContextFor,
|
|
710
|
+
index_default as default,
|
|
711
|
+
emitAgentEvent,
|
|
712
|
+
foldConsumedWork,
|
|
713
|
+
installModelSelection
|
|
714
|
+
};
|