Skip to content

Commit e846aa6

Browse files
stephentoubCopilot
andauthored
Export generated session event types (#1316)
* Export generated session event types Re-export generated session event types from the Node package root so consumers can import dedicated *Event and *Data types such as ToolExecutionStartData without deep-importing dist internals. Add a focused TypeScript regression test and wire it into typecheck so missing root exports fail in CI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Clarify session event type export test Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e27eecb commit e846aa6

4 files changed

Lines changed: 206 additions & 1 deletion

File tree

nodejs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
"format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" --ignore-path .prettierignore",
4141
"lint": "eslint \"src/**/*.ts\" \"test/**/*.ts\"",
4242
"lint:fix": "eslint --fix \"src/**/*.ts\" \"test/**/*.ts\"",
43-
"typecheck": "tsc --noEmit",
43+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
4444
"generate": "cd ../scripts/codegen && npm run generate",
4545
"update:protocol-version": "tsx scripts/update-protocol-version.ts",
4646
"prepublishOnly": "npm run build",

nodejs/src/index.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ export {
1717
createSessionFsAdapter,
1818
SYSTEM_PROMPT_SECTIONS,
1919
} from "./types.js";
20+
// Re-export the generated session-event types (every *Event interface and
21+
// its corresponding *Data payload type, plus supporting unions/aliases) so
22+
// consumers can import them directly from "@github/copilot-sdk" instead of
23+
// reaching into the package's internal dist layout. See issue #1156.
24+
//
25+
// Three names from this file are also explicitly exported elsewhere in this
26+
// module — `SessionEvent` (re-exported below from `./types.js`),
27+
// `PermissionRequest` (re-exported below from `./types.js`), and
28+
// `AssistantMessageEvent` (re-exported above from `./session.js`). Per the
29+
// ECMAScript module spec, the explicit named re-exports shadow the names
30+
// arriving via `export type *`, so the hand-authored public API surface for
31+
// those three identifiers is preserved unchanged.
32+
export type * from "./generated/session-events.js";
2033
export type {
2134
CommandContext,
2235
CommandDefinition,
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/**
2+
* Regression test for #1156: dedicated session event data/payload types are
3+
* importable from the package entry point (`@github/copilot-sdk` /
4+
* `src/index.js`).
5+
*
6+
* Before this fix, only the aggregate `SessionEvent` discriminated union was
7+
* re-exported. The constituent `*Event` wrapper interfaces and their `*Data`
8+
* payload types lived in `generated/session-events.ts` and could only be
9+
* reached via a deep import (`@github/copilot-sdk/dist/generated/...`).
10+
*
11+
* Most of this file exercises the *type* surface — if these type-only imports
12+
* compile, the public API exposes the types. The runtime assertions below only
13+
* validate representative object shapes for those annotations; they do not
14+
* prove that type-only exports exist at runtime.
15+
*/
16+
17+
import { describe, expect, it } from "vitest";
18+
import type {
19+
// The aggregate union; must still resolve via the package root.
20+
SessionEvent,
21+
22+
// *Data payload types from the v0.3.0 generated session-event schema.
23+
AssistantMessageData,
24+
AssistantMessageDeltaData,
25+
AssistantReasoningData,
26+
AssistantTurnStartData,
27+
ErrorData,
28+
IdleData,
29+
ResumeData,
30+
StartData,
31+
ToolExecutionCompleteData,
32+
ToolExecutionPartialData,
33+
ToolExecutionProgressData,
34+
ToolExecutionStartData,
35+
UserMessageData,
36+
37+
// *Event wrapper interfaces.
38+
AssistantMessageEvent,
39+
ErrorEvent,
40+
IdleEvent,
41+
ResumeEvent,
42+
StartEvent,
43+
ToolExecutionCompleteEvent,
44+
ToolExecutionStartEvent,
45+
UserMessageEvent,
46+
47+
// A sample of supporting auxiliary aliases/unions referenced by the
48+
// *Data shapes — these must also be reachable so that consumers can
49+
// narrow or annotate intermediate values.
50+
UserMessageAgentMode,
51+
UserMessageAttachment,
52+
WorkingDirectoryContextHostType,
53+
} from "../src/index.js";
54+
55+
/**
56+
* Type-only helper: forces the compiler to resolve the supplied type
57+
* parameter. If the type is not exported from `../src/index.js`, the file
58+
* fails to type-check and the test never runs. There is no runtime body —
59+
* the helper exists purely to make "is this type importable?" assertions
60+
* compile-time checked.
61+
*/
62+
function assertImportable<_T>(): void {
63+
/* no-op; compile-time check only */
64+
}
65+
66+
/**
67+
* Compile-time mutual-assignability check: passes only when `A` and `B`
68+
* are structurally equivalent. Used below to pin the package-root
69+
* `AssistantMessageEvent` (which is explicitly re-exported from
70+
* `./session.js` and therefore shadows the generated `AssistantMessageEvent`
71+
* arriving via `export type *`) to the corresponding arm of the generated
72+
* `SessionEvent` union. If a future schema regen ever caused these two
73+
* shapes to drift, this assertion would fail to type-check and `npm run
74+
* typecheck` would surface it before the public API silently changed.
75+
*/
76+
type _AssertEqual<A, B> =
77+
(<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
78+
type _AssistantMessageEventStaysAlignedWithSessionEventUnion = _AssertEqual<
79+
AssistantMessageEvent,
80+
Extract<SessionEvent, { type: "assistant.message" }>
81+
>;
82+
const _assistantMessageEventAlignmentCheck: _AssistantMessageEventStaysAlignedWithSessionEventUnion = true;
83+
84+
describe("Session event type exports (#1156)", () => {
85+
it("exposes the headline ToolExecutionStartData type with a usable shape", () => {
86+
// This is the specific type called out in issue #1156. The annotation
87+
// is the compile-time API-surface check; these assertions only validate
88+
// the representative runtime object shape a consumer would use.
89+
const data: ToolExecutionStartData = {
90+
toolCallId: "call-1",
91+
toolName: "shell",
92+
arguments: { command: "ls" },
93+
mcpServerName: "filesystem",
94+
mcpToolName: "list_dir",
95+
turnId: "turn-1",
96+
};
97+
98+
expect(data.toolName).toBe("shell");
99+
expect(data.toolCallId).toBe("call-1");
100+
expect(data.arguments?.command).toBe("ls");
101+
expect(data.mcpServerName).toBe("filesystem");
102+
expect(data.mcpToolName).toBe("list_dir");
103+
expect(data.turnId).toBe("turn-1");
104+
});
105+
106+
it("wraps ToolExecutionStartData inside the exported ToolExecutionStartEvent", () => {
107+
const event: ToolExecutionStartEvent = {
108+
id: "evt-1",
109+
parentId: null,
110+
timestamp: "2026-01-01T00:00:00.000Z",
111+
type: "tool.execution_start",
112+
data: {
113+
toolCallId: "call-1",
114+
toolName: "shell",
115+
},
116+
};
117+
118+
expect(event.type).toBe("tool.execution_start");
119+
expect(event.data.toolName).toBe("shell");
120+
expect(event.parentId).toBeNull();
121+
});
122+
123+
it("narrows the aggregate SessionEvent union to a dedicated *Data type", () => {
124+
const evt: SessionEvent = {
125+
id: "evt-2",
126+
parentId: null,
127+
timestamp: "2026-01-01T00:00:01.000Z",
128+
type: "tool.execution_start",
129+
data: {
130+
toolCallId: "call-2",
131+
toolName: "shell",
132+
},
133+
};
134+
135+
if (evt.type !== "tool.execution_start") {
136+
throw new Error("expected tool.execution_start narrowing");
137+
}
138+
139+
// After narrowing, `evt.data` must satisfy `ToolExecutionStartData`.
140+
// Annotating the local with the dedicated *Data type proves the
141+
// re-export is wired up correctly.
142+
const data: ToolExecutionStartData = evt.data;
143+
expect(data.toolCallId).toBe("call-2");
144+
expect(data.toolName).toBe("shell");
145+
});
146+
147+
it("re-exports the full set of *Data and *Event types named in v0.3.0", () => {
148+
// Compile-time checks: if any of these fail to resolve, the file
149+
// will not type-check and the test will not be executed.
150+
assertImportable<AssistantMessageData>();
151+
assertImportable<AssistantMessageDeltaData>();
152+
assertImportable<AssistantReasoningData>();
153+
assertImportable<AssistantTurnStartData>();
154+
assertImportable<ErrorData>();
155+
assertImportable<IdleData>();
156+
assertImportable<ResumeData>();
157+
assertImportable<StartData>();
158+
assertImportable<ToolExecutionCompleteData>();
159+
assertImportable<ToolExecutionPartialData>();
160+
assertImportable<ToolExecutionProgressData>();
161+
assertImportable<ToolExecutionStartData>();
162+
assertImportable<UserMessageData>();
163+
164+
assertImportable<AssistantMessageEvent>();
165+
assertImportable<ErrorEvent>();
166+
assertImportable<IdleEvent>();
167+
assertImportable<ResumeEvent>();
168+
assertImportable<StartEvent>();
169+
assertImportable<ToolExecutionCompleteEvent>();
170+
assertImportable<ToolExecutionStartEvent>();
171+
assertImportable<UserMessageEvent>();
172+
173+
// Supporting auxiliary types referenced by the *Data shapes — these
174+
// must round-trip through the package root too, otherwise consumers
175+
// annotating intermediate values would still need a deep import.
176+
assertImportable<UserMessageAgentMode>();
177+
assertImportable<UserMessageAttachment>();
178+
assertImportable<WorkingDirectoryContextHostType>();
179+
180+
expect(true).toBe(true);
181+
});
182+
});

nodejs/tsconfig.test.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"noEmit": true,
5+
"emitDeclarationOnly": false,
6+
"types": ["node"]
7+
},
8+
"include": ["src/**/*", "test/session-event-types.test.ts"],
9+
"exclude": ["node_modules", "dist"]
10+
}

0 commit comments

Comments
 (0)