@deepseek-ai/dsh-subagent 0.1.2-alpha.2 → 0.1.2-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,10 +5,9 @@
5
5
  *
6
6
  * @module @deepseek-ai/dsh-subagent/control-types
7
7
  */
8
- import type { EncodedImageAttachment } from '@deepseek-ai/dsh-attachment/types';
8
+ import type { PromptContentPart } from '@deepseek-ai/dsh-attachment/types';
9
9
  import type { Branded } from '@deepseek-ai/dsh-brand';
10
10
  import type { MessageId } from '@deepseek-ai/dsh-llm/brand';
11
- import type { ContentBlock } from '@deepseek-ai/dsh-llm/types';
12
11
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
13
12
  /**
14
13
  * Client-minted identity of one browser prompt, persisted on the exact accepted
@@ -83,19 +82,6 @@ export type SubagentAddress = {
83
82
  } | {
84
83
  readonly mode: 'continuable';
85
84
  });
86
- /**
87
- * One browser-encoded upload as the Session prompt wire carries it: the shared
88
- * attachment vocabulary under the content-block tag.
89
- */
90
- export interface EncodedImagePromptBlock extends EncodedImageAttachment {
91
- readonly type: 'image';
92
- }
93
- /**
94
- * One block a browser prompt may carry. The encoded upload is accepted by the
95
- * wire and refused by the Host, so the Client narrows nothing: a caller that
96
- * attaches an image is answered, not silently stripped.
97
- */
98
- export type SubagentPromptContentPart = ContentBlock | EncodedImagePromptBlock;
99
85
  /** One human message addressed to a continuable direct child. */
100
86
  export interface SubagentPromptRequest {
101
87
  /** Identity persisted on the accepted message, minted before the call. */
@@ -104,8 +90,12 @@ export interface SubagentPromptRequest {
104
90
  readonly childSessionId: SessionId;
105
91
  /** Required discriminator retained from the browser control address. */
106
92
  readonly mode: 'continuable';
107
- /** Content proposed as the child's user message; images are refused. */
108
- readonly content: readonly SubagentPromptContentPart[];
93
+ /**
94
+ * Browser prompt parts delivered as the child's user message. The Host
95
+ * admits and persists image parts before delivery, so the wire never
96
+ * carries a durable attachment reference the caller could fabricate.
97
+ */
98
+ readonly content: readonly PromptContentPart[];
109
99
  /** Optional browser zone sampled for this exact human prompt. */
110
100
  readonly clientTimeZone?: string;
111
101
  }
@@ -118,10 +108,8 @@ export interface SubagentInterruptReceipt {
118
108
  readonly accepted: true;
119
109
  }
120
110
  /**
121
- * Failure details the control surface answers with. The catalog read, the
122
- * prompt, and the interrupt produce these codes; a Client fabricates
123
- * `subagent/not-resumable` and `subagent/delivery-unavailable` for a one-shot
124
- * address it refuses before the call, so both planes read one vocabulary.
111
+ * Failure details the control surface answers with. Catalog reads, prompts,
112
+ * and interrupts share this vocabulary with the Client Remote result.
125
113
  */
126
114
  declare module '@deepseek-ai/dsh-typert-protocol' {
127
115
  interface RemoteErrorDetailsMap {
@@ -141,12 +129,8 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
141
129
  'subagent/unauthorized': {
142
130
  readonly childSessionId: SessionId;
143
131
  };
144
- /**
145
- * The continuation admits no attachment. `reason` names the refused plane
146
- * for the caller's copy, as the Session prompt's attachment refusals do.
147
- */
148
- 'subagent/attachment-unsupported': {
149
- readonly childSessionId: SessionId;
132
+ /** Image admission or model image-capability refusal. */
133
+ 'subagent/attachment-invalid': {
150
134
  readonly reason: string;
151
135
  };
152
136
  /** The child exists but its inbox cannot admit the message now. */
@@ -6,10 +6,9 @@
6
6
  * @module @deepseek-ai/dsh-subagent
7
7
  */
8
8
  import type { Context } from '@deepseek-ai/cordis';
9
- import type { ContentBlock } from '@deepseek-ai/dsh-llm';
10
9
  import type { SessionId } from '@deepseek-ai/dsh-session';
11
10
  import { z } from 'zod';
12
- import type { SubagentCatalog, SubagentListEntry, SubagentPromptContentPart } from './control-types.ts';
11
+ import type { SubagentCatalog, SubagentListEntry } from './control-types.ts';
13
12
  declare const CONTROL_ID_SCHEMAS: {
14
13
  readonly 'subagent.list': z.ZodObject<{
15
14
  parentSessionId: z.ZodString;
@@ -33,21 +32,6 @@ declare const CONTROL_ID_SCHEMAS: {
33
32
  * @throws {RemoteError} `gateway/bad-request` with the original Zod issues.
34
33
  */
35
34
  export declare function validateControlRequest(method: keyof typeof CONTROL_ID_SCHEMAS, payload: unknown): void;
36
- /**
37
- * Admit the content one continuation may deliver, refusing every image.
38
- *
39
- * The blocks become the child's user message verbatim, and this surface admits
40
- * no attachment: nothing here registers encoded bytes with the attachment
41
- * service, so an image would reach the child as a reference nothing resolves.
42
- * The wire accepts the encoded upload so this refusal — not a Client that
43
- * strips the block — is what the caller is answered with. Other block types
44
- * still cross unnarrowed.
45
- * @param childSessionId - the addressed child, named by the refusal.
46
- * @param content - blocks the caller asked to deliver.
47
- * @returns the admitted blocks, in order, as the durable content vocabulary.
48
- * @throws {RemoteError} `subagent/attachment-unsupported` when any block is an image.
49
- */
50
- export declare function admitPromptContent(childSessionId: SessionId, content: readonly SubagentPromptContentPart[]): ContentBlock[];
51
35
  /**
52
36
  * Project one durable listing onto the catalog view, replacing each row's
53
37
  * store-derived activity with the live Agent driver's status and reporting
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * @module @deepseek-ai/dsh-subagent
7
7
  */
8
+ import { AttachmentError } from '@deepseek-ai/dsh-attachment';
8
9
  import { RemoteError } from '@deepseek-ai/dsh-typert-protocol';
9
10
  import { z } from 'zod';
10
11
  import { SubagentError } from "./error.js";
@@ -35,30 +36,6 @@ export function validateControlRequest(method, payload) {
35
36
  throw new RemoteError('gateway/bad-request', `invalid payload for ${method}`, { issues: parsed.error.issues });
36
37
  }
37
38
  }
38
- /**
39
- * Admit the content one continuation may deliver, refusing every image.
40
- *
41
- * The blocks become the child's user message verbatim, and this surface admits
42
- * no attachment: nothing here registers encoded bytes with the attachment
43
- * service, so an image would reach the child as a reference nothing resolves.
44
- * The wire accepts the encoded upload so this refusal — not a Client that
45
- * strips the block — is what the caller is answered with. Other block types
46
- * still cross unnarrowed.
47
- * @param childSessionId - the addressed child, named by the refusal.
48
- * @param content - blocks the caller asked to deliver.
49
- * @returns the admitted blocks, in order, as the durable content vocabulary.
50
- * @throws {RemoteError} `subagent/attachment-unsupported` when any block is an image.
51
- */
52
- export function admitPromptContent(childSessionId, content) {
53
- const admitted = [];
54
- for (const block of content) {
55
- if (block.type === 'image') {
56
- throw new RemoteError('subagent/attachment-unsupported', 'subagent continuation does not accept images', { childSessionId, reason: 'SUBAGENT_IMAGE_UNSUPPORTED' });
57
- }
58
- admitted.push(block);
59
- }
60
- return admitted;
61
- }
62
39
  /**
63
40
  * Project one durable listing onto the catalog view, replacing each row's
64
41
  * store-derived activity with the live Agent driver's status and reporting
@@ -109,8 +86,13 @@ export function rejectPrompt(error, childSessionId, signal) {
109
86
  if (isCancellation(error, signal)) {
110
87
  throw new RemoteError('gateway/cancelled', 'subagent prompt was cancelled', {}, { cause: error });
111
88
  }
89
+ if (error instanceof AttachmentError) {
90
+ throw new RemoteError('subagent/attachment-invalid', error.message, { reason: error.code }, { cause: error });
91
+ }
112
92
  if (error instanceof SubagentError) {
113
93
  switch (error.code) {
94
+ case 'MODEL_DOES_NOT_SUPPORT_IMAGES':
95
+ throw new RemoteError('subagent/attachment-invalid', error.message, { reason: error.code }, { cause: error });
114
96
  case 'NOT_RESUMABLE':
115
97
  throw new RemoteError('subagent/not-resumable', 'subagent cannot be resumed', { childSessionId }, { cause: error });
116
98
  case 'UNAUTHORIZED':
@@ -17,5 +17,5 @@ import type { SubagentDescriptorData } from './descriptor.ts';
17
17
  * @param descriptor - the snapshotted composition record to persist.
18
18
  * @returns the complete seed events, contiguous from sequence zero.
19
19
  */
20
- export declare function seedDescriptorTurn(childId: SessionId, seed: readonly SessionEvent[] | undefined, descriptor: SubagentDescriptorData): SessionEvent[];
20
+ export declare function seedDescriptorTurn(childId: SessionId, seed: readonly SessionEvent[] | undefined, descriptor: SubagentDescriptorData): readonly SessionEvent[];
21
21
  //# sourceMappingURL=descriptor-seed.d.ts.map
@@ -19,6 +19,6 @@ import { Session } from '@deepseek-ai/dsh-session';
19
19
  export function seedDescriptorTurn(childId, seed, descriptor) {
20
20
  const staged = Session.create(childId, seed);
21
21
  staged.append('subagent/descriptor', descriptor);
22
- return [...staged.events];
22
+ return staged.snapshotEvents();
23
23
  }
24
24
  //# sourceMappingURL=descriptor-seed.js.map
@@ -13,8 +13,8 @@
13
13
  *
14
14
  * Public operations express caller intent: `start` returns one published owned
15
15
  * one-shot run, `startContinuable` establishes a durable continuable child, and
16
- * `followup` delivers later content without exposing whether the child is
17
- * resident. Continuable children never become a {@link SubagentRun}: the
16
+ * `sendMessage` steers between adjacent Agents without exposing whether a child
17
+ * is resident. Continuable children never become a {@link SubagentRun}: the
18
18
  * continuation manager holds their `AgentHandle` directly and orders every turn
19
19
  * through the child's own inbox, so providers contribute only the detached
20
20
  * creation spec and see no handle, turn, or teardown. Child and descendant
@@ -36,9 +36,9 @@ import type { SessionId } from '@deepseek-ai/dsh-session';
36
36
  import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
37
37
  import type { SubagentCatalog, SubagentInterruptReceipt, SubagentPromptReceipt, SubagentPromptRequest } from './control-types.ts';
38
38
  import type { SubagentProvider, SubagentRun, SubagentRunEndInfo, SubagentRunInfo, SubagentStartRequest } from './types.ts';
39
- import type { ContinuableStart, ContinuableStartSpec, SubagentFollowupOptions, SubagentInterruptAuthority, SubagentReportOptions } from './continuation.ts';
40
- import type { ContinuableSetupContribution } from './activation-setup-registry.ts';
39
+ import type { ContinuableStart, ContinuableStartSpec, SubagentInterruptAuthority, SubagentSendMessageOptions } from './continuation.ts';
41
40
  import type { SubagentDescendantListEntry, SubagentListEntry } from './list-children.ts';
41
+ import { queueSubagentPrompt } from './internal.ts';
42
42
  export * from './out-of-process.ts';
43
43
  export { AssistantOutputFold, finalAssistantOutput } from './assistant-output.ts';
44
44
  export { SubagentRunId } from './types.ts';
@@ -51,8 +51,7 @@ export { settleRun } from './run-settlement.ts';
51
51
  export { assertSubagentMaxDepth, delegationDepthOf } from './depth.ts';
52
52
  export { appendDelegatedPolicyOverrides, applyChildComposition, captureDelegatedPolicyOverrides, childSessionMeta, parentAgentOptionsForDelegation, resolveChildAgentOptions, resolveChildDepth, SubagentDepthError, } from './child-agent.ts';
53
53
  export type { ChildComposition, DelegatedPolicyOverrides } from './child-agent.ts';
54
- export type { ContinuableStart, ContinuableStartSpec, CoordinatorMessageSource, SubagentFollowupOptions, SubagentInterruptAuthority, SubagentReportDelivery, SubagentReportMessageSource, SubagentReportOptions, SubagentSettledMessageSource, } from './continuation.ts';
55
- export type { ContinuableSetupContribution } from './activation-setup-registry.ts';
54
+ export type { AgentMessageSource, ContinuableStart, ContinuableStartSpec, SubagentInterruptAuthority, SubagentSendMessageOptions, SubagentSettledMessageSource, } from './continuation.ts';
56
55
  export type * from './control-types.ts';
57
56
  export type { SubagentDescendantListEntry } from './list-children.ts';
58
57
  export type { SubagentRunEndInfo, SubagentRunInfo } from './types.ts';
@@ -100,8 +99,6 @@ declare module '@deepseek-ai/cordis' {
100
99
  export declare class SubagentRuntime extends TypertRemoteService {
101
100
  private providers;
102
101
  private continuations;
103
- /** Deployment contributions composed into unpublished continuable children. */
104
- private readonly setupRegistry;
105
102
  /**
106
103
  * The contained lifecycle-edge publisher. Built here because scoped dispatch
107
104
  * keys its carrier by this exact service instance, whose own context filter
@@ -120,21 +117,32 @@ export declare class SubagentRuntime extends TypertRemoteService {
120
117
  */
121
118
  startContinuable(spec: ContinuableStartSpec): Promise<ContinuableStart>;
122
119
  /**
123
- * Deliver one later message to a continuable child as its next FIFO turn. A
124
- * resident child's Agent inbox accepts it directly (waking a `waiting`
125
- * Activation), while an absent one is cold-resumed from its persisted
126
- * Session. The Agent inbox is the only queue, so every accepted message has
127
- * one observable order.
128
- * @param parent - the exact live direct parent authorizing this delivery.
129
- * @param childId - durable child session id.
130
- * @param content - user-role content to deliver.
131
- * @param options - the message source fields and caller cancellation, which stops the
132
- * operation only before inbox acceptance.
120
+ * Steer one model-authored message to the sender's direct parent or direct
121
+ * continuable child. A running target admits it at the nearest step boundary;
122
+ * an idle target starts a turn, and an absent direct child cold-resumes from
123
+ * persistence. The service derives durable sender attribution from the exact
124
+ * live sender. Caller cancellation stops only pre-acceptance work.
125
+ * @param sender - exact live Agent authorizing and originating the message.
126
+ * @param targetId - durable direct-parent or direct-child session id.
127
+ * @param content - model-authored content to deliver.
128
+ * @param options - caller cancellation before inbox acceptance.
133
129
  * @returns the accepted message's inbox id.
134
- * @throws when continuation services are unavailable, parent authority is
135
- * rejected, or the message was not admitted.
130
+ * @throws when continuation services are unavailable, adjacency is rejected,
131
+ * or the message was not admitted.
136
132
  */
137
- followup(parent: Agent, childId: SessionId, content: ContentBlock[], options: SubagentFollowupOptions): Promise<MessageId>;
133
+ sendMessage(sender: Agent, targetId: SessionId, content: ContentBlock[], options: SubagentSendMessageOptions): Promise<MessageId>;
134
+ /**
135
+ * Queue one host-protocol message as a distinct direct-child turn.
136
+ * Symbol-keyed so host adapters can preserve their own provenance without
137
+ * widening the public Service Definition or impersonating an Agent sender.
138
+ * @param parent - exact live direct parent authorizing delivery.
139
+ * @param childId - durable direct-child session id.
140
+ * @param content - host-authored content to deliver.
141
+ * @param source - durable host-protocol provenance.
142
+ * @param signal - caller cancellation before inbox acceptance.
143
+ * @returns the accepted message's inbox id.
144
+ */
145
+ private [queueSubagentPrompt];
138
146
  /**
139
147
  * Interrupt one live continuable child's current turn under a human parent
140
148
  * address or an exact live ancestor Agent. Fire-and-return: the cancel
@@ -151,27 +159,6 @@ export declare class SubagentRuntime extends TypertRemoteService {
151
159
  * live target.
152
160
  */
153
161
  interrupt(targetSessionId: SessionId, authority: SubagentInterruptAuthority): void;
154
- /**
155
- * Deliver selected content from one live continuable child to its durable
156
- * direct parent. The child is the authority credential; callers cannot name a
157
- * recipient. Reporting does not conclude the child's turn or Activation.
158
- * @param child - exact live reporting child.
159
- * @param content - selected model-facing content.
160
- * @param options - parent scheduling and pre-acceptance cancellation.
161
- * @returns the stable identity of the parent-accepted message.
162
- * @throws when continuation services are unavailable, sender authorization
163
- * fails, or the direct parent is not live.
164
- */
165
- reportFrom(child: Agent, content: ContentBlock[], options: SubagentReportOptions): Promise<MessageId>;
166
- /**
167
- * Compose one deployment capability into every continuable child's
168
- * unpublished creation context on fresh creation and cold resume. Grants wait
169
- * for the next Activation; removing the contribution revokes every resident
170
- * installation immediately.
171
- * @param contribution - synchronous child-scope installer.
172
- * @returns the exact Cordis effect disposer.
173
- */
174
- registerContinuableSetup(contribution: ContinuableSetupContribution): () => void;
175
162
  /**
176
163
  * Close continuable admission below exact live parent Agents, stop only their
177
164
  * visible descendant Activations synchronously, then await admitted scoped
@@ -248,10 +235,12 @@ export declare class SubagentRuntime extends TypertRemoteService {
248
235
  * validated browser zone on the accepted message. Success identifies the
249
236
  * message the child's FIFO inbox accepted; later execution is independent of
250
237
  * this call.
238
+ * Image parts are admitted and persisted through the attachment store
239
+ * before delivery, and the child's model must accept image input.
251
240
  * @param request - durable address, minted identity, content, and optional browser zone.
252
241
  * @param signal - carrier cancellation, owning the call until inbox acceptance.
253
242
  * @returns the accepted message's inbox identity.
254
- * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-unsupported`,
243
+ * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
255
244
  * `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
256
245
  * `subagent/not-resumable`, `subagent/unauthorized`,
257
246
  * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
@@ -13,8 +13,8 @@
13
13
  *
14
14
  * Public operations express caller intent: `start` returns one published owned
15
15
  * one-shot run, `startContinuable` establishes a durable continuable child, and
16
- * `followup` delivers later content without exposing whether the child is
17
- * resident. Continuable children never become a {@link SubagentRun}: the
16
+ * `sendMessage` steers between adjacent Agents without exposing whether a child
17
+ * is resident. Continuable children never become a {@link SubagentRun}: the
18
18
  * continuation manager holds their `AgentHandle` directly and orders every turn
19
19
  * through the child's own inbox, so providers contribute only the detached
20
20
  * creation spec and see no handle, turn, or teardown. Child and descendant
@@ -62,19 +62,20 @@ var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn,
62
62
  if (target) Object.defineProperty(target, contextIn.name, descriptor);
63
63
  done = true;
64
64
  };
65
+ import { admitPromptContent } from '@deepseek-ai/dsh-attachment';
65
66
  import { scopeTarget } from '@deepseek-ai/dsh-scope';
66
67
  import { assertObjectJsonSchema } from '@deepseek-ai/dsh-tools';
67
68
  import { canonicalClientTimeZone } from '@deepseek-ai/dsh-util-time';
68
69
  import { Remote, RemoteError, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
69
- import { admitPromptContent, catalogView, rejectCatalogRead, rejectPrompt, validateControlRequest, } from "./control.js";
70
+ import { catalogView, rejectCatalogRead, rejectPrompt, validateControlRequest, } from "./control.js";
70
71
  import { SubagentError } from "./error.js";
71
72
  import { assertSubagentMaxDepth } from "./depth.js";
72
73
  import { createActivationObserver, createLifecycleEmitter, observeRun } from "./lifecycle.js";
73
74
  import SubagentContinuationManager from "./continuation.js";
74
- import SubagentActivationSetupRegistry from "./activation-setup-registry.js";
75
75
  import { listChildren as listSubagentChildren, listDescendants as listSubagentDescendants } from "./list-children.js";
76
76
  import { snapshotSubagentDescriptor } from "./descriptor.js";
77
77
  import { subagentIdentityProjectionDefinition, subagentTimingProjectionDefinition } from "./projection.js";
78
+ import { queueSubagentPrompt } from "./internal.js";
78
79
  export * from "./out-of-process.js";
79
80
  export { AssistantOutputFold, finalAssistantOutput } from "./assistant-output.js";
80
81
  export { SubagentRunId } from "./types.js";
@@ -104,8 +105,6 @@ let SubagentRuntime = (() => {
104
105
  }
105
106
  providers = (__runInitializers(this, _instanceExtraInitializers), new Map());
106
107
  continuations;
107
- /** Deployment contributions composed into unpublished continuable children. */
108
- setupRegistry = new SubagentActivationSetupRegistry();
109
108
  /**
110
109
  * The contained lifecycle-edge publisher. Built here because scoped dispatch
111
110
  * keys its carrier by this exact service instance, whose own context filter
@@ -119,7 +118,7 @@ let SubagentRuntime = (() => {
119
118
  const manager = new SubagentContinuationManager(childCtx, {
120
119
  prepareContinuable: (name, request) => this.prepareContinuable(name, request),
121
120
  observeActivation: (provider, childId, parent) => this.observeActivation(provider, childId, parent),
122
- }, this.setupRegistry);
121
+ });
123
122
  this.continuations = manager;
124
123
  childCtx.effect(() => () => {
125
124
  /* v8 ignore else -- one injected binding owns the slot until its fiber disposes. */
@@ -145,22 +144,35 @@ let SubagentRuntime = (() => {
145
144
  return this.requireContinuations().startContinuable(spec);
146
145
  }
147
146
  /**
148
- * Deliver one later message to a continuable child as its next FIFO turn. A
149
- * resident child's Agent inbox accepts it directly (waking a `waiting`
150
- * Activation), while an absent one is cold-resumed from its persisted
151
- * Session. The Agent inbox is the only queue, so every accepted message has
152
- * one observable order.
153
- * @param parent - the exact live direct parent authorizing this delivery.
154
- * @param childId - durable child session id.
155
- * @param content - user-role content to deliver.
156
- * @param options - the message source fields and caller cancellation, which stops the
157
- * operation only before inbox acceptance.
147
+ * Steer one model-authored message to the sender's direct parent or direct
148
+ * continuable child. A running target admits it at the nearest step boundary;
149
+ * an idle target starts a turn, and an absent direct child cold-resumes from
150
+ * persistence. The service derives durable sender attribution from the exact
151
+ * live sender. Caller cancellation stops only pre-acceptance work.
152
+ * @param sender - exact live Agent authorizing and originating the message.
153
+ * @param targetId - durable direct-parent or direct-child session id.
154
+ * @param content - model-authored content to deliver.
155
+ * @param options - caller cancellation before inbox acceptance.
158
156
  * @returns the accepted message's inbox id.
159
- * @throws when continuation services are unavailable, parent authority is
160
- * rejected, or the message was not admitted.
157
+ * @throws when continuation services are unavailable, adjacency is rejected,
158
+ * or the message was not admitted.
161
159
  */
162
- async followup(parent, childId, content, options) {
163
- return this.requireContinuations().followup(parent, childId, content, options);
160
+ async sendMessage(sender, targetId, content, options) {
161
+ return this.requireContinuations().sendMessage(sender, targetId, content, options);
162
+ }
163
+ /**
164
+ * Queue one host-protocol message as a distinct direct-child turn.
165
+ * Symbol-keyed so host adapters can preserve their own provenance without
166
+ * widening the public Service Definition or impersonating an Agent sender.
167
+ * @param parent - exact live direct parent authorizing delivery.
168
+ * @param childId - durable direct-child session id.
169
+ * @param content - host-authored content to deliver.
170
+ * @param source - durable host-protocol provenance.
171
+ * @param signal - caller cancellation before inbox acceptance.
172
+ * @returns the accepted message's inbox id.
173
+ */
174
+ [queueSubagentPrompt](parent, childId, content, source, signal) {
175
+ return this.requireContinuations().queuePrompt(parent, childId, content, source, signal);
164
176
  }
165
177
  /**
166
178
  * Interrupt one live continuable child's current turn under a human parent
@@ -180,32 +192,6 @@ let SubagentRuntime = (() => {
180
192
  interrupt(targetSessionId, authority) {
181
193
  this.continuations?.interrupt(targetSessionId, authority);
182
194
  }
183
- /**
184
- * Deliver selected content from one live continuable child to its durable
185
- * direct parent. The child is the authority credential; callers cannot name a
186
- * recipient. Reporting does not conclude the child's turn or Activation.
187
- * @param child - exact live reporting child.
188
- * @param content - selected model-facing content.
189
- * @param options - parent scheduling and pre-acceptance cancellation.
190
- * @returns the stable identity of the parent-accepted message.
191
- * @throws when continuation services are unavailable, sender authorization
192
- * fails, or the direct parent is not live.
193
- */
194
- async reportFrom(child, content, options) {
195
- return this.requireContinuations().reportFrom(child, content, options);
196
- }
197
- /**
198
- * Compose one deployment capability into every continuable child's
199
- * unpublished creation context on fresh creation and cold resume. Grants wait
200
- * for the next Activation; removing the contribution revokes every resident
201
- * installation immediately.
202
- * @param contribution - synchronous child-scope installer.
203
- * @returns the exact Cordis effect disposer.
204
- */
205
- registerContinuableSetup(contribution) {
206
- // oxlint-disable-next-line typescript/no-misused-promises -- synchronous disposer
207
- return this.ctx.effect(() => this.setupRegistry.register(contribution), 'subagents.registerContinuableSetup()');
208
- }
209
195
  /**
210
196
  * Close continuable admission below exact live parent Agents, stop only their
211
197
  * visible descendant Activations synchronously, then await admitted scoped
@@ -305,10 +291,12 @@ let SubagentRuntime = (() => {
305
291
  * validated browser zone on the accepted message. Success identifies the
306
292
  * message the child's FIFO inbox accepted; later execution is independent of
307
293
  * this call.
294
+ * Image parts are admitted and persisted through the attachment store
295
+ * before delivery, and the child's model must accept image input.
308
296
  * @param request - durable address, minted identity, content, and optional browser zone.
309
297
  * @param signal - carrier cancellation, owning the call until inbox acceptance.
310
298
  * @returns the accepted message's inbox identity.
311
- * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-unsupported`,
299
+ * @throws {RemoteError} `gateway/bad-request`, `subagent/attachment-invalid`,
312
300
  * `subagent/invalid-time-zone`, `subagent/parent-unavailable`,
313
301
  * `subagent/not-resumable`, `subagent/unauthorized`,
314
302
  * `subagent/delivery-unavailable`, `gateway/cancelled`, or `gateway/internal`.
@@ -316,7 +304,6 @@ let SubagentRuntime = (() => {
316
304
  async prompt(request, signal) {
317
305
  const { parentSessionId, childSessionId, clientTimeZone } = request;
318
306
  validateControlRequest('subagent.prompt', request);
319
- const content = admitPromptContent(childSessionId, request.content);
320
307
  const canonicalTimeZone = clientTimeZone === undefined
321
308
  ? undefined
322
309
  : canonicalClientTimeZone(clientTimeZone);
@@ -333,7 +320,21 @@ let SubagentRuntime = (() => {
333
320
  ...(canonicalTimeZone === undefined ? {} : { clientTimeZone: canonicalTimeZone }),
334
321
  };
335
322
  try {
336
- return { messageId: await this.followup(parent, childSessionId, content, { source, signal }) };
323
+ // Admission precedes delivery: image parts become durable references
324
+ // here, so the child inbox only ever accepts Host-persisted attachments.
325
+ let content;
326
+ if (request.content.every((part) => part.type === 'text')) {
327
+ content = request.content.map(part => ({ type: 'text', text: part.text }));
328
+ }
329
+ else {
330
+ const attachments = this.ctx.get('attachments');
331
+ if (attachments === undefined)
332
+ throw new Error('subagent image prompt requires an attachment store');
333
+ content = await admitPromptContent(attachments, request.content);
334
+ }
335
+ return {
336
+ messageId: await this[queueSubagentPrompt](parent, childSessionId, content, source, signal),
337
+ };
337
338
  }
338
339
  catch (error) {
339
340
  return rejectPrompt(error, childSessionId, signal);
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Continuation integration markers and host adapters outside the public
3
+ * Service Definition and model-facing Agent messaging contract.
4
+ * @module @deepseek-ai/dsh-subagent/internal
5
+ */
6
+ import type { Agent } from '@deepseek-ai/dsh-agent';
7
+ import type { ContentBlock, MessageId, MessageSource } from '@deepseek-ai/dsh-llm';
8
+ import type { SessionId } from '@deepseek-ai/dsh-session';
9
+ import type { ToolDefinition } from '@deepseek-ai/dsh-tools';
10
+ import type SubagentRuntime from './index.ts';
11
+ /** Process-stable identity carried only by the standard adjacent-Agent messaging tool. */
12
+ export declare const adjacentAgentSendMessageTool: unique symbol;
13
+ /**
14
+ * Mark the standard adjacent-Agent messaging tool without changing its model-visible schema.
15
+ * @param definition - the standard `send_message` definition.
16
+ * @returns the same definition with its internal identity installed.
17
+ */
18
+ export declare function markAdjacentAgentSendMessageTool(definition: ToolDefinition): ToolDefinition;
19
+ /**
20
+ * Test whether one visible definition is the standard adjacent-Agent messaging tool.
21
+ * @param definition - the scope-resolved `send_message` candidate.
22
+ * @returns whether the definition carries the internal standard-tool identity.
23
+ */
24
+ export declare function isAdjacentAgentSendMessageTool(definition: ToolDefinition | undefined): boolean;
25
+ /**
26
+ * Process-stable symbol-keyed Queue delivery shared by the bundled runtime
27
+ * entry and this unbundled internal subpath.
28
+ * @internal
29
+ */
30
+ export declare const queueSubagentPrompt: unique symbol;
31
+ /** Runtime face required by the host-only Queue adapter. */
32
+ export interface HostPromptQueue {
33
+ [queueSubagentPrompt](parent: Agent, childId: SessionId, content: ContentBlock[], source: MessageSource, signal: AbortSignal): Promise<MessageId>;
34
+ }
35
+ /**
36
+ * Queue one host-protocol message without exposing another Service operation.
37
+ * @param runtime - subagent runtime owning continuation residency.
38
+ * @param parent - exact live direct parent authorizing delivery.
39
+ * @param childId - durable direct-child session id.
40
+ * @param content - host-authored content to deliver.
41
+ * @param source - durable host-protocol provenance.
42
+ * @param signal - caller cancellation before inbox acceptance.
43
+ * @returns the accepted message's inbox id.
44
+ */
45
+ export declare function queueHostSubagentPrompt(runtime: SubagentRuntime, parent: Agent, childId: SessionId, content: ContentBlock[], source: MessageSource, signal: AbortSignal): Promise<MessageId>;
46
+ //# sourceMappingURL=internal.d.ts.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Continuation integration markers and host adapters outside the public
3
+ * Service Definition and model-facing Agent messaging contract.
4
+ * @module @deepseek-ai/dsh-subagent/internal
5
+ */
6
+ /** Process-stable identity carried only by the standard adjacent-Agent messaging tool. */
7
+ export const adjacentAgentSendMessageTool = Symbol.for('dsh.subagent.adjacentAgentSendMessageTool');
8
+ /**
9
+ * Mark the standard adjacent-Agent messaging tool without changing its model-visible schema.
10
+ * @param definition - the standard `send_message` definition.
11
+ * @returns the same definition with its internal identity installed.
12
+ */
13
+ export function markAdjacentAgentSendMessageTool(definition) {
14
+ Object.defineProperty(definition, adjacentAgentSendMessageTool, { value: true });
15
+ return definition;
16
+ }
17
+ /**
18
+ * Test whether one visible definition is the standard adjacent-Agent messaging tool.
19
+ * @param definition - the scope-resolved `send_message` candidate.
20
+ * @returns whether the definition carries the internal standard-tool identity.
21
+ */
22
+ export function isAdjacentAgentSendMessageTool(definition) {
23
+ return definition !== undefined
24
+ && definition[adjacentAgentSendMessageTool] === true;
25
+ }
26
+ /**
27
+ * Process-stable symbol-keyed Queue delivery shared by the bundled runtime
28
+ * entry and this unbundled internal subpath.
29
+ * @internal
30
+ */
31
+ export const queueSubagentPrompt = Symbol.for('dsh.subagent.queuePrompt');
32
+ /**
33
+ * Queue one host-protocol message without exposing another Service operation.
34
+ * @param runtime - subagent runtime owning continuation residency.
35
+ * @param parent - exact live direct parent authorizing delivery.
36
+ * @param childId - durable direct-child session id.
37
+ * @param content - host-authored content to deliver.
38
+ * @param source - durable host-protocol provenance.
39
+ * @param signal - caller cancellation before inbox acceptance.
40
+ * @returns the accepted message's inbox id.
41
+ */
42
+ export function queueHostSubagentPrompt(runtime, parent, childId, content, source, signal) {
43
+ return runtime[queueSubagentPrompt](parent, childId, content, source, signal);
44
+ }
45
+ //# sourceMappingURL=internal.js.map
@@ -15,6 +15,7 @@
15
15
  */
16
16
  import { randomUUID } from 'node:crypto';
17
17
  import { foldConsumedWork } from '@deepseek-ai/dsh-agent';
18
+ import { SessionLogOffset } from '@deepseek-ai/dsh-session';
18
19
  import { finalAssistantOutput } from "./assistant-output.js";
19
20
  import { SubagentRunId } from "./types.js";
20
21
  /**
@@ -90,7 +91,7 @@ export function createActivationObserver(emit, provider, childId, parent) {
90
91
  // A cold resume replays earlier turns, so this epoch's telemetry must come
91
92
  // from the suffix it actually produced — never the whole session, which
92
93
  // would report a previous epoch's answer when this one opened no turn.
93
- let boundary = 0;
94
+ let boundary = SessionLogOffset(0);
94
95
  // Assigned by `capture()`, which the disposal path always runs before
95
96
  // `settle()`; a resident epoch therefore always has its facts by then.
96
97
  let captured = { stopReason: 'completed' };
@@ -101,11 +102,11 @@ export function createActivationObserver(emit, provider, childId, parent) {
101
102
  : { stopReason: 'error' };
102
103
  return {
103
104
  start: (child) => {
104
- boundary = child.session.events.length;
105
+ boundary = child.session.seq;
105
106
  emit('subagent/start', identity, parent);
106
107
  },
107
108
  capture: (child) => {
108
- const own = child.session.events.slice(boundary);
109
+ const own = child.session.snapshotEvents(boundary);
109
110
  const output = finalAssistantOutput(own);
110
111
  captured = {
111
112
  stopReason: epochStopReason(own),
@@ -4,9 +4,10 @@
4
4
  * corpus; each child's mode/label is the registered `subagent` projection
5
5
  * unit's value, resolved
6
6
  * down a three-rung ladder: the registry's watermark cache for a live child,
7
- * a durable projection-cache row when it serves an own-suffix identity (the
8
- * seq gate), and one shared Session observation otherwise, validated against
9
- * the enumerated lifecycle. The projection fold is the single classification
7
+ * an unseeded durable projection-cache row, and one shared Session observation
8
+ * otherwise. A seeded header deliberately lacks its exact inherited cut, so
9
+ * it takes the body-bearing observation path before classifying an identity.
10
+ * The projection fold is the single classification
10
11
  * authority — this module parses no descriptor
11
12
  * itself. Absent persistence, enumeration is live-only: a cold child is
12
13
  * unreachable for resume anyway, so its absence is capability absence, not an
@@ -35,8 +36,8 @@ export type SubagentDescendantListEntry = SubagentListEntry & {
35
36
  * live-preferred merge of `ctx.sessions` and optional session persistence,
36
37
  * serving each identity from the `subagent` projection unit: the registry's
37
38
  * watermark snapshot for a live child; for a cold one, a durable
38
- * projection-cache read when it serves an own-suffix identity (the seq gate),
39
- * else one bounded-concurrency shared Session observation.
39
+ * projection-cache read for an unseeded lifecycle, else one bounded-concurrency
40
+ * shared Session observation carrying the exact inherited cut.
40
41
  * @see SubagentRuntime.listChildren for the public cancellation and failure contract.
41
42
  * @param ctx - context carrying the session store, the projection registry,
42
43
  * optional persistence, and the optional projection cache.