Skip to content

Commit ed760f7

Browse files
bloveclaude
andcommitted
docs(dx): JSDoc sweep — undocumented dev-facing helpers (audit PR 1)
Re-baselined the TS DX audit against post-#685 main (#685 already documented tools/action/view/ask + made view/ask generic). Close the remaining missing-summary holes on the dev-facing surface: - chat: isUserMessage/isAssistantMessage/isToolMessage/isSystemMessage, getInterrupt, isTyping, mockAgent — summaries + @PARAM + @example - render: provideViews, signalStateStore — summaries + @PARAM + @example Regenerated chat/render api-docs.json; updated the findings doc with a post-#685 re-baseline note. Comment-only; chat + render build green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 1cdb906 commit ed760f7

9 files changed

Lines changed: 154 additions & 27 deletions

File tree

apps/website/content/docs/chat/api/api-docs.json

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7793,21 +7793,23 @@
77937793
{
77947794
"name": "getInterrupt",
77957795
"kind": "function",
7796-
"description": "",
7796+
"description": "Read the agent's current human-in-the-loop interrupt, if any.",
77977797
"signature": "getInterrupt(agent: Agent<>): AgentInterrupt | undefined",
77987798
"params": [
77997799
{
78007800
"name": "agent",
78017801
"type": "Agent<>",
7802-
"description": "",
7802+
"description": "The agent to inspect.",
78037803
"optional": false
78047804
}
78057805
],
78067806
"returns": {
78077807
"type": "AgentInterrupt | undefined",
78087808
"description": ""
78097809
},
7810-
"examples": []
7810+
"examples": [
7811+
"```ts\nconst interrupt = getInterrupt(agent);\nif (interrupt) agent.resume('approved');\n```"
7812+
]
78117813
},
78127814
{
78137815
"name": "getMessageType",
@@ -7871,97 +7873,107 @@
78717873
{
78727874
"name": "isAssistantMessage",
78737875
"kind": "function",
7874-
"description": "",
7876+
"description": "Type guard narrowing a Message to `role: 'assistant'`.",
78757877
"signature": "isAssistantMessage(m: Message): m is Message & { role: \"assistant\" }",
78767878
"params": [
78777879
{
78787880
"name": "m",
78797881
"type": "Message",
7880-
"description": "",
7882+
"description": "The message to test.",
78817883
"optional": false
78827884
}
78837885
],
78847886
"returns": {
78857887
"type": "m is Message & { role: \"assistant\" }",
78867888
"description": ""
78877889
},
7888-
"examples": []
7890+
"examples": [
7891+
"```ts\nconst reply = agent.messages().findLast(isAssistantMessage);\n```"
7892+
]
78897893
},
78907894
{
78917895
"name": "isSystemMessage",
78927896
"kind": "function",
7893-
"description": "",
7897+
"description": "Type guard narrowing a Message to `role: 'system'`.",
78947898
"signature": "isSystemMessage(m: Message): m is Message & { role: \"system\" }",
78957899
"params": [
78967900
{
78977901
"name": "m",
78987902
"type": "Message",
7899-
"description": "",
7903+
"description": "The message to test.",
79007904
"optional": false
79017905
}
79027906
],
79037907
"returns": {
79047908
"type": "m is Message & { role: \"system\" }",
79057909
"description": ""
79067910
},
7907-
"examples": []
7911+
"examples": [
7912+
"```ts\nconst visible = agent.messages().filter((m) => !isSystemMessage(m));\n```"
7913+
]
79087914
},
79097915
{
79107916
"name": "isToolMessage",
79117917
"kind": "function",
7912-
"description": "",
7918+
"description": "Type guard narrowing a Message to `role: 'tool'` (a tool result turn).",
79137919
"signature": "isToolMessage(m: Message): m is Message & { role: \"tool\" }",
79147920
"params": [
79157921
{
79167922
"name": "m",
79177923
"type": "Message",
7918-
"description": "",
7924+
"description": "The message to test.",
79197925
"optional": false
79207926
}
79217927
],
79227928
"returns": {
79237929
"type": "m is Message & { role: \"tool\" }",
79247930
"description": ""
79257931
},
7926-
"examples": []
7932+
"examples": [
7933+
"```ts\nif (isToolMessage(m)) console.log(m.toolCallId);\n```"
7934+
]
79277935
},
79287936
{
79297937
"name": "isTyping",
79307938
"kind": "function",
7931-
"description": "",
7939+
"description": "Whether the agent should show a \"typing\" indicator — it is loading and has\nnot yet started streaming the assistant's reply.",
79327940
"signature": "isTyping(agent: Agent<>): boolean",
79337941
"params": [
79347942
{
79357943
"name": "agent",
79367944
"type": "Agent<>",
7937-
"description": "",
7945+
"description": "The agent to inspect.",
79387946
"optional": false
79397947
}
79407948
],
79417949
"returns": {
79427950
"type": "boolean",
79437951
"description": ""
79447952
},
7945-
"examples": []
7953+
"examples": [
7954+
"```ts\n\\@if (isTyping(agent)) { <chat-typing-indicator [agent]=\"agent\" /> }\n```"
7955+
]
79467956
},
79477957
{
79487958
"name": "isUserMessage",
79497959
"kind": "function",
7950-
"description": "",
7960+
"description": "Type guard narrowing a Message to `role: 'user'`.",
79517961
"signature": "isUserMessage(m: Message): m is Message & { role: \"user\" }",
79527962
"params": [
79537963
{
79547964
"name": "m",
79557965
"type": "Message",
7956-
"description": "",
7966+
"description": "The message to test.",
79577967
"optional": false
79587968
}
79597969
],
79607970
"returns": {
79617971
"type": "m is Message & { role: \"user\" }",
79627972
"description": ""
79637973
},
7964-
"examples": []
7974+
"examples": [
7975+
"```ts\nconst userTurns = agent.messages().filter(isUserMessage);\n```"
7976+
]
79657977
},
79667978
{
79677979
"name": "messageContent",
@@ -7985,21 +7997,23 @@
79857997
{
79867998
"name": "mockAgent",
79877999
"kind": "function",
7988-
"description": "",
8000+
"description": "Build an in-memory Agent for tests and stories — no transport, no\nnetwork. Every field is a writable signal so a test can drive UI states\n(loading, error, interrupts, tool calls, subagents) deterministically.",
79898001
"signature": "mockAgent(opts: MockAgentOptions): MockAgent<>",
79908002
"params": [
79918003
{
79928004
"name": "opts",
79938005
"type": "MockAgentOptions",
7994-
"description": "",
8006+
"description": "Initial values for the mock's signals; all optional.",
79958007
"optional": true
79968008
}
79978009
],
79988010
"returns": {
79998011
"type": "MockAgent<>",
80008012
"description": ""
80018013
},
8002-
"examples": []
8014+
"examples": [
8015+
"```ts\nconst agent = mockAgent({\n messages: [{ id: '1', role: 'assistant', content: 'Hi' }],\n isLoading: true,\n});\n```"
8016+
]
80038017
},
80048018
{
80058019
"name": "normalizeEnvelopeArgs",

apps/website/content/docs/render/api/api-docs.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -781,40 +781,44 @@
781781
{
782782
"name": "provideViews",
783783
"kind": "function",
784-
"description": "",
784+
"description": "Register a ViewRegistry for the render engine so generative-UI specs\ncan resolve element `type`s to Angular components. Provide at the application\n(or a feature route's) environment injector.",
785785
"signature": "provideViews(registry: ViewRegistry): EnvironmentProviders",
786786
"params": [
787787
{
788788
"name": "registry",
789789
"type": "ViewRegistry",
790-
"description": "",
790+
"description": "Map of spec element types to the components that render them,\n typically built with `views()` / `withViews()`.",
791791
"optional": false
792792
}
793793
],
794794
"returns": {
795795
"type": "EnvironmentProviders",
796796
"description": ""
797797
},
798-
"examples": []
798+
"examples": [
799+
"```ts\nbootstrapApplication(App, {\n providers: [provideViews(views({ metric: MetricCardComponent }))],\n});\n```"
800+
]
799801
},
800802
{
801803
"name": "signalStateStore",
802804
"kind": "function",
803-
"description": "",
805+
"description": "Create a signal-backed StateStore for a generative-UI surface —\nholds the bound state that spec `$bindState` paths read and interactive\nelements write, with path-addressable get/set and change subscriptions.",
804806
"signature": "signalStateStore(initialState: StateModel): StateStore",
805807
"params": [
806808
{
807809
"name": "initialState",
808810
"type": "StateModel",
809-
"description": "",
811+
"description": "Optional starting state object.",
810812
"optional": true
811813
}
812814
],
813815
"returns": {
814816
"type": "StateStore",
815817
"description": ""
816818
},
817-
"examples": []
819+
"examples": [
820+
"```ts\nconst store = signalStateStore({ count: 0 });\nstore.set('count', 1);\n```"
821+
]
818822
},
819823
{
820824
"name": "toRenderRegistry",

docs/superpowers/specs/2026-06-19-ts-dx-audit-findings.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
**Date:** 2026-06-19 · **Scope:** `@threadplane/chat`, `ag-ui`, `langgraph`, `render` (302 public exports)
44
**Method:** TypeDoc extraction (`api-docs.json`) for JSDoc/signature grading across all exports + real `tsserver` quick-info probing for hover/inference on the authoring surface + source confirmation. See `2026-06-19-ts-dx-audit-design.md`.
55

6+
> **Re-baselined 2026-06-19 (post #685).** The original grades were extracted from `api-docs.json` generated *before* PR #685 ("TypeScript DX pass") merged. #685 already implemented the headline items: `view`/`ask` are now generic with `AcceptComponent<S,C>` so component inputs are checked against the schema (**F4 done**), and `tools`/`action`/`view`/`ask` carry full `@param`/`@example`/`@returns` JSDoc (**F1/F2 done for client-tools**); `provideChat` and `AgentError`/`toAgentError` were documented too. The remaining gap is narrower — undocumented secondary functions (message type-guards, `getInterrupt`, `mockAgent`, `provideViews`, `signalStateStore`, `bridgeCitationsState`, `extractCitations`) and `@example`-less wiring APIs (`provideAgent`/`injectAgent` on both adapters, `injectRenderHost`). PR 1 on this branch (`docs/ts-dx-audit`) closes the missing-summary holes first.
7+
68
## Executive summary
79

810
The public **type shapes and hovers are in good condition** — the IDE quick-info for the headline authoring APIs is clean and readable, and generic inference works where it's been wired (`action<S>`). The gap is almost entirely **inline guidance (JSDoc)** and a few **surface-hygiene + inference** items.

libs/chat/src/lib/agent/message.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,58 @@ export interface Message {
4444
toolCallIds?: string[];
4545
}
4646

47+
/**
48+
* Type guard narrowing a {@link Message} to `role: 'user'`.
49+
*
50+
* @param m The message to test.
51+
* @returns `true` (and narrows `m`) when the message was sent by the user.
52+
* @example
53+
* ```ts
54+
* const userTurns = agent.messages().filter(isUserMessage);
55+
* ```
56+
*/
4757
export function isUserMessage(m: Message): m is Message & { role: 'user' } {
4858
return m.role === 'user';
4959
}
5060

61+
/**
62+
* Type guard narrowing a {@link Message} to `role: 'assistant'`.
63+
*
64+
* @param m The message to test.
65+
* @returns `true` (and narrows `m`) when the message came from the assistant.
66+
* @example
67+
* ```ts
68+
* const reply = agent.messages().findLast(isAssistantMessage);
69+
* ```
70+
*/
5171
export function isAssistantMessage(m: Message): m is Message & { role: 'assistant' } {
5272
return m.role === 'assistant';
5373
}
5474

75+
/**
76+
* Type guard narrowing a {@link Message} to `role: 'tool'` (a tool result turn).
77+
*
78+
* @param m The message to test.
79+
* @returns `true` (and narrows `m`) when the message is a tool result.
80+
* @example
81+
* ```ts
82+
* if (isToolMessage(m)) console.log(m.toolCallId);
83+
* ```
84+
*/
5585
export function isToolMessage(m: Message): m is Message & { role: 'tool' } {
5686
return m.role === 'tool';
5787
}
5888

89+
/**
90+
* Type guard narrowing a {@link Message} to `role: 'system'`.
91+
*
92+
* @param m The message to test.
93+
* @returns `true` (and narrows `m`) when the message is a system message.
94+
* @example
95+
* ```ts
96+
* const visible = agent.messages().filter((m) => !isSystemMessage(m));
97+
* ```
98+
*/
5999
export function isSystemMessage(m: Message): m is Message & { role: 'system' } {
60100
return m.role === 'system';
61101
}

libs/chat/src/lib/primitives/chat-interrupt/chat-interrupt.component.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ import type { AgentInterrupt } from '../../agent/agent-interrupt';
1414
import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens';
1515
import { CHAT_INTERRUPT_STYLES } from '../../styles/chat-interrupt.styles';
1616

17+
/**
18+
* Read the agent's current human-in-the-loop interrupt, if any.
19+
*
20+
* @param agent The agent to inspect.
21+
* @returns The pending {@link AgentInterrupt}, or `undefined` when the agent is
22+
* not currently waiting on an interrupt.
23+
* @example
24+
* ```ts
25+
* const interrupt = getInterrupt(agent);
26+
* if (interrupt) agent.resume('approved');
27+
* ```
28+
*/
1729
export function getInterrupt(agent: Agent): AgentInterrupt | undefined {
1830
return agent.interrupt?.();
1931
}

libs/chat/src/lib/primitives/chat-typing-indicator/chat-typing-indicator.component.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ import type { Agent } from '../../agent';
55
import { CHAT_HOST_TOKENS } from '../../styles/chat-tokens';
66
import { CHAT_TYPING_INDICATOR_STYLES } from '../../styles/chat-typing-indicator.styles';
77

8+
/**
9+
* Whether the agent should show a "typing" indicator — it is loading and has
10+
* not yet started streaming the assistant's reply.
11+
*
12+
* @param agent The agent to inspect.
13+
* @returns `true` while the agent is awaiting a response but no assistant text
14+
* has streamed yet; `false` once tokens arrive or the agent is idle.
15+
* @example
16+
* ```ts
17+
* \@if (isTyping(agent)) { <chat-typing-indicator [agent]="agent" /> }
18+
* ```
19+
*/
820
export function isTyping(agent: Agent): boolean {
921
if (!agent.isLoading()) return false;
1022
const msgs = agent.messages();

libs/chat/src/lib/testing/mock-agent.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,21 @@ export interface MockAgentOptions {
6363
events$?: Observable<AgentEvent>;
6464
}
6565

66+
/**
67+
* Build an in-memory {@link Agent} for tests and stories — no transport, no
68+
* network. Every field is a writable signal so a test can drive UI states
69+
* (loading, error, interrupts, tool calls, subagents) deterministically.
70+
*
71+
* @param opts Initial values for the mock's signals; all optional.
72+
* @returns A {@link MockAgent} satisfying the full `Agent` contract.
73+
* @example
74+
* ```ts
75+
* const agent = mockAgent({
76+
* messages: [{ id: '1', role: 'assistant', content: 'Hi' }],
77+
* isLoading: true,
78+
* });
79+
* ```
80+
*/
6681
export function mockAgent(opts: MockAgentOptions = {}): MockAgent {
6782
const messages = signal<Message[]>(opts.messages ?? []);
6883
const status = signal<AgentStatus>(opts.status ?? 'idle');

libs/render/src/lib/provide-views.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ import type { ViewRegistry } from './views';
44

55
export const VIEW_REGISTRY = new InjectionToken<ViewRegistry>('VIEW_REGISTRY');
66

7+
/**
8+
* Register a {@link ViewRegistry} for the render engine so generative-UI specs
9+
* can resolve element `type`s to Angular components. Provide at the application
10+
* (or a feature route's) environment injector.
11+
*
12+
* @param registry Map of spec element types to the components that render them,
13+
* typically built with `views()` / `withViews()`.
14+
* @returns Environment providers to spread into `providers` / `provideChat`.
15+
* @example
16+
* ```ts
17+
* bootstrapApplication(App, {
18+
* providers: [provideViews(views({ metric: MetricCardComponent }))],
19+
* });
20+
* ```
21+
*/
722
export function provideViews(registry: ViewRegistry) {
823
return makeEnvironmentProviders([
924
{ provide: VIEW_REGISTRY, useValue: registry },

libs/render/src/lib/signal-state-store.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ function setByPath(obj: unknown, segments: string[], value: unknown): unknown {
3434
return record;
3535
}
3636

37+
/**
38+
* Create a signal-backed {@link StateStore} for a generative-UI surface —
39+
* holds the bound state that spec `$bindState` paths read and interactive
40+
* elements write, with path-addressable get/set and change subscriptions.
41+
*
42+
* @param initialState Optional starting state object.
43+
* @returns A {@link StateStore} bridging Angular signals to the render engine.
44+
* @example
45+
* ```ts
46+
* const store = signalStateStore({ count: 0 });
47+
* store.set('count', 1);
48+
* ```
49+
*/
3750
export function signalStateStore(initialState: StateModel = {}): StateStore {
3851
const state = signal<StateModel>(initialState);
3952
const listeners = new Set<() => void>();

0 commit comments

Comments
 (0)