@qlzt/dsh-subagent 0.0.0-stage → 0.2.1-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 +94 -0
- package/README.md +215 -2
- package/README.zh.md +216 -0
- package/lib/index.js +3333 -0
- package/lib/typert.host.d.ts +3 -0
- package/lib/typert.host.js +991 -0
- package/lib/typert.remote-client.d.ts +25 -0
- package/lib/typert.remote-client.js +114 -0
- package/lib/types/activation.d.ts +114 -0
- package/lib/types/activation.js +112 -0
- package/lib/types/archive-admission.d.ts +16 -0
- package/lib/types/archive-admission.js +151 -0
- package/lib/types/assistant-output.d.ts +48 -0
- package/lib/types/assistant-output.js +73 -0
- package/lib/types/catalog.d.ts +92 -0
- package/lib/types/catalog.js +113 -0
- package/lib/types/child-agent.d.ts +154 -0
- package/lib/types/child-agent.js +211 -0
- package/lib/types/client.d.ts +8 -0
- package/lib/types/client.js +7 -0
- package/lib/types/continuation-messages.d.ts +63 -0
- package/lib/types/continuation-messages.js +114 -0
- package/lib/types/control-types.d.ts +142 -0
- package/lib/types/control-types.js +8 -0
- package/lib/types/control.d.ts +45 -0
- package/lib/types/control.js +79 -0
- package/lib/types/depth.d.ts +31 -0
- package/lib/types/depth.js +39 -0
- package/lib/types/descriptor.d.ts +105 -0
- package/lib/types/descriptor.js +192 -0
- package/lib/types/error.d.ts +11 -0
- package/lib/types/error.js +14 -0
- package/lib/types/index.d.ts +319 -0
- package/lib/types/index.js +455 -0
- package/lib/types/internal.d.ts +58 -0
- package/lib/types/internal.js +58 -0
- package/lib/types/lifecycle.d.ts +83 -0
- package/lib/types/lifecycle.js +115 -0
- package/lib/types/list-children.d.ts +36 -0
- package/lib/types/list-children.js +166 -0
- package/lib/types/manager.d.ts +247 -0
- package/lib/types/manager.js +1294 -0
- package/lib/types/out-of-process.d.ts +92 -0
- package/lib/types/out-of-process.js +178 -0
- package/lib/types/projection-types.d.ts +85 -0
- package/lib/types/projection-types.js +7 -0
- package/lib/types/projection.d.ts +100 -0
- package/lib/types/projection.js +157 -0
- package/lib/types/structured.d.ts +49 -0
- package/lib/types/structured.js +130 -0
- package/lib/types/types.d.ts +353 -0
- package/lib/types/types.js +19 -0
- package/package.json +136 -4
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-visible messages owned by continuable-subagent orchestration.
|
|
3
|
+
*
|
|
4
|
+
* @module @qlzt/dsh-subagent/continuation-messages
|
|
5
|
+
*/
|
|
6
|
+
import { boundContextSummary, createUserMessage } from '@qlzt/dsh-llm';
|
|
7
|
+
/** Build durable attribution for one adjacent-Agent message. */
|
|
8
|
+
function agentMessageSource(sender) {
|
|
9
|
+
return {
|
|
10
|
+
kind: 'agent-message',
|
|
11
|
+
form: 'relay',
|
|
12
|
+
senderSessionId: sender.id,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build the model-visible and durable representation of one adjacent-Agent message.
|
|
17
|
+
* @param sender - exact live Agent that authored the message.
|
|
18
|
+
* @param content - model-visible message blocks supplied by the sender.
|
|
19
|
+
* @returns the durable user-message representation delivered to the recipient.
|
|
20
|
+
*/
|
|
21
|
+
export function createAgentMessage(sender, content) {
|
|
22
|
+
return createUserMessage({
|
|
23
|
+
content: [
|
|
24
|
+
{ type: 'text', text: `Agent ${sender.id} sent a message: ` },
|
|
25
|
+
...content,
|
|
26
|
+
],
|
|
27
|
+
source: agentMessageSource(sender),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Append adjacent-Agent return guidance to a continuable child's initial task.
|
|
32
|
+
* @param parentId - durable parent session id named in the guidance.
|
|
33
|
+
* @param prompt - initial model-visible task blocks.
|
|
34
|
+
* @returns task blocks followed by the continuable return guidance.
|
|
35
|
+
*/
|
|
36
|
+
export function withContinuableReturnGuidance(parentId, prompt) {
|
|
37
|
+
const encodedParentId = JSON.stringify(parentId);
|
|
38
|
+
return [
|
|
39
|
+
...prompt,
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: `Your parent agent id is ${encodedParentId}. Before you finish, send your result to that agent with `
|
|
43
|
+
+ `send_message({ agent_id: ${encodedParentId}, message: "<self-contained result>" }). The parent shares `
|
|
44
|
+
+ 'your workspace but does not automatically receive your transcript, tool output, or reasoning. Send '
|
|
45
|
+
+ 'earlier messages as well when a finding changes what the parent should do next; sending a message '
|
|
46
|
+
+ 'does not end your turn.',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One line telling a parent that a background child is finished and why, in
|
|
52
|
+
* the parent's own task vocabulary.
|
|
53
|
+
* @param childId - the durable child the parent knows by id.
|
|
54
|
+
* @param stopReason - how the child's last ordinary turn ended.
|
|
55
|
+
* @param continuable - whether the child accepts a later message.
|
|
56
|
+
* @returns the model-facing opening line of the settlement notice.
|
|
57
|
+
*/
|
|
58
|
+
function settlementSummary(childId, stopReason, continuable) {
|
|
59
|
+
const subject = `Background subagent ${childId}`;
|
|
60
|
+
switch (stopReason) {
|
|
61
|
+
case 'completed':
|
|
62
|
+
return continuable
|
|
63
|
+
? `${subject} finished and will do no further work unless you send it more.`
|
|
64
|
+
: `${subject} finished. It cannot receive follow-up messages.`;
|
|
65
|
+
case 'aborted':
|
|
66
|
+
return `${subject} was stopped before it finished.`;
|
|
67
|
+
case 'max-tokens':
|
|
68
|
+
return `${subject} ran out of room before it finished.`;
|
|
69
|
+
// A pre-step rejection — a hook deny, a policy plugin — discarded input
|
|
70
|
+
// the child had claimed, so the parent must not treat the task as done.
|
|
71
|
+
case 'refusal':
|
|
72
|
+
return `${subject} declined the task.`;
|
|
73
|
+
case 'error':
|
|
74
|
+
return `${subject} failed before it finished.`;
|
|
75
|
+
/* v8 ignore next 4 -- `SubagentResult['stopReason']` is merge-extensible, so this arm
|
|
76
|
+
* needs a backend that adds a variant; an unnameable ending is reported as unfinished
|
|
77
|
+
* rather than silently as success. */
|
|
78
|
+
default:
|
|
79
|
+
return `${subject} ended abnormally (${String(stopReason)}) before it finished.`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build the runtime-owned settlement notice from the child's nonempty closing text.
|
|
84
|
+
* @param childId - durable child session id named in the notice.
|
|
85
|
+
* @param terminal - recorded terminal state for the settled Activation.
|
|
86
|
+
* @param continuable - whether the child can accept another task.
|
|
87
|
+
* @returns the durable user-message representation delivered to the parent.
|
|
88
|
+
*/
|
|
89
|
+
export function createSettlementMessage(childId, terminal, continuable = true) {
|
|
90
|
+
const summary = settlementSummary(childId, terminal.stopReason, continuable);
|
|
91
|
+
// Parent providers receive this notice as a user message and may reject
|
|
92
|
+
// nontext assistant blocks. Keep this conversion local so SDK/UI consumers
|
|
93
|
+
// retain the complete child output.
|
|
94
|
+
const closingText = terminal.output.flatMap(block => block.type === 'text' && block.text.length > 0 ? [block] : []);
|
|
95
|
+
return createUserMessage({
|
|
96
|
+
content: [
|
|
97
|
+
{ type: 'text', text: summary },
|
|
98
|
+
...closingText.length === 0
|
|
99
|
+
? [{ type: 'text', text: 'It left no closing message.' }]
|
|
100
|
+
: [{ type: 'text', text: 'Its closing message:' }, ...closingText],
|
|
101
|
+
...terminal.structured !== undefined
|
|
102
|
+
? [{ type: 'text', text: `Structured result: ${JSON.stringify(terminal.structured)}` }] : [],
|
|
103
|
+
...terminal.diagnostic !== undefined
|
|
104
|
+
? [{ type: 'text', text: terminal.diagnostic }] : [],
|
|
105
|
+
],
|
|
106
|
+
source: {
|
|
107
|
+
kind: 'subagent-settled',
|
|
108
|
+
form: 'notice',
|
|
109
|
+
summary: boundContextSummary(summary),
|
|
110
|
+
senderSessionId: childId,
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=continuation-messages.js.map
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-safe recursive catalog rows and browser continuation requests,
|
|
3
|
+
* receipts, and failures.
|
|
4
|
+
*
|
|
5
|
+
* @module @qlzt/dsh-subagent/control-types
|
|
6
|
+
*/
|
|
7
|
+
import type { PromptContentPart } from '@qlzt/dsh-attachment/types';
|
|
8
|
+
import type { Branded } from '@qlzt/dsh-brand';
|
|
9
|
+
import type { MessageId } from '@qlzt/dsh-llm/brand';
|
|
10
|
+
import type { SessionId } from '@qlzt/dsh-session/types';
|
|
11
|
+
/**
|
|
12
|
+
* Client-minted identity of one browser prompt, persisted on the exact accepted
|
|
13
|
+
* message. It carries the Session Controller's `session-request-id` brand so a
|
|
14
|
+
* subagent prompt and an ordinary Session prompt share one identity
|
|
15
|
+
* vocabulary; that package depends on this one, so the brand is spelled here
|
|
16
|
+
* rather than imported.
|
|
17
|
+
*/
|
|
18
|
+
export type SubagentPromptRequestId = Branded<'session-request-id'>;
|
|
19
|
+
/** Shared child fields for recursive catalog listing. */
|
|
20
|
+
export type SubagentCatalogRow = {
|
|
21
|
+
/** The durable child session id, stable across Activations. */
|
|
22
|
+
readonly id: SessionId;
|
|
23
|
+
/**
|
|
24
|
+
* Whether recursive catalog listing observed a resident Session. This
|
|
25
|
+
* does not encode a durable outcome or guarantee continuation delivery.
|
|
26
|
+
*/
|
|
27
|
+
readonly activity: 'running' | 'inactive';
|
|
28
|
+
} & ({
|
|
29
|
+
/** A terminal one-shot child. */
|
|
30
|
+
readonly mode: 'one-shot';
|
|
31
|
+
/** Optional durable creation label from the parent catalog. */
|
|
32
|
+
readonly label?: string;
|
|
33
|
+
} | {
|
|
34
|
+
/** An external execution without a local child Session. */
|
|
35
|
+
readonly mode: 'external';
|
|
36
|
+
/** Optional durable creation label from the parent catalog. */
|
|
37
|
+
readonly label?: string;
|
|
38
|
+
} | {
|
|
39
|
+
/** A resumable conversation. */
|
|
40
|
+
readonly mode: 'continuable';
|
|
41
|
+
/** Durable creation label from the parent catalog. */
|
|
42
|
+
readonly label: string;
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* One recursive catalog row. Traversal may also return diagnostics for
|
|
46
|
+
* unknown catalog modes or unreadable child catalogs.
|
|
47
|
+
*/
|
|
48
|
+
export type SubagentListEntry = SubagentCatalogRow & {
|
|
49
|
+
readonly kind: 'child';
|
|
50
|
+
/** Whether the child catalog contains a direct child. */
|
|
51
|
+
readonly hasChildren: boolean;
|
|
52
|
+
} | {
|
|
53
|
+
readonly kind: 'diagnostic';
|
|
54
|
+
/** The candidate's session id. */
|
|
55
|
+
readonly id: SessionId;
|
|
56
|
+
/**
|
|
57
|
+
* `corrupt` means the child catalog read found invalid or conflicting
|
|
58
|
+
* Session data; `unavailable` means it could not be read. Either failure
|
|
59
|
+
* stops that branch. `unsupported` means the parent catalog records an
|
|
60
|
+
* unknown child mode; its readable catalog is still traversed.
|
|
61
|
+
*/
|
|
62
|
+
readonly reason: 'corrupt' | 'unsupported' | 'unavailable';
|
|
63
|
+
};
|
|
64
|
+
/** Durable parent/child browsing address; unknown mode is resolved when child history is read. */
|
|
65
|
+
export type SubagentAddress = {
|
|
66
|
+
readonly parentSessionId: SessionId;
|
|
67
|
+
readonly childSessionId: SessionId;
|
|
68
|
+
} & ({
|
|
69
|
+
readonly mode: 'one-shot';
|
|
70
|
+
} | {
|
|
71
|
+
readonly mode: 'continuable';
|
|
72
|
+
} | {
|
|
73
|
+
readonly mode: 'unknown';
|
|
74
|
+
});
|
|
75
|
+
/** One Agent inbox destination, as the prompt request selects it. */
|
|
76
|
+
export type SubagentDelivery = SubagentPromptRequest['delivery'];
|
|
77
|
+
/** One human message addressed to a continuable direct child. */
|
|
78
|
+
export interface SubagentPromptRequest {
|
|
79
|
+
/** Identity persisted on the accepted message, minted before the call. */
|
|
80
|
+
readonly requestId: SubagentPromptRequestId;
|
|
81
|
+
readonly parentSessionId: SessionId;
|
|
82
|
+
readonly childSessionId: SessionId;
|
|
83
|
+
/** Required discriminator retained from the browser control address. */
|
|
84
|
+
readonly mode: 'continuable';
|
|
85
|
+
/** Whether this message queues a later turn or targets the nearest step. */
|
|
86
|
+
readonly delivery: 'queue' | 'steer';
|
|
87
|
+
/**
|
|
88
|
+
* Browser prompt parts delivered as the child's user message. The Host
|
|
89
|
+
* admits and persists image parts before delivery, so the wire never
|
|
90
|
+
* carries a durable attachment reference the caller could fabricate.
|
|
91
|
+
*/
|
|
92
|
+
readonly content: readonly PromptContentPart[];
|
|
93
|
+
/** Optional browser zone sampled for this exact human prompt. */
|
|
94
|
+
readonly clientTimeZone?: string;
|
|
95
|
+
}
|
|
96
|
+
/** Inbox identity returned once the continuation accepts one human message. */
|
|
97
|
+
export interface SubagentPromptReceipt {
|
|
98
|
+
readonly messageId: MessageId;
|
|
99
|
+
}
|
|
100
|
+
/** Uniform acknowledgement that one interrupt request was admitted. */
|
|
101
|
+
export interface SubagentInterruptReceipt {
|
|
102
|
+
readonly accepted: true;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Failure details the control surface answers with. Prompts and interrupts
|
|
106
|
+
* share these failures with the Client Remote result.
|
|
107
|
+
*/
|
|
108
|
+
declare module '@qlzt/dsh-workspace/types' {
|
|
109
|
+
interface SessionActivityKindMap {
|
|
110
|
+
/** A subagent session delegated from this session (at any depth) is inside a turn. */
|
|
111
|
+
subagent: true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
declare module '@qlzt/dsh-typert-protocol' {
|
|
115
|
+
interface RemoteErrorDetailsMap {
|
|
116
|
+
/** A browser-supplied zone is neither UTC nor a canonical IANA name. */
|
|
117
|
+
'subagent/invalid-time-zone': {
|
|
118
|
+
readonly value: string;
|
|
119
|
+
};
|
|
120
|
+
/** No live Agent carries the addressed parent session. */
|
|
121
|
+
'subagent/parent-unavailable': {
|
|
122
|
+
readonly parentSessionId: SessionId;
|
|
123
|
+
};
|
|
124
|
+
/** The addressed child cannot take a continuation. */
|
|
125
|
+
'subagent/not-resumable': {
|
|
126
|
+
readonly childSessionId: SessionId;
|
|
127
|
+
};
|
|
128
|
+
/** The claimed parent does not own the addressed child. */
|
|
129
|
+
'subagent/unauthorized': {
|
|
130
|
+
readonly childSessionId: SessionId;
|
|
131
|
+
};
|
|
132
|
+
/** Image admission or model image-capability refusal. */
|
|
133
|
+
'subagent/attachment-invalid': {
|
|
134
|
+
readonly reason: string;
|
|
135
|
+
};
|
|
136
|
+
/** The child exists but its inbox cannot admit the message now. */
|
|
137
|
+
'subagent/delivery-unavailable': {
|
|
138
|
+
readonly childSessionId: SessionId;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=control-types.d.ts.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-facing subagent prompt and interrupt request validation plus the
|
|
3
|
+
* stable prompt failure codes returned by the Remote surface.
|
|
4
|
+
*
|
|
5
|
+
* @module @qlzt/dsh-subagent
|
|
6
|
+
*/
|
|
7
|
+
import type { SessionId } from '@qlzt/dsh-session';
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
declare const CONTROL_ID_SCHEMAS: {
|
|
10
|
+
readonly 'subagent.prompt': z.ZodObject<{
|
|
11
|
+
parentSessionId: z.ZodString;
|
|
12
|
+
childSessionId: z.ZodString;
|
|
13
|
+
mode: z.ZodLiteral<"continuable">;
|
|
14
|
+
delivery: z.ZodEnum<{
|
|
15
|
+
queue: "queue";
|
|
16
|
+
steer: "steer";
|
|
17
|
+
}>;
|
|
18
|
+
}, z.core.$strip>;
|
|
19
|
+
readonly 'subagent.interrupt': z.ZodObject<{
|
|
20
|
+
parentSessionId: z.ZodString;
|
|
21
|
+
childSessionId: z.ZodString;
|
|
22
|
+
mode: z.ZodLiteral<"continuable">;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Apply the subagent payload checks that are stricter than generated
|
|
27
|
+
* branded-string codecs.
|
|
28
|
+
* @param method - method name carried in the failure message.
|
|
29
|
+
* @param payload - decoded control fields to validate.
|
|
30
|
+
* @throws {RemoteError} `gateway/bad-request` with the original Zod issues.
|
|
31
|
+
*/
|
|
32
|
+
export declare function validateControlRequest(method: keyof typeof CONTROL_ID_SCHEMAS, payload: unknown): void;
|
|
33
|
+
/**
|
|
34
|
+
* Refuse one continuation prompt without exposing provider detail: admission
|
|
35
|
+
* failures the caller can act on keep their own code, everything else is
|
|
36
|
+
* internal.
|
|
37
|
+
* @param error - the thrown value.
|
|
38
|
+
* @param childSessionId - the addressed child.
|
|
39
|
+
* @param signal - the caller's cancellation.
|
|
40
|
+
* @returns Never — the refusal is thrown.
|
|
41
|
+
* @throws {RemoteError} always.
|
|
42
|
+
*/
|
|
43
|
+
export declare function rejectPrompt(error: unknown, childSessionId: SessionId, signal: AbortSignal): never;
|
|
44
|
+
export {};
|
|
45
|
+
//# sourceMappingURL=control.d.ts.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-facing subagent prompt and interrupt request validation plus the
|
|
3
|
+
* stable prompt failure codes returned by the Remote surface.
|
|
4
|
+
*
|
|
5
|
+
* @module @qlzt/dsh-subagent
|
|
6
|
+
*/
|
|
7
|
+
import { AttachmentError } from '@qlzt/dsh-attachment';
|
|
8
|
+
import { RemoteError } from '@qlzt/dsh-typert-protocol';
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
import { SubagentError } from "./error.js";
|
|
11
|
+
const SESSION_ID_SCHEMA = z.string().min(1);
|
|
12
|
+
const CONTROL_ID_SCHEMAS = {
|
|
13
|
+
'subagent.prompt': z.object({
|
|
14
|
+
parentSessionId: SESSION_ID_SCHEMA,
|
|
15
|
+
childSessionId: SESSION_ID_SCHEMA,
|
|
16
|
+
mode: z.literal('continuable'),
|
|
17
|
+
delivery: z.enum(['queue', 'steer']),
|
|
18
|
+
}),
|
|
19
|
+
'subagent.interrupt': z.object({
|
|
20
|
+
parentSessionId: SESSION_ID_SCHEMA,
|
|
21
|
+
childSessionId: SESSION_ID_SCHEMA,
|
|
22
|
+
mode: z.literal('continuable'),
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Apply the subagent payload checks that are stricter than generated
|
|
27
|
+
* branded-string codecs.
|
|
28
|
+
* @param method - method name carried in the failure message.
|
|
29
|
+
* @param payload - decoded control fields to validate.
|
|
30
|
+
* @throws {RemoteError} `gateway/bad-request` with the original Zod issues.
|
|
31
|
+
*/
|
|
32
|
+
export function validateControlRequest(method, payload) {
|
|
33
|
+
const parsed = CONTROL_ID_SCHEMAS[method].safeParse(payload);
|
|
34
|
+
if (!parsed.success) {
|
|
35
|
+
throw new RemoteError('gateway/bad-request', `invalid payload for ${method}`, { issues: parsed.error.issues });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Refuse one continuation prompt without exposing provider detail: admission
|
|
40
|
+
* failures the caller can act on keep their own code, everything else is
|
|
41
|
+
* internal.
|
|
42
|
+
* @param error - the thrown value.
|
|
43
|
+
* @param childSessionId - the addressed child.
|
|
44
|
+
* @param signal - the caller's cancellation.
|
|
45
|
+
* @returns Never — the refusal is thrown.
|
|
46
|
+
* @throws {RemoteError} always.
|
|
47
|
+
*/
|
|
48
|
+
export function rejectPrompt(error, childSessionId, signal) {
|
|
49
|
+
if (isCancellation(error, signal)) {
|
|
50
|
+
throw new RemoteError('gateway/cancelled', 'subagent prompt was cancelled', {}, { cause: error });
|
|
51
|
+
}
|
|
52
|
+
if (error instanceof AttachmentError) {
|
|
53
|
+
throw new RemoteError('subagent/attachment-invalid', error.message, { reason: error.code }, { cause: error });
|
|
54
|
+
}
|
|
55
|
+
if (error instanceof SubagentError) {
|
|
56
|
+
switch (error.code) {
|
|
57
|
+
case 'MODEL_DOES_NOT_SUPPORT_IMAGES':
|
|
58
|
+
throw new RemoteError('subagent/attachment-invalid', error.message, { reason: error.code }, { cause: error });
|
|
59
|
+
case 'NOT_RESUMABLE':
|
|
60
|
+
throw new RemoteError('subagent/not-resumable', 'subagent cannot be resumed', { childSessionId }, { cause: error });
|
|
61
|
+
case 'UNAUTHORIZED':
|
|
62
|
+
throw new RemoteError('subagent/unauthorized', 'subagent does not belong to this parent', { childSessionId }, { cause: error });
|
|
63
|
+
case 'DRAINING':
|
|
64
|
+
case 'ACTIVATION_CLOSING':
|
|
65
|
+
case 'ACTIVATION_LIMIT_REACHED':
|
|
66
|
+
case 'CONTINUATION_UNAVAILABLE':
|
|
67
|
+
case 'PERSISTENCE_UNAVAILABLE':
|
|
68
|
+
throw new RemoteError('subagent/delivery-unavailable', 'subagent follow-up is temporarily unavailable', { childSessionId }, { cause: error });
|
|
69
|
+
// A code outside the admission vocabulary is not the caller's move to make.
|
|
70
|
+
default:
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
throw new RemoteError('gateway/internal', 'subagent prompt failed', {}, { cause: error });
|
|
75
|
+
}
|
|
76
|
+
function isCancellation(error, signal) {
|
|
77
|
+
return signal.aborted || (error instanceof SubagentError && error.code === 'CANCELLED');
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=control.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delegation-depth accounting: the recursion budget a parent passes to its
|
|
3
|
+
* children. Kept apart from the service so composition helpers can read it
|
|
4
|
+
* without importing the registry.
|
|
5
|
+
*
|
|
6
|
+
* @module @qlzt/dsh-subagent/depth
|
|
7
|
+
*/
|
|
8
|
+
import type { Agent } from '@qlzt/dsh-agent';
|
|
9
|
+
declare module '@qlzt/dsh-agent' {
|
|
10
|
+
interface AgentOptions {
|
|
11
|
+
/** Delegation depth: zero for a top-level agent and parent depth + 1 for a child. */
|
|
12
|
+
subagentDepth?: number;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Read an agent's delegation depth, treating absence as top-level depth zero.
|
|
17
|
+
* The persisted session header is authoritative and monotone: runtime
|
|
18
|
+
* `AgentOptions.subagentDepth` may DEEPEN the count but can never lower it —
|
|
19
|
+
* a resumed child arrives with fresh options, and counting it from zero would
|
|
20
|
+
* let it delegate as if it were top-level.
|
|
21
|
+
* @param agent - the agent whose header and options carry the depth.
|
|
22
|
+
* @returns its non-negative safe-integer depth.
|
|
23
|
+
* @throws if the runtime `AgentOptions.subagentDepth` is not a non-negative safe integer.
|
|
24
|
+
*/
|
|
25
|
+
export declare function delegationDepthOf(agent: Agent): number;
|
|
26
|
+
/**
|
|
27
|
+
* Reject a recursion cap that cannot represent an exact delegation depth.
|
|
28
|
+
* @param maxDepth - the optional runtime value to validate.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assertSubagentMaxDepth(maxDepth: unknown): void;
|
|
31
|
+
//# sourceMappingURL=depth.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Delegation-depth accounting: the recursion budget a parent passes to its
|
|
3
|
+
* children. Kept apart from the service so composition helpers can read it
|
|
4
|
+
* without importing the registry.
|
|
5
|
+
*
|
|
6
|
+
* @module @qlzt/dsh-subagent/depth
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Read an agent's delegation depth, treating absence as top-level depth zero.
|
|
10
|
+
* The persisted session header is authoritative and monotone: runtime
|
|
11
|
+
* `AgentOptions.subagentDepth` may DEEPEN the count but can never lower it —
|
|
12
|
+
* a resumed child arrives with fresh options, and counting it from zero would
|
|
13
|
+
* let it delegate as if it were top-level.
|
|
14
|
+
* @param agent - the agent whose header and options carry the depth.
|
|
15
|
+
* @returns its non-negative safe-integer depth.
|
|
16
|
+
* @throws if the runtime `AgentOptions.subagentDepth` is not a non-negative safe integer.
|
|
17
|
+
*/
|
|
18
|
+
export function delegationDepthOf(agent) {
|
|
19
|
+
const runtime = agent.options.subagentDepth;
|
|
20
|
+
if (runtime !== undefined && (!Number.isSafeInteger(runtime) || runtime < 0 || Object.is(runtime, -0))) {
|
|
21
|
+
throw new TypeError('agent subagentDepth must be a non-negative safe integer');
|
|
22
|
+
}
|
|
23
|
+
// The header value was validated at the session boundary (creation and
|
|
24
|
+
// persistence load both construct through the store).
|
|
25
|
+
return Math.max(agent.session.header.delegationDepth ?? 0, runtime ?? 0);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Reject a recursion cap that cannot represent an exact delegation depth.
|
|
29
|
+
* @param maxDepth - the optional runtime value to validate.
|
|
30
|
+
*/
|
|
31
|
+
export function assertSubagentMaxDepth(maxDepth) {
|
|
32
|
+
if (maxDepth !== undefined && (typeof maxDepth !== 'number'
|
|
33
|
+
|| !Number.isSafeInteger(maxDepth)
|
|
34
|
+
|| maxDepth < 0
|
|
35
|
+
|| Object.is(maxDepth, -0))) {
|
|
36
|
+
throw new TypeError('subagent maxDepth must be a non-negative safe integer');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=depth.js.map
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durable subagent-child descriptor: the versioned, model-hidden
|
|
3
|
+
* `subagent/descriptor` session event that identifies every session-backed
|
|
4
|
+
* subagent, including historical one-shot records. The manager appends each
|
|
5
|
+
* continuable child's descriptor inside its initial turn, preserving the
|
|
6
|
+
* declared composition required for cold resume.
|
|
7
|
+
*
|
|
8
|
+
* The descriptor deliberately snapshots explicit fields rather than the
|
|
9
|
+
* merge-extensible `AgentOptions` object: an unrelated extension value cannot
|
|
10
|
+
* make continuation fail merely because it is not JSON, and later composition
|
|
11
|
+
* inputs require a deliberate {@link SUBAGENT_DESCRIPTOR_VERSION} change. It
|
|
12
|
+
* omits `subagentDepth` — cold resume trusts the persisted header's
|
|
13
|
+
* `delegationDepth` as the monotone floor — and `outputSchema`, which belongs
|
|
14
|
+
* to one activation's result contract rather than durable child composition.
|
|
15
|
+
* Per-activation knobs such as `maxTokens` are omitted for the same reason as
|
|
16
|
+
* `outputSchema`: they budget one activation. Cold resume requires the exact
|
|
17
|
+
* live parent for authorization but reconstructs child options only from the
|
|
18
|
+
* durable descriptor, so it neither restores the prior budget nor inherits
|
|
19
|
+
* the parent's current one; the resumed route's defaults apply instead.
|
|
20
|
+
*
|
|
21
|
+
* @module @qlzt/dsh-subagent/descriptor
|
|
22
|
+
*/
|
|
23
|
+
import type { SessionEvent } from '@qlzt/dsh-session';
|
|
24
|
+
import type { ReasoningEffortId } from '@qlzt/dsh-llm';
|
|
25
|
+
import type { ToolRestriction } from '@qlzt/dsh-tools';
|
|
26
|
+
declare module '@qlzt/dsh-session/types' {
|
|
27
|
+
interface SessionEventMap {
|
|
28
|
+
/**
|
|
29
|
+
* Durable identity and lifecycle mode of a session-backed subagent child,
|
|
30
|
+
* appended once inside the child's initial turn, before its first
|
|
31
|
+
* request. Continuable records also carry their
|
|
32
|
+
* resumable composition. Log-only: it carries no `surfaceOp`, never enters
|
|
33
|
+
* model history, and survives compaction.
|
|
34
|
+
*/
|
|
35
|
+
'subagent/descriptor': SubagentDescriptorData;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* The current descriptor format version, stamped into every appended
|
|
40
|
+
* `subagent/descriptor` event and required verbatim by {@link foldSubagentDescriptor}.
|
|
41
|
+
* Supporting another composition input is a deliberate version change, never
|
|
42
|
+
* an implicit extra field.
|
|
43
|
+
*/
|
|
44
|
+
export declare const SUBAGENT_DESCRIPTOR_VERSION = 3;
|
|
45
|
+
/** Fields shared by every supported `subagent/descriptor` payload. */
|
|
46
|
+
interface SubagentDescriptorBase {
|
|
47
|
+
/** Descriptor format version ({@link SUBAGENT_DESCRIPTOR_VERSION}). */
|
|
48
|
+
readonly version: number;
|
|
49
|
+
/** Whether the child is a terminal one-shot run or a resumable conversation. */
|
|
50
|
+
readonly mode: 'one-shot' | 'continuable';
|
|
51
|
+
/** The `ctx.subagents` provider name that established the child. */
|
|
52
|
+
readonly provider: string;
|
|
53
|
+
}
|
|
54
|
+
/** A session-backed subagent that cannot be cold-resumed after its run. */
|
|
55
|
+
export interface OneShotSubagentDescriptorData extends SubagentDescriptorBase {
|
|
56
|
+
readonly mode: 'one-shot';
|
|
57
|
+
/**
|
|
58
|
+
* The initial delegation's short `description`, kept as the child's durable
|
|
59
|
+
* creation label so enumeration can identify the conversation without
|
|
60
|
+
* replaying parent tool results or exposing the child prompt.
|
|
61
|
+
*/
|
|
62
|
+
readonly label?: string;
|
|
63
|
+
}
|
|
64
|
+
/** A session-backed subagent whose declared composition supports cold resume. */
|
|
65
|
+
export interface ContinuableSubagentDescriptorData extends SubagentDescriptorBase {
|
|
66
|
+
readonly mode: 'continuable';
|
|
67
|
+
/** The initial delegation's short `description`, used for durable enumeration. */
|
|
68
|
+
readonly label: string;
|
|
69
|
+
/** Resolved child `agentOptions.provider`, when one was declared. */
|
|
70
|
+
readonly agentProvider?: string;
|
|
71
|
+
/** Resolved child `agentOptions.model`, when one was declared. */
|
|
72
|
+
readonly agentModel?: string;
|
|
73
|
+
/** Resolved child `agentOptions.reasoningEffort`, when one was declared. */
|
|
74
|
+
readonly agentReasoningEffort?: ReasoningEffortId;
|
|
75
|
+
/** Per-child persona that shadows the deployment persona on resume. */
|
|
76
|
+
readonly persona?: string;
|
|
77
|
+
/** Child tool scoping reapplied on resume. */
|
|
78
|
+
readonly toolFilter?: ToolRestriction;
|
|
79
|
+
}
|
|
80
|
+
/** The supported durable subagent identity and optional continuation composition. */
|
|
81
|
+
export type SubagentDescriptorData = OneShotSubagentDescriptorData | ContinuableSubagentDescriptorData;
|
|
82
|
+
/** Continuable composition fields before the snapshot stamps their format version. */
|
|
83
|
+
export type ContinuableSubagentDescriptorInput = Omit<ContinuableSubagentDescriptorData, 'version'>;
|
|
84
|
+
/**
|
|
85
|
+
* Validate and detach a continuable descriptor input.
|
|
86
|
+
* @param input - the caller-collected continuable composition fields.
|
|
87
|
+
* @returns the versioned, detached continuable descriptor payload.
|
|
88
|
+
* @throws when a field is not losslessly JSON-serializable.
|
|
89
|
+
*/
|
|
90
|
+
export declare function snapshotSubagentDescriptor(input: ContinuableSubagentDescriptorInput): ContinuableSubagentDescriptorData;
|
|
91
|
+
/**
|
|
92
|
+
* Fold a persisted child log to its supported descriptor. The first
|
|
93
|
+
* `subagent/descriptor` event is authoritative — the establishing provider
|
|
94
|
+
* appends exactly one, so a later same-type event cannot rewrite the declared
|
|
95
|
+
* composition.
|
|
96
|
+
* @param events - the loaded child session events.
|
|
97
|
+
* @returns the descriptor, or `undefined` when the log has none or its
|
|
98
|
+
* version is not {@link SUBAGENT_DESCRIPTOR_VERSION} (the child cannot be
|
|
99
|
+
* classified by this runtime).
|
|
100
|
+
* @throws when a current-version persisted payload does not match its complete
|
|
101
|
+
* declared schema.
|
|
102
|
+
*/
|
|
103
|
+
export declare function foldSubagentDescriptor(events: readonly SessionEvent[]): SubagentDescriptorData | undefined;
|
|
104
|
+
export {};
|
|
105
|
+
//# sourceMappingURL=descriptor.d.ts.map
|