From 3187912d98eacbb77d4f984422d8bb8565aa9bd3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 31 Jul 2026 16:20:47 -0700 Subject: [PATCH 1/2] types: export every public declaration from the package entry point `types/index.ts` was a hand-maintained allowlist and had silently fallen 60 declarations behind the sources. It is copied verbatim into the published TypeScript client and is its root export, so those declarations were unreachable for consumers of `@microsoft/agent-host-protocol` -- not merely undocumented. Verified against `origin/main`: importing `Customization`, `McpServerState`, `Icon`, `SessionMetadata` or `CustomizationType` from the entry point fails with `TS2305: has no exported member`. The gaps were not obscure corners: - the entire customization subsystem -- `Customization`, `CustomizationType`, `ChildCustomization`, and every variant (`PluginCustomization`, `DirectoryCustomization`, `AgentCustomization`, `SkillCustomization`, `PromptCustomization`, `RuleCustomization`, `HookCustomization`, `McpServerCustomization`), plus `CustomizationLoadState` and its variants - the entire MCP-server state machine -- `McpServerState`, `McpServerStatus`, `McpAuthRequiredReason`, and the five state variants - 13 action types, including all four working-directory actions, all four customization actions and all three MCP-server actions - `Icon`, `ChatInteractivity`, `InputRequestResponsePart`, `ToolCallContributor`, `SessionMetadata`, `JsonPrimitive`, `Implementation` Replace the allowlist with `export *` from the per-channel shims, so the entry point is complete by construction rather than by maintenance. The reducer block stays explicit because the `reducers.ts` shim also re-exports `softAssertNever`, an internal helper that should not be public. `ChatAction` is declared twice with identical member sets -- once by hand in `channels-chat/actions.ts`, once in `action-origin.generated.ts` -- so it is named explicitly to resolve the `export *` ambiguity, preserving the generated declaration the previous list exported. The duplicate declaration itself is left alone; collapsing it is a source change unrelated to this packaging fix. Add `scripts/public-api.test.ts`, which walks the canonical channel modules and asserts every declaration is reachable from the entry point, with a small justified withhold-list. It also asserts the reverse -- that nothing on the withhold-list has become reachable -- so the list cannot go stale. Verified it fails when a re-export is removed. Also refresh four stale `workingDirectory` (singular) references in the specification left over from the 0.7.0 rename to `workingDirectories`. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../20260731-export-all-public-types.json | 5 + docs/specification/chat-channel.md | 4 +- docs/specification/session-channel.md | 4 +- scripts/public-api.test.ts | 106 +++++ types/index.ts | 426 ++---------------- 5 files changed, 156 insertions(+), 389 deletions(-) create mode 100644 docs/.changes/20260731-export-all-public-types.json create mode 100644 scripts/public-api.test.ts diff --git a/docs/.changes/20260731-export-all-public-types.json b/docs/.changes/20260731-export-all-public-types.json new file mode 100644 index 00000000..20ced09d --- /dev/null +++ b/docs/.changes/20260731-export-all-public-types.json @@ -0,0 +1,5 @@ +{ + "type": "fixed", + "message": "The package entry point now re-exports every public protocol declaration. 60 were unreachable from it — including the whole customization subsystem (`Customization`, `CustomizationType` and every variant), the whole MCP-server state machine (`McpServerState`, `McpServerStatus`, `McpAuthRequiredReason` and the state variants), 13 action types, and `Icon`, `ChatInteractivity`, `InputRequestResponsePart`, `ToolCallContributor` and `SessionMetadata`.", + "targets": ["typescript"] +} diff --git a/docs/specification/chat-channel.md b/docs/specification/chat-channel.md index a2c8d020..647646ac 100644 --- a/docs/specification/chat-channel.md +++ b/docs/specification/chat-channel.md @@ -14,7 +14,7 @@ Multiple chat channels may be active simultaneously. Clients subscribe to each c ## State -Subscribers receive a [`ChatState`](/reference/chat#chatstate) snapshot. `ChatState` denormalizes the [`ChatSummary`](/reference/chat#chatsummary) fields directly onto itself (`resource`, `title`, `status`, `activity`, `modifiedAt`, `origin`, `workingDirectory`) and adds the conversation contents (history of completed turns, the active turn if any, pending messages, outstanding input requests, and the user's in-progress [`draft`](#drafts)). Producers MUST keep the chat's `ChatSummary` in the session catalog consistent with these inlined summary fields — typically by dispatching a matching [`session/chatUpdated`](/reference/session#actions) whenever any summary field on the chat changes. Refer to the [State Model guide](/guide/state-model) for a structural overview. +Subscribers receive a [`ChatState`](/reference/chat#chatstate) snapshot. `ChatState` denormalizes the [`ChatSummary`](/reference/chat#chatsummary) fields directly onto itself (`resource`, `title`, `status`, `activity`, `modifiedAt`, `origin`, `workingDirectories`) and adds the conversation contents (history of completed turns, the active turn if any, pending messages, outstanding input requests, and the user's in-progress [`draft`](#drafts)). Producers MUST keep the chat's `ChatSummary` in the session catalog consistent with these inlined summary fields — typically by dispatching a matching [`session/chatUpdated`](/reference/session#actions) whenever any summary field on the chat changes. Refer to the [State Model guide](/guide/state-model) for a structural overview. When a client subscribes with `view.turns`, the server MAY expose only a tail of the most recent completed turns in the initial snapshot. The requested number is @@ -39,7 +39,7 @@ Clients MAY periodically sync their local input state into the draft by dispatch ### Per-chat working directory -`ChatState.workingDirectory` (and its mirror on [`ChatSummary`](/reference/chat#chatsummary)) is **optional**. When absent, the chat inherits the session's [`workingDirectory`](/reference/session#sessionsummary). Hosts MAY set a per-chat working directory to give individual chats their own filesystem context — for example, allocating a separate git worktree per chat so multiple chats in the same session can make independent edits that the orchestrating chat later merges back. The session-level `workingDirectory` is then the default/primary location for chats that do not override it. +`ChatState.workingDirectories` (and its mirror on [`ChatSummary`](/reference/chat#chatsummary)) is **optional**. When absent, the chat inherits the session's full [`workingDirectories`](/reference/session#sessionsummary) set; when present it MUST be a subset of that set. Hosts MAY set a per-chat subset to give individual chats their own filesystem context — for example, allocating a separate git worktree per chat so multiple chats in the same session can make independent edits that the orchestrating chat later merges back. ## Relationship to the session channel diff --git a/docs/specification/session-channel.md b/docs/specification/session-channel.md index d03f0c39..e326cb59 100644 --- a/docs/specification/session-channel.md +++ b/docs/specification/session-channel.md @@ -58,14 +58,14 @@ The producer of the chat's own [`ChatState`](./chat-channel#state) is responsibl ### Chat aggregation -[`SessionSummary`](/reference/session#sessionsummary) carries session-wide identity (`resource`, `provider`, `createdAt`, `workingDirectory`) but several of its mutable fields are aggregates derived from the session's chats. Producers SHOULD apply these rules so clients that only consume the session summary (a session list, for example) still see meaningful state: +[`SessionSummary`](/reference/session#sessionsummary) carries session-wide identity (`resource`, `provider`, `createdAt`, `workingDirectories`) but several of its mutable fields are aggregates derived from the session's chats. Producers SHOULD apply these rules so clients that only consume the session summary (a session list, for example) still see meaningful state: | Field | Derivation rule | |---|---| | `status` | Take the activity bits (`Idle` / `InProgress` / `InputNeeded` / `Error`) from the [`defaultChat`](#defaultchat) when set, else from the most recently modified chat. Promote `InputNeeded` if **any** chat needs input. Promote `Error` if **any** chat is in an error state. The orthogonal `IsRead` / `IsArchived` flags remain session-scoped and pass through unchanged. | | `activity` | Mirror the activity string of the chat that contributes the activity bits — usually the default chat, but the chat that raised `InputNeeded` / `Error` when a non-default chat wins the promotion. | | `modifiedAt` | The maximum of every chat's `modifiedAt`. | -| `workingDirectory` | The session-level **default**. Individual chats MAY override via [`ChatSummary.workingDirectory`](/reference/chat#chatsummary); aggregating per-chat overrides up is meaningless and SHOULD NOT be attempted. | +| `workingDirectories` | The session-level set. Individual chats MAY restrict to a subset via [`ChatSummary.workingDirectories`](/reference/chat#chatsummary); aggregating per-chat subsets up is meaningless and SHOULD NOT be attempted. | | `changes` | Optional roll-up. Producers MAY sum per-chat changeset stats or report the most expensive chat's stats — whichever is cheaper to compute. | Sessions with a single chat satisfy all of the above trivially (the chat's values pass through). The rules only matter once a session carries multiple chats. diff --git a/scripts/public-api.test.ts b/scripts/public-api.test.ts new file mode 100644 index 00000000..93e75522 --- /dev/null +++ b/scripts/public-api.test.ts @@ -0,0 +1,106 @@ +/** + * Asserts that every public protocol declaration is reachable from the + * package entry point (`types/index.ts`). + * + * `types/index.ts` is copied verbatim into the published TypeScript client and + * is its root export, so anything unreachable from here is unreachable for + * consumers of `@microsoft/agent-host-protocol`. It used to be a hand-written + * allowlist, which silently fell 60 declarations behind the sources — the whole + * customization subsystem (`Customization`, `CustomizationType`, every variant), + * the whole MCP-server state machine (`McpServerState` and friends), 13 action + * types, and assorted others such as `Icon` and `ChatInteractivity`. + * + * The barrel now uses `export *`, so this guards against a regression in that + * mechanism (for example a name collision resolved by dropping one side, which + * TypeScript will not otherwise flag). + * + * Run: npx tsx --test scripts/public-api.test.ts + */ + +import { describe, it, before } from 'node:test'; +import { strict as assert } from 'node:assert'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { Project } from 'ts-morph'; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); + +/** + * Declarations that are intentionally absent from the public entry point. + * Keep this as small as possible and justify every entry — an addition here is + * a decision to withhold something from consumers. + */ +const INTENTIONALLY_INTERNAL: ReadonlySet = new Set([ + // Reducer-internal exhaustiveness helper. Re-exported by the `reducers.ts` + // shim for the per-channel reducers, but not part of the protocol surface. + 'softAssertNever', +]); + +/** Canonical source folders holding protocol declarations. */ +const CHANNEL_MODULE_RE = /types[\\/](common|channels-[a-z-]+)[\\/]/; + +describe('public API surface', () => { + let declared: Set; + let exported: Set; + + before(() => { + // Built here rather than at module load so the cost is only paid when this + // suite actually runs. + const project = new Project({ + tsConfigFilePath: resolve(root, 'types/tsconfig.json'), + skipAddingFilesFromTsConfig: false, + }); + + declared = new Set(); + for (const sourceFile of project.getSourceFiles()) { + const filePath = sourceFile.getFilePath(); + if (!CHANNEL_MODULE_RE.test(filePath)) continue; + if (filePath.endsWith('.test.ts')) continue; + for (const name of sourceFile.getExportedDeclarations().keys()) { + declared.add(name); + } + } + + exported = new Set( + project.getSourceFileOrThrow(resolve(root, 'types/index.ts')) + .getExportedDeclarations() + .keys(), + ); + }); + + it('finds the protocol declarations (guards the extraction itself)', () => { + // Without this, a broken walk would make the assertions below vacuous. + assert.ok( + declared.size > 100, + `expected to find many protocol declarations, found ${declared.size} — the extraction is probably broken`, + ); + assert.ok( + exported.size > 100, + `expected types/index.ts to re-export many declarations, found ${exported.size}`, + ); + }); + + it('re-exports every public declaration from types/index.ts', () => { + const missing = [...declared] + .filter(name => !exported.has(name) && !INTENTIONALLY_INTERNAL.has(name)) + .sort(); + + assert.deepEqual( + missing, + [], + `${missing.length} public declaration(s) are unreachable from types/index.ts, so consumers of the published package cannot name them:\n ${missing.join('\n ')}`, + ); + }); + + it('keeps every intentionally-internal declaration internal', () => { + // The flip side: if something on the withhold-list becomes reachable, the + // list is stale and should be pruned rather than silently ignored. + const leaked = [...INTENTIONALLY_INTERNAL].filter(name => exported.has(name)).sort(); + + assert.deepEqual( + leaked, + [], + `${leaked.length} declaration(s) marked internal are now exported from types/index.ts — either that is a mistake, or they should be removed from INTENTIONALLY_INTERNAL:\n ${leaked.join('\n ')}`, + ); + }); +}); diff --git a/types/index.ts b/types/index.ts index 172400d5..6669983d 100644 --- a/types/index.ts +++ b/types/index.ts @@ -5,262 +5,57 @@ * @description Canonical TypeScript type definitions for the Agent Host Protocol. * These types are the source of truth from which documentation and JSON Schema * are generated. + * + * Every declaration in the per-channel modules is re-exported wholesale via + * `export *` rather than through an explicit allowlist. The previous + * hand-maintained list had silently fallen 60 declarations behind the sources + * — including the whole customization and MCP-server subsystems — leaving them + * unreachable for consumers of the published package. `export *` keeps this + * entry point complete by construction, and `scripts/public-api.test.ts` + * asserts it stays that way. + * + * The one curated section is the reducer block below, which deliberately omits + * an internal helper. */ // State types -export type { - URI, - StringOrMarkdown, - RootState, - RootConfigState, - AgentInfo, - AgentCapabilities, - MultipleChatsCapability, - ProtectedResourceMetadata, - ProjectInfo, - SessionModelInfo, - ModelSelection, - AgentSelection, - SessionState, - SessionSummary, - ChatState, - ChatSummary, - ChatOrigin, - SideChatSelection, - ChangesSummary, - SessionConfigState, - Turn, - ActiveTurn, - Message, - MessageOrigin, - MessageAttachment, - MessageAttachmentBase, - TextPosition, - TextRange, - TextSelection, - SimpleMessageAttachment, - MessageEmbeddedResourceAttachment, - MessageResourceAttachment, - MessageAnnotationsAttachment, - MessageChatAttachment, - MarkdownResponsePart, - ContentRef, - ToolCallResponsePart, - ReasoningResponsePart, - SystemNotificationResponsePart, - ResponsePart, - ToolCallResult, - ToolInput, - ToolCallStreamingState, - ToolCallPendingConfirmationState, - ToolCallRunningState, - ToolCallAuthRequiredState, - ToolCallPendingResultConfirmationState, - ToolCallCompletedState, - ToolCallCancelledState, - ToolCallState, - ToolCallConfirmationState, - ToolCallRiskAssessmentLoadingState, - ToolCallRiskAssessmentCompleteState, - ToolCallRiskAssessment, - ConfirmationOption, - ToolDefinition, - ToolAnnotations, - ToolResultTextContent, - ToolResultEmbeddedResourceContent, - ToolResultResourceContent, - ToolResultContent, - FileEdit, - ToolResultFileEditContent, - ToolResultTerminalContent, - TerminalCommandResult, - ToolResultSubagentContent, - SessionActiveClient, - SessionInputRequest, - SessionChatInputRequest, - SessionToolConfirmationRequest, - SessionToolClientExecutionRequest, - SessionToolAuthenticationRequest, - McpOAuthClient, - McpAuthRequirement, - PendingMessage, - ChatInputAnswer, - ChatInputAnswerValue, - ChatInputTextAnswerValue, - ChatInputNumberAnswerValue, - ChatInputBooleanAnswerValue, - ChatInputSelectedAnswerValue, - ChatInputSelectedManyAnswerValue, - ChatInputAnswered, - ChatInputSkipped, - ChatInputOption, - ChatInputQuestion, - ChatInputTextQuestion, - ChatInputNumberQuestion, - ChatInputBooleanQuestion, - ChatInputSingleSelectQuestion, - ChatInputMultiSelectQuestion, - ChatInputRequest, - UsageInfo, - ErrorInfo, - Snapshot, - TerminalInfo, - TerminalClientClaim, - TerminalSessionClaim, - TerminalClaim, - TerminalState, - TerminalContentPart, - TerminalUnclassifiedPart, - TerminalCommandPart, - Changeset, - ChangesetState, - ChangesetFile, - ChangesetOperation, - AnnotationsSummary, - AnnotationsState, - Annotation, - AnnotationEntry, - TelemetryCapabilities, - ResourceWatchState, - ResourceChange, -} from './state.js'; - -export { - PolicyState, - SessionLifecycle, - SessionStatus, - SessionInputRequestKind, - ChatOriginKind, - TurnState, - MessageKind, - MessageAttachmentKind, - ResponsePartKind, - ToolCallStatus, - ToolCallConfirmationReason, - ToolCallRiskAssessmentKind, - ToolCallRiskAssessmentStatus, - ToolCallCancellationReason, - ConfirmationOptionKind, - ToolResultContentType, - PendingMessageKind, - ChatInputAnswerState, - ChatInputAnswerValueKind, - ChatInputQuestionKind, - ChatInputResponseKind, - TerminalClaimKind, - ChangesetStatus, - ChangesetOperationStatus, - ChangesetOperationScope, - ResourceChangeType, -} from './state.js'; +export * from './state.js'; // Action types -export type { - ActionEnvelope, - ActionOrigin, - RootAgentsChangedAction, - RootActiveSessionsChangedAction, - SessionReadyAction, - SessionCreationFailedAction, - SessionChatAddedAction, - SessionChatRemovedAction, - SessionChatUpdatedAction, - SessionDefaultChatChangedAction, - ChatTurnStartedAction, - ChatDeltaAction, - ChatResponsePartAction, - ChatToolCallStartAction, - ChatToolCallDeltaAction, - ChatToolCallReadyAction, - ChatToolCallApprovedAction, - ChatToolCallDeniedAction, - ChatToolCallConfirmedAction, - ChatToolCallCompleteAction, - ChatToolCallResultConfirmedAction, - ChatToolCallContentChangedAction, - ChatToolCallAuthRequiredAction, - ChatToolCallAuthResolvedAction, - ChatTurnCompleteAction, - ChatTurnCancelledAction, - ChatErrorAction, - ChatActivityChangedAction, - SessionTitleChangedAction, - ChatUsageAction, - ChatReasoningAction, - SessionServerToolsChangedAction, - SessionActiveClientSetAction, - SessionActiveClientRemovedAction, - SessionInputNeededSetAction, - SessionInputNeededRemovedAction, - ChatPendingMessageSetAction, - ChatPendingMessageRemovedAction, - ChatQueuedMessagesReorderedAction, - ChatDraftChangedAction, - ChatInputAnswerChangedAction, - ChatInputCompletedAction, - ChatInputRequestedAction, - ChatTruncatedAction, - ChatTurnsLoadedAction, - SessionIsReadChangedAction, - SessionIsArchivedChangedAction, - SessionActivityChangedAction, - SessionConfigChangedAction, - ChangesetStatusChangedAction, - ChangesetFileSetAction, - ChangesetFileRemovedAction, - ChangesetContentChangedAction, - ChangesetOperationsChangedAction, - ChangesetOperationStatusChangedAction, - ChangesetClearedAction, - AnnotationsSetAction, - AnnotationsRemovedAction, - AnnotationsEntrySetAction, - AnnotationsEntryRemovedAction, - StateAction, - RootTerminalsChangedAction, - RootConfigChangedAction, - TerminalDataAction, - TerminalInputAction, - TerminalResizedAction, - TerminalClaimedAction, - TerminalTitleChangedAction, - TerminalCwdChangedAction, - TerminalExitedAction, - TerminalClearedAction, - TerminalCommandExecutedAction, - TerminalCommandFinishedAction, - TerminalCommandDetectionAvailableAction, - ResourceWatchChangedAction, -} from './actions.js'; - -export { ActionType } from './actions.js'; +export * from './actions.js'; // Generated action origin types -export type { - RootAction, - SessionAction, - ClientSessionAction, - ServerSessionAction, - ChatAction, - ClientChatAction, - ServerChatAction, - TerminalAction, - ClientTerminalAction, - ServerTerminalAction, - ChangesetAction, - ClientChangesetAction, - ServerChangesetAction, - AnnotationsAction, - ClientAnnotationsAction, - ServerAnnotationsAction, - ResourceWatchAction, - ClientResourceWatchAction, - ServerResourceWatchAction, -} from './action-origin.generated.js'; +export * from './action-origin.generated.js'; + +// `ChatAction` is declared twice — once by hand in `channels-chat/actions.ts` +// and once in the generated origin module — with identical member sets. Both +// reach this barrel through `export *`, so name it explicitly to resolve the +// ambiguity, preserving the generated declaration that the previous curated +// list exported. (The duplicate declaration itself is worth collapsing +// separately; doing it here would mix an unrelated source change into a +// packaging fix.) +export type { ChatAction } from './action-origin.generated.js'; + +// Command types +export * from './commands.js'; -export { IS_CLIENT_DISPATCHABLE } from './action-origin.generated.js'; +// Notification types +export * from './notifications.js'; + +// Message types (JSON-RPC wire format) +export * from './messages.js'; + +// Error codes +export * from './errors.js'; + +// Version registry +export * from './version/registry.js'; // Reducer functions +// +// Explicit rather than `export *`: the `./reducers.js` shim also re-exports +// `softAssertNever`, an internal reducer helper that is not part of the public +// protocol surface. export { rootReducer, sessionReducer, @@ -271,142 +66,3 @@ export { resourceWatchReducer, isClientDispatchable, } from './reducers.js'; - -// Command types -export type { - InitializeParams, - InitializeResult, - ClientCapabilities, - PingParams, - ReconnectParams, - ReconnectReplayResult, - ReconnectSnapshotResult, - ReconnectResult, - SubscribeParams, - SubscribeView, - SubscriptionDeliveryOptions, - SubscribeResult, - CreateSessionParams, - SessionForkSource, - DisposeSessionParams, - CreateChatParams, - ChatSource, - ForkChatSource, - SideChatSource, - DisposeChatParams, - CreateTerminalParams, - DisposeTerminalParams, - PaginatedParams, - PaginatedResult, - ListSessionsParams, - ListSessionsResult, - ResourceReadParams, - ResourceReadResult, - ResourceWriteParams, - ResourceWriteResult, - ResourceListParams, - DirectoryEntry, - ResourceListResult, - ResourceCopyParams, - ResourceCopyResult, - ResourceDeleteParams, - ResourceDeleteResult, - ResourceMoveParams, - ResourceMoveResult, - ResourceResolveParams, - ResourceResolveResult, - ResourceMkdirParams, - ResourceMkdirResult, - ResourceRequestParams, - ResourceRequestResult, - CreateResourceWatchParams, - CreateResourceWatchResult, - FetchTurnsParams, - FetchTurnsResult, - UnsubscribeParams, - DispatchActionParams, - AuthenticateParams, - AuthenticateResult, - ResolveSessionConfigParams, - ResolveSessionConfigResult, - ConfigPropertySchema, - ConfigSchema, - SessionConfigPropertySchema, - SessionConfigSchema, - SessionConfigCompletionsParams, - SessionConfigCompletionsResult, - SessionConfigValueItem, - CompletionsParams, - CompletionsResult, - CompletionItem, - InvokeChangesetOperationParams, - InvokeChangesetOperationResult, - ChangesetOperationTarget, - ChangesetOperationFollowUp, -} from './commands.js'; - -export { ReconnectResultType, ChatSourceKind, ContentEncoding, CompletionItemKind, ResourceType, ResourceWriteMode } from './commands.js'; - -// Notification types -export type { - SessionAddedParams, - SessionRemovedParams, - SessionSummaryChangedParams, - ProgressParams, - AuthRequiredParams, - OtlpExportLogsParams, - OtlpExportTracesParams, - OtlpExportMetricsParams, -} from './notifications.js'; - -export { AuthRequiredReason } from './notifications.js'; - -// Message types (JSON-RPC wire format) -export type { - JsonRpcRequest, - JsonRpcSuccessResponse, - JsonRpcErrorResponse, - JsonRpcResponse, - JsonRpcNotification, - AhpErrorResponse, - CommandMap, - ServerCommandMap, - ClientNotificationMap, - ServerNotificationMap, - AhpRequest, - AhpServerRequest, - AhpSuccessResponse, - AhpServerSuccessResponse, - AhpResponse, - AhpServerResponse, - AhpNotification, - AhpClientNotification, - AhpServerNotification, - ProtocolMessage, -} from './messages.js'; - -// Error codes -export { - JsonRpcErrorCodes, - AhpErrorCodes, -} from './errors.js'; -export type { - AhpErrorCode, - JsonRpcErrorCode, - AhpError, - AhpErrorCodeWithData, - AhpErrorDetailsMap, - AuthRequiredErrorData, - PermissionDeniedErrorData, -} from './errors.js'; - -// Version registry -export { - PROTOCOL_VERSION, - SUPPORTED_PROTOCOL_VERSIONS, - ACTION_INTRODUCED_IN, - NOTIFICATION_INTRODUCED_IN, - isActionKnownToVersion, - isNotificationKnownToVersion, - compareProtocolVersions, -} from './version/registry.js'; From c7d4ae2bf22909a24e79458ceeefe1c1d52110f4 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 31 Jul 2026 16:29:36 -0700 Subject: [PATCH 2/2] trim excessive comments --- scripts/public-api.test.ts | 46 +++++++------------------------------- types/index.ts | 45 +++++-------------------------------- 2 files changed, 13 insertions(+), 78 deletions(-) diff --git a/scripts/public-api.test.ts b/scripts/public-api.test.ts index 93e75522..b45e1323 100644 --- a/scripts/public-api.test.ts +++ b/scripts/public-api.test.ts @@ -1,18 +1,6 @@ /** - * Asserts that every public protocol declaration is reachable from the - * package entry point (`types/index.ts`). - * - * `types/index.ts` is copied verbatim into the published TypeScript client and - * is its root export, so anything unreachable from here is unreachable for - * consumers of `@microsoft/agent-host-protocol`. It used to be a hand-written - * allowlist, which silently fell 60 declarations behind the sources — the whole - * customization subsystem (`Customization`, `CustomizationType`, every variant), - * the whole MCP-server state machine (`McpServerState` and friends), 13 action - * types, and assorted others such as `Icon` and `ChatInteractivity`. - * - * The barrel now uses `export *`, so this guards against a regression in that - * mechanism (for example a name collision resolved by dropping one side, which - * TypeScript will not otherwise flag). + * Asserts that every public protocol declaration is reachable from + * `types/index.ts`, which is the published TypeScript client's root export. * * Run: npx tsx --test scripts/public-api.test.ts */ @@ -25,18 +13,11 @@ import { Project } from 'ts-morph'; const root = resolve(dirname(fileURLToPath(import.meta.url)), '..'); -/** - * Declarations that are intentionally absent from the public entry point. - * Keep this as small as possible and justify every entry — an addition here is - * a decision to withhold something from consumers. - */ +/** Declarations deliberately withheld from the entry point. */ const INTENTIONALLY_INTERNAL: ReadonlySet = new Set([ - // Reducer-internal exhaustiveness helper. Re-exported by the `reducers.ts` - // shim for the per-channel reducers, but not part of the protocol surface. - 'softAssertNever', + 'softAssertNever', // reducer-internal exhaustiveness helper ]); -/** Canonical source folders holding protocol declarations. */ const CHANNEL_MODULE_RE = /types[\\/](common|channels-[a-z-]+)[\\/]/; describe('public API surface', () => { @@ -44,8 +25,6 @@ describe('public API surface', () => { let exported: Set; before(() => { - // Built here rather than at module load so the cost is only paid when this - // suite actually runs. const project = new Project({ tsConfigFilePath: resolve(root, 'types/tsconfig.json'), skipAddingFilesFromTsConfig: false, @@ -69,15 +48,8 @@ describe('public API surface', () => { }); it('finds the protocol declarations (guards the extraction itself)', () => { - // Without this, a broken walk would make the assertions below vacuous. - assert.ok( - declared.size > 100, - `expected to find many protocol declarations, found ${declared.size} — the extraction is probably broken`, - ); - assert.ok( - exported.size > 100, - `expected types/index.ts to re-export many declarations, found ${exported.size}`, - ); + assert.ok(declared.size > 100, `found only ${declared.size} declarations — the extraction is probably broken`); + assert.ok(exported.size > 100, `types/index.ts re-exports only ${exported.size} declarations`); }); it('re-exports every public declaration from types/index.ts', () => { @@ -88,19 +60,17 @@ describe('public API surface', () => { assert.deepEqual( missing, [], - `${missing.length} public declaration(s) are unreachable from types/index.ts, so consumers of the published package cannot name them:\n ${missing.join('\n ')}`, + `${missing.length} declaration(s) unreachable from types/index.ts, so consumers of the published package cannot name them:\n ${missing.join('\n ')}`, ); }); it('keeps every intentionally-internal declaration internal', () => { - // The flip side: if something on the withhold-list becomes reachable, the - // list is stale and should be pruned rather than silently ignored. const leaked = [...INTENTIONALLY_INTERNAL].filter(name => exported.has(name)).sort(); assert.deepEqual( leaked, [], - `${leaked.length} declaration(s) marked internal are now exported from types/index.ts — either that is a mistake, or they should be removed from INTENTIONALLY_INTERNAL:\n ${leaked.join('\n ')}`, + `${leaked.length} internal declaration(s) are now exported — remove them from INTENTIONALLY_INTERNAL if that is intended:\n ${leaked.join('\n ')}`, ); }); }); diff --git a/types/index.ts b/types/index.ts index 6669983d..a9b0f0f3 100644 --- a/types/index.ts +++ b/types/index.ts @@ -5,57 +5,22 @@ * @description Canonical TypeScript type definitions for the Agent Host Protocol. * These types are the source of truth from which documentation and JSON Schema * are generated. - * - * Every declaration in the per-channel modules is re-exported wholesale via - * `export *` rather than through an explicit allowlist. The previous - * hand-maintained list had silently fallen 60 declarations behind the sources - * — including the whole customization and MCP-server subsystems — leaving them - * unreachable for consumers of the published package. `export *` keeps this - * entry point complete by construction, and `scripts/public-api.test.ts` - * asserts it stays that way. - * - * The one curated section is the reducer block below, which deliberately omits - * an internal helper. */ -// State types export * from './state.js'; - -// Action types export * from './actions.js'; - -// Generated action origin types export * from './action-origin.generated.js'; - -// `ChatAction` is declared twice — once by hand in `channels-chat/actions.ts` -// and once in the generated origin module — with identical member sets. Both -// reach this barrel through `export *`, so name it explicitly to resolve the -// ambiguity, preserving the generated declaration that the previous curated -// list exported. (The duplicate declaration itself is worth collapsing -// separately; doing it here would mix an unrelated source change into a -// packaging fix.) -export type { ChatAction } from './action-origin.generated.js'; - -// Command types export * from './commands.js'; - -// Notification types export * from './notifications.js'; - -// Message types (JSON-RPC wire format) export * from './messages.js'; - -// Error codes export * from './errors.js'; - -// Version registry export * from './version/registry.js'; -// Reducer functions -// -// Explicit rather than `export *`: the `./reducers.js` shim also re-exports -// `softAssertNever`, an internal reducer helper that is not part of the public -// protocol surface. +// `ChatAction` is declared both by hand and in the generated origin module, so +// disambiguate the `export *`s above. +export type { ChatAction } from './action-origin.generated.js'; + +// Explicit: the shim also re-exports the internal `softAssertNever`. export { rootReducer, sessionReducer,