Add extensions option to createAgentSession SDK

- Accept ExtensionFactory[] for inline extensions (merged with discovery)
- Mark preloadedExtensions as @internal (CLI implementation detail)
- Update sdk.md with inline extension example
- Update CHANGELOG
This commit is contained in:
Mario Zechner 2026-01-05 03:38:56 +01:00
parent 8da793b1ba
commit 79cb8f0906
6 changed files with 138 additions and 24 deletions

View file

@ -16,14 +16,15 @@ Sessions have a version field in the header:
- **Version 1**: Linear entry sequence (legacy, auto-migrated on load)
- **Version 2**: Tree structure with `id`/`parentId` linking
- **Version 3**: Renamed `hookMessage` role to `custom` (extensions unification)
Existing v1 sessions are automatically migrated to v2 when loaded.
Existing sessions are automatically migrated to the current version (v3) when loaded.
## Type Definitions
- [`src/core/session-manager.ts`](../src/core/session-manager.ts) - Session entry types
- [`packages/agent/src/types.ts`](../../agent/src/types.ts) - `AgentMessage`, `Attachment`, `ThinkingLevel`
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `UserMessage`, `AssistantMessage`, `ToolResultMessage`, `Usage`, `ToolCall`
- [`packages/agent-core/src/types.ts`](../../agent-core/src/types.ts) - `AgentMessage`
- [`packages/ai/src/types.ts`](../../ai/src/types.ts) - `UserMessage`, `AssistantMessage`, `ToolResultMessage`, `Usage`, `ToolCall`, `ImageContent`, `TextContent`
## Entry Base
@ -45,13 +46,13 @@ interface SessionEntryBase {
First line of the file. Metadata only, not part of the tree (no `id`/`parentId`).
```json
{"type":"session","version":2,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project"}
```
For sessions with a parent (created via `/branch` or `newSession({ parentSession })`):
```json
{"type":"session","version":2,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}
{"type":"session","version":3,"id":"uuid","timestamp":"2024-12-03T14:00:00.000Z","cwd":"/path/to/project","parentSession":"/path/to/original/session.jsonl"}
```
### SessionMessageEntry
@ -89,8 +90,8 @@ Created when context is compacted. Stores a summary of earlier messages.
```
Optional fields:
- `details`: Compaction-implementation specific data (e.g., file operations for default implementation, or custom data for custom hook implementations)
- `fromHook`: `true` if generated by a hook, `false`/`undefined` if pi-generated
- `details`: Compaction-implementation specific data (e.g., file operations for default implementation, or custom data for extension implementations)
- `fromHook`: `true` if generated by an extension, `false`/`undefined` if pi-generated
### BranchSummaryEntry
@ -102,30 +103,30 @@ Created when switching branches via `/tree` with an LLM generated summary of the
Optional fields:
- `details`: File tracking data (`{ readFiles: string[], modifiedFiles: string[] }`) for default implementation, arbitrary for custom implementation
- `fromHook`: `true` if generated by a hook
- `fromHook`: `true` if generated by an extension
### CustomEntry
Hook state persistence. Does NOT participate in LLM context.
Extension state persistence. Does NOT participate in LLM context.
```json
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-hook","data":{"count":42}}
{"type":"custom","id":"h8i9j0k1","parentId":"g7h8i9j0","timestamp":"2024-12-03T14:20:00.000Z","customType":"my-extension","data":{"count":42}}
```
Use `customType` to identify your hook's entries on reload.
Use `customType` to identify your extension's entries on reload.
### CustomMessageEntry
Hook-injected messages that DO participate in LLM context.
Extension-injected messages that DO participate in LLM context.
```json
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-hook","content":"Injected context...","display":true}
{"type":"custom_message","id":"i9j0k1l2","parentId":"h8i9j0k1","timestamp":"2024-12-03T14:25:00.000Z","customType":"my-extension","content":"Injected context...","display":true}
```
Fields:
- `content`: String or `(TextContent | ImageContent)[]` (same as UserMessage)
- `display`: `true` = show in TUI with purple styling, `false` = hidden
- `details`: Optional hook-specific metadata (not sent to LLM)
- `display`: `true` = show in TUI with distinct styling, `false` = hidden
- `details`: Optional extension-specific metadata (not sent to LLM)
### LabelEntry
@ -190,7 +191,7 @@ for (const line of lines) {
console.log(`[${entry.id}] Custom (${entry.customType}): ${JSON.stringify(entry.data)}`);
break;
case "custom_message":
console.log(`[${entry.id}] Hook message (${entry.customType}): ${entry.content}`);
console.log(`[${entry.id}] Extension message (${entry.customType}): ${entry.content}`);
break;
case "label":
console.log(`[${entry.id}] Label "${entry.label}" on ${entry.targetId}`);
@ -220,18 +221,20 @@ Key methods for working with sessions programmatically:
- `appendThinkingLevelChange(level)` - Record thinking change
- `appendModelChange(provider, modelId)` - Record model change
- `appendCompaction(summary, firstKeptEntryId, tokensBefore, details?, fromHook?)` - Add compaction
- `appendCustomEntry(customType, data?)` - Hook state (not in context)
- `appendCustomMessageEntry(customType, content, display, details?)` - Hook message (in context)
- `appendCustomEntry(customType, data?)` - Extension state (not in context)
- `appendCustomMessageEntry(customType, content, display, details?)` - Extension message (in context)
- `appendLabelChange(targetId, label)` - Set/clear label
### Tree Navigation
- `getLeafId()` - Current position
- `getLeafEntry()` - Get current leaf entry
- `getEntry(id)` - Get entry by ID
- `getPath(fromId?)` - Walk from entry to root
- `getBranch(fromId?)` - Walk from entry to root
- `getTree()` - Get full tree structure
- `getChildren(parentId)` - Get direct children
- `getLabel(id)` - Get label for entry
- `branch(entryId)` - Move leaf to earlier entry
- `resetLeaf()` - Reset leaf to null (before any entries)
- `branchWithSummary(entryId, summary, details?, fromHook?)` - Branch with context summary
### Context