From 243f6629ef4eae3e671199a512e7154946828935 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Tue, 25 Aug 2026 20:52:46 +0800 Subject: [PATCH 1/3] refactor(apiproxy): delete the goal unary domain The goal domain has been served by GoalService's @Remote namespace since it shipped; the API Proxy copy was a second implementation of the same six mutations. Remove the goals contract, schemas, route rows, IApiClient stub, host implementation, and the fixture's compatibility face, leaving ctx.remote.goals as the only path. The fixture's goal fold keeps its coverage through the Goal Remotes: its lifecycle case moves out of the unary-dispatch test, which no longer has goal rows to cover. --- .../tests/fake-api.client.ts | 9 -- packages/client/connection/src/client/api.ts | 1 - .../client/connection/src/client/fixture.ts | 58 ----------- .../client/connection/src/client/index.ts | 1 - .../connection/tests/fake-api.client.ts | 9 -- .../connection/tests/fixture.client.spec.ts | 46 +++++---- .../tests/workspaces-service.client.spec.ts | 1 - packages/host/apiproxy/src/api-proxy.ts | 96 +------------------ .../host/apiproxy/src/api/goals.schema.ts | 79 --------------- packages/host/apiproxy/src/api/goals.ts | 53 ---------- packages/host/apiproxy/src/api/index.ts | 3 - packages/host/apiproxy/src/api/rpc-map.ts | 7 -- packages/host/apiproxy/src/fetch/client.ts | 31 ------ packages/host/apiproxy/src/fetch/handler.ts | 14 --- packages/host/apiproxy/src/index.ts | 2 - .../tests/api-proxy-agent-preset.spec.ts | 18 ---- .../apiproxy/tests/client-handler.spec.ts | 69 +------------ .../host/apiproxy/tests/fetch-carrier.spec.ts | 20 ---- .../host/apiproxy/tests/rpc-schemas.spec.ts | 10 -- 19 files changed, 28 insertions(+), 499 deletions(-) delete mode 100644 packages/host/apiproxy/src/api/goals.schema.ts delete mode 100644 packages/host/apiproxy/src/api/goals.ts diff --git a/packages/api/session-controller/tests/fake-api.client.ts b/packages/api/session-controller/tests/fake-api.client.ts index c0a1b1b780..9415ef74eb 100644 --- a/packages/api/session-controller/tests/fake-api.client.ts +++ b/packages/api/session-controller/tests/fake-api.client.ts @@ -255,15 +255,6 @@ export class FakeApiClient implements IApiClient { list: (payload: unknown) => this.record('skill.list', payload, this.onSkillList(payload)), } - readonly goals: IApiClient['goals'] = { - create: payload => this.record('goal.create', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - edit: payload => this.record('goal.edit', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - pause: payload => this.record('goal.pause', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - resume: payload => this.record('goal.resume', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - complete: payload => this.record('goal.complete', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - clear: payload => this.record('goal.clear', payload, Promise.resolve(ok({ cleared: true as const }))), - } - readonly settings: IApiClient['settings'] = { describe: payload => this.record('settings.describe', payload, Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [] }))), openDocument: payload => this.record('settings.openDocument', payload, Promise.resolve(ok({ opened: true as const }))), diff --git a/packages/client/connection/src/client/api.ts b/packages/client/connection/src/client/api.ts index 2d0170cb5f..a0ea093533 100644 --- a/packages/client/connection/src/client/api.ts +++ b/packages/client/connection/src/client/api.ts @@ -12,7 +12,6 @@ export type { SkillsApi, SkillEntry, ModelCatalog, ModelCatalogFailure, ModelCatalogModel, ModelProviderGroup, ModelReasoning, ModelReasoningEffort, ModelSelection, - GoalsApi, GoalRef, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, SubagentsApi, SubagentAddress, SubagentCatalog, SubagentListEntry, SubagentPromptReceipt, diff --git a/packages/client/connection/src/client/fixture.ts b/packages/client/connection/src/client/fixture.ts index c27a1cae4c..b569d26f7b 100644 --- a/packages/client/connection/src/client/fixture.ts +++ b/packages/client/connection/src/client/fixture.ts @@ -2235,18 +2235,6 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld { return { ok: true, value: goalView(projection) } } - const mapGoalResult = (result: RpcResult, map: (value: T) => U): RpcResult => ( - result.ok ? { ok: true, value: map(result.value) } : result - ) - - const goalRefResult = (result: RpcResult): RpcResult<{ ref: { id: never; revision: number } }> => ( - mapGoalResult(result, view => ({ ref: { id: view.id as never, revision: view.revision } })) - ) - - const legacyGoalResponse = (request: RpcRequest

, result: RpcResult): Promise> => ( - Promise.resolve({ rpcId: request.rpcId, result }) - ) - /** At most one in-flight replay per session; cancel clears it. */ const replays = new Map; finish(aborted: boolean): void }>() @@ -3301,46 +3289,6 @@ function createFixtureWorld(options: FixtureOptions): FixtureWorld { }) }, }, - goals: { - // Compatibility face only: old API Proxy payloads and acknowledgements - // adapt to the canonical fixture Remote implementation above. - create: request => legacyGoalResponse( - request, - mapGoalResult( - goalRemotes.create(request.payload.sessionId, { - objective: request.payload.objective, - ...request.payload.maxGoalRounds === undefined ? {} : { maxGoalRounds: request.payload.maxGoalRounds }, - }), - value => ({ ref: { id: value.ref.id as never, revision: value.ref.revision } }), - ), - ), - edit: request => legacyGoalResponse( - request, - goalRefResult(goalRemotes.edit(request.payload.sessionId, request.payload.ref, { - ...request.payload.objective === undefined ? {} : { objective: request.payload.objective }, - ...request.payload.maxGoalRounds === undefined ? {} : { maxGoalRounds: request.payload.maxGoalRounds }, - })), - ), - pause: request => legacyGoalResponse( - request, - goalRefResult(goalRemotes.pause(request.payload.sessionId, request.payload.ref)), - ), - resume: request => legacyGoalResponse( - request, - goalRefResult(goalRemotes.resume(request.payload.sessionId, request.payload.ref)), - ), - complete: request => legacyGoalResponse( - request, - goalRefResult(goalRemotes.complete(request.payload.sessionId, request.payload.ref)), - ), - clear: request => legacyGoalResponse( - request, - mapGoalResult( - goalRemotes.clear(request.payload.sessionId, request.payload.ref), - () => ({ cleared: true as const }), - ), - ), - }, settings: { // Only the resolved DeepSeek address needed by first-run readiness is // represented here. Fixture-backed journeys do not open its Models @@ -3600,12 +3548,6 @@ export class FixtureApiClient extends AbstractApiClient { case 'agentPreset.copy': return this.api.agentPresets.copy(request) case 'agentPreset.openDocument': return this.api.agentPresets.openDocument(request, new AbortController().signal) case 'agentPreset.remove': return this.api.agentPresets.remove(request) - case 'goal.create': return this.api.goals.create(request) - case 'goal.edit': return this.api.goals.edit(request) - case 'goal.pause': return this.api.goals.pause(request) - case 'goal.resume': return this.api.goals.resume(request) - case 'goal.complete': return this.api.goals.complete(request) - case 'goal.clear': return this.api.goals.clear(request) case 'settings.describe': return this.api.settings.describe(request) case 'settings.openDocument': return this.api.settings.openDocument(request, signal) case 'settings.update': return this.api.settings.update(request) diff --git a/packages/client/connection/src/client/index.ts b/packages/client/connection/src/client/index.ts index 0b01c1378a..d53c48d019 100644 --- a/packages/client/connection/src/client/index.ts +++ b/packages/client/connection/src/client/index.ts @@ -39,7 +39,6 @@ export type { RpcRequest, RpcResponse, RpcResult, RpcError, RpcErrorCode, ClientRequest, ServerResponse, RpcMessage, HostDescription, IApiClient, SessionId, SessionEvent, ContentBlock, StreamChunk, - GoalsApi, GoalRef, SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView, CredentialsApi, CredentialView, ConfigurableProviderView, DiscoveredModelView, LlmApi, } from './api.ts' diff --git a/packages/client/connection/tests/fake-api.client.ts b/packages/client/connection/tests/fake-api.client.ts index 74e153f15c..1d0266b8d0 100644 --- a/packages/client/connection/tests/fake-api.client.ts +++ b/packages/client/connection/tests/fake-api.client.ts @@ -116,15 +116,6 @@ export class FakeApiClient implements IApiClient { list: (payload: unknown) => this.record('skill.list', payload, this.onSkillList(payload)), } - readonly goals: IApiClient['goals'] = { - create: payload => this.record('goal.create', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - edit: payload => this.record('goal.edit', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - pause: payload => this.record('goal.pause', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - resume: payload => this.record('goal.resume', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - complete: payload => this.record('goal.complete', payload, Promise.resolve(ok({ ref: { id: 'fake-goal' as never, revision: 1 } }))), - clear: payload => this.record('goal.clear', payload, Promise.resolve(ok({ cleared: true as const }))), - } - readonly settings: IApiClient['settings'] = { describe: payload => this.record('settings.describe', payload, Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [] }))), openDocument: payload => this.record('settings.openDocument', payload, Promise.resolve(ok({ opened: true as const }))), diff --git a/packages/client/connection/tests/fixture.client.spec.ts b/packages/client/connection/tests/fixture.client.spec.ts index 918b802aab..0bd00622bc 100644 --- a/packages/client/connection/tests/fixture.client.spec.ts +++ b/packages/client/connection/tests/fixture.client.spec.ts @@ -1567,29 +1567,33 @@ describe('FixtureApiClient (protocol-level fake carrier)', () => { const moved = await workspaces.insertSessionBefore({ workspaceId: wsid, sessionId: attached.result.value.sessionId }) if (!moved.result.ok) throw new Error('workspace move failed') expect(moved.result.value.workspace.sessionIds).toEqual([attached.result.value.sessionId]) - // Goal lifecycle over the fixture fold: create → edit → pause → resume → complete → clear; - // every mutation acknowledges with the NEW CAS ref (state rides the projection frames). - const goalCreated = await client.goals.create({ sessionId: id, objective: 'ship it' }) - if (!goalCreated.result.ok) throw new Error('goal create failed') - let ref = goalCreated.result.value.ref - expect(ref.revision).toBe(1) - const edited = await client.goals.edit({ sessionId: id, ref, objective: 'ship it v2' }) - if (!edited.result.ok) throw new Error('goal edit failed') - ref = edited.result.value.ref - const paused = await client.goals.pause({ sessionId: id, ref }) - if (!paused.result.ok) throw new Error('goal pause failed') - ref = paused.result.value.ref - const resumed = await client.goals.resume({ sessionId: id, ref }) - if (!resumed.result.ok) throw new Error('goal resume failed') - ref = resumed.result.value.ref + }) + + it('folds the goal lifecycle over the Goal Remotes', async () => { + const client = new FixtureApiClient() + const sessions = createSessionClient(client.rpc) + const created = await sessions.create({}) + if (!created.result.ok) throw new Error('create failed') + const id = created.result.value.sessionId + const goal = (endpoint: string, args: Record) => + client.rpc.call('/api', endpoint, { args: { agentId: id, ...args } }) + + // create → edit → pause → resume → complete → clear; each mutation advances the CAS + // revision by one (state rides the projection frames). + const goalCreated = await goal('goals/create', { request: { objective: 'ship it' } }) + if (!goalCreated.ok) throw new Error('goal create failed') + const { id: goalId, revision } = (goalCreated.value as { ref: { id: string; revision: number } }).ref + expect(revision).toBe(1) + const ref = (at: number) => ({ id: goalId, revision: at }) + expect((await goal('goals/edit', { ref: ref(1), request: { objective: 'ship it v2' } })).ok).toBe(true) + expect((await goal('goals/pause', { ref: ref(2) })).ok).toBe(true) + expect((await goal('goals/resume', { ref: ref(3) })).ok).toBe(true) // A stale ref loses the CAS check. - expect((await client.goals.pause({ sessionId: id, ref: { ...ref, revision: 1 } })).result.ok).toBe(false) - const completed = await client.goals.complete({ sessionId: id, ref }) - if (!completed.result.ok) throw new Error('goal complete failed') - ref = completed.result.value.ref + expect((await goal('goals/pause', { ref: ref(1) })).ok).toBe(false) + expect((await goal('goals/complete', { ref: ref(4) })).ok).toBe(true) // complete → complete is an invalid transition. - expect((await client.goals.complete({ sessionId: id, ref })).result.ok).toBe(false) - expect((await client.goals.clear({ sessionId: id, ref })).result).toEqual({ ok: true, value: { cleared: true } }) + expect((await goal('goals/complete', { ref: ref(5) })).ok).toBe(false) + expect(await goal('goals/clear', { ref: ref(5) })).toEqual({ ok: true, value: ref(6) }) const goalHistory = await sessions.history({ sessionId: id }) if (!goalHistory.result.ok) throw new Error('goal history failed') diff --git a/packages/client/ui-workspace/tests/workspaces-service.client.spec.ts b/packages/client/ui-workspace/tests/workspaces-service.client.spec.ts index 9c2dfd4fb8..ad9baff764 100644 --- a/packages/client/ui-workspace/tests/workspaces-service.client.spec.ts +++ b/packages/client/ui-workspace/tests/workspaces-service.client.spec.ts @@ -188,7 +188,6 @@ class FakeApiClient implements IApiClient { declare readonly subagents: IApiClient['subagents'] declare readonly skills: IApiClient['skills'] declare readonly agentPresets: IApiClient['agentPresets'] - declare readonly goals: IApiClient['goals'] declare readonly settings: IApiClient['settings'] declare readonly credentials: IApiClient['credentials'] declare readonly llm: IApiClient['llm'] diff --git a/packages/host/apiproxy/src/api-proxy.ts b/packages/host/apiproxy/src/api-proxy.ts index 1a8835383a..56b90e64f2 100644 --- a/packages/host/apiproxy/src/api-proxy.ts +++ b/packages/host/apiproxy/src/api-proxy.ts @@ -6,7 +6,7 @@ import { homedir } from 'node:os' import { dirname } from 'node:path' import type { Context } from '@deepseek-ai/cordis' -import type { Agent, ModelSelection } from '@deepseek-ai/dsh-agent' +import type { ModelSelection } from '@deepseek-ai/dsh-agent' import type {} from '@deepseek-ai/dsh-agent-presets/types' import type { Session, SessionId } from '@deepseek-ai/dsh-session' import { SubagentError } from '@deepseek-ai/dsh-subagent' @@ -16,7 +16,7 @@ import { PresetNotWritableError, UnknownPresetError, } from '@deepseek-ai/dsh-agent-presets' import type { - ApiProxy, ConfigurableProviderView, CredentialView, GoalRef, + ApiProxy, ConfigurableProviderView, CredentialView, SettingsNamespaceView, } from './api/index.ts' import type { SessionRequestId } from '@deepseek-ai/dsh-api-session-controller/types' @@ -32,9 +32,6 @@ import { type SessionLogCompressionLevel, } from './session-export.ts' import type { SessionRawArtifact } from '@deepseek-ai/dsh-session-persistence' -// GoalError narrows domain rejections to their stable codes at the wire boundary. -import { GoalError } from '@deepseek-ai/dsh-goal' -import type { GoalRef as CoreGoalRef } from '@deepseek-ai/dsh-goal' // Type-only edges: resolve the command-change stream and `ctx.get('skills')`. import type {} from '@deepseek-ai/dsh-commands' import type {} from '@deepseek-ai/dsh-skill' @@ -270,47 +267,6 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro } } - /** - * Resolve the goal service THIS agent runs. - * - * The service is per session: an agent preset mounts it behind an `isolate` - * realm, which no host context resolves. Reading it from the root would - * answer "absent" for a session whose composition mounts it — so the lookup - * is keyed by the agent, and only a deployment composing it nowhere is - * genuinely absent. - */ - function goalServiceFor(agent: Agent): NonNullable>> | { error: RpcError } { - const presets = ctx.get('agentPresets') - const goals = presets?.serviceFor(agent, 'goals') ?? ctx.get('goals') - if (goals === undefined) { - return { error: { code: 'internal', message: 'goal service is absent: neither this session\'s agent preset nor the host composition mounts @deepseek-ai/dsh-goal', details: {} } } - } - return goals - } - - /** Map one goal-domain rejection to the wire error (stable GoalError codes ride in details). */ - function goalError(request: RpcRequest, error: unknown): RpcResponse { - const details = error instanceof GoalError ? { goalCode: error.code } : {} - return err(request, { code: 'internal', message: String(error), details }) - } - - /** Resolve a session's agent, apply one goal mutation, and acknowledge with the new CAS ref. */ - async function mutateGoal( - request: RpcRequest<{ sessionId: SessionId }>, - mutation: (goals: NonNullable>>, agent: Agent) => CoreGoalRef, - ): Promise> { - const found = await agentFor(request.payload.sessionId) - if ('error' in found) return err(request, found.error) - const goals = goalServiceFor(found.agent) - if ('error' in goals) return err(request, goals.error) - try { - const ref = mutation(goals, found.agent) - return ok(request, { ref: { id: ref.id, revision: ref.revision } }) - } catch (error: unknown) { - return goalError(request, error) - } - } - /** Missing-service report shared by the settings domain (skills-domain stance). */ function settingsAbsent(): RpcError { return { code: 'internal', message: 'settings service is absent: this deployment does not mount a settings provider (e.g. @deepseek-ai/dsh-settings-file) in its composition', details: {} } @@ -623,54 +579,6 @@ export function createApiProxy(ctx: Context, defaults: ApiProxyDefaults): ApiPro }, }, - goals: { - // Mutations only — the read side is the 'goal' session projection. - // Every verb resolves the session's agent (agentFor: implicit cold - // resume, the command.* precedent) and acknowledges with the new CAS - // ref; the committed goal/change event carries the whole value to every - // client through the projection frames. - async create(request) { - const { objective, maxGoalRounds } = request.payload - return mutateGoal(request, (goals, agent) => goals.create(agent, { - objective, - ...(maxGoalRounds !== undefined ? { maxGoalRounds } : {}), - })) - }, - - async edit(request) { - const { ref, objective, maxGoalRounds } = request.payload - return mutateGoal(request, (goals, agent) => goals.edit(agent, ref, { - ...(objective !== undefined ? { objective } : {}), - ...(maxGoalRounds !== undefined ? { maxGoalRounds } : {}), - })) - }, - - async pause(request) { - return mutateGoal(request, (goals, agent) => goals.pause(agent, request.payload.ref)) - }, - - async resume(request) { - return mutateGoal(request, (goals, agent) => goals.resume(agent, request.payload.ref)) - }, - - async complete(request) { - return mutateGoal(request, (goals, agent) => goals.complete(agent, request.payload.ref)) - }, - - async clear(request) { - const found = await agentFor(request.payload.sessionId) - if ('error' in found) return err(request, found.error) - const goals = goalServiceFor(found.agent) - if ('error' in goals) return err(request, goals.error) - try { - goals.clear(found.agent, request.payload.ref) - return ok(request, { cleared: true as const }) - } catch (error: unknown) { - return goalError(request, error) - } - }, - }, - agentPresets: { // A deployment with no roster answers with an empty list rather than an // error: composing no presets is a valid deployment, and the browser diff --git a/packages/host/apiproxy/src/api/goals.schema.ts b/packages/host/apiproxy/src/api/goals.schema.ts deleted file mode 100644 index 24615502c3..0000000000 --- a/packages/host/apiproxy/src/api/goals.schema.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * goals domain zod schemas. Mutation-only shapes: every value schema is a - * `{ ref }` acknowledgement (clear: `{ cleared }`) — the current goal state - * travels exclusively on the 'goal' session projection. - */ - -import { z } from 'zod' -import type { Wire } from './rpc.schema.ts' -import type { GoalRef, RequestPayload, ResponseValue } from './index.ts' - -/** GoalRef schema. */ -export const goalRefSchema = z.object({ - id: z.string(), - revision: z.number().int().positive(), -}) as unknown as z.ZodType> - -/** Shared `{ ref }` acknowledgement value of every non-clear mutation. */ -const goalRefValueSchema = z.object({ ref: goalRefSchema }) - -/** goal.create request payload. */ -export const goalCreateRequestSchema = z.object({ - sessionId: z.string(), - objective: z.string().min(1), - maxGoalRounds: z.number().int().positive().optional(), -}) as unknown as z.ZodType>> - -/** goal.create response value. */ -export const goalCreateValueSchema = goalRefValueSchema as unknown as z.ZodType>> - -/** goal.edit request payload. */ -export const goalEditRequestSchema = z.object({ - sessionId: z.string(), - ref: goalRefSchema, - objective: z.string().min(1).optional(), - maxGoalRounds: z.number().int().positive().optional(), -}).refine(value => value.objective !== undefined || value.maxGoalRounds !== undefined, { - message: 'goal.edit requires objective or maxGoalRounds', -}) as unknown as z.ZodType>> - -/** goal.edit response value. */ -export const goalEditValueSchema = goalRefValueSchema as unknown as z.ZodType>> - -/** goal.pause request payload. */ -export const goalPauseRequestSchema = z.object({ - sessionId: z.string(), - ref: goalRefSchema, -}) as unknown as z.ZodType>> - -/** goal.pause response value. */ -export const goalPauseValueSchema = goalRefValueSchema as unknown as z.ZodType>> - -/** goal.resume request payload. */ -export const goalResumeRequestSchema = z.object({ - sessionId: z.string(), - ref: goalRefSchema, -}) as unknown as z.ZodType>> - -/** goal.resume response value. */ -export const goalResumeValueSchema = goalRefValueSchema as unknown as z.ZodType>> - -/** goal.complete request payload. */ -export const goalCompleteRequestSchema = z.object({ - sessionId: z.string(), - ref: goalRefSchema, -}) as unknown as z.ZodType>> - -/** goal.complete response value. */ -export const goalCompleteValueSchema = goalRefValueSchema as unknown as z.ZodType>> - -/** goal.clear request payload. */ -export const goalClearRequestSchema = z.object({ - sessionId: z.string(), - ref: goalRefSchema, -}) as unknown as z.ZodType>> - -/** goal.clear response value. */ -export const goalClearValueSchema = z.object({ - cleared: z.literal(true), -}) as unknown as z.ZodType>> diff --git a/packages/host/apiproxy/src/api/goals.ts b/packages/host/apiproxy/src/api/goals.ts deleted file mode 100644 index abf6e53a07..0000000000 --- a/packages/host/apiproxy/src/api/goals.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * goals domain contract. Method signatures are the source of truth: - * unary methods take the RpcRequest

narrow form and the impl echoes rpcId. - * - * Mutations only: the read side is the `goal` Session projection carried by - * Session Controller history and control streams. There is no goal.get or - * separate wire goal view; responses acknowledge with the new CAS ref, and - * committed goal/change events update the projection. - */ - -import type { Branded } from '@deepseek-ai/dsh-brand' -import type { SessionId } from '@deepseek-ai/dsh-session/types' -import type { RpcRequest, RpcResponse } from './rpc.ts' - -/** Identifies one goal across its durable revisions. */ -export type GoalId = Branded<'GoalId'> - -/** Compare-and-set identity for one exact goal revision. */ -export interface GoalRef { - readonly id: GoalId - readonly revision: number -} - -/** - * Goal-domain unary methods. Every mutation resolves an ordinary session's - * Agent and applies one CAS-guarded verb; session-backed subagents reject with - * `agent-busy`. - */ -export interface GoalsApi { - /** Create and arm a goal. */ - create(request: RpcRequest<{ sessionId: SessionId; objective: string; maxGoalRounds?: number }>): - Promise> - - /** Edit objective and/or round cap without changing phase. */ - edit(request: RpcRequest<{ sessionId: SessionId; ref: GoalRef; objective?: string; maxGoalRounds?: number }>): - Promise> - - /** Pause an active goal and disarm automatic continuation. */ - pause(request: RpcRequest<{ sessionId: SessionId; ref: GoalRef }>): - Promise> - - /** Resume and arm a stopped goal. */ - resume(request: RpcRequest<{ sessionId: SessionId; ref: GoalRef }>): - Promise> - - /** Mark a current non-complete goal complete and disarm it. */ - complete(request: RpcRequest<{ sessionId: SessionId; ref: GoalRef }>): - Promise> - - /** Clear the current goal while retaining a durable tombstone and history. */ - clear(request: RpcRequest<{ sessionId: SessionId; ref: GoalRef }>): - Promise> -} diff --git a/packages/host/apiproxy/src/api/index.ts b/packages/host/apiproxy/src/api/index.ts index 4cfcea6418..5ab6e52043 100644 --- a/packages/host/apiproxy/src/api/index.ts +++ b/packages/host/apiproxy/src/api/index.ts @@ -8,7 +8,6 @@ import type { HostApi } from './host.ts' import type { AgentPresetsApi } from './agent-presets.ts' import type { SkillsApi } from './skills.ts' import type { SubagentsApi } from './subagents.ts' -import type { GoalsApi } from './goals.ts' import type { SettingsApi } from './settings.ts' import type { CredentialsApi } from './credentials.ts' import type { LlmApi } from './llm.ts' @@ -20,7 +19,6 @@ export interface ApiProxy { host: HostApi skills: SkillsApi agentPresets: AgentPresetsApi - goals: GoalsApi settings: SettingsApi credentials: CredentialsApi llm: LlmApi @@ -40,7 +38,6 @@ export type { } from './subagents.ts' export type { SkillsApi, SkillEntry } from './skills.ts' export type { AgentPresetsApi, AgentPresetEntry } from './agent-presets.ts' -export type { GoalsApi, GoalId, GoalRef } from './goals.ts' export type { SettingsApi, SettingsNamespaceView, SettingsPathOpView, SettingsSecretView } from './settings.ts' export type { CredentialsApi, CredentialView } from './credentials.ts' export type { ConfigurableProviderView, DiscoveredModelView, LlmApi } from './llm.ts' diff --git a/packages/host/apiproxy/src/api/rpc-map.ts b/packages/host/apiproxy/src/api/rpc-map.ts index b24c5d9dfe..edeb2d10a8 100644 --- a/packages/host/apiproxy/src/api/rpc-map.ts +++ b/packages/host/apiproxy/src/api/rpc-map.ts @@ -6,7 +6,6 @@ import type { HostApi } from './host.ts' import type { AgentPresetsApi } from './agent-presets.ts' import type { SkillsApi } from './skills.ts' -import type { GoalsApi } from './goals.ts' import type { SettingsApi } from './settings.ts' import type { CredentialsApi } from './credentials.ts' import type { LlmApi } from './llm.ts' @@ -34,12 +33,6 @@ export interface RpcMethodMap { 'agentPreset.copy': AgentPresetsApi['copy'] 'agentPreset.openDocument': AgentPresetsApi['openDocument'] 'agentPreset.remove': AgentPresetsApi['remove'] - 'goal.create': GoalsApi['create'] - 'goal.edit': GoalsApi['edit'] - 'goal.pause': GoalsApi['pause'] - 'goal.resume': GoalsApi['resume'] - 'goal.complete': GoalsApi['complete'] - 'goal.clear': GoalsApi['clear'] 'settings.describe': SettingsApi['describe'] 'settings.openDocument': SettingsApi['openDocument'] 'settings.update': SettingsApi['update'] diff --git a/packages/host/apiproxy/src/fetch/client.ts b/packages/host/apiproxy/src/fetch/client.ts index 8cb839d20a..c68ca65410 100644 --- a/packages/host/apiproxy/src/fetch/client.ts +++ b/packages/host/apiproxy/src/fetch/client.ts @@ -21,14 +21,6 @@ import { agentPresetCopyValueSchema, agentPresetListValueSchema, agentPresetOpenDocumentValueSchema, agentPresetReadValueSchema, agentPresetRemoveValueSchema, agentPresetSelectValueSchema, } from '../api/agent-presets.schema.ts' -import { - goalCreateValueSchema, - goalEditValueSchema, - goalPauseValueSchema, - goalResumeValueSchema, - goalCompleteValueSchema, - goalClearValueSchema, -} from '../api/goals.schema.ts' import { settingsDescribeValueSchema, settingsMutateValueSchema, settingsOpenDocumentValueSchema, settingsReplaceValueSchema, settingsUpdateValueSchema, @@ -79,14 +71,6 @@ export interface IApiClient { openDocument(payload: RequestPayload<'agentPreset.openDocument'>, signal?: AbortSignal): Promise>> remove(payload: RequestPayload<'agentPreset.remove'>, signal?: AbortSignal): Promise>> } - goals: { - create(payload: RequestPayload<'goal.create'>, signal?: AbortSignal): Promise>> - edit(payload: RequestPayload<'goal.edit'>, signal?: AbortSignal): Promise>> - pause(payload: RequestPayload<'goal.pause'>, signal?: AbortSignal): Promise>> - resume(payload: RequestPayload<'goal.resume'>, signal?: AbortSignal): Promise>> - complete(payload: RequestPayload<'goal.complete'>, signal?: AbortSignal): Promise>> - clear(payload: RequestPayload<'goal.clear'>, signal?: AbortSignal): Promise>> - } settings: { describe(payload: RequestPayload<'settings.describe'>, signal?: AbortSignal): Promise>> openDocument(payload: RequestPayload<'settings.openDocument'>, signal?: AbortSignal): Promise>> @@ -126,12 +110,6 @@ const UNARY_VALUE_SCHEMAS: { [K in keyof RpcMethodMap]: z.ZodType this.callUnary('agentPreset.remove', payload, signal), } - readonly goals: IApiClient['goals'] = { - create: (payload, signal) => this.callUnary('goal.create', payload, signal), - edit: (payload, signal) => this.callUnary('goal.edit', payload, signal), - pause: (payload, signal) => this.callUnary('goal.pause', payload, signal), - resume: (payload, signal) => this.callUnary('goal.resume', payload, signal), - complete: (payload, signal) => this.callUnary('goal.complete', payload, signal), - clear: (payload, signal) => this.callUnary('goal.clear', payload, signal), - } - readonly settings: IApiClient['settings'] = { describe: (payload, signal) => this.callUnary('settings.describe', payload, signal), openDocument: (payload, signal) => this.callUnary('settings.openDocument', payload, signal), diff --git a/packages/host/apiproxy/src/fetch/handler.ts b/packages/host/apiproxy/src/fetch/handler.ts index 7e3a86cfe6..5ba7197cbb 100644 --- a/packages/host/apiproxy/src/fetch/handler.ts +++ b/packages/host/apiproxy/src/fetch/handler.ts @@ -24,14 +24,6 @@ import { agentPresetCopyRequestSchema, agentPresetListRequestSchema, agentPresetOpenDocumentRequestSchema, agentPresetReadRequestSchema, agentPresetRemoveRequestSchema, agentPresetSelectRequestSchema, } from '../api/agent-presets.schema.ts' -import { - goalCreateRequestSchema, - goalEditRequestSchema, - goalPauseRequestSchema, - goalResumeRequestSchema, - goalCompleteRequestSchema, - goalClearRequestSchema, -} from '../api/goals.schema.ts' import { settingsDescribeRequestSchema, settingsMutateRequestSchema, settingsOpenDocumentRequestSchema, settingsReplaceRequestSchema, settingsUpdateRequestSchema, @@ -78,12 +70,6 @@ const UNARY_ROUTES: UnaryRoutes = { 'agentPreset.copy': { schema: agentPresetCopyRequestSchema, invoke: (api, r) => api.agentPresets.copy(r) }, 'agentPreset.openDocument': { schema: agentPresetOpenDocumentRequestSchema, invoke: (api, r, signal) => api.agentPresets.openDocument(r, signal) }, 'agentPreset.remove': { schema: agentPresetRemoveRequestSchema, invoke: (api, r) => api.agentPresets.remove(r) }, - 'goal.create': { schema: goalCreateRequestSchema, invoke: (api, r) => api.goals.create(r) }, - 'goal.edit': { schema: goalEditRequestSchema, invoke: (api, r) => api.goals.edit(r) }, - 'goal.pause': { schema: goalPauseRequestSchema, invoke: (api, r) => api.goals.pause(r) }, - 'goal.resume': { schema: goalResumeRequestSchema, invoke: (api, r) => api.goals.resume(r) }, - 'goal.complete': { schema: goalCompleteRequestSchema, invoke: (api, r) => api.goals.complete(r) }, - 'goal.clear': { schema: goalClearRequestSchema, invoke: (api, r) => api.goals.clear(r) }, 'settings.describe': { schema: settingsDescribeRequestSchema, invoke: (api, r) => api.settings.describe(r) }, 'settings.openDocument': { schema: settingsOpenDocumentRequestSchema, invoke: (api, r, signal) => api.settings.openDocument(r, signal) }, 'settings.update': { schema: settingsUpdateRequestSchema, invoke: (api, r) => api.settings.update(r) }, diff --git a/packages/host/apiproxy/src/index.ts b/packages/host/apiproxy/src/index.ts index 546ad8d336..98ee7eca20 100644 --- a/packages/host/apiproxy/src/index.ts +++ b/packages/host/apiproxy/src/index.ts @@ -74,7 +74,6 @@ export class ApiProxyService extends Service implements ApiProxy { readonly subagents: ApiProxy['subagents'] readonly host: ApiProxy['host'] - readonly goals: ApiProxy['goals'] readonly skills: ApiProxy['skills'] readonly agentPresets: ApiProxy['agentPresets'] readonly settings: ApiProxy['settings'] @@ -94,7 +93,6 @@ export class ApiProxyService extends Service implements ApiProxy { }) this.subagents = api.subagents this.host = api.host - this.goals = api.goals this.skills = api.skills this.agentPresets = api.agentPresets this.settings = api.settings diff --git a/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts b/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts index ce7942cf38..990b658c9d 100644 --- a/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts +++ b/packages/host/apiproxy/tests/api-proxy-agent-preset.spec.ts @@ -13,7 +13,6 @@ import { agentPresetProjectionDefinition, InvalidPresetIdError, PresetExistsError, UnknownPresetError, } from '@deepseek-ai/dsh-agent-presets' import type {} from '@deepseek-ai/dsh-agent-presets/types' -import { GoalId } from '@deepseek-ai/dsh-goal' import { createApiProxy } from '../src/api-proxy.ts' import { describe, expect, it } from 'vitest' import { SessionQueryError, type SessionObservation } from '@deepseek-ai/dsh-session-query' @@ -210,23 +209,6 @@ async function harness( * instance through the agent instead of reading a root-realm singleton. */ describe('a capability the session\'s preset mounts', () => { - it('serves the goal RPC from the session\'s own goal service', async () => { - const { api } = await harness(['standard']) - await createSession(api, { sessionId: SessionId('g1'), agentPreset: 'standard' }) - const ref = { id: GoalId('goal-1'), revision: 1 } - const paused: unknown[] = [] - services.set('g1', { - goals: { pause: (agent: { id: unknown }, r: unknown) => { paused.push([String(agent.id), r]); return ref } }, - }) - - const response = await api.goals.pause(request({ sessionId: SessionId('g1'), ref })) - - expect(response.result).toMatchObject({ ok: true, value: { ref } }) - // Reached the instance this session mounted, and was handed its own agent. - expect(paused).toEqual([['g1', ref]]) - services.delete('g1') - }) - it('serves the skill catalog from the session\'s own registry', async () => { const { api } = await harness(['standard']) await createSession(api, { sessionId: SessionId('k1'), agentPreset: 'standard' }) diff --git a/packages/host/apiproxy/tests/client-handler.spec.ts b/packages/host/apiproxy/tests/client-handler.spec.ts index 09509a0782..6f5fd528f0 100644 --- a/packages/host/apiproxy/tests/client-handler.spec.ts +++ b/packages/host/apiproxy/tests/client-handler.spec.ts @@ -7,7 +7,7 @@ import { describe, expect, it, vi } from 'vitest' import type { SessionId } from '@deepseek-ai/dsh-session' -import type { ApiProxy, GoalRef, RpcMessage, RpcRequest, RpcResponse } from '@deepseek-ai/dsh-host-apiproxy' +import type { ApiProxy, RpcMessage, RpcRequest, RpcResponse } from '@deepseek-ai/dsh-host-apiproxy' import { InProcessApiClient, RpcId, toFetchHandler } from '@deepseek-ai/dsh-host-apiproxy' const sid = (id: string): SessionId => id as SessionId @@ -22,7 +22,6 @@ function scriptedApi(overrides: { host?: Partial skills?: Partial agentPresets?: Partial - goals?: Partial settings?: Partial credentials?: Partial llm?: Partial @@ -56,15 +55,6 @@ function scriptedApi(overrides: { remove: r => ok(r, {}), ...overrides.agentPresets, }, - goals: { - create: err, - edit: err, - pause: err, - resume: err, - complete: err, - clear: err, - ...overrides.goals, - }, settings: { describe: r => ok(r, { writable: true, hasDocument: false, namespaces: [] }), openDocument: r => ok(r, { opened: true as const }), @@ -309,63 +299,6 @@ describe('unary round trip', () => { }) }) -describe('goals unary surface', () => { - const ref: GoalRef = { id: 'goal-1' as GoalRef['id'], revision: 1 } - /** The `{ ref }` acknowledgement every non-clear mutation answers (state travels on the projection). */ - const ack = { ref: { id: 'goal-1' as GoalRef['id'], revision: 2 } } - - it('round-trips every goal method with its own payload and value shape', async () => { - const seen: { method: string; payload: unknown }[] = [] - const record = recorderInto(seen) - const api = scriptedApi({ - goals: { - create: record('goal.create', r => ok(r, ack)), - edit: record('goal.edit', r => ok(r, { ref: { ...ack.ref, revision: 3 } })), - pause: record('goal.pause', r => ok(r, ack)), - resume: record('goal.resume', r => ok(r, ack)), - complete: record('goal.complete', r => ok(r, ack)), - clear: record('goal.clear', r => ok(r, { cleared: true as const })), - }, - }) - const c = client(api) - - const created = await c.goals.create({ sessionId: sid('s1'), objective: 'ship it', maxGoalRounds: 4 }) - expect(created.result).toEqual({ ok: true, value: ack }) - const edited = await c.goals.edit({ sessionId: sid('s1'), ref, objective: 'ship v2' }) - expect(edited.result).toEqual({ ok: true, value: { ref: { ...ack.ref, revision: 3 } } }) - expect((await c.goals.pause({ sessionId: sid('s1'), ref })).result).toEqual({ ok: true, value: ack }) - expect((await c.goals.resume({ sessionId: sid('s1'), ref })).result).toEqual({ ok: true, value: ack }) - expect((await c.goals.complete({ sessionId: sid('s1'), ref })).result).toEqual({ ok: true, value: ack }) - const cleared = await c.goals.clear({ sessionId: sid('s1'), ref }) - expect(cleared.result).toEqual({ ok: true, value: { cleared: true } }) - - // The handler dispatched each call through its own route row: payload parsed per method. - expect(seen.map(s => s.method)).toEqual(['goal.create', 'goal.edit', 'goal.pause', 'goal.resume', 'goal.complete', 'goal.clear']) - expect(seen[0]?.payload).toEqual({ sessionId: 's1', objective: 'ship it', maxGoalRounds: 4 }) - expect(seen[1]?.payload).toEqual({ sessionId: 's1', ref, objective: 'ship v2' }) - }) - - it('passes business errors through as results, not throws', async () => { - // Default scripted goals impl answers an err result: it must arrive as a result, not a throw. - const failed = await client(scriptedApi()).goals.pause({ sessionId: sid('s1'), ref }) - expect(failed.result.ok).toBe(false) - if (!failed.result.ok) expect(failed.result.error.code).toBe('internal') - }) - - it('rejects an invalid goal payload at the handler as bad-request', async () => { - const response = await client(scriptedApi()).goals.create({ sessionId: sid('s1'), objective: '' }) - expect(response.result.ok).toBe(false) - if (!response.result.ok) expect(response.result.error.code).toBe('bad-request') - - let editCalls = 0 - const api = scriptedApi({ goals: { edit: (r) => { editCalls++; return ok(r, ack) } } }) - const emptyEdit = await client(api).goals.edit({ sessionId: sid('s1'), ref }) - expect(emptyEdit.result.ok).toBe(false) - if (!emptyEdit.result.ok) expect(emptyEdit.result.error.code).toBe('bad-request') - expect(editCalls).toBe(0) - }) -}) - describe('envelope tap', () => { it('delivers one microtask batch of full forms per unary call', async () => { const api = scriptedApi() diff --git a/packages/host/apiproxy/tests/fetch-carrier.spec.ts b/packages/host/apiproxy/tests/fetch-carrier.spec.ts index f16136eeff..125d2a4171 100644 --- a/packages/host/apiproxy/tests/fetch-carrier.spec.ts +++ b/packages/host/apiproxy/tests/fetch-carrier.spec.ts @@ -87,26 +87,6 @@ function fakeApi(overrides: Partial<{ crashOn: string }> = {}): ApiProxy { return { rpcId: request.rpcId, result: { ok: true, value: { skills: [{ name: 'commit-helper', description: 'Git commits', modelInvocable: true }] } } } }, }, - goals: { - async create(request) { - return { rpcId: request.rpcId, result: { ok: false, error: { code: 'internal', message: 'stub', details: {} } } } - }, - async edit(request) { - return { rpcId: request.rpcId, result: { ok: false, error: { code: 'internal', message: 'stub', details: {} } } } - }, - async pause(request) { - return { rpcId: request.rpcId, result: { ok: false, error: { code: 'internal', message: 'stub', details: {} } } } - }, - async resume(request) { - return { rpcId: request.rpcId, result: { ok: false, error: { code: 'internal', message: 'stub', details: {} } } } - }, - async complete(request) { - return { rpcId: request.rpcId, result: { ok: false, error: { code: 'internal', message: 'stub', details: {} } } } - }, - async clear(request) { - return { rpcId: request.rpcId, result: { ok: false, error: { code: 'internal', message: 'stub', details: {} } } } - }, - }, settings: { async describe(request) { return { rpcId: request.rpcId, result: { ok: true, value: { writable: true, hasDocument: false, namespaces: [] } } } diff --git a/packages/host/apiproxy/tests/rpc-schemas.spec.ts b/packages/host/apiproxy/tests/rpc-schemas.spec.ts index 6213135c6b..3a821a8e3f 100644 --- a/packages/host/apiproxy/tests/rpc-schemas.spec.ts +++ b/packages/host/apiproxy/tests/rpc-schemas.spec.ts @@ -14,7 +14,6 @@ import { skillEntrySchema, skillListRequestSchema, skillListValueSchema } from ' import { agentPresetEntrySchema, agentPresetListValueSchema, agentPresetOpenDocumentValueSchema, } from '../src/api/agent-presets.schema.ts' -import { goalEditRequestSchema } from '../src/api/goals.schema.ts' import { subagentPromptRequestSchema } from '../src/api/subagents.schema.ts' describe('RpcId', () => { @@ -178,15 +177,6 @@ describe('skills domain schemas', () => { }) }) -describe('goals domain schemas', () => { - it('requires at least one replacement field for goal.edit', () => { - const ref = { id: 'g1', revision: 1 } - expect(goalEditRequestSchema.parse({ sessionId: 's1', ref, objective: 'updated' }).objective).toBe('updated') - expect(goalEditRequestSchema.parse({ sessionId: 's1', ref, maxGoalRounds: 3 }).maxGoalRounds).toBe(3) - expect(() => goalEditRequestSchema.parse({ sessionId: 's1', ref })).toThrow() - }) -}) - describe('agent-preset schemas', () => { it('accepts a roster row and rejects an unknown trust', () => { expect(agentPresetEntrySchema.parse({ id: 'standard', trust: 'system', isDefault: true })) From f04e2fe44a81e5476d609e185da012ebdc771b59 Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Tue, 25 Aug 2026 20:55:03 +0800 Subject: [PATCH 2/3] build(apiproxy): drop the now-unused goal dependency Nothing under packages/host/apiproxy imports @deepseek-ai/dsh-goal after the unary domain deletion, so the dependency and its project reference go too. --- packages/host/apiproxy/package.json | 1 - packages/host/apiproxy/tsconfig.json | 3 --- pnpm-lock.yaml | 9 +++------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/host/apiproxy/package.json b/packages/host/apiproxy/package.json index 2658e32f42..959b35ed9f 100644 --- a/packages/host/apiproxy/package.json +++ b/packages/host/apiproxy/package.json @@ -52,7 +52,6 @@ "@deepseek-ai/dsh-brand": "workspace:^", "@deepseek-ai/dsh-commands": "workspace:^", "@deepseek-ai/dsh-credentials": "workspace:^", - "@deepseek-ai/dsh-goal": "workspace:^", "@deepseek-ai/dsh-host-directory-picker": "workspace:^", "@deepseek-ai/dsh-llm": "workspace:^", "@deepseek-ai/dsh-native-command": "workspace:^", diff --git a/packages/host/apiproxy/tsconfig.json b/packages/host/apiproxy/tsconfig.json index 7133fd8c15..87fb2a1b11 100644 --- a/packages/host/apiproxy/tsconfig.json +++ b/packages/host/apiproxy/tsconfig.json @@ -8,9 +8,6 @@ "src" ], "references": [ - { - "path": "../../goal/goal" - }, { "path": "../../settings/settings" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e67d278395..88ccfd02d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1597,12 +1597,12 @@ importers: '@deepseek-ai/dsh-attachment': specifier: workspace:^ version: link:../../attachment/attachment - '@deepseek-ai/dsh-credentials': - specifier: workspace:^ - version: link:../../credentials/credentials '@deepseek-ai/dsh-commands': specifier: workspace:^ version: link:../../interaction/commands + '@deepseek-ai/dsh-credentials': + specifier: workspace:^ + version: link:../../credentials/credentials '@deepseek-ai/dsh-host-apiproxy': specifier: workspace:^ version: link:../../host/apiproxy @@ -5556,9 +5556,6 @@ importers: '@deepseek-ai/dsh-credentials': specifier: workspace:^ version: link:../../credentials/credentials - '@deepseek-ai/dsh-goal': - specifier: workspace:^ - version: link:../../goal/goal '@deepseek-ai/dsh-host-directory-picker': specifier: workspace:^ version: link:../directory-picker From 97405878c0896694f6e5264d3dc2eb4751eb54de Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Tue, 25 Aug 2026 21:23:24 +0800 Subject: [PATCH 3/3] chore(tool-cordis): regenerate the Cordis catalog after the goal unary deletion --- packages/extensions/tool-cordis/src/api-catalog.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/packages/extensions/tool-cordis/src/api-catalog.ts b/packages/extensions/tool-cordis/src/api-catalog.ts index 4fd4e714ae..d3407395f2 100644 --- a/packages/extensions/tool-cordis/src/api-catalog.ts +++ b/packages/extensions/tool-cordis/src/api-catalog.ts @@ -3763,6 +3763,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'GoalChanged', declaration: 'export interface GoalChanged {\n readonly operation: GoalOperation;\n readonly ref: GoalRef;\n readonly goal?: GoalView;\n}', }, + { + name: 'GoalId', + declaration: 'export type GoalId = Branded<\'GoalId\'>;', + }, { name: 'GoalOperation', declaration: 'export type GoalOperation = \'create\' | \'edit\' | \'pause\' | \'resume\' | \'complete\' | \'block\' | \'clear\';', @@ -3771,6 +3775,10 @@ export const TYPE_API: readonly TypeApiEntry[] = [ name: 'GoalPhase', declaration: 'export type GoalPhase = \'active\' | \'paused\' | \'blocked\' | \'complete\';', }, + { + name: 'GoalRef', + declaration: 'export interface GoalRef {\n readonly id: GoalId;\n readonly revision: number;\n}', + }, { name: 'GoalSnapshot', declaration: 'export interface GoalSnapshot extends GoalRef {\n readonly objective: string;\n readonly phase: GoalPhase;\n readonly blockedReason?: GoalBlockReason;\n readonly maxGoalRounds: number;\n}',