@deepseek-ai/dsh-subagent 0.1.1-rc.2 → 0.1.2-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/README.i18n.yaml +2 -2
- package/README.md +105 -75
- package/README.zh.md +110 -84
- package/lib/index.js +910 -433
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +1016 -0
- package/lib/typert.remote-client.d.ts +27 -0
- package/lib/typert.remote-client.js +242 -0
- package/lib/types/child-agent.d.ts +14 -3
- package/lib/types/child-agent.js +48 -10
- package/lib/types/client.d.ts +2 -1
- package/lib/types/client.js +1 -1
- package/lib/types/continuation.d.ts +4 -2
- package/lib/types/continuation.js +124 -46
- package/lib/types/control-types.d.ts +160 -0
- package/lib/types/control-types.js +9 -0
- package/lib/types/control.d.ts +83 -0
- package/lib/types/control.js +133 -0
- package/lib/types/descriptor.d.ts +6 -1
- package/lib/types/descriptor.js +6 -2
- package/lib/types/index.d.ts +60 -26
- package/lib/types/index.js +430 -299
- package/lib/types/list-children.d.ts +10 -58
- package/lib/types/list-children.js +161 -97
- package/lib/types/out-of-process.d.ts +3 -2
- package/lib/types/out-of-process.js +11 -3
- package/lib/types/run-settlement.js +7 -3
- package/lib/types/types.d.ts +18 -0
- package/package.json +63 -37
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Read-only enumeration of durable subagent children and descendant trees
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* through the Session query service. Candidates come from one live-preferred
|
|
4
|
+
* corpus; each child's mode/label is the registered `subagent` projection
|
|
5
|
+
* unit's value, resolved
|
|
6
6
|
* down a three-rung ladder: the registry's watermark cache for a live child,
|
|
7
7
|
* a durable projection-cache row when it serves an own-suffix identity (the
|
|
8
|
-
* seq gate), and one
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* seq gate), and one shared Session observation otherwise, validated against
|
|
9
|
+
* the enumerated lifecycle. The projection fold is the single classification
|
|
10
|
+
* authority — this module parses no descriptor
|
|
11
11
|
* itself. Absent persistence, enumeration is live-only: a cold child is
|
|
12
12
|
* unreachable for resume anyway, so its absence is capability absence, not an
|
|
13
13
|
* error. The module owns no catalog state and does not consult Activation,
|
|
@@ -17,55 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { Context } from '@deepseek-ai/cordis';
|
|
19
19
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* with ties broken on id. Only a candidate whose durable header has
|
|
23
|
-
* `origin: 'subagent'` is interpreted. A served `subagent` projection value
|
|
24
|
-
* produces a `child`; a settled candidate whose fold served no identity
|
|
25
|
-
* produces a `diagnostic`; a running candidate without one is omitted — its
|
|
26
|
-
* descriptor may not be appended yet (the creation window). Diagnostics
|
|
27
|
-
* relay the projection fold's outcome or a failed read, never a per-child
|
|
28
|
-
* event scan, and never expose model-hidden descriptor content.
|
|
29
|
-
*/
|
|
30
|
-
export type SubagentListEntry = {
|
|
31
|
-
readonly kind: 'child';
|
|
32
|
-
/** The durable child session id, stable across Activations. */
|
|
33
|
-
readonly id: SessionId;
|
|
34
|
-
/**
|
|
35
|
-
* Store snapshot activity: `running` means the logical record is live in
|
|
36
|
-
* `ctx.sessions`; `inactive` means it exists only in persistence. Neither
|
|
37
|
-
* encodes a durable outcome, and a continuable child may still reject
|
|
38
|
-
* delivery as an ownership conflict.
|
|
39
|
-
*/
|
|
40
|
-
readonly activity: 'running' | 'inactive';
|
|
41
|
-
/** Whether a direct descendant has durable `origin: 'subagent'`. */
|
|
42
|
-
readonly hasChildren: boolean;
|
|
43
|
-
} & ({
|
|
44
|
-
/** A terminal one-shot child. */
|
|
45
|
-
readonly mode: 'one-shot';
|
|
46
|
-
/** Optional durable creation label from the child's descriptor. */
|
|
47
|
-
readonly label?: string;
|
|
48
|
-
} | {
|
|
49
|
-
/** A resumable conversation. */
|
|
50
|
-
readonly mode: 'continuable';
|
|
51
|
-
/** Durable creation label from the child's descriptor. */
|
|
52
|
-
readonly label: string;
|
|
53
|
-
}) | {
|
|
54
|
-
readonly kind: 'diagnostic';
|
|
55
|
-
/** The candidate's session id. */
|
|
56
|
-
readonly id: SessionId;
|
|
57
|
-
/**
|
|
58
|
-
* Why the candidate has no `child` row: `corrupt` for a settled candidate
|
|
59
|
-
* whose projection fold served no identity (a missing, malformed, or
|
|
60
|
-
* unrecognized-version descriptor — deliberately undistinguished), and
|
|
61
|
-
* for any candidate whose log makes a registered unit's fold or schema
|
|
62
|
-
* throw (deterministic data damage, contained per child); `unavailable`
|
|
63
|
-
* when the candidate's persistence inspection failed (retried on the
|
|
64
|
-
* next listing). `unsupported` is never produced; it remains in the
|
|
65
|
-
* union for consumers that route on it.
|
|
66
|
-
*/
|
|
67
|
-
readonly reason: 'corrupt' | 'unsupported' | 'unavailable';
|
|
68
|
-
};
|
|
20
|
+
import type { SubagentListEntry } from './control-types.ts';
|
|
21
|
+
export type { SubagentListEntry } from './control-types.ts';
|
|
69
22
|
/**
|
|
70
23
|
* One entry of a descendant listing: the interpreted subagent facts plus its
|
|
71
24
|
* position in the complete session tree. `parentId` is the durable direct
|
|
@@ -82,9 +35,8 @@ export type SubagentDescendantListEntry = SubagentListEntry & {
|
|
|
82
35
|
* live-preferred merge of `ctx.sessions` and optional session persistence,
|
|
83
36
|
* serving each identity from the `subagent` projection unit: the registry's
|
|
84
37
|
* watermark snapshot for a live child; for a cold one, a durable
|
|
85
|
-
* projection-cache
|
|
86
|
-
* else one bounded-concurrency
|
|
87
|
-
* registry.
|
|
38
|
+
* projection-cache read when it serves an own-suffix identity (the seq gate),
|
|
39
|
+
* else one bounded-concurrency shared Session observation.
|
|
88
40
|
* @see SubagentRuntime.listChildren for the public cancellation and failure contract.
|
|
89
41
|
* @param ctx - context carrying the session store, the projection registry,
|
|
90
42
|
* optional persistence, and the optional projection cache.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Read-only enumeration of durable subagent children and descendant trees
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* through the Session query service. Candidates come from one live-preferred
|
|
4
|
+
* corpus; each child's mode/label is the registered `subagent` projection
|
|
5
|
+
* unit's value, resolved
|
|
6
6
|
* down a three-rung ladder: the registry's watermark cache for a live child,
|
|
7
7
|
* a durable projection-cache row when it serves an own-suffix identity (the
|
|
8
|
-
* seq gate), and one
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* seq gate), and one shared Session observation otherwise, validated against
|
|
9
|
+
* the enumerated lifecycle. The projection fold is the single classification
|
|
10
|
+
* authority — this module parses no descriptor
|
|
11
11
|
* itself. Absent persistence, enumeration is live-only: a cold child is
|
|
12
12
|
* unreachable for resume anyway, so its absence is capability absence, not an
|
|
13
13
|
* error. The module owns no catalog state and does not consult Activation,
|
|
@@ -15,11 +15,63 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @module @deepseek-ai/dsh-subagent
|
|
17
17
|
*/
|
|
18
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
19
|
+
if (value !== null && value !== void 0) {
|
|
20
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
21
|
+
var dispose, inner;
|
|
22
|
+
if (async) {
|
|
23
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
24
|
+
dispose = value[Symbol.asyncDispose];
|
|
25
|
+
}
|
|
26
|
+
if (dispose === void 0) {
|
|
27
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
28
|
+
dispose = value[Symbol.dispose];
|
|
29
|
+
if (async) inner = dispose;
|
|
30
|
+
}
|
|
31
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
32
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
33
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
34
|
+
}
|
|
35
|
+
else if (async) {
|
|
36
|
+
env.stack.push({ async: true });
|
|
37
|
+
}
|
|
38
|
+
return value;
|
|
39
|
+
};
|
|
40
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
41
|
+
return function (env) {
|
|
42
|
+
function fail(e) {
|
|
43
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
44
|
+
env.hasError = true;
|
|
45
|
+
}
|
|
46
|
+
var r, s = 0;
|
|
47
|
+
function next() {
|
|
48
|
+
while (r = env.stack.pop()) {
|
|
49
|
+
try {
|
|
50
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
51
|
+
if (r.dispose) {
|
|
52
|
+
var result = r.dispose.call(r.value);
|
|
53
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
54
|
+
}
|
|
55
|
+
else s |= 1;
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
fail(e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
62
|
+
if (env.hasError) throw env.error;
|
|
63
|
+
}
|
|
64
|
+
return next();
|
|
65
|
+
};
|
|
66
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
67
|
+
var e = new Error(message);
|
|
68
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
69
|
+
});
|
|
18
70
|
import { SubagentError } from "./error.js";
|
|
19
71
|
/**
|
|
20
|
-
* Concurrent cold
|
|
21
|
-
*
|
|
22
|
-
*
|
|
72
|
+
* Concurrent cold observations per explicit catalog listing. Current Session
|
|
73
|
+
* persistence providers are local; a networked provider must promote this to
|
|
74
|
+
* a validated deployment setting.
|
|
23
75
|
*/
|
|
24
76
|
const COLD_READ_CONCURRENCY = 4;
|
|
25
77
|
/**
|
|
@@ -27,9 +79,8 @@ const COLD_READ_CONCURRENCY = 4;
|
|
|
27
79
|
* live-preferred merge of `ctx.sessions` and optional session persistence,
|
|
28
80
|
* serving each identity from the `subagent` projection unit: the registry's
|
|
29
81
|
* watermark snapshot for a live child; for a cold one, a durable
|
|
30
|
-
* projection-cache
|
|
31
|
-
* else one bounded-concurrency
|
|
32
|
-
* registry.
|
|
82
|
+
* projection-cache read when it serves an own-suffix identity (the seq gate),
|
|
83
|
+
* else one bounded-concurrency shared Session observation.
|
|
33
84
|
* @see SubagentRuntime.listChildren for the public cancellation and failure contract.
|
|
34
85
|
* @param ctx - context carrying the session store, the projection registry,
|
|
35
86
|
* optional persistence, and the optional projection cache.
|
|
@@ -91,31 +142,32 @@ async function prepareListing(ctx, signal) {
|
|
|
91
142
|
throw new SubagentError('listing subagents requires the session store (load @deepseek-ai/dsh-session)', 'SUBAGENT_CONTROL_SESSION_STORE_UNAVAILABLE');
|
|
92
143
|
}
|
|
93
144
|
assertListingNotCancelled(signal);
|
|
94
|
-
const
|
|
145
|
+
const query = ctx.get('sessionQuery');
|
|
146
|
+
if (query === undefined) {
|
|
147
|
+
throw new SubagentError('listing subagents requires the sessionQuery service (load @deepseek-ai/dsh-session-query)', 'SUBAGENT_CONTROL_QUERY_UNAVAILABLE');
|
|
148
|
+
}
|
|
95
149
|
// Optional acceleration only: an absent cache service just means every
|
|
96
150
|
// cold candidate takes the authoritative preparation rung, so it carries
|
|
97
151
|
// no error code and no configuration check.
|
|
98
152
|
const cache = ctx.get('sessionProjectionCache');
|
|
99
|
-
let
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
catch (error) {
|
|
105
|
-
// The backend may reject with its own abort failure after observing the
|
|
106
|
-
// forwarded signal; cancellation stays a stable subagent failure.
|
|
107
|
-
assertListingNotCancelled(signal);
|
|
108
|
-
throw error;
|
|
109
|
-
}
|
|
153
|
+
let records;
|
|
154
|
+
try {
|
|
155
|
+
records = await query.listSessions(signal);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
110
158
|
assertListingNotCancelled(signal);
|
|
159
|
+
throw error;
|
|
111
160
|
}
|
|
161
|
+
assertListingNotCancelled(signal);
|
|
112
162
|
// Live-preferred merge without header reconciliation: a live record wins
|
|
113
163
|
// its id wholesale, exactly as a live-preferred corpus would serve it.
|
|
114
164
|
const corpus = new Map();
|
|
115
|
-
for (const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
165
|
+
for (const record of records) {
|
|
166
|
+
const live = sessions.get(record.header.id);
|
|
167
|
+
corpus.set(record.header.id, {
|
|
168
|
+
header: live?.header ?? record.header,
|
|
169
|
+
live,
|
|
170
|
+
});
|
|
119
171
|
}
|
|
120
172
|
const subagentParents = new Set();
|
|
121
173
|
for (const record of corpus.values()) {
|
|
@@ -123,11 +175,11 @@ async function prepareListing(ctx, signal) {
|
|
|
123
175
|
subagentParents.add(record.header.parentSession);
|
|
124
176
|
}
|
|
125
177
|
}
|
|
126
|
-
return { projections,
|
|
178
|
+
return { projections, query, cache, corpus, subagentParents };
|
|
127
179
|
}
|
|
128
180
|
/** Resolve projection-backed rows for aligned candidates with bounded cold reads. */
|
|
129
181
|
async function resolveCandidateRows(candidates, listing, signal) {
|
|
130
|
-
const { projections,
|
|
182
|
+
const { projections, query, cache, subagentParents } = listing;
|
|
131
183
|
const rows = Array.from({ length: candidates.length });
|
|
132
184
|
const coldReads = [];
|
|
133
185
|
candidates.forEach((candidate, index) => {
|
|
@@ -136,34 +188,31 @@ async function resolveCandidateRows(candidates, listing, signal) {
|
|
|
136
188
|
coldReads.push({ index, header: candidate.header });
|
|
137
189
|
return;
|
|
138
190
|
}
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
// before the establishing provider appends its descriptor.
|
|
191
|
+
// Read only the identity unit. A live child without an identity yet is the
|
|
192
|
+
// creation window before the establishing provider appends its descriptor.
|
|
142
193
|
let identity;
|
|
143
194
|
try {
|
|
144
|
-
identity = projections.snapshot(candidate.live).values.subagent;
|
|
195
|
+
identity = projections.snapshot(candidate.live, ['subagent']).values.subagent;
|
|
145
196
|
}
|
|
146
197
|
catch {
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
// deterministic data damage in this one child; it degrades to one
|
|
150
|
-
// corrupt diagnostic instead of failing the whole listing.
|
|
198
|
+
// A rejecting identity fold is deterministic data damage in this child;
|
|
199
|
+
// contain it as one diagnostic instead of failing the whole listing.
|
|
151
200
|
rows[index] = { kind: 'diagnostic', id: childId, reason: 'corrupt' };
|
|
152
201
|
return;
|
|
153
202
|
}
|
|
154
203
|
// The unit's serializable no-value sentinel is `null`; `undefined` can
|
|
155
204
|
// only mean the key was dropped at a JSON boundary. Both are no value.
|
|
156
|
-
if (identity === undefined || identity === null
|
|
205
|
+
if (identity === undefined || identity === null
|
|
206
|
+
|| identity.seq < (candidate.header.seedLength ?? 0))
|
|
157
207
|
return;
|
|
158
208
|
rows[index] = childRow(childId, identity, 'running', subagentParents.has(childId));
|
|
159
209
|
});
|
|
160
|
-
// Cold candidates
|
|
161
|
-
|
|
162
|
-
if (persistence !== undefined && coldReads.length > 0) {
|
|
210
|
+
// Cold candidates came from the query corpus and are resolved concurrently.
|
|
211
|
+
if (coldReads.length > 0) {
|
|
163
212
|
const queue = [...coldReads];
|
|
164
213
|
await Promise.all(Array.from({ length: Math.min(COLD_READ_CONCURRENCY, queue.length) }, async () => {
|
|
165
214
|
for (let job = queue.shift(); job !== undefined; job = queue.shift()) {
|
|
166
|
-
rows[job.index] = await resolveColdIdentity(
|
|
215
|
+
rows[job.index] = await resolveColdIdentity(query, cache, job.header, subagentParents.has(job.header.id), signal);
|
|
167
216
|
}
|
|
168
217
|
}));
|
|
169
218
|
}
|
|
@@ -214,70 +263,81 @@ function compareCorpusRecords(a, b) {
|
|
|
214
263
|
/**
|
|
215
264
|
* Resolve one cold candidate down the remaining ladder: a durable
|
|
216
265
|
* projection-cache row when it serves an own-suffix identity (the seq gate),
|
|
217
|
-
* otherwise one
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* retried on the next listing; an inspection naming another lifecycle, and a
|
|
266
|
+
* otherwise one shared Session observation. An absent or transiently failed
|
|
267
|
+
* observation is one `unavailable` row retried on the next listing; an observation
|
|
268
|
+
* source naming another lifecycle, and a
|
|
221
269
|
* settled log the fold cannot identify — or that makes any registered unit
|
|
222
270
|
* throw — are final, so they report `corrupt`.
|
|
223
271
|
*/
|
|
224
|
-
async function resolveColdIdentity(
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
cached
|
|
272
|
+
async function resolveColdIdentity(query, cache, header, hasChildren, signal) {
|
|
273
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
274
|
+
try {
|
|
275
|
+
const childId = header.id;
|
|
276
|
+
if (cache !== undefined) {
|
|
277
|
+
let cached;
|
|
278
|
+
try {
|
|
279
|
+
cached = cache.cachedSnapshot(header, ['subagent'])?.values.subagent;
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
// Unlike the preparation fold below, a throwing cache read renders no
|
|
283
|
+
// verdict: the cache is derived data, so its damage (a poisoned stored
|
|
284
|
+
// row of ANY unit) silently falls through to the authoritative re-fold.
|
|
285
|
+
cached = undefined;
|
|
286
|
+
}
|
|
287
|
+
// A child's OWN descriptor is immutable once appended, so a cached
|
|
288
|
+
// identity is final only when the seq gate proves it was folded from the
|
|
289
|
+
// own suffix: a creation-window checkpoint may instead carry a fork
|
|
290
|
+
// seed's replayed ANCESTOR descriptor (seq below `seedLength`), which
|
|
291
|
+
// must not outrank the re-fold. Everything else also falls through to
|
|
292
|
+
// preparation: an absent key (a cut before any descriptor) and the
|
|
293
|
+
// `null` sentinel, whose verdict belongs to the authoritative re-fold,
|
|
294
|
+
// not to a derived row.
|
|
295
|
+
if (cached !== undefined && cached !== null && cached.seq >= (header.seedLength ?? 0)) {
|
|
296
|
+
return childRow(childId, cached, 'inactive', hasChildren);
|
|
297
|
+
}
|
|
230
298
|
}
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
299
|
+
assertListingNotCancelled(signal);
|
|
300
|
+
let observation;
|
|
301
|
+
try {
|
|
302
|
+
observation = await query.observeSession(childId, {
|
|
303
|
+
...(signal === undefined ? {} : { signal }),
|
|
304
|
+
});
|
|
236
305
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
306
|
+
catch (error) {
|
|
307
|
+
// Per-child isolation: durable corruption is stable; absence and backend
|
|
308
|
+
// failures remain retryable. Either way, the listing itself still succeeds.
|
|
309
|
+
assertListingNotCancelled(signal);
|
|
310
|
+
return {
|
|
311
|
+
kind: 'diagnostic',
|
|
312
|
+
id: childId,
|
|
313
|
+
reason: sessionQueryCode(error) === 'SESSION_QUERY_CORRUPT_SESSION'
|
|
314
|
+
|| sessionQueryCode(error) === 'SESSION_QUERY_SOURCE_CONFLICT'
|
|
315
|
+
? 'corrupt'
|
|
316
|
+
: 'unavailable',
|
|
317
|
+
};
|
|
247
318
|
}
|
|
248
|
-
|
|
249
|
-
assertListingNotCancelled(signal);
|
|
250
|
-
let inspected;
|
|
251
|
-
try {
|
|
252
|
-
inspected = await persistence.inspect(childId, signal);
|
|
253
|
-
}
|
|
254
|
-
catch {
|
|
255
|
-
// Per-child isolation: the child vanished or its backend read failed —
|
|
256
|
-
// one diagnostic row, and the listing itself still succeeds.
|
|
319
|
+
const ownedObservation = __addDisposableResource(env_1, observation, false);
|
|
257
320
|
assertListingNotCancelled(signal);
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
321
|
+
// A session id names a slot, not a lifecycle: a child deleted and
|
|
322
|
+
// re-published under another owner between the enumeration and this read
|
|
323
|
+
// must not leak into the old parent's listing.
|
|
324
|
+
if (!sameLifecycle(ownedObservation.header, header)) {
|
|
325
|
+
return { kind: 'diagnostic', id: childId, reason: 'corrupt' };
|
|
326
|
+
}
|
|
327
|
+
const identity = ownedObservation.projections?.values.subagent;
|
|
328
|
+
if (identity === undefined || identity === null
|
|
329
|
+
|| identity.seq < (header.seedLength ?? 0)) {
|
|
330
|
+
return { kind: 'diagnostic', id: childId, reason: 'corrupt' };
|
|
331
|
+
}
|
|
332
|
+
return childRow(childId, identity, 'inactive', hasChildren);
|
|
270
333
|
}
|
|
271
|
-
catch {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
// damage in this one child, contained as its own corrupt diagnostic.
|
|
275
|
-
return { kind: 'diagnostic', id: childId, reason: 'corrupt' };
|
|
334
|
+
catch (e_1) {
|
|
335
|
+
env_1.error = e_1;
|
|
336
|
+
env_1.hasError = true;
|
|
276
337
|
}
|
|
277
|
-
|
|
278
|
-
|
|
338
|
+
finally {
|
|
339
|
+
__disposeResources(env_1);
|
|
279
340
|
}
|
|
280
|
-
return childRow(childId, identity, 'inactive', hasChildren);
|
|
281
341
|
}
|
|
282
342
|
/** Materialize one served identity as its child row. */
|
|
283
343
|
function childRow(id, identity, activity, hasChildren) {
|
|
@@ -302,6 +362,7 @@ function childRow(id, identity, activity, hasChildren) {
|
|
|
302
362
|
/** Immutable header fields that distinguish one session lifecycle from another under the same id. */
|
|
303
363
|
const LIFECYCLE_WITNESS_KEYS = [
|
|
304
364
|
'version', 'id', 'createdAt', 'cwd', 'parentSession', 'seedLength', 'delegationDepth',
|
|
365
|
+
'origin', 'agentPreset',
|
|
305
366
|
];
|
|
306
367
|
/** Whether an inspected log still belongs to the enumerated lifecycle. */
|
|
307
368
|
function sameLifecycle(meta, expected) {
|
|
@@ -313,4 +374,7 @@ function assertListingNotCancelled(signal) {
|
|
|
313
374
|
throw new SubagentError('subagent listing was cancelled', 'CANCELLED');
|
|
314
375
|
}
|
|
315
376
|
}
|
|
377
|
+
function sessionQueryCode(error) {
|
|
378
|
+
return error instanceof Error && 'code' in error ? error.code : undefined;
|
|
379
|
+
}
|
|
316
380
|
//# sourceMappingURL=list-children.js.map
|
|
@@ -15,7 +15,7 @@ import type { SubagentCapabilities, SubagentResult, SubagentRun, SubagentStopRea
|
|
|
15
15
|
/**
|
|
16
16
|
* The capability advertisement of an out-of-process backend: NONE. A child in
|
|
17
17
|
* another process cannot honor parent-enforced start features
|
|
18
|
-
* (`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
|
|
18
|
+
* (`agentOptions`/`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
|
|
19
19
|
* request needing any of them before `start` runs — never accepted-then-ignored.
|
|
20
20
|
*/
|
|
21
21
|
export declare const NO_START_CAPABILITIES: SubagentCapabilities;
|
|
@@ -85,7 +85,8 @@ export interface RunResultSettlement {
|
|
|
85
85
|
* rejects after publication. A normally completed or rejected attempt resolves
|
|
86
86
|
* as `aborted` when cancellation already settled locally; another rejection is
|
|
87
87
|
* flattened to `stopReason: 'error'` through the contained diagnostic sink.
|
|
88
|
-
* The abort listener is
|
|
88
|
+
* Provider-returned diagnostics use the same byte limit. The abort listener is
|
|
89
|
+
* removed on every path.
|
|
89
90
|
* @param parts - the attempt, output snapshot, cancellation state, sink, and signal wiring.
|
|
90
91
|
* @returns the terminal result (never a rejection).
|
|
91
92
|
*/
|
|
@@ -34,13 +34,20 @@ function limitSubagentDiagnostic(diagnostic) {
|
|
|
34
34
|
return utf8Decoder.decode(bytes.subarray(0, prefixBytes))
|
|
35
35
|
+ DIAGNOSTIC_TRUNCATION_SUFFIX;
|
|
36
36
|
}
|
|
37
|
+
/** Enforce the byte limit on a provider-returned diagnostic. */
|
|
38
|
+
function normalizeSubagentDiagnostic(result) {
|
|
39
|
+
return result.diagnostic === undefined
|
|
40
|
+
? result
|
|
41
|
+
: { ...result, diagnostic: limitSubagentDiagnostic(result.diagnostic) };
|
|
42
|
+
}
|
|
37
43
|
/**
|
|
38
44
|
* The capability advertisement of an out-of-process backend: NONE. A child in
|
|
39
45
|
* another process cannot honor parent-enforced start features
|
|
40
|
-
* (`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
|
|
46
|
+
* (`agentOptions`/`outputSchema`/`maxDepth`/`toolFilter`/`persona`), so the service rejects a
|
|
41
47
|
* request needing any of them before `start` runs — never accepted-then-ignored.
|
|
42
48
|
*/
|
|
43
49
|
export const NO_START_CAPABILITIES = Object.freeze({
|
|
50
|
+
agentOptions: false,
|
|
44
51
|
outputSchema: false,
|
|
45
52
|
depthLimit: false,
|
|
46
53
|
toolFilter: false,
|
|
@@ -148,7 +155,8 @@ function toError(value) {
|
|
|
148
155
|
* rejects after publication. A normally completed or rejected attempt resolves
|
|
149
156
|
* as `aborted` when cancellation already settled locally; another rejection is
|
|
150
157
|
* flattened to `stopReason: 'error'` through the contained diagnostic sink.
|
|
151
|
-
* The abort listener is
|
|
158
|
+
* Provider-returned diagnostics use the same byte limit. The abort listener is
|
|
159
|
+
* removed on every path.
|
|
152
160
|
* @param parts - the attempt, output snapshot, cancellation state, sink, and signal wiring.
|
|
153
161
|
* @returns the terminal result (never a rejection).
|
|
154
162
|
*/
|
|
@@ -157,7 +165,7 @@ export async function settleRunResult(parts) {
|
|
|
157
165
|
const result = await parts.attempt();
|
|
158
166
|
return parts.cancelled()
|
|
159
167
|
? { output: parts.collectOutput(), stopReason: 'aborted' }
|
|
160
|
-
: result;
|
|
168
|
+
: normalizeSubagentDiagnostic(result);
|
|
161
169
|
}
|
|
162
170
|
catch (error) {
|
|
163
171
|
// Cover a rejection already queued when cancellation arrives.
|
|
@@ -20,8 +20,10 @@ function failureDetail(result) {
|
|
|
20
20
|
: `${stopReason}; diagnostic: ${result.diagnostic}`;
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Map a child result to the task outcome: completed carries final text,
|
|
24
|
-
* aborted is killed, and
|
|
23
|
+
* Map a child result to the task outcome: completed carries final text, local
|
|
24
|
+
* cancellation (`aborted` without a diagnostic) is killed, and provider-
|
|
25
|
+
* diagnosed remote aborts plus every other reason are failed without partial
|
|
26
|
+
* output.
|
|
25
27
|
* @param result - child terminal result.
|
|
26
28
|
* @returns outcome for the `ctx.jobs` registration.
|
|
27
29
|
*/
|
|
@@ -30,7 +32,9 @@ function runOutcome(result) {
|
|
|
30
32
|
case 'completed':
|
|
31
33
|
return { status: 'completed', output: finalText(result.output) };
|
|
32
34
|
case 'aborted':
|
|
33
|
-
return
|
|
35
|
+
return result.diagnostic === undefined
|
|
36
|
+
? { status: 'killed' }
|
|
37
|
+
: { status: 'failed', detail: failureDetail(result) };
|
|
34
38
|
case 'error':
|
|
35
39
|
case 'max-tokens':
|
|
36
40
|
case 'refusal':
|
package/lib/types/types.d.ts
CHANGED
|
@@ -76,6 +76,7 @@ export interface SubagentRunEndInfo {
|
|
|
76
76
|
* to `maxDepth`; the other names match.
|
|
77
77
|
*/
|
|
78
78
|
export interface SubagentCapabilities {
|
|
79
|
+
readonly agentOptions: boolean;
|
|
79
80
|
readonly outputSchema: boolean;
|
|
80
81
|
readonly depthLimit: boolean;
|
|
81
82
|
readonly toolFilter: boolean;
|
|
@@ -107,6 +108,13 @@ export interface SubagentStartRequest {
|
|
|
107
108
|
* remaining turn work when it fires afterward.
|
|
108
109
|
*/
|
|
109
110
|
readonly signal: AbortSignal;
|
|
111
|
+
/**
|
|
112
|
+
* Optional host-Agent provider, model, reasoning-effort, and output-token
|
|
113
|
+
* overrides. Requires {@link SubagentCapabilities.agentOptions}; in-process
|
|
114
|
+
* providers merge them over the parent Agent's options when they create the
|
|
115
|
+
* child, while the DSH SDK provider merges them over its instance defaults
|
|
116
|
+
* before initializing the separate child runtime.
|
|
117
|
+
*/
|
|
110
118
|
readonly agentOptions?: AgentOptions;
|
|
111
119
|
/**
|
|
112
120
|
* Object-rooted JSON Schema within `assertObjectJsonSchema`'s enforced subset. Start rejects
|
|
@@ -283,6 +291,16 @@ export interface SubagentProvider {
|
|
|
283
291
|
* It says nothing about tool registration, injected services, or authority inheritance.
|
|
284
292
|
*/
|
|
285
293
|
readonly inheritsParentContext: boolean;
|
|
294
|
+
/**
|
|
295
|
+
* Optional static provider-owned provider/model route for one-shot Agent
|
|
296
|
+
* options. Consumers merge tool/model overrides over these values before
|
|
297
|
+
* preflight; providers whose route derives from the parent omit it. The value
|
|
298
|
+
* is detached immutable data and requires `agentOptions` support.
|
|
299
|
+
*/
|
|
300
|
+
readonly agentRouteDefaults?: Readonly<{
|
|
301
|
+
provider: string;
|
|
302
|
+
model: string;
|
|
303
|
+
}>;
|
|
286
304
|
/**
|
|
287
305
|
* Establish a ONE-SHOT child and return its handle after publication.
|
|
288
306
|
* The service has already validated that every requested start-time
|