@nimbalyst/extension-sdk 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/__tests__/createTextCollabContentAdapter.test.d.ts +2 -0
  3. package/dist/__tests__/createTextCollabContentAdapter.test.d.ts.map +1 -0
  4. package/dist/__tests__/createTextCollabContentAdapter.test.js +60 -0
  5. package/dist/__tests__/hasSeedableContent.test.d.ts +2 -0
  6. package/dist/__tests__/hasSeedableContent.test.d.ts.map +1 -0
  7. package/dist/__tests__/hasSeedableContent.test.js +25 -0
  8. package/dist/__tests__/manifestValidation.agentProviders.test.d.ts +2 -0
  9. package/dist/__tests__/manifestValidation.agentProviders.test.d.ts.map +1 -0
  10. package/dist/__tests__/manifestValidation.agentProviders.test.js +44 -0
  11. package/dist/agents/AgentProtocol.d.ts +227 -0
  12. package/dist/agents/AgentProtocol.d.ts.map +1 -0
  13. package/dist/agents/AgentProtocol.js +23 -0
  14. package/dist/agents/AgentProtocolHost.d.ts +190 -0
  15. package/dist/agents/AgentProtocolHost.d.ts.map +1 -0
  16. package/dist/agents/AgentProtocolHost.js +42 -0
  17. package/dist/agents/index.d.ts +26 -0
  18. package/dist/agents/index.d.ts.map +1 -0
  19. package/dist/agents/index.js +24 -0
  20. package/dist/collab/createTextCollabContentAdapter.d.ts +26 -0
  21. package/dist/collab/createTextCollabContentAdapter.d.ts.map +1 -0
  22. package/dist/collab/createTextCollabContentAdapter.js +79 -0
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -0
  26. package/dist/manifestValidation.d.ts +118 -0
  27. package/dist/manifestValidation.d.ts.map +1 -0
  28. package/dist/manifestValidation.js +475 -0
  29. package/dist/types/collab.d.ts +138 -0
  30. package/dist/types/collab.d.ts.map +1 -0
  31. package/dist/types/collab.js +1 -0
  32. package/dist/types/editor.d.ts +71 -0
  33. package/dist/types/editor.d.ts.map +1 -1
  34. package/dist/types/editors.d.ts +4 -0
  35. package/dist/types/editors.d.ts.map +1 -1
  36. package/dist/types/extension.d.ts +364 -5
  37. package/dist/types/extension.d.ts.map +1 -1
  38. package/dist/types/extension.js +11 -1
  39. package/dist/types/index.d.ts +3 -0
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/types/index.js +3 -0
  42. package/dist/types/panel.d.ts +50 -0
  43. package/dist/types/panel.d.ts.map +1 -1
  44. package/dist/types/permissions.d.ts +117 -0
  45. package/dist/types/permissions.d.ts.map +1 -0
  46. package/dist/types/permissions.js +33 -0
  47. package/dist/types/theme.d.ts +50 -0
  48. package/dist/types/theme.d.ts.map +1 -1
  49. package/dist/types/theme.js +7 -1
  50. package/dist/types/trackerImporter.d.ts +147 -0
  51. package/dist/types/trackerImporter.d.ts.map +1 -0
  52. package/dist/types/trackerImporter.js +33 -0
  53. package/dist/useCollaborativeEditor.d.ts +84 -30
  54. package/dist/useCollaborativeEditor.d.ts.map +1 -1
  55. package/dist/useCollaborativeEditor.js +95 -7
  56. package/dist/validate.browser.d.ts +5 -0
  57. package/dist/validate.browser.d.ts.map +1 -0
  58. package/dist/validate.browser.js +27 -0
  59. package/dist/validate.d.ts +2 -8
  60. package/dist/validate.d.ts.map +1 -1
  61. package/dist/validate.js +117 -0
  62. package/dist/validationTypes.d.ts +9 -0
  63. package/dist/validationTypes.d.ts.map +1 -0
  64. package/dist/validationTypes.js +1 -0
  65. package/package.json +4 -1
package/CHANGELOG.md CHANGED
@@ -8,12 +8,25 @@ The SDK is versioned independently of the Nimbalyst app. Each release declares i
8
8
 
9
9
  | SDK version | Minimum Nimbalyst app version |
10
10
  | --- | --- |
11
+ | 0.2.1 | 0.58.5 |
11
12
  | 0.2.0 | 0.58.5 |
12
13
  | 0.1.5 | 0.58.5 |
13
14
  | 0.1.0 | 0.58.5 |
14
15
 
15
16
  ## [Unreleased]
16
17
 
18
+ ## [0.2.1]
19
+
20
+ ### Added
21
+
22
+ - `ExtensionAITool.access` declares whether a tool uses filesystem, editor-read, or editor-write access; `readOnly` remains as a compatibility alias.
23
+ - `useCollaborativeEditor` accepts `{ codec, bind }` so an editor's pure collab codec is defined once and shared with the host's headless seeding; the previous `createBinding`/`initializeFromContent` config keeps working.
24
+ - `CollaborationContext.flushWithAck` (server-persisted flush) and optional `hasUndecodedContent`.
25
+
26
+ ### Fixed
27
+
28
+ - The first-open seed no longer runs from empty initial content or when the transport skipped payloads it could not decode — both cases wrote a default document over the shared room's real content.
29
+
17
30
  ## [0.2.0]
18
31
 
19
32
  Adds opt-in collaborative editing for custom editors. Backwards-compatible: extensions built against 0.2.0 continue to work on older Nimbalyst hosts -- the collaboration hook detects the absence of `host.collaboration` and reports `isCollaborative: false` so the editor falls back to local-only editing.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=createTextCollabContentAdapter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createTextCollabContentAdapter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/createTextCollabContentAdapter.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,60 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import * as Y from 'yjs';
3
+ import { createTextCollabContentAdapter } from '../collab/createTextCollabContentAdapter';
4
+ const adapter = createTextCollabContentAdapter({
5
+ documentType: 'calc.md',
6
+ fileExtensions: ['.calc.md'],
7
+ mimeType: 'text/markdown',
8
+ textField: 'content',
9
+ });
10
+ describe('createTextCollabContentAdapter', () => {
11
+ it('reports identity (documentType, extensions, version)', () => {
12
+ expect(adapter.documentType).toBe('calc.md');
13
+ expect(adapter.fileExtensions).toEqual(['.calc.md']);
14
+ expect(adapter.layoutVersion).toBe(1);
15
+ });
16
+ it('isEmpty is true for a fresh Y.Doc and false after seeding', () => {
17
+ const doc = new Y.Doc();
18
+ expect(adapter.isEmpty(doc)).toBe(true);
19
+ adapter.seedFromFile(doc, 'price = 100 USD\n');
20
+ expect(adapter.isEmpty(doc)).toBe(false);
21
+ });
22
+ it('round-trips file content through seed -> export -> plain text', () => {
23
+ const doc = new Y.Doc();
24
+ const source = '# Inputs\nprice = 100 USD\nqty = 3\n';
25
+ adapter.seedFromFile(doc, source);
26
+ expect(adapter.exportToFile(doc)).toBe(source);
27
+ expect(adapter.toPlainText(doc)).toBe(source);
28
+ });
29
+ it('decodes Uint8Array file sources', () => {
30
+ const doc = new Y.Doc();
31
+ const source = 'total = price * qty\n';
32
+ adapter.seedFromFile(doc, new TextEncoder().encode(source));
33
+ expect(adapter.exportToFile(doc)).toBe(source);
34
+ });
35
+ it('seedFromFile is a no-op when the Y.Text is already populated (open race)', () => {
36
+ const doc = new Y.Doc();
37
+ adapter.seedFromFile(doc, 'first');
38
+ adapter.seedFromFile(doc, 'second');
39
+ expect(adapter.exportToFile(doc)).toBe('first');
40
+ });
41
+ it('applyFromFile replaces existing content on a populated Y.Doc', () => {
42
+ const doc = new Y.Doc();
43
+ adapter.seedFromFile(doc, 'old content');
44
+ adapter.applyFromFile(doc, 'brand new content');
45
+ expect(adapter.exportToFile(doc)).toBe('brand new content');
46
+ });
47
+ it('does not re-seed a doc that already received content via sync (race guard)', () => {
48
+ // Models the bootstrap race on a SINGLE shared doc: another client's seed
49
+ // arrives over the wire first, then this client (which had decided the doc
50
+ // was empty) calls seedFromFile. The length guard makes the late seed a
51
+ // no-op, so the synced content is preserved verbatim (no concatenation).
52
+ // The hook layer additionally re-checks isEmpty before seeding.
53
+ const remote = new Y.Doc();
54
+ adapter.seedFromFile(remote, 'a = 1\nb = 2\n');
55
+ const local = new Y.Doc();
56
+ Y.applyUpdate(local, Y.encodeStateAsUpdate(remote)); // remote seed arrives
57
+ adapter.seedFromFile(local, 'a = 1\nb = 2\n'); // late local seed -> no-op
58
+ expect(adapter.exportToFile(local)).toBe('a = 1\nb = 2\n');
59
+ });
60
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hasSeedableContent.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hasSeedableContent.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hasSeedableContent.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { hasSeedableContent } from '../useCollaborativeEditor';
3
+ /**
4
+ * NIM-1520 fallout guard: a host with no bytes for a collab doc returns ''
5
+ * from loadInitialContent; seeding from that writes a DEFAULT document over
6
+ * the room's real content ("Untitled map" clobber). Empty content must never
7
+ * be considered seedable.
8
+ */
9
+ describe('hasSeedableContent', () => {
10
+ it('rejects null/undefined/empty/whitespace strings', () => {
11
+ expect(hasSeedableContent(null)).toBe(false);
12
+ expect(hasSeedableContent(undefined)).toBe(false);
13
+ expect(hasSeedableContent('')).toBe(false);
14
+ expect(hasSeedableContent(' \n\t')).toBe(false);
15
+ });
16
+ it('rejects empty binary buffers', () => {
17
+ expect(hasSeedableContent(new ArrayBuffer(0))).toBe(false);
18
+ expect(hasSeedableContent(new Uint8Array(0))).toBe(false);
19
+ });
20
+ it('accepts real content', () => {
21
+ expect(hasSeedableContent('# Title')).toBe(true);
22
+ expect(hasSeedableContent(new Uint8Array([1, 2, 3]))).toBe(true);
23
+ expect(hasSeedableContent(new ArrayBuffer(8))).toBe(true);
24
+ });
25
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=manifestValidation.agentProviders.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestValidation.agentProviders.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/manifestValidation.agentProviders.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { validateAgentProviders } from '../manifestValidation';
3
+ // The validator must agree with the AiAgentProviderContribution /
4
+ // AiAgentProviderModel types: the provider label field is `displayName`, and
5
+ // model entries carry no per-entry `provider`. A manifest matching the shipped
6
+ // gemini-antigravity contribution should pass cleanly. Before the alignment,
7
+ // the validator required `name` and `model.provider`, so the real manifest
8
+ // failed its own validator (#558 review, point 1).
9
+ const backendModules = [{ id: 'antigravity-server', entry: 'backend/index.js' }];
10
+ function geminiLikeProvider(overrides = {}) {
11
+ return {
12
+ id: 'antigravity-gemini-agent',
13
+ displayName: 'Gemini',
14
+ platform: 'antigravity-gemini',
15
+ backendModuleId: 'antigravity-server',
16
+ modelDiscovery: 'static',
17
+ models: [
18
+ { id: 'antigravity-gemini-agent:gemini-3-flash-agent', name: 'Gemini 3.5 Flash (High)', default: true },
19
+ { id: 'antigravity-gemini-agent:gemini-3.5-flash-low', name: 'Gemini 3.5 Flash (Medium)' },
20
+ ],
21
+ ...overrides,
22
+ };
23
+ }
24
+ describe('validateAgentProviders - alignment with the SDK type (#558)', () => {
25
+ it('accepts a provider that uses displayName and models without a provider field', () => {
26
+ const issues = validateAgentProviders([geminiLikeProvider()], backendModules);
27
+ expect(issues).toEqual([]);
28
+ });
29
+ it('flags a provider missing displayName', () => {
30
+ const { displayName, ...noDisplayName } = geminiLikeProvider();
31
+ const issues = validateAgentProviders([noDisplayName], backendModules);
32
+ expect(issues.some((i) => i.message.includes('displayName'))).toBe(true);
33
+ });
34
+ it('does not require a per-model provider field', () => {
35
+ // A model entry with only id + name is valid; the host derives the
36
+ // provider from the contribution id.
37
+ const issues = validateAgentProviders([geminiLikeProvider({ models: [{ id: 'm1', name: 'Model One' }] })], backendModules);
38
+ expect(issues).toEqual([]);
39
+ });
40
+ it('still requires a model name', () => {
41
+ const issues = validateAgentProviders([geminiLikeProvider({ models: [{ id: 'm1' }] })], backendModules);
42
+ expect(issues.some((i) => i.message.includes('.name must be a non-empty string'))).toBe(true);
43
+ });
44
+ });
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Protocol Interface for Agent SDK Adapters
3
+ *
4
+ * This interface normalizes the differences between various agent SDKs
5
+ * (Claude Agent SDK, OpenAI Codex SDK) to provide a unified abstraction
6
+ * layer for agent providers.
7
+ *
8
+ * The protocol adapters isolate platform-specific SDK details from the
9
+ * provider implementations, making it easier to:
10
+ * - Add new agent SDKs
11
+ * - Update SDK versions without touching provider logic
12
+ * - Test providers with mock protocols
13
+ * - Share common infrastructure across providers
14
+ *
15
+ * Ownership note: this module is the canonical home of the protocol
16
+ * contract. The runtime re-exports these types via
17
+ * `@nimbalyst/runtime/ai/server/protocols/ProtocolInterface` so that
18
+ * existing runtime imports keep working without source churn, but the
19
+ * SDK now owns the type bodies. Adding a new field, changing a tag, or
20
+ * tightening a discriminator happens here first; the runtime picks it
21
+ * up via the re-export.
22
+ */
23
+ /**
24
+ * MCP server configuration
25
+ */
26
+ export interface MCPServerConfig {
27
+ command: string;
28
+ args?: string[];
29
+ env?: Record<string, string>;
30
+ [key: string]: unknown;
31
+ }
32
+ /**
33
+ * Platform-specific session data for internal use
34
+ */
35
+ export interface RawProtocolSession {
36
+ [key: string]: unknown;
37
+ }
38
+ /**
39
+ * Structured result from tool execution
40
+ */
41
+ export interface ToolResult {
42
+ success: boolean;
43
+ result?: unknown;
44
+ output?: unknown;
45
+ error?: string | unknown;
46
+ status?: string;
47
+ command?: string;
48
+ exit_code?: number;
49
+ changes?: unknown;
50
+ query?: string;
51
+ action?: unknown;
52
+ fileSnapshots?: Record<string, {
53
+ content: string | null;
54
+ error?: string;
55
+ isBinary?: boolean;
56
+ truncated?: boolean;
57
+ }>;
58
+ }
59
+ /**
60
+ * Options for creating or resuming a session
61
+ */
62
+ export interface SessionOptions {
63
+ /** Working directory path for the session */
64
+ workspacePath: string;
65
+ /** Model identifier (e.g., 'sonnet', 'gpt-5') */
66
+ model?: string;
67
+ /** System prompt to initialize the agent */
68
+ systemPrompt?: string;
69
+ /** Abort signal for cancelling the session */
70
+ abortSignal?: AbortSignal;
71
+ /** Permission mode for tool approvals ('ask', 'auto', 'plan') */
72
+ permissionMode?: string;
73
+ /** MCP server configurations */
74
+ mcpServers?: Record<string, MCPServerConfig>;
75
+ /** Environment variables for the session */
76
+ env?: Record<string, string>;
77
+ /** Tools to allow (whitelist) */
78
+ allowedTools?: string[];
79
+ /** Tools to disallow (blacklist) */
80
+ disallowedTools?: string[];
81
+ /** Platform-specific options that don't fit the common schema */
82
+ raw?: Record<string, unknown>;
83
+ }
84
+ /**
85
+ * Attachment shape used by ProtocolMessage.
86
+ *
87
+ * Inlined into the SDK (rather than re-imported from the runtime's
88
+ * `ChatAttachment`) so the protocol contract has no dependency on
89
+ * runtime types. The runtime's `ChatAttachment` is structurally
90
+ * assignable to this shape, so existing runtime call sites that pass
91
+ * `ChatAttachment[]` continue to type-check at the SDK boundary.
92
+ */
93
+ export interface ProtocolAttachment {
94
+ id: string;
95
+ filename: string;
96
+ filepath: string;
97
+ mimeType: string;
98
+ size: number;
99
+ type: 'image' | 'pdf' | 'document';
100
+ thumbnail?: string;
101
+ addedAt: number;
102
+ }
103
+ /**
104
+ * Message sent to the agent
105
+ */
106
+ export interface ProtocolMessage {
107
+ /** Text content of the message */
108
+ content: string;
109
+ /** Optional attachments (images, PDFs, documents) */
110
+ attachments?: ProtocolAttachment[];
111
+ /** Session ID for logging and tracking */
112
+ sessionId?: string;
113
+ /** AI mode when message was sent ('planning' or 'agent' or 'auto') */
114
+ mode?: 'planning' | 'agent' | 'auto';
115
+ }
116
+ /**
117
+ * Session created or resumed by the protocol
118
+ */
119
+ export interface ProtocolSession {
120
+ /** Platform-specific session identifier */
121
+ id: string;
122
+ /** Platform identifier (e.g., 'claude-sdk', 'codex-sdk') */
123
+ platform: string;
124
+ /** Platform-specific session data (for internal use) */
125
+ raw?: RawProtocolSession;
126
+ }
127
+ /**
128
+ * Event types emitted during message streaming
129
+ */
130
+ export type ProtocolEventType = 'raw_event' | 'text' | 'reasoning' | 'tool_call' | 'tool_result' | 'error' | 'complete' | 'usage' | 'planning_mode_entered' | 'planning_mode_exited';
131
+ /**
132
+ * Event emitted during message streaming
133
+ */
134
+ export interface ProtocolEvent {
135
+ /** Event type */
136
+ type: ProtocolEventType;
137
+ /** Text content (for 'text' events) */
138
+ content?: string;
139
+ /** Tool call data (for 'tool_call' events) */
140
+ toolCall?: {
141
+ id?: string;
142
+ name: string;
143
+ arguments?: Record<string, any>;
144
+ result?: ToolResult | string;
145
+ };
146
+ /** Tool result data (for 'tool_result' events) */
147
+ toolResult?: {
148
+ id?: string;
149
+ name: string;
150
+ result?: ToolResult | string;
151
+ };
152
+ /** Error message (for 'error' events) */
153
+ error?: string;
154
+ /** Token usage (for 'usage' or 'complete' events) */
155
+ usage?: {
156
+ input_tokens: number;
157
+ output_tokens: number;
158
+ total_tokens: number;
159
+ };
160
+ /** Current context fill tokens for this turn (provider-reported snapshot) */
161
+ contextFillTokens?: number;
162
+ /** Maximum context window for the active model */
163
+ contextWindow?: number;
164
+ /** Additional metadata */
165
+ metadata?: Record<string, unknown>;
166
+ }
167
+ /**
168
+ * Agent Protocol Interface
169
+ *
170
+ * All agent SDK adapters must implement this interface to provide
171
+ * a consistent abstraction layer for agent providers.
172
+ */
173
+ export interface AgentProtocol {
174
+ /**
175
+ * Platform identifier (e.g., 'claude-sdk', 'codex-sdk')
176
+ */
177
+ readonly platform: string;
178
+ /**
179
+ * Create a new session
180
+ *
181
+ * @param options - Session configuration
182
+ * @returns Protocol session with platform-specific ID
183
+ */
184
+ createSession(options: SessionOptions): Promise<ProtocolSession>;
185
+ /**
186
+ * Resume an existing session
187
+ *
188
+ * @param sessionId - Platform-specific session ID to resume
189
+ * @param options - Session configuration
190
+ * @returns Protocol session
191
+ */
192
+ resumeSession(sessionId: string, options: SessionOptions): Promise<ProtocolSession>;
193
+ /**
194
+ * Fork an existing session (create a branch)
195
+ *
196
+ * Not all platforms support forking. If unsupported, implementations
197
+ * should either throw an error or create a new session.
198
+ *
199
+ * @param sessionId - Platform-specific session ID to fork from
200
+ * @param options - Session configuration for the fork
201
+ * @returns New protocol session branched from the source
202
+ */
203
+ forkSession(sessionId: string, options: SessionOptions): Promise<ProtocolSession>;
204
+ /**
205
+ * Send a message and receive streaming events
206
+ *
207
+ * @param session - Active protocol session
208
+ * @param message - Message to send
209
+ * @returns Async iterable of protocol events
210
+ */
211
+ sendMessage(session: ProtocolSession, message: ProtocolMessage): AsyncIterable<ProtocolEvent>;
212
+ /**
213
+ * Abort an active session
214
+ *
215
+ * @param session - Session to abort
216
+ */
217
+ abortSession(session: ProtocolSession): void;
218
+ /**
219
+ * Clean up session resources
220
+ *
221
+ * Called when a session is deleted or no longer needed.
222
+ *
223
+ * @param session - Session to clean up
224
+ */
225
+ cleanupSession(session: ProtocolSession): void;
226
+ }
227
+ //# sourceMappingURL=AgentProtocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentProtocol.d.ts","sourceRoot":"","sources":["../../src/agents/AgentProtocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Protocol Interface for Agent SDK Adapters
3
+ *
4
+ * This interface normalizes the differences between various agent SDKs
5
+ * (Claude Agent SDK, OpenAI Codex SDK) to provide a unified abstraction
6
+ * layer for agent providers.
7
+ *
8
+ * The protocol adapters isolate platform-specific SDK details from the
9
+ * provider implementations, making it easier to:
10
+ * - Add new agent SDKs
11
+ * - Update SDK versions without touching provider logic
12
+ * - Test providers with mock protocols
13
+ * - Share common infrastructure across providers
14
+ *
15
+ * Ownership note: this module is the canonical home of the protocol
16
+ * contract. The runtime re-exports these types via
17
+ * `@nimbalyst/runtime/ai/server/protocols/ProtocolInterface` so that
18
+ * existing runtime imports keep working without source churn, but the
19
+ * SDK now owns the type bodies. Adding a new field, changing a tag, or
20
+ * tightening a discriminator happens here first; the runtime picks it
21
+ * up via the re-export.
22
+ */
23
+ export {};
@@ -0,0 +1,190 @@
1
+ /**
2
+ * AgentProtocolHost
3
+ * -----------------
4
+ *
5
+ * Surface that the host (Nimbalyst) provides to an extension-owned
6
+ * `AgentProtocol` implementation at runtime. The protocol implementation
7
+ * lives in an extension's backend module and is brokered to the host by
8
+ * the privileged-host backend-module runtime; it must never reach for
9
+ * host internals directly.
10
+ *
11
+ * Identity fields are immutable for the lifetime of the host: they
12
+ * identify which extension contribution this protocol instance belongs
13
+ * to and which backend module is hosting it. Per-turn inputs (session
14
+ * id, workspace path, model, abort signal, permission mode, MCP
15
+ * servers, environment) are immutable for the lifetime of a single
16
+ * turn but may differ between turns on the same host instance.
17
+ *
18
+ * All async methods reject when the underlying broker call is rejected
19
+ * by the host (missing permission, unknown session, workspace
20
+ * mismatch, etc.) -- the protocol implementation is expected to
21
+ * surface those rejections as protocol-level errors rather than
22
+ * swallowing them.
23
+ *
24
+ * -------------------------------------------------------------------
25
+ * Q7 -- scope of this host (Phase-4 SDK proposal, resolved 2026-06-02)
26
+ * -------------------------------------------------------------------
27
+ *
28
+ * Per Q7 (Phase-4 SDK proposal, resolved 2026-06-02):
29
+ * emitEvent / requestPermission / askUserQuestion stay provider-private
30
+ * per the established upstream pattern (OpenAICodexProvider:1422-1491,
31
+ * claudeCode/askUserQuestion.ts:10, ProviderSessionManager.ts:49).
32
+ * Section 4.3's full facade is the agreed end-state, hoisted
33
+ * incrementally when each in-tree provider is next touched. Phase 4
34
+ * therefore ships the minimal 5-method host below; the missing
35
+ * methods are NOT a regression -- they live on the provider's own
36
+ * surface today and migrate behind a stable facade as each provider
37
+ * is reworked.
38
+ *
39
+ * See `docs/EXTENSION_ARCHITECTURE.md` and the Phase 4 SDK design doc
40
+ * (Section 4.3) for the full host contract.
41
+ */
42
+ import type { MCPServerConfig } from './index';
43
+ /**
44
+ * Permission posture the host has set for the current turn.
45
+ *
46
+ * - `ask` -- prompt the user for every tool call that requires
47
+ * consent.
48
+ * - `auto` -- pre-approved tool calls run without prompting;
49
+ * everything else still goes through the provider's own
50
+ * permission path (see Q7 above).
51
+ * - `plan` -- read-only / planning posture; the protocol
52
+ * implementation MUST NOT invoke side-effecting tools
53
+ * and SHOULD use this hint to bias the model toward
54
+ * planning output.
55
+ *
56
+ * This is advisory metadata about the host's posture. The provider's
57
+ * own permission code (kept provider-private per Q7) is still the
58
+ * authoritative gate at tool-call time.
59
+ */
60
+ export type PermissionMode = 'ask' | 'auto' | 'plan';
61
+ /**
62
+ * MCP tool the protocol implementation wants to register with the
63
+ * host for this session. The host federates these into the
64
+ * session-wide tool registry and exposes them to the model alongside
65
+ * built-in tools.
66
+ */
67
+ export interface McpToolDefinition {
68
+ /**
69
+ * Tool name as the model will see it (e.g.
70
+ * `mcp__antigravity__plan`).
71
+ */
72
+ name: string;
73
+ /**
74
+ * Short description for the model and for the consent prompt.
75
+ */
76
+ description: string;
77
+ /**
78
+ * JSON Schema for the tool's input. The host validates calls
79
+ * against this before invoking the handler.
80
+ */
81
+ inputSchema: Record<string, unknown>;
82
+ }
83
+ /**
84
+ * Host surface passed to an extension's `AgentProtocol`
85
+ * implementation.
86
+ *
87
+ * Identity fields are constant; per-turn inputs are constant for the
88
+ * lifetime of a single turn. Methods may be invoked multiple times
89
+ * per turn (subject to the host's rate-limits and permission gates).
90
+ *
91
+ * Surface size is intentionally minimal -- see the Q7 note in the
92
+ * file header for why emitEvent / requestPermission /
93
+ * askUserQuestion are not on this interface.
94
+ */
95
+ export interface AgentProtocolHost {
96
+ /** Extension that owns the protocol implementation. */
97
+ readonly extensionId: string;
98
+ /**
99
+ * Contribution id this host instance was created for (matches the
100
+ * `id` field on an `AiAgentProviderContribution`).
101
+ */
102
+ readonly providerContributionId: string;
103
+ /**
104
+ * Backend module id hosting the protocol implementation (matches
105
+ * the `id` on a `BackendModuleContribution`).
106
+ */
107
+ readonly backendModuleId: string;
108
+ /** Active AI session id this turn belongs to. */
109
+ readonly sessionId: string;
110
+ /** Absolute workspace path the session is scoped to. */
111
+ readonly workspacePath: string;
112
+ /**
113
+ * Model id the user picked for this turn (`undefined` if the
114
+ * provider does not require a model selection).
115
+ */
116
+ readonly model?: string;
117
+ /**
118
+ * Abort signal that fires when the user cancels the turn or the
119
+ * host tears the session down. Protocol implementations MUST
120
+ * honour it promptly.
121
+ */
122
+ readonly abortSignal: AbortSignal;
123
+ /** Permission posture the host has set for this turn. */
124
+ readonly permissionMode: PermissionMode;
125
+ /**
126
+ * MCP servers the user has configured for this session. The
127
+ * protocol implementation may pass these through to the
128
+ * underlying agent runtime; the host is responsible for credential
129
+ * resolution.
130
+ */
131
+ readonly mcpServers: readonly MCPServerConfig[];
132
+ /**
133
+ * Environment overrides the host wants applied when the protocol
134
+ * spawns its agent runtime. Keys are environment variable names;
135
+ * values are already-resolved strings (no template substitution).
136
+ */
137
+ readonly env: Readonly<Record<string, string>>;
138
+ /**
139
+ * Append a raw provider-native payload to `ai_agent_messages`.
140
+ * This is the single source of truth for the transcript; the
141
+ * host's transcript transformer derives canonical events from it.
142
+ *
143
+ * `direction` is `'input'` for payloads the host sent to the
144
+ * provider and `'output'` for payloads the provider returned.
145
+ * `metadata` is opaque to the host; surfaced verbatim in raw-log
146
+ * tooling.
147
+ *
148
+ * Resolves once the row is durably written.
149
+ */
150
+ logRaw(sessionId: string, direction: 'input' | 'output', content: string, metadata?: Record<string, unknown>): Promise<void>;
151
+ /**
152
+ * Resolve an API key for the given provider id from the host's
153
+ * settings store (the same store the user manages from Settings).
154
+ *
155
+ * Resolves to `undefined` when no key is configured; the protocol
156
+ * implementation MUST NOT fall back to `process.env` (see the
157
+ * "Never Use Environment Variables as Implicit API Key Sources"
158
+ * rule in the project CLAUDE.md).
159
+ */
160
+ getApiKey(providerId: string): Promise<string | undefined>;
161
+ /**
162
+ * Read a workspace file via the host's filesystem broker. The
163
+ * path is resolved against `workspacePath`; reads outside the
164
+ * workspace are rejected.
165
+ *
166
+ * Returns the raw bytes; the protocol implementation is
167
+ * responsible for decoding.
168
+ */
169
+ readWorkspaceFile(path: string): Promise<Uint8Array>;
170
+ /**
171
+ * Write a workspace file via the host's filesystem broker.
172
+ * Subject to the extension's granted workspace-files permission;
173
+ * writes outside the workspace are rejected.
174
+ */
175
+ writeWorkspaceFile(path: string, content: Uint8Array): Promise<void>;
176
+ /**
177
+ * Register a batch of MCP tools with the host's session-scoped
178
+ * tool registry for the lifetime of this session. The returned
179
+ * promise resolves once the tools are visible to the model.
180
+ *
181
+ * Calling again replaces the prior registration for this host
182
+ * instance; pass an empty array to clear it. Tool handlers
183
+ * themselves are NOT part of this contract -- per Q7, the
184
+ * provider keeps its own dispatch path. The host only needs the
185
+ * advertised name / description / schema to expose the tool to
186
+ * the model and (eventually) to the provider's permission path.
187
+ */
188
+ registerMcpTools(tools: McpToolDefinition[]): Promise<void>;
189
+ }
190
+ //# sourceMappingURL=AgentProtocolHost.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AgentProtocolHost.d.ts","sourceRoot":"","sources":["../../src/agents/AgentProtocolHost.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAErD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAGhC,uDAAuD;IACvD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B;;;OAGG;IACH,QAAQ,CAAC,sBAAsB,EAAE,MAAM,CAAC;IAExC;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAIjC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,wDAAwD;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAE/B;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAElC,yDAAyD;IACzD,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IAExC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,eAAe,EAAE,CAAC;IAEhD;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAI/C;;;;;;;;;;;OAWG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,GAAG,QAAQ,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;;;;;OAQG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAE3D;;;;;;;OAOG;IACH,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAErD;;;;OAIG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErE;;;;;;;;;;;OAWG;IACH,gBAAgB,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D"}