@maintainer-pro/ai-cli 0.1.4 → 0.1.5

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.
package/dist/index.d.cts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Logger } from 'pino';
2
+ export { Logger } from 'pino';
1
3
  import { z } from 'zod';
2
4
 
3
5
  type AiCliProviderId = "claude" | "cursor" | "antigravity" | (string & {});
@@ -54,6 +56,11 @@ interface CallAiOptions {
54
56
  * Injected into the prompt for the model to use when relevant.
55
57
  */
56
58
  priorConversationsContext?: string;
59
+ /**
60
+ * Explicit reply-to parent chain (oldest → newest ancestor of the current
61
+ * user message). Injected so the model anchors on the thread being answered.
62
+ */
63
+ parentChainContext?: string;
57
64
  /** Extra / override providers (e.g. future HTTP model backends). */
58
65
  providers?: AiProvider[];
59
66
  /** When true, skip the end-user chat footer so the agent can return structured output. */
@@ -84,18 +91,97 @@ declare function providerLabel(providerId: string): string;
84
91
  /** Extract ```json ... ``` tool blocks and strip them from assistant text. */
85
92
  declare function parseAiResponse(raw: string, provider: AiCliProviderId): AiResponse;
86
93
 
94
+ /** Stable delimiters so models can weight sections distinctly. */
95
+ declare const PROMPT_SECTION: {
96
+ readonly system: {
97
+ readonly begin: "<<<SYSTEM_BEGIN>>>";
98
+ readonly end: "<<<SYSTEM_END>>>";
99
+ };
100
+ readonly clientContext: {
101
+ readonly begin: "<<<CLIENT_CONTEXT_BEGIN>>>";
102
+ readonly end: "<<<CLIENT_CONTEXT_END>>>";
103
+ };
104
+ readonly priorConversations: {
105
+ readonly begin: "<<<PRIOR_CONVERSATIONS_BEGIN>>>";
106
+ readonly end: "<<<PRIOR_CONVERSATIONS_END>>>";
107
+ };
108
+ readonly parentChain: {
109
+ readonly begin: "<<<PARENT_CHAIN_BEGIN>>>";
110
+ readonly end: "<<<PARENT_CHAIN_END>>>";
111
+ };
112
+ readonly history: {
113
+ readonly begin: "<<<CONVERSATION_HISTORY_BEGIN>>>";
114
+ readonly end: "<<<CONVERSATION_HISTORY_END>>>";
115
+ };
116
+ readonly attachments: {
117
+ readonly begin: "<<<ATTACHMENTS_BEGIN>>>";
118
+ readonly end: "<<<ATTACHMENTS_END>>>";
119
+ };
120
+ readonly currentRequest: {
121
+ readonly begin: "<<<CURRENT_REQUEST_BEGIN>>>";
122
+ readonly end: "<<<CURRENT_REQUEST_END>>>";
123
+ };
124
+ };
87
125
  declare function buildConversationPrompt(messages: ChatMessage[]): string;
88
126
  declare function formatClientContext(context?: ClientContext): string;
89
127
  /** Full prompt for providers without a native --system-prompt flag (Cursor). */
90
- declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
128
+ declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean, parentChainContext?: string): string;
91
129
  /** Conversation + context for Claude (system prompt passed separately). */
92
- declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
130
+ declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean, parentChainContext?: string): string;
131
+
132
+ /** Stored row fields needed to walk reply-to links. */
133
+ type ParentChainSource = {
134
+ id: string;
135
+ role: string;
136
+ content: string;
137
+ parentMessageId?: string | null;
138
+ senderName?: string | null;
139
+ provider?: string | null;
140
+ };
141
+ type ParentChainEntry = {
142
+ id: string;
143
+ role: "user" | "assistant" | "system";
144
+ content: string;
145
+ senderName?: string;
146
+ };
147
+ /**
148
+ * Walk `parentMessageId` from `startId` up to the root (oldest first).
149
+ * Skips empty / working placeholders.
150
+ */
151
+ declare function collectParentChain(rows: ParentChainSource[], startId: string | null | undefined): ParentChainEntry[];
152
+ /**
153
+ * Resolve the reply-to id for the current user turn, then walk ancestors.
154
+ * `currentMessageId` is the user message being answered this turn.
155
+ */
156
+ declare function parentChainForTurn(rows: ParentChainSource[], currentMessageId: string | null | undefined, fallbackParentId?: string | null): ParentChainEntry[];
157
+ /** Prompt block so the model treats the chain as the reply target. */
158
+ declare function formatParentChainContext(chain: ParentChainEntry[]): string;
93
159
 
94
160
  declare function createDefaultSystemPrompt(input: {
95
161
  productDescription: string;
96
162
  relevantFilesHint?: string;
97
163
  runtimeToolsHint?: string;
164
+ /** Relative ignore globs (already merged with defaults). */
165
+ ignorePaths?: string[];
166
+ }): string;
167
+
168
+ /** Always blocked relative to the project folder (secrets / credentials). */
169
+ declare const DEFAULT_AI_IGNORE_PATHS: readonly [".env", ".env.*", "**/.env", "**/.env.*"];
170
+ declare function parseIgnorePathsEnv(raw: string | undefined | null): string[];
171
+ declare function normalizeIgnorePaths(paths: string[]): string[];
172
+ /** Partner paths plus built-in secret ignores (deduped). */
173
+ declare function resolveIgnorePaths(partnerPaths: string[]): string[];
174
+ declare function isInsideWorkspace(workspaceDir: string, targetPath: string): boolean;
175
+ declare function isIgnoredRelative(relativePath: string, patterns: string[]): boolean;
176
+ declare function isPathAllowed(workspaceDir: string, targetPath: string, ignorePaths: string[]): boolean;
177
+ declare function formatAccessPolicyPromptSection(input: {
178
+ ignorePaths: string[];
98
179
  }): string;
180
+ /** Marker block written into .cursorignore (and similar) by the bridge. */
181
+ declare const ACCESS_IGNORE_BEGIN = "# maintainer-pro:access-begin";
182
+ declare const ACCESS_IGNORE_END = "# maintainer-pro:access-end";
183
+ declare function renderManagedIgnoreBlock(ignorePaths: string[]): string;
184
+ declare function upsertManagedIgnoreFile(existing: string, ignorePaths: string[]): string;
99
185
 
100
186
  declare function createClaudeProvider(): AiProvider;
101
187
 
@@ -103,6 +189,29 @@ declare function createCursorProvider(): AiProvider;
103
189
 
104
190
  declare function createAntigravityProvider(): AiProvider;
105
191
 
192
+ /** True when NODE_ENV / AI_ENV is development (or AI_DEV=1). */
193
+ declare function isDevMode(): boolean;
194
+ /**
195
+ * Resolve log level from env.
196
+ * Defaults to `debug` in development, otherwise `info`.
197
+ * Override anytime with LOG_LEVEL / AI_LOG_LEVEL.
198
+ */
199
+ declare function resolveLogLevel(): string;
200
+ /**
201
+ * Shared logger for Maintainer Pro Node packages.
202
+ *
203
+ * Levels: fatal | error | warn | info | debug | trace | silent
204
+ * - Dev (`NODE_ENV=development` or unset on local sidecars): default `debug`
205
+ * - Production: default `info`
206
+ * - Override: `LOG_LEVEL=debug` / `AI_LOG_LEVEL`
207
+ * - Pretty TTY output (disable with `LOG_PRETTY=0`)
208
+ */
209
+ declare function createLogger(name: string): Logger;
210
+ /** Adapter for older `(msg) => void` callbacks — logs at info. */
211
+ declare function createInfoLogger(name: string): (msg: string) => void;
212
+ /** Short preview for debug logs (never dump huge prompts). */
213
+ declare function previewText(text: string | undefined | null, max?: number): string;
214
+
106
215
  type ToolSchemaMap = Record<string, z.ZodTypeAny>;
107
216
  declare function createToolValidator(schemas: ToolSchemaMap): (toolCall: ToolCall) => {
108
217
  valid: true;
@@ -120,13 +229,27 @@ interface ChatStoreLike {
120
229
  ensureConversation(id: string): Promise<void>;
121
230
  saveMessage(input: {
122
231
  conversationId: string;
232
+ id?: string;
123
233
  role: "user" | "assistant" | "system";
124
234
  content: string;
125
235
  provider?: string;
126
236
  attachmentPaths?: string[];
127
237
  senderType?: "client" | "developer" | "ai";
128
238
  senderName?: string;
129
- }): Promise<unknown>;
239
+ /** Reply chain: assistant/working rows point at the user message. */
240
+ parentMessageId?: string | null;
241
+ /** queue = persist only; run = this user turn starts the AI. */
242
+ intent?: "queue" | "run";
243
+ queueStatus?: "working" | "queued" | null;
244
+ }): Promise<{
245
+ id: string;
246
+ queueStatus?: string | null;
247
+ queuePosition?: number | null;
248
+ nextQueued?: {
249
+ id: string;
250
+ content: string;
251
+ } | null;
252
+ } | unknown>;
130
253
  listMessages?(conversationId: string): Promise<Array<{
131
254
  id: string;
132
255
  role: string;
@@ -136,9 +259,29 @@ interface ChatStoreLike {
136
259
  attachmentPaths?: string[];
137
260
  senderType?: "client" | "developer" | "ai" | null;
138
261
  senderName?: string | null;
262
+ parentMessageId?: string | null;
263
+ queueStatus?: string | null;
264
+ queuePosition?: number | null;
139
265
  }>>;
140
266
  /** Remove in-progress working placeholders (at most one per conversation). */
141
267
  clearWorkingMessages?(conversationId: string): Promise<void>;
268
+ /** Clear the working user turn and promote the oldest queued message. */
269
+ releaseWorkingTurn?(conversationId: string, parentMessageId?: string | null): Promise<{
270
+ nextQueued?: {
271
+ id: string;
272
+ content: string;
273
+ } | null;
274
+ } | void>;
275
+ /** Unstick working/queued turns after crashes or raced promotes. */
276
+ recoverQueue?(conversationId: string): Promise<{
277
+ action: string;
278
+ workingId?: string | null;
279
+ promotedId?: string | null;
280
+ nextQueued?: {
281
+ id: string;
282
+ content: string;
283
+ } | null;
284
+ } | void>;
142
285
  /** All conversations under the store (for prior-chat context). */
143
286
  listConversations?(): Promise<Array<{
144
287
  id: string;
@@ -173,6 +316,7 @@ interface ChatHandlerOptions {
173
316
  db?: ChatStoreLike;
174
317
  conversationId?: string | ((request: Request) => string | undefined);
175
318
  onToolCalls?: (toolCalls: ToolCall[], context?: ClientContext) => void | Promise<void>;
319
+ logger?: Logger;
176
320
  }
177
321
  interface ChatHandlers {
178
322
  GET: (request: Request) => Promise<Response>;
@@ -197,6 +341,9 @@ interface LocalStoredMessage {
197
341
  attachmentPaths?: string[];
198
342
  senderType?: "client" | "developer" | "ai" | null;
199
343
  senderName?: string | null;
344
+ parentMessageId?: string | null;
345
+ queueStatus?: string | null;
346
+ queuePosition?: number | null;
200
347
  }
201
348
  /**
202
349
  * Persist conversations as JSON files under a local directory.
@@ -207,12 +354,61 @@ declare function createLocalDirectoryStore(baseDir: string): ChatStoreLike & {
207
354
  listConversations: NonNullable<ChatStoreLike["listConversations"]>;
208
355
  };
209
356
 
357
+ type CachedChatMessage = NonNullable<Awaited<ReturnType<NonNullable<ChatStoreLike["listMessages"]>>>>[number];
358
+ type CachedConversation = {
359
+ id: string;
360
+ updatedAt: string;
361
+ messages: CachedChatMessage[];
362
+ };
363
+ type SyncedStoreStats = {
364
+ conversations: number;
365
+ messages: number;
366
+ };
367
+ type SyncedChatStore = ChatStoreLike & {
368
+ uploadAttachments?: NonNullable<ChatStoreLike["uploadAttachments"]>;
369
+ ready: Promise<SyncedStoreStats>;
370
+ stop: () => void;
371
+ };
372
+ type RemoteStore = ChatStoreLike & {
373
+ uploadAttachments?: NonNullable<ChatStoreLike["uploadAttachments"]>;
374
+ listSnapshot?: () => Promise<CachedConversation[]>;
375
+ };
376
+ type WorkingTurnEvent = {
377
+ conversationId: string;
378
+ message: CachedChatMessage;
379
+ };
380
+ type SyncedChatStoreOptions = {
381
+ remote: RemoteStore;
382
+ maintainerProUrl: string;
383
+ apiKey: string;
384
+ /** Pino logger. Prefer this over `log`. */
385
+ logger?: Logger;
386
+ /** @deprecated Prefer `logger`. Info-level string messages only. */
387
+ log?: (msg: string) => void;
388
+ onWorkingTurn?: (event: WorkingTurnEvent) => void;
389
+ };
390
+ /**
391
+ * In-memory message cache in front of a remote Maintainer Pro store.
392
+ * Hydrates a full snapshot on start, writes through on local changes,
393
+ * and applies live WebSocket events from the other party.
394
+ */
395
+ declare function createSyncedChatStore(options: SyncedChatStoreOptions): SyncedChatStore;
396
+
210
397
  interface MaintainerProStoreOptions {
211
398
  baseUrl: string;
212
399
  apiKey: string;
213
400
  /** Directory for temporary local copies of images for CLI providers. */
214
401
  tempDir?: string;
215
402
  fetchImpl?: typeof fetch;
403
+ /**
404
+ * Keep a local message cache and live-sync over WebSocket.
405
+ * Default true. Set false for HTTP-only (tests / one-shot scripts).
406
+ */
407
+ sync?: boolean;
408
+ logger?: Logger;
409
+ /** @deprecated Prefer `logger`. */
410
+ log?: (msg: string) => void;
411
+ onWorkingTurn?: (event: WorkingTurnEvent) => void;
216
412
  }
217
413
  type UploadResult = {
218
414
  /** Refs persisted on messages (maintainer-pro://uuid). */
@@ -224,11 +420,16 @@ type UploadResult = {
224
420
  /**
225
421
  * Persist chat data via Maintainer Pro HTTP API (no local .maintainer-pro writes).
226
422
  */
227
- declare function createMaintainerProStore(options: MaintainerProStoreOptions): ChatStoreLike & {
423
+ declare function createMaintainerProStore(options: MaintainerProStoreOptions): SyncedChatStore & {
228
424
  uploadAttachments: (conversationId: string, attachments: ChatAttachment[] | undefined) => Promise<UploadResult>;
229
425
  };
230
426
  /** Build a Maintainer Pro store from env when configured. */
231
- declare function createMaintainerProStoreFromEnv(): ReturnType<typeof createMaintainerProStore> | null;
427
+ declare function createMaintainerProStoreFromEnv(options?: {
428
+ logger?: Logger;
429
+ log?: (msg: string) => void;
430
+ sync?: boolean;
431
+ onWorkingTurn?: (event: WorkingTurnEvent) => void;
432
+ }): ReturnType<typeof createMaintainerProStore> | null;
232
433
 
233
434
  interface PriorConversationOptions {
234
435
  excludeConversationId?: string;
@@ -278,4 +479,4 @@ interface WorkspaceInspectResult {
278
479
  */
279
480
  declare function inspectAndRepairWorkspace(input: WorkspaceInspectInput): Promise<WorkspaceInspectResult>;
280
481
 
281
- export { type AiCliProviderId, type AiProvider, type AiResponse, type CallAiOptions, type ChatAttachment, type ChatHandlerOptions, type ChatHandlers, type ChatMessage, type ChatSenderType, type ChatStoreLike, type ChatUser, type ClientContext, type LocalStoredMessage, type MaintainerProStoreOptions, type PriorConversationOptions, type ProviderPreference, type ToolCall, type ToolSchemaMap, WORKING_PROVIDER, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceKind, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createLocalDirectoryStore, createMaintainerProStore, createMaintainerProStoreFromEnv, createToolValidator, formatClientContext, getProviderPreference, inspectAndRepairWorkspace, parseAiResponse, providerLabel, resolveCliBinary, resolveProvider, saveChatAttachments, toNextRoute };
482
+ export { ACCESS_IGNORE_BEGIN, ACCESS_IGNORE_END, type AiCliProviderId, type AiProvider, type AiResponse, type CachedChatMessage, type CachedConversation, type CallAiOptions, type ChatAttachment, type ChatHandlerOptions, type ChatHandlers, type ChatMessage, type ChatSenderType, type ChatStoreLike, type ChatUser, type ClientContext, DEFAULT_AI_IGNORE_PATHS, type LocalStoredMessage, type MaintainerProStoreOptions, PROMPT_SECTION, type ParentChainEntry, type ParentChainSource, type PriorConversationOptions, type ProviderPreference, type SyncedChatStore, type SyncedChatStoreOptions, type SyncedStoreStats, type ToolCall, type ToolSchemaMap, WORKING_PROVIDER, type WorkingTurnEvent, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceKind, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, collectParentChain, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createInfoLogger, createLocalDirectoryStore, createLogger, createMaintainerProStore, createMaintainerProStoreFromEnv, createSyncedChatStore, createToolValidator, formatAccessPolicyPromptSection, formatClientContext, formatParentChainContext, getProviderPreference, inspectAndRepairWorkspace, isDevMode, isIgnoredRelative, isInsideWorkspace, isPathAllowed, normalizeIgnorePaths, parentChainForTurn, parseAiResponse, parseIgnorePathsEnv, previewText, providerLabel, renderManagedIgnoreBlock, resolveCliBinary, resolveIgnorePaths, resolveLogLevel, resolveProvider, saveChatAttachments, toNextRoute, upsertManagedIgnoreFile };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { Logger } from 'pino';
2
+ export { Logger } from 'pino';
1
3
  import { z } from 'zod';
2
4
 
3
5
  type AiCliProviderId = "claude" | "cursor" | "antigravity" | (string & {});
@@ -54,6 +56,11 @@ interface CallAiOptions {
54
56
  * Injected into the prompt for the model to use when relevant.
55
57
  */
56
58
  priorConversationsContext?: string;
59
+ /**
60
+ * Explicit reply-to parent chain (oldest → newest ancestor of the current
61
+ * user message). Injected so the model anchors on the thread being answered.
62
+ */
63
+ parentChainContext?: string;
57
64
  /** Extra / override providers (e.g. future HTTP model backends). */
58
65
  providers?: AiProvider[];
59
66
  /** When true, skip the end-user chat footer so the agent can return structured output. */
@@ -84,18 +91,97 @@ declare function providerLabel(providerId: string): string;
84
91
  /** Extract ```json ... ``` tool blocks and strip them from assistant text. */
85
92
  declare function parseAiResponse(raw: string, provider: AiCliProviderId): AiResponse;
86
93
 
94
+ /** Stable delimiters so models can weight sections distinctly. */
95
+ declare const PROMPT_SECTION: {
96
+ readonly system: {
97
+ readonly begin: "<<<SYSTEM_BEGIN>>>";
98
+ readonly end: "<<<SYSTEM_END>>>";
99
+ };
100
+ readonly clientContext: {
101
+ readonly begin: "<<<CLIENT_CONTEXT_BEGIN>>>";
102
+ readonly end: "<<<CLIENT_CONTEXT_END>>>";
103
+ };
104
+ readonly priorConversations: {
105
+ readonly begin: "<<<PRIOR_CONVERSATIONS_BEGIN>>>";
106
+ readonly end: "<<<PRIOR_CONVERSATIONS_END>>>";
107
+ };
108
+ readonly parentChain: {
109
+ readonly begin: "<<<PARENT_CHAIN_BEGIN>>>";
110
+ readonly end: "<<<PARENT_CHAIN_END>>>";
111
+ };
112
+ readonly history: {
113
+ readonly begin: "<<<CONVERSATION_HISTORY_BEGIN>>>";
114
+ readonly end: "<<<CONVERSATION_HISTORY_END>>>";
115
+ };
116
+ readonly attachments: {
117
+ readonly begin: "<<<ATTACHMENTS_BEGIN>>>";
118
+ readonly end: "<<<ATTACHMENTS_END>>>";
119
+ };
120
+ readonly currentRequest: {
121
+ readonly begin: "<<<CURRENT_REQUEST_BEGIN>>>";
122
+ readonly end: "<<<CURRENT_REQUEST_END>>>";
123
+ };
124
+ };
87
125
  declare function buildConversationPrompt(messages: ChatMessage[]): string;
88
126
  declare function formatClientContext(context?: ClientContext): string;
89
127
  /** Full prompt for providers without a native --system-prompt flag (Cursor). */
90
- declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
128
+ declare function buildCursorPrompt(systemPrompt: string, messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean, parentChainContext?: string): string;
91
129
  /** Conversation + context for Claude (system prompt passed separately). */
92
- declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean): string;
130
+ declare function buildClaudeUserPrompt(messages: ChatMessage[], context?: ClientContext, attachmentPaths?: string[], priorConversationsContext?: string, technical?: boolean, parentChainContext?: string): string;
131
+
132
+ /** Stored row fields needed to walk reply-to links. */
133
+ type ParentChainSource = {
134
+ id: string;
135
+ role: string;
136
+ content: string;
137
+ parentMessageId?: string | null;
138
+ senderName?: string | null;
139
+ provider?: string | null;
140
+ };
141
+ type ParentChainEntry = {
142
+ id: string;
143
+ role: "user" | "assistant" | "system";
144
+ content: string;
145
+ senderName?: string;
146
+ };
147
+ /**
148
+ * Walk `parentMessageId` from `startId` up to the root (oldest first).
149
+ * Skips empty / working placeholders.
150
+ */
151
+ declare function collectParentChain(rows: ParentChainSource[], startId: string | null | undefined): ParentChainEntry[];
152
+ /**
153
+ * Resolve the reply-to id for the current user turn, then walk ancestors.
154
+ * `currentMessageId` is the user message being answered this turn.
155
+ */
156
+ declare function parentChainForTurn(rows: ParentChainSource[], currentMessageId: string | null | undefined, fallbackParentId?: string | null): ParentChainEntry[];
157
+ /** Prompt block so the model treats the chain as the reply target. */
158
+ declare function formatParentChainContext(chain: ParentChainEntry[]): string;
93
159
 
94
160
  declare function createDefaultSystemPrompt(input: {
95
161
  productDescription: string;
96
162
  relevantFilesHint?: string;
97
163
  runtimeToolsHint?: string;
164
+ /** Relative ignore globs (already merged with defaults). */
165
+ ignorePaths?: string[];
166
+ }): string;
167
+
168
+ /** Always blocked relative to the project folder (secrets / credentials). */
169
+ declare const DEFAULT_AI_IGNORE_PATHS: readonly [".env", ".env.*", "**/.env", "**/.env.*"];
170
+ declare function parseIgnorePathsEnv(raw: string | undefined | null): string[];
171
+ declare function normalizeIgnorePaths(paths: string[]): string[];
172
+ /** Partner paths plus built-in secret ignores (deduped). */
173
+ declare function resolveIgnorePaths(partnerPaths: string[]): string[];
174
+ declare function isInsideWorkspace(workspaceDir: string, targetPath: string): boolean;
175
+ declare function isIgnoredRelative(relativePath: string, patterns: string[]): boolean;
176
+ declare function isPathAllowed(workspaceDir: string, targetPath: string, ignorePaths: string[]): boolean;
177
+ declare function formatAccessPolicyPromptSection(input: {
178
+ ignorePaths: string[];
98
179
  }): string;
180
+ /** Marker block written into .cursorignore (and similar) by the bridge. */
181
+ declare const ACCESS_IGNORE_BEGIN = "# maintainer-pro:access-begin";
182
+ declare const ACCESS_IGNORE_END = "# maintainer-pro:access-end";
183
+ declare function renderManagedIgnoreBlock(ignorePaths: string[]): string;
184
+ declare function upsertManagedIgnoreFile(existing: string, ignorePaths: string[]): string;
99
185
 
100
186
  declare function createClaudeProvider(): AiProvider;
101
187
 
@@ -103,6 +189,29 @@ declare function createCursorProvider(): AiProvider;
103
189
 
104
190
  declare function createAntigravityProvider(): AiProvider;
105
191
 
192
+ /** True when NODE_ENV / AI_ENV is development (or AI_DEV=1). */
193
+ declare function isDevMode(): boolean;
194
+ /**
195
+ * Resolve log level from env.
196
+ * Defaults to `debug` in development, otherwise `info`.
197
+ * Override anytime with LOG_LEVEL / AI_LOG_LEVEL.
198
+ */
199
+ declare function resolveLogLevel(): string;
200
+ /**
201
+ * Shared logger for Maintainer Pro Node packages.
202
+ *
203
+ * Levels: fatal | error | warn | info | debug | trace | silent
204
+ * - Dev (`NODE_ENV=development` or unset on local sidecars): default `debug`
205
+ * - Production: default `info`
206
+ * - Override: `LOG_LEVEL=debug` / `AI_LOG_LEVEL`
207
+ * - Pretty TTY output (disable with `LOG_PRETTY=0`)
208
+ */
209
+ declare function createLogger(name: string): Logger;
210
+ /** Adapter for older `(msg) => void` callbacks — logs at info. */
211
+ declare function createInfoLogger(name: string): (msg: string) => void;
212
+ /** Short preview for debug logs (never dump huge prompts). */
213
+ declare function previewText(text: string | undefined | null, max?: number): string;
214
+
106
215
  type ToolSchemaMap = Record<string, z.ZodTypeAny>;
107
216
  declare function createToolValidator(schemas: ToolSchemaMap): (toolCall: ToolCall) => {
108
217
  valid: true;
@@ -120,13 +229,27 @@ interface ChatStoreLike {
120
229
  ensureConversation(id: string): Promise<void>;
121
230
  saveMessage(input: {
122
231
  conversationId: string;
232
+ id?: string;
123
233
  role: "user" | "assistant" | "system";
124
234
  content: string;
125
235
  provider?: string;
126
236
  attachmentPaths?: string[];
127
237
  senderType?: "client" | "developer" | "ai";
128
238
  senderName?: string;
129
- }): Promise<unknown>;
239
+ /** Reply chain: assistant/working rows point at the user message. */
240
+ parentMessageId?: string | null;
241
+ /** queue = persist only; run = this user turn starts the AI. */
242
+ intent?: "queue" | "run";
243
+ queueStatus?: "working" | "queued" | null;
244
+ }): Promise<{
245
+ id: string;
246
+ queueStatus?: string | null;
247
+ queuePosition?: number | null;
248
+ nextQueued?: {
249
+ id: string;
250
+ content: string;
251
+ } | null;
252
+ } | unknown>;
130
253
  listMessages?(conversationId: string): Promise<Array<{
131
254
  id: string;
132
255
  role: string;
@@ -136,9 +259,29 @@ interface ChatStoreLike {
136
259
  attachmentPaths?: string[];
137
260
  senderType?: "client" | "developer" | "ai" | null;
138
261
  senderName?: string | null;
262
+ parentMessageId?: string | null;
263
+ queueStatus?: string | null;
264
+ queuePosition?: number | null;
139
265
  }>>;
140
266
  /** Remove in-progress working placeholders (at most one per conversation). */
141
267
  clearWorkingMessages?(conversationId: string): Promise<void>;
268
+ /** Clear the working user turn and promote the oldest queued message. */
269
+ releaseWorkingTurn?(conversationId: string, parentMessageId?: string | null): Promise<{
270
+ nextQueued?: {
271
+ id: string;
272
+ content: string;
273
+ } | null;
274
+ } | void>;
275
+ /** Unstick working/queued turns after crashes or raced promotes. */
276
+ recoverQueue?(conversationId: string): Promise<{
277
+ action: string;
278
+ workingId?: string | null;
279
+ promotedId?: string | null;
280
+ nextQueued?: {
281
+ id: string;
282
+ content: string;
283
+ } | null;
284
+ } | void>;
142
285
  /** All conversations under the store (for prior-chat context). */
143
286
  listConversations?(): Promise<Array<{
144
287
  id: string;
@@ -173,6 +316,7 @@ interface ChatHandlerOptions {
173
316
  db?: ChatStoreLike;
174
317
  conversationId?: string | ((request: Request) => string | undefined);
175
318
  onToolCalls?: (toolCalls: ToolCall[], context?: ClientContext) => void | Promise<void>;
319
+ logger?: Logger;
176
320
  }
177
321
  interface ChatHandlers {
178
322
  GET: (request: Request) => Promise<Response>;
@@ -197,6 +341,9 @@ interface LocalStoredMessage {
197
341
  attachmentPaths?: string[];
198
342
  senderType?: "client" | "developer" | "ai" | null;
199
343
  senderName?: string | null;
344
+ parentMessageId?: string | null;
345
+ queueStatus?: string | null;
346
+ queuePosition?: number | null;
200
347
  }
201
348
  /**
202
349
  * Persist conversations as JSON files under a local directory.
@@ -207,12 +354,61 @@ declare function createLocalDirectoryStore(baseDir: string): ChatStoreLike & {
207
354
  listConversations: NonNullable<ChatStoreLike["listConversations"]>;
208
355
  };
209
356
 
357
+ type CachedChatMessage = NonNullable<Awaited<ReturnType<NonNullable<ChatStoreLike["listMessages"]>>>>[number];
358
+ type CachedConversation = {
359
+ id: string;
360
+ updatedAt: string;
361
+ messages: CachedChatMessage[];
362
+ };
363
+ type SyncedStoreStats = {
364
+ conversations: number;
365
+ messages: number;
366
+ };
367
+ type SyncedChatStore = ChatStoreLike & {
368
+ uploadAttachments?: NonNullable<ChatStoreLike["uploadAttachments"]>;
369
+ ready: Promise<SyncedStoreStats>;
370
+ stop: () => void;
371
+ };
372
+ type RemoteStore = ChatStoreLike & {
373
+ uploadAttachments?: NonNullable<ChatStoreLike["uploadAttachments"]>;
374
+ listSnapshot?: () => Promise<CachedConversation[]>;
375
+ };
376
+ type WorkingTurnEvent = {
377
+ conversationId: string;
378
+ message: CachedChatMessage;
379
+ };
380
+ type SyncedChatStoreOptions = {
381
+ remote: RemoteStore;
382
+ maintainerProUrl: string;
383
+ apiKey: string;
384
+ /** Pino logger. Prefer this over `log`. */
385
+ logger?: Logger;
386
+ /** @deprecated Prefer `logger`. Info-level string messages only. */
387
+ log?: (msg: string) => void;
388
+ onWorkingTurn?: (event: WorkingTurnEvent) => void;
389
+ };
390
+ /**
391
+ * In-memory message cache in front of a remote Maintainer Pro store.
392
+ * Hydrates a full snapshot on start, writes through on local changes,
393
+ * and applies live WebSocket events from the other party.
394
+ */
395
+ declare function createSyncedChatStore(options: SyncedChatStoreOptions): SyncedChatStore;
396
+
210
397
  interface MaintainerProStoreOptions {
211
398
  baseUrl: string;
212
399
  apiKey: string;
213
400
  /** Directory for temporary local copies of images for CLI providers. */
214
401
  tempDir?: string;
215
402
  fetchImpl?: typeof fetch;
403
+ /**
404
+ * Keep a local message cache and live-sync over WebSocket.
405
+ * Default true. Set false for HTTP-only (tests / one-shot scripts).
406
+ */
407
+ sync?: boolean;
408
+ logger?: Logger;
409
+ /** @deprecated Prefer `logger`. */
410
+ log?: (msg: string) => void;
411
+ onWorkingTurn?: (event: WorkingTurnEvent) => void;
216
412
  }
217
413
  type UploadResult = {
218
414
  /** Refs persisted on messages (maintainer-pro://uuid). */
@@ -224,11 +420,16 @@ type UploadResult = {
224
420
  /**
225
421
  * Persist chat data via Maintainer Pro HTTP API (no local .maintainer-pro writes).
226
422
  */
227
- declare function createMaintainerProStore(options: MaintainerProStoreOptions): ChatStoreLike & {
423
+ declare function createMaintainerProStore(options: MaintainerProStoreOptions): SyncedChatStore & {
228
424
  uploadAttachments: (conversationId: string, attachments: ChatAttachment[] | undefined) => Promise<UploadResult>;
229
425
  };
230
426
  /** Build a Maintainer Pro store from env when configured. */
231
- declare function createMaintainerProStoreFromEnv(): ReturnType<typeof createMaintainerProStore> | null;
427
+ declare function createMaintainerProStoreFromEnv(options?: {
428
+ logger?: Logger;
429
+ log?: (msg: string) => void;
430
+ sync?: boolean;
431
+ onWorkingTurn?: (event: WorkingTurnEvent) => void;
432
+ }): ReturnType<typeof createMaintainerProStore> | null;
232
433
 
233
434
  interface PriorConversationOptions {
234
435
  excludeConversationId?: string;
@@ -278,4 +479,4 @@ interface WorkspaceInspectResult {
278
479
  */
279
480
  declare function inspectAndRepairWorkspace(input: WorkspaceInspectInput): Promise<WorkspaceInspectResult>;
280
481
 
281
- export { type AiCliProviderId, type AiProvider, type AiResponse, type CallAiOptions, type ChatAttachment, type ChatHandlerOptions, type ChatHandlers, type ChatMessage, type ChatSenderType, type ChatStoreLike, type ChatUser, type ClientContext, type LocalStoredMessage, type MaintainerProStoreOptions, type PriorConversationOptions, type ProviderPreference, type ToolCall, type ToolSchemaMap, WORKING_PROVIDER, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceKind, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createLocalDirectoryStore, createMaintainerProStore, createMaintainerProStoreFromEnv, createToolValidator, formatClientContext, getProviderPreference, inspectAndRepairWorkspace, parseAiResponse, providerLabel, resolveCliBinary, resolveProvider, saveChatAttachments, toNextRoute };
482
+ export { ACCESS_IGNORE_BEGIN, ACCESS_IGNORE_END, type AiCliProviderId, type AiProvider, type AiResponse, type CachedChatMessage, type CachedConversation, type CallAiOptions, type ChatAttachment, type ChatHandlerOptions, type ChatHandlers, type ChatMessage, type ChatSenderType, type ChatStoreLike, type ChatUser, type ClientContext, DEFAULT_AI_IGNORE_PATHS, type LocalStoredMessage, type MaintainerProStoreOptions, PROMPT_SECTION, type ParentChainEntry, type ParentChainSource, type PriorConversationOptions, type ProviderPreference, type SyncedChatStore, type SyncedChatStoreOptions, type SyncedStoreStats, type ToolCall, type ToolSchemaMap, WORKING_PROVIDER, type WorkingTurnEvent, type WorkspaceInspectInput, type WorkspaceInspectResult, type WorkspaceKind, buildClaudeUserPrompt, buildConversationPrompt, buildCursorPrompt, buildPriorConversationsContext, callAi, collectParentChain, commandExists, createAntigravityProvider, createBuiltinProviders, createChatHandler, createClaudeProvider, createCursorProvider, createDefaultSystemPrompt, createInfoLogger, createLocalDirectoryStore, createLogger, createMaintainerProStore, createMaintainerProStoreFromEnv, createSyncedChatStore, createToolValidator, formatAccessPolicyPromptSection, formatClientContext, formatParentChainContext, getProviderPreference, inspectAndRepairWorkspace, isDevMode, isIgnoredRelative, isInsideWorkspace, isPathAllowed, normalizeIgnorePaths, parentChainForTurn, parseAiResponse, parseIgnorePathsEnv, previewText, providerLabel, renderManagedIgnoreBlock, resolveCliBinary, resolveIgnorePaths, resolveLogLevel, resolveProvider, saveChatAttachments, toNextRoute, upsertManagedIgnoreFile };