From 264bc41a13cc24d66e1d5b2b275854b7f99331d5 Mon Sep 17 00:00:00 2001 From: Dudu-0223 Date: Tue, 28 Jul 2026 11:19:22 +0800 Subject: [PATCH] fix(subagent): preserve ordinary start requests --- docs/cordis-catalog/services.md | 2 +- docs/core-data-structures/subagent.i18n.yaml | 4 ++-- docs/core-data-structures/subagent.md | 2 +- docs/core-data-structures/subagent.zh.md | 2 +- .../cordis/tool-cordis/src/api-catalog.ts | 2 +- packages/subagent/subagent/src/index.ts | 6 ++---- .../subagent/subagent/tests/service.spec.ts | 20 +++++++------------ 7 files changed, 15 insertions(+), 23 deletions(-) diff --git a/docs/cordis-catalog/services.md b/docs/cordis-catalog/services.md index ca1d6f75fc..a1ff16cb7e 100644 --- a/docs/cordis-catalog/services.md +++ b/docs/cordis-catalog/services.md @@ -2005,7 +2005,7 @@ list(): string[] * @param request - child prompt, parent, signal, and optional capabilities. * @returns the ready holder-owned run. */ -async start(name: string, request: SubagentStartRequest): Promise +async start(name: string, request: SubagentStartRequest & { readonly continuation?: never }): Promise ``` Types: [Agent](../core-data-structures/core.md) · [ContentBlock](../core-data-structures/core.md) · [ContinuableStart](../core-data-structures/subagent.md) · [ContinuableStartSpec](../core-data-structures/subagent.md) · [SessionId](../core-data-structures/core.md) · [SubagentFollowupOptions](../core-data-structures/subagent.md) · [SubagentFollowupResult](../core-data-structures/subagent.md) · [SubagentProvider](../core-data-structures/subagent.md) · [SubagentRun](../core-data-structures/subagent.md) · [SubagentStartRequest](../core-data-structures/subagent.md) diff --git a/docs/core-data-structures/subagent.i18n.yaml b/docs/core-data-structures/subagent.i18n.yaml index 6fd7de2f74..a535c7ab81 100644 --- a/docs/core-data-structures/subagent.i18n.yaml +++ b/docs/core-data-structures/subagent.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write docs/core-data-structures/subagent.md -subagent.md: 2dc25dfb14b1506edf7f53f6ce0d8681fefa98c6 -subagent.zh.md: 00f2748ad92ae37b0a2fe2616d9e052f9c4b916f +subagent.md: 8f24afec47a970711aae49cae6b3535b9f532e5f +subagent.zh.md: 50c5cb887ef814c074a85fc4fee9cd2fe85d685c diff --git a/docs/core-data-structures/subagent.md b/docs/core-data-structures/subagent.md index 2dc25dfb14..8f24afec47 100644 --- a/docs/core-data-structures/subagent.md +++ b/docs/core-data-structures/subagent.md @@ -94,7 +94,7 @@ interface SubagentStartRequest { `signal` is the single cancellation channel before and after readiness. The [subagent composition-controls Agent Note](../../.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md) owns the persona, live global-tool filter, absolute-depth, and visibility-not-authority rationale. -Providers receive a separate resolved shape. Raw `SubagentService.start()` clears continuation state, while `startContinuable()` alone supplies the service-allocated identity and descriptor. +Providers receive a separate resolved shape. The `SubagentService.start()` parameter type excludes continuation state, while `startContinuable()` alone supplies the service-allocated identity and descriptor. ```ts type-equiv /** diff --git a/docs/core-data-structures/subagent.zh.md b/docs/core-data-structures/subagent.zh.md index 00f2748ad9..50c5cb887e 100644 --- a/docs/core-data-structures/subagent.zh.md +++ b/docs/core-data-structures/subagent.zh.md @@ -94,7 +94,7 @@ interface SubagentStartRequest { `signal` 是就绪前后唯一的取消通道。[subagent 组合控制 Agent Note](../../.agents/notes/implemented/feature/2026-07-12-subagent-persona-tool-filter-and-depth.md)规定 persona、live 全局工具过滤、绝对深度以及「可见性而非权限」的设计理由。 -提供方会接收单独的已解析请求类型。直接调用 `SubagentService.start()` 会清除继续执行状态;只有 `startContinuable()` 才会提供由服务分配的标识和描述符。 +提供方会接收单独的已解析请求类型。`SubagentService.start()` 的参数类型不包含继续执行状态;只有 `startContinuable()` 才会提供由服务分配的标识和描述符。 ```ts type-equiv /** diff --git a/packages/cordis/tool-cordis/src/api-catalog.ts b/packages/cordis/tool-cordis/src/api-catalog.ts index 224bf25e0b..6acad85629 100644 --- a/packages/cordis/tool-cordis/src/api-catalog.ts +++ b/packages/cordis/tool-cordis/src/api-catalog.ts @@ -905,7 +905,7 @@ export const SERVICE_API: readonly ServiceApiEntry[] = [ jsDoc: '/**\n * List registered provider names in insertion order.\n * @returns the registered names.\n */', }, { - signature: 'async start(name: string, request: SubagentStartRequest): Promise', + signature: 'async start(name: string, request: SubagentStartRequest & { readonly continuation?: never }): Promise', jsDoc: '/**\n * Establish a ready child on the named provider. Capability and semantic\n * checks run before delegation. Provider ownership lasts until its promise\n * fulfills; a rejection therefore has no run for the caller to dispose and\n * emits no run lifecycle events.\n * @param name - the provider to use.\n * @param request - child prompt, parent, signal, and optional capabilities.\n * @returns the ready holder-owned run.\n */', }, ], diff --git a/packages/subagent/subagent/src/index.ts b/packages/subagent/subagent/src/index.ts index 508388e4f5..023ac1fe26 100644 --- a/packages/subagent/subagent/src/index.ts +++ b/packages/subagent/subagent/src/index.ts @@ -298,10 +298,8 @@ export class SubagentService extends Service { * @param request - child prompt, parent, signal, and optional capabilities. * @returns the ready holder-owned run. */ - async start(name: string, request: SubagentStartRequest): Promise { - // A provider request is structurally assignable to the caller shape. Clear - // its wider field so only startContinuable can supply service-owned state. - return this.startProvider(name, { ...request, continuation: undefined }) + async start(name: string, request: SubagentStartRequest & { readonly continuation?: never }): Promise { + return this.startProvider(name, request) } /** Validate and dispatch one ordinary or service-resolved provider start. */ diff --git a/packages/subagent/subagent/tests/service.spec.ts b/packages/subagent/subagent/tests/service.spec.ts index 9a274abe22..e86de737ed 100644 --- a/packages/subagent/subagent/tests/service.spec.ts +++ b/packages/subagent/subagent/tests/service.spec.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, vi } from 'vitest' +import { describe, expect, expectTypeOf, it, vi } from 'vitest' import { Context } from 'cordis' import { type Agent } from '@deepseek-ai/dsh-agent' @@ -105,22 +105,16 @@ describe('SubagentService', () => { .rejects.toMatchObject({ code: 'NO_PROVIDER' }) }) - it('keeps provider continuation state out of raw start and exposes no raw resume operation', async () => { + it('borrows ordinary start requests and exposes no provider continuation operations', async () => { const { subagents } = await service() const provider = new StubProvider('one-shot') subagents.registerProvider(provider) - const descriptor = snapshotSubagentDescriptor({ provider: 'one-shot' }) - const sessionId = SessionId('continuable-child') - const parent = fakeParent() - const signal = new AbortController().signal + const request = baseRequest() + await subagents.start('one-shot', request) - const providerRequest: SubagentProviderStartRequest = { - ...baseRequest({ parent, signal }), - continuation: { sessionId, descriptor }, - } - await subagents.start('one-shot', providerRequest) - - expect(provider.lastRequest?.continuation).toBeUndefined() + expect(provider.lastRequest).toBe(request) + expectTypeOf() + .not.toExtend[1]>() expect('resume' in subagents).toBe(false) })