@openacp/cli 2026.331.5 → 2026.401.3

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.ts CHANGED
@@ -3,6 +3,7 @@ import * as zod from 'zod';
3
3
  import { z, ZodSchema } from 'zod';
4
4
  import { EventEmitter } from 'node:events';
5
5
  import { SetSessionConfigOptionResponse, ListSessionsResponse, LoadSessionResponse, ForkSessionResponse, PromptResponse as PromptResponse$1 } from '@agentclientprotocol/sdk';
6
+ import { FastifyInstance, FastifyPluginAsync, preHandlerHookHandler, FastifyRequest, FastifyReply } from 'fastify';
6
7
  import * as http from 'node:http';
7
8
 
8
9
  type OutputMode = "low" | "medium" | "high";
@@ -138,15 +139,9 @@ type AgentEvent = {
138
139
  title?: string;
139
140
  updatedAt?: string;
140
141
  _meta?: Record<string, unknown>;
141
- } | {
142
- type: "current_mode_update";
143
- modeId: string;
144
142
  } | {
145
143
  type: "config_option_update";
146
144
  options: ConfigOption[];
147
- } | {
148
- type: "model_update";
149
- modelId: string;
150
145
  } | {
151
146
  type: "user_message_chunk";
152
147
  content: string;
@@ -276,15 +271,27 @@ interface SessionRecord<P = Record<string, unknown>> {
276
271
  lastActiveAt: string;
277
272
  name?: string;
278
273
  dangerousMode?: boolean;
274
+ clientOverrides?: {
275
+ bypassPermissions?: boolean;
276
+ };
279
277
  outputMode?: OutputMode;
280
278
  platform: P;
281
279
  firstAgent?: string;
282
280
  currentPromptCount?: number;
283
281
  agentSwitchHistory?: AgentSwitchEntry[];
282
+ acpState?: {
283
+ configOptions?: ConfigOption[];
284
+ agentCapabilities?: AgentCapabilities;
285
+ currentMode?: string;
286
+ availableModes?: SessionMode[];
287
+ currentModel?: string;
288
+ availableModels?: ModelInfo[];
289
+ };
284
290
  }
285
291
  interface TelegramPlatformData {
286
292
  topicId: number;
287
293
  skillMsgId?: number;
294
+ controlMsgId?: number;
288
295
  }
289
296
  interface UsageRecord {
290
297
  id: string;
@@ -320,7 +327,7 @@ interface SessionModeState {
320
327
  }
321
328
  interface ConfigSelectChoice {
322
329
  value: string;
323
- label: string;
330
+ name: string;
324
331
  description?: string;
325
332
  }
326
333
  interface ConfigSelectGroup {
@@ -1060,6 +1067,8 @@ interface IChannelAdapter {
1060
1067
  stripTTSBlock?(sessionId: string): Promise<void>;
1061
1068
  sendSkillCommands?(sessionId: string, commands: AgentCommand[]): Promise<void>;
1062
1069
  cleanupSkillCommands?(sessionId: string): Promise<void>;
1070
+ /** Flush skill commands that were queued before threadId was available */
1071
+ flushPendingSkillCommands?(sessionId: string): Promise<void>;
1063
1072
  cleanupSessionState?(sessionId: string): Promise<void>;
1064
1073
  }
1065
1074
  /**
@@ -1200,6 +1209,13 @@ declare class AgentInstance extends TypedEmitter<AgentInstanceEvents> {
1200
1209
  image?: boolean;
1201
1210
  audio?: boolean;
1202
1211
  };
1212
+ agentCapabilities?: AgentCapabilities;
1213
+ /** Preserved from newSession/resumeSession response for ACP state propagation */
1214
+ initialSessionResponse?: {
1215
+ modes?: unknown;
1216
+ configOptions?: unknown;
1217
+ models?: unknown;
1218
+ };
1203
1219
  middlewareChain?: MiddlewareChain;
1204
1220
  debugTracer: DebugTracer | null;
1205
1221
  onPermissionRequest: (request: PermissionRequest) => Promise<string>;
@@ -1209,9 +1225,7 @@ declare class AgentInstance extends TypedEmitter<AgentInstanceEvents> {
1209
1225
  static spawn(agentDef: AgentDefinition, workingDirectory: string, mcpServers?: McpServerConfig[]): Promise<AgentInstance>;
1210
1226
  static resume(agentDef: AgentDefinition, workingDirectory: string, agentSessionId: string, mcpServers?: McpServerConfig[]): Promise<AgentInstance>;
1211
1227
  private createClient;
1212
- setMode(modeId: string): Promise<void>;
1213
1228
  setConfigOption(configId: string, value: SetConfigOptionValue): Promise<SetSessionConfigOptionResponse>;
1214
- setModel(modelId: string): Promise<void>;
1215
1229
  listSessions(cwd?: string, cursor?: string): Promise<ListSessionsResponse>;
1216
1230
  loadSession(sessionId: string, cwd: string, mcpServers?: McpServerConfig[]): Promise<LoadSessionResponse>;
1217
1231
  authenticate(methodId: string): Promise<void>;
@@ -1240,7 +1254,8 @@ declare class AgentCatalog {
1240
1254
  private store;
1241
1255
  private registryAgents;
1242
1256
  private cachePath;
1243
- constructor(store?: AgentStore, cachePath?: string);
1257
+ private agentsDir;
1258
+ constructor(store?: AgentStore, cachePath?: string, agentsDir?: string);
1244
1259
  load(): void;
1245
1260
  fetchRegistry(): Promise<void>;
1246
1261
  refreshRegistryIfStale(): Promise<void>;
@@ -1394,12 +1409,10 @@ declare class Session extends TypedEmitter<SessionEvents> {
1394
1409
  name?: string;
1395
1410
  createdAt: Date;
1396
1411
  voiceMode: "off" | "next" | "on";
1397
- dangerousMode: boolean;
1398
- currentMode?: string;
1399
- availableModes: SessionMode[];
1400
1412
  configOptions: ConfigOption[];
1401
- currentModel?: string;
1402
- availableModels: ModelInfo[];
1413
+ clientOverrides: {
1414
+ bypassPermissions?: boolean;
1415
+ };
1403
1416
  agentCapabilities?: AgentCapabilities;
1404
1417
  archiving: boolean;
1405
1418
  promptCount: number;
@@ -1439,20 +1452,18 @@ declare class Session extends TypedEmitter<SessionEvents> {
1439
1452
  private maybeTranscribeAudio;
1440
1453
  private processTTSResponse;
1441
1454
  private autoName;
1442
- /** Fire-and-forget warm-up: primes model cache while user types their first message */
1443
- warmup(): Promise<void>;
1444
- private runWarmup;
1445
- setInitialAcpState(state: {
1446
- modes?: SessionModeState | null;
1447
- configOptions?: ConfigOption[] | null;
1448
- models?: SessionModelState | null;
1449
- agentCapabilities?: AgentCapabilities | null;
1450
- }): void;
1455
+ setInitialConfigOptions(options: ConfigOption[]): void;
1456
+ setAgentCapabilities(caps: AgentCapabilities | undefined): void;
1457
+ getConfigOption(id: string): ConfigOption | undefined;
1458
+ getConfigByCategory(category: string): ConfigOption | undefined;
1459
+ getConfigValue(id: string): string | undefined;
1451
1460
  /** Set session name explicitly and emit 'named' event */
1452
1461
  setName(name: string): void;
1453
- updateMode(modeId: string): Promise<void>;
1454
1462
  updateConfigOptions(options: ConfigOption[]): Promise<void>;
1455
- updateModel(modelId: string): Promise<void>;
1463
+ /** Snapshot of current ACP state for persistence */
1464
+ toAcpStateSnapshot(): NonNullable<SessionRecord["acpState"]>;
1465
+ /** Check if the agent supports a specific session capability */
1466
+ supportsCapability(cap: 'list' | 'fork' | 'close' | 'loadSession'): boolean;
1456
1467
  /** Cancel the current prompt and clear the queue. Stays in active state. */
1457
1468
  abortPrompt(): Promise<void>;
1458
1469
  /** Search backward through agentSwitchHistory for the last entry matching agentName */
@@ -1802,22 +1813,12 @@ interface MiddlewarePayloadMap {
1802
1813
  durationMs: number;
1803
1814
  promptCount: number;
1804
1815
  };
1805
- 'mode:beforeChange': {
1806
- sessionId: string;
1807
- fromMode: string | undefined;
1808
- toMode: string;
1809
- };
1810
1816
  'config:beforeChange': {
1811
1817
  sessionId: string;
1812
1818
  configId: string;
1813
1819
  oldValue: unknown;
1814
1820
  newValue: unknown;
1815
1821
  };
1816
- 'model:beforeChange': {
1817
- sessionId: string;
1818
- fromModel: string | undefined;
1819
- toModel: string;
1820
- };
1821
1822
  'agent:beforeCancel': {
1822
1823
  sessionId: string;
1823
1824
  reason?: string;
@@ -1958,7 +1959,9 @@ interface EventBusEvents {
1958
1959
  sessionId: string;
1959
1960
  status?: SessionStatus;
1960
1961
  name?: string;
1961
- dangerousMode?: boolean;
1962
+ clientOverrides?: {
1963
+ bypassPermissions?: boolean;
1964
+ };
1962
1965
  }) => void;
1963
1966
  "session:deleted": (data: {
1964
1967
  sessionId: string;
@@ -2014,6 +2017,9 @@ interface EventBusEvents {
2014
2017
  attachments?: unknown[];
2015
2018
  }) => void;
2016
2019
  "usage:recorded": (data: UsageRecordEvent) => void;
2020
+ "session:configChanged": (data: {
2021
+ sessionId: string;
2022
+ }) => void;
2017
2023
  "session:agentSwitch": (data: {
2018
2024
  sessionId: string;
2019
2025
  fromAgent: string;
@@ -2094,6 +2100,8 @@ declare class SessionBridge {
2094
2100
  private wireAgentToSession;
2095
2101
  private wireSessionToAdapter;
2096
2102
  private handleAgentEvent;
2103
+ /** Persist current ACP state (configOptions, agentCapabilities) to session store as cache */
2104
+ private persistAcpState;
2097
2105
  private wirePermissions;
2098
2106
  private wireLifecycle;
2099
2107
  }
@@ -2144,7 +2152,7 @@ declare class TunnelService {
2144
2152
  private config;
2145
2153
  private systemPort;
2146
2154
  private startError;
2147
- constructor(config: TunnelConfig, registryPath?: string);
2155
+ constructor(config: TunnelConfig, registryPath?: string, binDir?: string);
2148
2156
  start(): Promise<string>;
2149
2157
  stop(): Promise<void>;
2150
2158
  addTunnel(port: number, opts?: {
@@ -2164,6 +2172,63 @@ declare class TunnelService {
2164
2172
  outputUrl(entryId: string): string;
2165
2173
  }
2166
2174
 
2175
+ interface ContextProvider {
2176
+ readonly name: string;
2177
+ isAvailable(repoPath: string): Promise<boolean>;
2178
+ listSessions(query: ContextQuery): Promise<SessionListResult>;
2179
+ buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult>;
2180
+ }
2181
+ interface ContextQuery {
2182
+ repoPath: string;
2183
+ type: "branch" | "commit" | "pr" | "latest" | "checkpoint" | "session";
2184
+ value: string;
2185
+ }
2186
+ interface ContextOptions {
2187
+ maxTokens?: number;
2188
+ limit?: number;
2189
+ /** When true, insert `## [agentName]` headers at agent boundaries in merged history */
2190
+ labelAgent?: boolean;
2191
+ }
2192
+ interface SessionInfo {
2193
+ checkpointId: string;
2194
+ sessionIndex: string;
2195
+ transcriptPath: string;
2196
+ createdAt: string;
2197
+ endedAt: string;
2198
+ branch: string;
2199
+ agent: string;
2200
+ turnCount: number;
2201
+ filesTouched: string[];
2202
+ sessionId: string;
2203
+ }
2204
+ interface SessionListResult {
2205
+ sessions: SessionInfo[];
2206
+ estimatedTokens: number;
2207
+ }
2208
+ type ContextMode = "full" | "balanced" | "compact";
2209
+ interface ContextResult {
2210
+ markdown: string;
2211
+ tokenEstimate: number;
2212
+ sessionCount: number;
2213
+ totalTurns: number;
2214
+ mode: ContextMode;
2215
+ truncated: boolean;
2216
+ timeRange: {
2217
+ start: string;
2218
+ end: string;
2219
+ };
2220
+ }
2221
+
2222
+ declare class ContextManager {
2223
+ private providers;
2224
+ private cache;
2225
+ constructor(cachePath?: string);
2226
+ register(provider: ContextProvider): void;
2227
+ getProvider(repoPath: string): Promise<ContextProvider | null>;
2228
+ listSessions(query: ContextQuery): Promise<SessionListResult | null>;
2229
+ buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult | null>;
2230
+ }
2231
+
2167
2232
  interface SessionCreateParams {
2168
2233
  channelId: string;
2169
2234
  agentName: string;
@@ -2184,9 +2249,48 @@ declare class SessionFactory {
2184
2249
  private eventBus;
2185
2250
  private instanceRoot?;
2186
2251
  middlewareChain?: MiddlewareChain;
2252
+ private resumeLocks;
2253
+ /** Injected by Core after construction — needed for lazy resume error feedback */
2254
+ adapters?: Map<string, IChannelAdapter>;
2255
+ /** Injected by Core after construction — needed for lazy resume store lookup */
2256
+ sessionStore?: SessionStore | null;
2257
+ /** Injected by Core — creates full session with thread + bridge + persist */
2258
+ createFullSession?: (params: SessionCreateParams & {
2259
+ threadId?: string;
2260
+ createThread?: boolean;
2261
+ }) => Promise<Session>;
2262
+ /** Injected by Core — needed for resolving default agent and workspace */
2263
+ configManager?: ConfigManager$1;
2264
+ /** Injected by Core — needed for resolving agent definitions */
2265
+ agentCatalog?: AgentCatalog;
2266
+ /** Injected by Core — needed for context-aware session creation */
2267
+ getContextManager?: () => ContextManager | undefined;
2187
2268
  constructor(agentManager: AgentManager, sessionManager: SessionManager, speechServiceAccessor: SpeechService | (() => SpeechService), eventBus: EventBus, instanceRoot?: string | undefined);
2188
2269
  private get speechService();
2189
2270
  create(params: SessionCreateParams): Promise<Session>;
2271
+ /**
2272
+ * Get active session by thread, or attempt lazy resume from store.
2273
+ * Used by adapter command handlers and handleMessage().
2274
+ */
2275
+ getOrResume(channelId: string, threadId: string): Promise<Session | null>;
2276
+ private lazyResume;
2277
+ handleNewSession(channelId: string, agentName?: string, workspacePath?: string, options?: {
2278
+ createThread?: boolean;
2279
+ }): Promise<Session>;
2280
+ /** NOTE: handleNewChat is currently dead code — never called outside core.ts itself.
2281
+ * Moving it anyway for completeness; can be removed in a future cleanup. */
2282
+ handleNewChat(channelId: string, currentThreadId: string): Promise<Session | null>;
2283
+ createSessionWithContext(params: {
2284
+ channelId: string;
2285
+ agentName: string;
2286
+ workingDirectory: string;
2287
+ contextQuery: ContextQuery;
2288
+ contextOptions?: ContextOptions;
2289
+ createThread?: boolean;
2290
+ }): Promise<{
2291
+ session: Session;
2292
+ contextResult: ContextResult | null;
2293
+ }>;
2190
2294
  wireSideEffects(session: Session, deps: SideEffectDeps): void;
2191
2295
  }
2192
2296
 
@@ -2304,63 +2408,6 @@ declare class LifecycleManager {
2304
2408
  shutdown(): Promise<void>;
2305
2409
  }
2306
2410
 
2307
- interface ContextProvider {
2308
- readonly name: string;
2309
- isAvailable(repoPath: string): Promise<boolean>;
2310
- listSessions(query: ContextQuery): Promise<SessionListResult>;
2311
- buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult>;
2312
- }
2313
- interface ContextQuery {
2314
- repoPath: string;
2315
- type: "branch" | "commit" | "pr" | "latest" | "checkpoint" | "session";
2316
- value: string;
2317
- }
2318
- interface ContextOptions {
2319
- maxTokens?: number;
2320
- limit?: number;
2321
- /** When true, insert `## [agentName]` headers at agent boundaries in merged history */
2322
- labelAgent?: boolean;
2323
- }
2324
- interface SessionInfo {
2325
- checkpointId: string;
2326
- sessionIndex: string;
2327
- transcriptPath: string;
2328
- createdAt: string;
2329
- endedAt: string;
2330
- branch: string;
2331
- agent: string;
2332
- turnCount: number;
2333
- filesTouched: string[];
2334
- sessionId: string;
2335
- }
2336
- interface SessionListResult {
2337
- sessions: SessionInfo[];
2338
- estimatedTokens: number;
2339
- }
2340
- type ContextMode = "full" | "balanced" | "compact";
2341
- interface ContextResult {
2342
- markdown: string;
2343
- tokenEstimate: number;
2344
- sessionCount: number;
2345
- totalTurns: number;
2346
- mode: ContextMode;
2347
- truncated: boolean;
2348
- timeRange: {
2349
- start: string;
2350
- end: string;
2351
- };
2352
- }
2353
-
2354
- declare class ContextManager {
2355
- private providers;
2356
- private cache;
2357
- constructor(cachePath?: string);
2358
- register(provider: ContextProvider): void;
2359
- getProvider(repoPath: string): Promise<ContextProvider | null>;
2360
- listSessions(query: ContextQuery): Promise<SessionListResult | null>;
2361
- buildContext(query: ContextQuery, options?: ContextOptions): Promise<ContextResult | null>;
2362
- }
2363
-
2364
2411
  interface InstanceContext {
2365
2412
  id: string;
2366
2413
  root: string;
@@ -2398,11 +2445,10 @@ declare class OpenACPCore {
2398
2445
  requestRestart: (() => Promise<void>) | null;
2399
2446
  private _tunnelService?;
2400
2447
  private sessionStore;
2401
- private resumeLocks;
2402
- private switchingLocks;
2403
2448
  eventBus: EventBus;
2404
2449
  sessionFactory: SessionFactory;
2405
2450
  readonly lifecycleManager: LifecycleManager;
2451
+ private agentSwitchHandler;
2406
2452
  readonly instanceContext?: InstanceContext;
2407
2453
  private getService;
2408
2454
  get securityGuard(): SecurityGuard;
@@ -2461,13 +2507,7 @@ declare class OpenACPCore {
2461
2507
  switchSessionAgent(sessionId: string, toAgent: string): Promise<{
2462
2508
  resumed: boolean;
2463
2509
  }>;
2464
- private _doSwitchSessionAgent;
2465
- /**
2466
- * Get active session by thread, or attempt lazy resume from store.
2467
- * Used by adapter command handlers that need a session but don't go through handleMessage().
2468
- */
2469
2510
  getOrResumeSession(channelId: string, threadId: string): Promise<Session | null>;
2470
- private lazyResume;
2471
2511
  /** Create a SessionBridge for the given session and adapter */
2472
2512
  createBridge(session: Session, adapter: IChannelAdapter): SessionBridge;
2473
2513
  }
@@ -2636,6 +2676,114 @@ declare class FileService {
2636
2676
  static extensionFromMime(mimeType: string): string;
2637
2677
  }
2638
2678
 
2679
+ interface ApiConfig {
2680
+ port: number;
2681
+ host: string;
2682
+ }
2683
+
2684
+ interface StoredToken {
2685
+ id: string;
2686
+ name: string;
2687
+ role: string;
2688
+ scopes?: string[];
2689
+ createdAt: string;
2690
+ refreshDeadline: string;
2691
+ lastUsedAt?: string;
2692
+ revoked: boolean;
2693
+ }
2694
+ interface CreateTokenOpts {
2695
+ role: string;
2696
+ name: string;
2697
+ expire: string;
2698
+ scopes?: string[];
2699
+ }
2700
+
2701
+ declare class TokenStore {
2702
+ private filePath;
2703
+ private tokens;
2704
+ private savePromise;
2705
+ private savePending;
2706
+ constructor(filePath: string);
2707
+ load(): Promise<void>;
2708
+ save(): Promise<void>;
2709
+ private scheduleSave;
2710
+ create(opts: CreateTokenOpts): StoredToken;
2711
+ get(id: string): StoredToken | undefined;
2712
+ revoke(id: string): void;
2713
+ list(): StoredToken[];
2714
+ private lastUsedSaveTimer;
2715
+ updateLastUsed(id: string): void;
2716
+ /** Wait for any in-flight save to complete */
2717
+ flush(): Promise<void>;
2718
+ destroy(): void;
2719
+ cleanup(): void;
2720
+ }
2721
+
2722
+ interface ApiServerOptions {
2723
+ port: number;
2724
+ host: string;
2725
+ getSecret: () => string;
2726
+ getJwtSecret: () => string;
2727
+ tokenStore: TokenStore;
2728
+ logger?: boolean;
2729
+ }
2730
+ interface ApiServerInstance {
2731
+ app: FastifyInstance;
2732
+ start(): Promise<{
2733
+ port: number;
2734
+ host: string;
2735
+ }>;
2736
+ stop(): Promise<void>;
2737
+ registerPlugin(prefix: string, plugin: FastifyPluginAsync, opts?: {
2738
+ auth?: boolean;
2739
+ }): void;
2740
+ }
2741
+ declare function createApiServer(options: ApiServerOptions): Promise<ApiServerInstance>;
2742
+
2743
+ interface ApiServerService {
2744
+ registerPlugin(prefix: string, plugin: FastifyPluginAsync, opts?: {
2745
+ auth?: boolean;
2746
+ }): void;
2747
+ authPreHandler: preHandlerHookHandler;
2748
+ requireScopes(...scopes: string[]): preHandlerHookHandler;
2749
+ requireRole(role: string): preHandlerHookHandler;
2750
+ getPort(): number;
2751
+ getBaseUrl(): string;
2752
+ getTunnelUrl(): string | null;
2753
+ }
2754
+ declare function createApiServerService(server: ApiServerInstance, getPort: () => number, getBaseUrl: () => string, getTunnelUrl: () => string | null, authPreHandler: preHandlerHookHandler): ApiServerService;
2755
+
2756
+ interface SessionStats {
2757
+ active: number;
2758
+ total: number;
2759
+ }
2760
+ declare class SSEManager {
2761
+ private eventBus;
2762
+ private getSessionStats;
2763
+ private startedAt;
2764
+ private sseConnections;
2765
+ private sseCleanupHandlers;
2766
+ private healthInterval?;
2767
+ private boundHandlers;
2768
+ constructor(eventBus: EventBus | undefined, getSessionStats: () => SessionStats, startedAt: number);
2769
+ setup(): void;
2770
+ handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void;
2771
+ broadcast(event: string, data: unknown): void;
2772
+ /**
2773
+ * Returns a Fastify route handler that hijacks the response
2774
+ * and delegates to the raw http SSE handler.
2775
+ */
2776
+ createFastifyHandler(): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
2777
+ stop(): void;
2778
+ }
2779
+
2780
+ declare class StaticServer {
2781
+ private uiDir;
2782
+ constructor(uiDir?: string);
2783
+ isAvailable(): boolean;
2784
+ serve(req: http.IncomingMessage, res: http.ServerResponse): boolean;
2785
+ }
2786
+
2639
2787
  interface TopicInfo {
2640
2788
  sessionId: string;
2641
2789
  topicId: number | null;
@@ -2681,63 +2829,6 @@ declare class TopicManager {
2681
2829
  private isSystemTopic;
2682
2830
  }
2683
2831
 
2684
- interface ApiConfig {
2685
- port: number;
2686
- host: string;
2687
- }
2688
- declare class ApiServer {
2689
- private core;
2690
- private config;
2691
- private topicManager?;
2692
- private server;
2693
- private actualPort;
2694
- private portFilePath;
2695
- private startedAt;
2696
- private secret;
2697
- private secretFilePath;
2698
- private sseManager;
2699
- private staticServer;
2700
- private router;
2701
- constructor(core: OpenACPCore, config: ApiConfig, portFilePath?: string, topicManager?: TopicManager | undefined, secretFilePath?: string, uiDir?: string);
2702
- start(): Promise<void>;
2703
- stop(): Promise<void>;
2704
- getPort(): number;
2705
- getSecret(): string;
2706
- private writePortFile;
2707
- private removePortFile;
2708
- private loadOrCreateSecret;
2709
- private authenticate;
2710
- private handleRequest;
2711
- private sendJson;
2712
- private readBody;
2713
- }
2714
-
2715
- interface SessionStats {
2716
- active: number;
2717
- total: number;
2718
- }
2719
- declare class SSEManager {
2720
- private eventBus;
2721
- private getSessionStats;
2722
- private startedAt;
2723
- private sseConnections;
2724
- private sseCleanupHandlers;
2725
- private healthInterval?;
2726
- private boundHandlers;
2727
- constructor(eventBus: EventBus | undefined, getSessionStats: () => SessionStats, startedAt: number);
2728
- setup(): void;
2729
- handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void;
2730
- broadcast(event: string, data: unknown): void;
2731
- stop(): void;
2732
- }
2733
-
2734
- declare class StaticServer {
2735
- private uiDir;
2736
- constructor(uiDir?: string);
2737
- isAvailable(): boolean;
2738
- serve(req: http.IncomingMessage, res: http.ServerResponse): boolean;
2739
- }
2740
-
2741
2832
  declare class EntireProvider implements ContextProvider {
2742
2833
  readonly name = "entire";
2743
2834
  isAvailable(repoPath: string): Promise<boolean>;
@@ -3111,10 +3202,8 @@ interface ConfigManagerLike {
3111
3202
  get(): Record<string, unknown>;
3112
3203
  }
3113
3204
  interface SessionManagerLike {
3114
- getSession(id: string): {
3115
- record?: {
3116
- outputMode?: OutputMode;
3117
- };
3205
+ getSessionRecord(id: string): {
3206
+ outputMode?: OutputMode;
3118
3207
  } | undefined;
3119
3208
  }
3120
3209
  declare class OutputModeResolver {
@@ -3125,7 +3214,7 @@ declare class OutputModeResolver {
3125
3214
  * OpenACP Product Guide — comprehensive reference for the AI assistant.
3126
3215
  * The assistant reads this at runtime to answer user questions about features.
3127
3216
  */
3128
- declare const PRODUCT_GUIDE = "\n# OpenACP \u2014 Product Guide\n\nOpenACP lets you chat with AI coding agents (like Claude Code) through messaging platforms (Telegram, Discord).\nYou type messages in your chat platform, the agent reads/writes/runs code in your project folder, and results stream back in real time.\n\n---\n\n## Quick Start\n\n1. Start OpenACP: `openacp` (or `openacp start` for background daemon)\n2. Open your messaging platform (Telegram group or Discord server) \u2014 you'll see the Assistant topic/thread\n3. Tap/click \uD83C\uDD95 New Session or type /new\n4. Pick an agent and a project folder\n5. Chat in the session topic/thread \u2014 the agent works on your code\n\n---\n\n## Core Concepts\n\n### Sessions\nA session = one conversation with one AI agent working in one project folder.\nEach session gets its own topic (Telegram) or forum thread (Discord). Chat there to give instructions to the agent.\n\n### Agents\nAn agent is an AI coding tool (e.g., Claude Code, Gemini, Cursor, Codex, etc.).\nOpenACP supports 28+ agents from the official ACP Registry (agentclientprotocol.com).\nYou can install multiple agents and choose which one to use per session.\nThe default agent is used when you don't specify one.\n\n### Agent Management\n- Browse agents: `/agents` in your chat platform or `openacp agents` in CLI\n- Install: tap the install button in /agents, or `openacp agents install <name>`\n- Uninstall: `openacp agents uninstall <name>`\n- Setup/login: `openacp agents run <name> -- <args>` (e.g., `openacp agents run gemini -- auth login`)\n- Details: `openacp agents info <name>` shows version, dependencies, and setup steps\n\nSome agents need additional setup before they can be used:\n- Claude: requires `claude login`\n- Gemini: requires `openacp agents run gemini -- auth login`\n- Codex: requires setting `OPENAI_API_KEY` environment variable\n- GitHub Copilot: requires `openacp agents run copilot -- auth login`\n\nAgents are installed in three ways depending on the agent:\n- **npx** \u2014 Node.js agents, downloaded automatically on first use\n- **uvx** \u2014 Python agents, downloaded automatically on first use\n- **binary** \u2014 Platform-specific binaries, downloaded to `~/.openacp/agents/`\n\n### Project Folder (Workspace)\nThe directory where the agent reads, writes, and runs code.\nWhen creating a session, you choose which folder the agent works in.\nYou can type a full path like `~/code/my-project` or just a name like `my-project` (it becomes `<base-dir>/my-project`).\n\n### System Topics\n- **Assistant** \u2014 Always-on helper that can answer questions, create sessions, check status, troubleshoot\n- **Notifications** \u2014 System alerts (permission requests, session errors, completions)\n\n---\n\n## Creating Sessions\n\n### From menu\nTap \uD83C\uDD95 New Session \u2192 choose agent (if multiple) \u2192 choose project folder \u2192 confirm\n\n### From command\n- `/new` \u2014 Interactive flow (asks agent + folder)\n- `/new claude ~/code/my-project` \u2014 Create directly with specific agent and folder\n\n### From Assistant topic\nJust ask: \"Create a session for my-project with claude\" \u2014 the assistant handles it\n\n### Quick new chat\n`/newchat` in a session topic \u2014 creates new session with same agent and folder as current one\n\n---\n\n## Working with Sessions\n\n### Chat\nType messages in the session topic. The agent responds with code changes, explanations, tool outputs.\n\n### What you see while the agent works\n- **\uD83D\uDCAD Thinking indicator** \u2014 Shows when the agent is reasoning, with elapsed time\n- **Text responses** \u2014 Streamed in real time, updated every few seconds\n- **Tool calls** \u2014 When the agent runs commands or edits files, you see tool name, input, status, and output\n- **\uD83D\uDCCB Plan card** \u2014 Visual task progress with completed/in-progress/pending items and progress bar\n- **\"View File\" / \"View Diff\" buttons** \u2014 Opens in browser with Monaco editor (requires tunnel)\n\n### Session lifecycle\n1. **Creating** \u2014 Topic created, agent spawning\n2. **Warming up** \u2014 Agent primes its cache (happens automatically, invisible to you)\n3. **Active** \u2014 Ready for your messages\n4. **Auto-naming** \u2014 After your first message, the session gets a descriptive name (agent summarizes in ~5 words). The topic title updates automatically.\n5. **Finished/Error** \u2014 Session completed or hit an error\n\n### Agent skills\nSome agents provide slash commands (e.g., /compact, /review). Available skills are pinned in the session topic.\n\n### Permission requests\nWhen the agent wants to run a command, it asks for permission.\nYou see buttons: \u2705 Allow, \u274C Reject (and sometimes \"Always Allow\").\nA notification also appears in the Notifications topic with a link to the request.\n\n### Dangerous mode\nAuto-approves ALL permission requests \u2014 the agent runs any command without asking.\n- Enable: `/enable_dangerous` or tap the \u2620\uFE0F button in the session\n- Disable: `/disable_dangerous` or tap the \uD83D\uDD10 button\n- \u26A0\uFE0F Use with caution \u2014 the agent can execute anything\n\n### Session timeout\nIdle sessions are automatically cancelled after a configurable timeout (default: 60 minutes).\nConfigure via `security.sessionTimeoutMinutes` in config.\n\n---\n\n## Session Transfer (Handoff)\n\n### Chat \u2192 Terminal\n1. Type `/handoff` in a session topic/thread\n2. You get a command like `claude --resume <SESSION_ID>`\n3. Copy and run it in your terminal \u2014 the session continues there with full conversation history\n\n### Terminal \u2192 Chat\n1. First time: run `openacp integrate claude` to install the handoff skill (one-time setup)\n2. In Claude Code, use the /openacp:handoff slash command\n3. The session appears as a new topic/thread and you can continue chatting there\n\n### How it works\n- The agent session ID is shared between platforms\n- Conversation history is preserved \u2014 pick up where you left off\n- The agent that supports resume (e.g., Claude with `--resume`) handles the actual transfer\n\n---\n\n## Managing Sessions\n\n### Status\n- `/status` \u2014 Shows active sessions count and details\n- Ask the Assistant: \"What sessions are running?\"\n\n### List all sessions\n- `/sessions` \u2014 Shows all sessions with status (active, finished, error)\n\n### Cancel\n- `/cancel` in a session topic \u2014 cancels that session\n- Ask the Assistant: \"Cancel the stuck session\"\n\n### Cleanup\n- From `/sessions` \u2192 tap cleanup buttons (finished, errors, all)\n- Ask the Assistant: \"Clean up old sessions\"\n\n---\n\n## Assistant Topic\n\nThe Assistant is an always-on AI helper in its own topic. It can:\n- Answer questions about OpenACP\n- Create sessions for you\n- Check status and health\n- Cancel sessions\n- Clean up old sessions\n- Troubleshoot issues\n- Manage configuration\n\nJust chat naturally: \"How do I create a session?\", \"What's the status?\", \"Something is stuck\"\n\n### Clear history\n`/clear` in the Assistant topic \u2014 resets the conversation\n\n---\n\n## System Commands\n\n| Command | Where | What it does |\n|---------|-------|-------------|\n| `/new [agent] [path]` | Anywhere | Create new session |\n| `/newchat` | Session topic | New session, same agent + folder |\n| `/cancel` | Session topic | Cancel current session |\n| `/status` | Anywhere | Show status |\n| `/sessions` | Anywhere | List all sessions |\n| `/agents` | Anywhere | Browse & install agents from ACP Registry |\n| `/install <name>` | Anywhere | Install an agent |\n| `/enable_dangerous` | Session topic | Auto-approve all permissions |\n| `/disable_dangerous` | Session topic | Restore permission prompts |\n| `/handoff` | Session topic | Transfer session to terminal |\n| `/clear` | Assistant topic | Clear assistant history |\n| `/menu` | Anywhere | Show action menu |\n| `/help` | Anywhere | Show help |\n| `/restart` | Anywhere | Restart OpenACP |\n| `/update` | Anywhere | Update to latest version |\n| `/integrate` | Anywhere | Manage agent integrations |\n\n---\n\n## Menu Buttons\n\n| Button | Action |\n|--------|--------|\n| \uD83C\uDD95 New Session | Create new session (interactive) |\n| \uD83D\uDCCB Sessions | List all sessions with cleanup options |\n| \uD83D\uDCCA Status | Show active/total session count |\n| \uD83E\uDD16 Agents | List available agents |\n| \uD83D\uDD17 Integrate | Manage agent integrations |\n| \u2753 Help | Show help text |\n| \uD83D\uDD04 Restart | Restart OpenACP |\n| \u2B06\uFE0F Update | Check and install updates |\n\n---\n\n## CLI Commands\n\n### Server\n- `openacp` \u2014 Start (uses configured mode: foreground or daemon)\n- `openacp start` \u2014 Start as background daemon\n- `openacp stop` \u2014 Stop daemon\n- `openacp status` \u2014 Show daemon status\n- `openacp logs` \u2014 Tail daemon logs\n- `openacp --foreground` \u2014 Force foreground mode (useful for debugging or containers)\n\n### Auto-start (run on boot)\n- macOS: installs a LaunchAgent in `~/Library/LaunchAgents/`\n- Linux: installs a systemd user service in `~/.config/systemd/user/`\n- Enabled automatically when you start the daemon. Remove with `openacp stop`.\n\n### Configuration\n- `openacp config` \u2014 Interactive config editor\n- `openacp reset` \u2014 Delete all data and start fresh\n\n### Agent Management (CLI)\n- `openacp agents` \u2014 List all agents (installed + available from ACP Registry)\n- `openacp agents install <name>` \u2014 Install an agent\n- `openacp agents uninstall <name>` \u2014 Remove an agent\n- `openacp agents info <name>` \u2014 Show details, dependencies, and setup guide\n- `openacp agents run <name> [-- args]` \u2014 Run agent CLI directly (for login, config, etc.)\n- `openacp agents refresh` \u2014 Force-refresh registry cache\n\n### Plugins\n- `openacp install <package>` \u2014 Install adapter plugin\n- `openacp uninstall <package>` \u2014 Remove adapter plugin\n- `openacp plugins` \u2014 List installed plugins\n\n### Integration\n- `openacp integrate <agent>` \u2014 Install agent integration (e.g., Claude handoff skill)\n- `openacp integrate <agent> --uninstall` \u2014 Remove integration\n\n### API (requires running daemon)\n`openacp api <command>` \u2014 Interact with running daemon:\n\n| Command | Description |\n|---------|-------------|\n| `status` | List active sessions |\n| `session <id>` | Session details |\n| `new <agent> <path>` | Create session |\n| `send <id> \"text\"` | Send prompt |\n| `cancel <id>` | Cancel session |\n| `dangerous <id> on/off` | Toggle dangerous mode |\n| `topics [--status x,y]` | List topics |\n| `delete-topic <id> [--force]` | Delete topic |\n| `cleanup [--status x,y]` | Cleanup old topics |\n| `agents` | List agents |\n| `health` | System health |\n| `config` | Show config |\n| `config set <key> <value>` | Update config |\n| `adapters` | List adapters |\n| `tunnel` | Tunnel status |\n| `notify \"message\"` | Send notification |\n| `version` | Daemon version |\n| `restart` | Restart daemon |\n\n---\n\n## File Viewer (Tunnel)\n\nWhen tunnel is enabled, file edits and diffs get \"View\" buttons that open in your browser:\n- **Monaco Editor** \u2014 Full VS Code editor with syntax highlighting\n- **Diff viewer** \u2014 Side-by-side or inline comparison\n- **Line highlighting** \u2014 Click lines to highlight\n- Dark/light theme toggle\n\n### Setup\nEnable in config: set `tunnel.enabled` to `true`.\nProviders: Cloudflare (default, free), ngrok, bore, Tailscale Funnel.\n\n### Port Tunneling\n\nExpose any local port (dev servers, APIs, etc.) to the internet:\n\n**CLI commands** (agent can call these directly):\n- `openacp tunnel add <port> --label <name>` \u2014 Create tunnel to a local port\n- `openacp tunnel list` \u2014 List active tunnels\n- `openacp tunnel stop <port>` \u2014 Stop a tunnel\n- `openacp tunnel stop-all` \u2014 Stop all user tunnels\n\n**Telegram commands**:\n- `/tunnel <port> [label]` \u2014 Create tunnel\n- `/tunnels` \u2014 List active tunnels\n- `/tunnel stop <port>` \u2014 Stop tunnel\n\nExample: after starting a dev server on port 3000, run `openacp tunnel add 3000 --label my-app` to get a public URL.\n\n---\n\n## Configuration\n\nConfig file: `~/.openacp/config.json`\n\n### Channels\n- **channels.telegram.botToken** \u2014 Your Telegram bot token\n- **channels.telegram.chatId** \u2014 Your Telegram supergroup ID\n- **channels.discord.botToken** \u2014 Your Discord bot token\n- **channels.discord.guildId** \u2014 Your Discord server (guild) ID\n\n### Agents\n- **defaultAgent** \u2014 Which agent to use by default\n- Agents are managed via `/agents` (Telegram) or `openacp agents` (CLI)\n- Installed agents are stored in `~/.openacp/agents.json`\n- Agent list is fetched from the ACP Registry CDN and cached locally (24h)\n\n### Workspace\n- **workspace.baseDir** \u2014 Base directory for project folders (default: `~/openacp-workspace`)\n\n### Security\n- **security.allowedUserIds** \u2014 Restrict who can use the bot (empty = everyone)\n- **security.maxConcurrentSessions** \u2014 Max parallel sessions (default: 5)\n- **security.sessionTimeoutMinutes** \u2014 Auto-cancel idle sessions (default: 60)\n\n### Tunnel / File Viewer\n- **tunnel.enabled** \u2014 Enable file viewer tunnel\n- **tunnel.provider** \u2014 Tunnel provider: cloudflare (default, free), ngrok, bore, tailscale\n- **tunnel.port** \u2014 Local port for tunnel server (default: 3100)\n- **tunnel.auth.enabled** \u2014 Enable authentication for tunnel URLs\n- **tunnel.auth.token** \u2014 Auth token for tunnel access\n- **tunnel.storeTtlMinutes** \u2014 How long viewer links stay cached (default: 60)\n\n### Logging\n- **logging.level** \u2014 Log level: silent, debug, info, warn, error, fatal (default: info)\n- **logging.logDir** \u2014 Log directory (default: `~/.openacp/logs`)\n- **logging.maxFileSize** \u2014 Max log file size before rotation\n- **logging.maxFiles** \u2014 Max number of rotated log files\n- **logging.sessionLogRetentionDays** \u2014 Auto-delete old session logs (default: 30)\n\n### Data Retention\n- **sessionStore.ttlDays** \u2014 How long session records persist (default: 30). Old records are cleaned up automatically.\n\n### Environment variables\nOverride config with env vars:\n- `OPENACP_TELEGRAM_BOT_TOKEN`\n- `OPENACP_TELEGRAM_CHAT_ID`\n- `OPENACP_DISCORD_BOT_TOKEN`\n- `OPENACP_DISCORD_GUILD_ID`\n- `OPENACP_DEFAULT_AGENT`\n- `OPENACP_RUN_MODE` \u2014 foreground or daemon\n- `OPENACP_API_PORT` \u2014 API server port (default: 21420)\n- `OPENACP_TUNNEL_ENABLED`\n- `OPENACP_TUNNEL_PORT`\n- `OPENACP_TUNNEL_PROVIDER`\n- `OPENACP_LOG_LEVEL`\n- `OPENACP_LOG_DIR`\n- `OPENACP_DEBUG` \u2014 Sets log level to debug\n\n---\n\n## Troubleshooting\n\n### Session stuck / not responding\n- Check status: ask Assistant \"Is anything stuck?\"\n- Cancel and create new: `/cancel` then `/new`\n- Check system health: Assistant can run health check\n\n### Agent not found\n- Check available agents: `/agents` or `openacp agents`\n- Install missing agent: `openacp agents install <name>`\n- Some agents need login first: `openacp agents info <name>` to see setup steps\n- Run agent CLI for setup: `openacp agents run <name> -- <args>`\n\n### Permission request not showing\n- Check Notifications topic for the alert\n- Try `/enable_dangerous` to auto-approve (if you trust the agent)\n\n### Session disappeared after restart\n- Sessions persist across restarts\n- Send a message in the old topic \u2014 it auto-resumes\n- If topic was deleted, the session record may still exist in status\n\n### Bot not responding at all\n- Check daemon: `openacp status`\n- Check logs: `openacp logs`\n- Restart: `openacp start` or `/restart`\n\n### Messages going to wrong topic\n- Each session is bound to a specific topic/thread\n- If you see messages appearing in the Assistant topic instead of the session topic, try creating a new session\n\n### Viewing logs\n- Session-specific logs: `~/.openacp/logs/sessions/`\n- System logs: `openacp logs` to tail live\n- Set `OPENACP_DEBUG=true` for verbose output\n\n---\n\n## Data & Storage\n\nAll data is stored in `~/.openacp/`:\n- `config.json` \u2014 Configuration\n- `agents.json` \u2014 Installed agents (managed by AgentCatalog)\n- `registry-cache.json` \u2014 Cached ACP Registry data (refreshes every 24h)\n- `agents/` \u2014 Downloaded binary agents\n- `sessions/` \u2014 Session records and state\n- `topics/` \u2014 Topic-to-session mappings\n- `logs/` \u2014 System and session logs\n- `plugins/` \u2014 Installed adapter plugins\n- `openacp.pid` \u2014 Daemon PID file\n\nSession records auto-cleanup: 30 days (configurable via `sessionStore.ttlDays`).\nSession logs auto-cleanup: 30 days (configurable via `logging.sessionLogRetentionDays`).\n";
3217
+ declare const PRODUCT_GUIDE = "\n# OpenACP \u2014 Product Guide\n\nOpenACP lets you chat with AI coding agents (like Claude Code) through messaging platforms (Telegram, Discord).\nYou type messages in your chat platform, the agent reads/writes/runs code in your project folder, and results stream back in real time.\n\n---\n\n## Quick Start\n\n1. Start OpenACP: `openacp` (or `openacp start` for background daemon)\n2. Open your messaging platform (Telegram group or Discord server) \u2014 you'll see the Assistant topic/thread\n3. Tap/click \uD83C\uDD95 New Session or type /new\n4. Pick an agent and a project folder\n5. Chat in the session topic/thread \u2014 the agent works on your code\n\n---\n\n## Core Concepts\n\n### Sessions\nA session = one conversation with one AI agent working in one project folder.\nEach session gets its own topic (Telegram) or forum thread (Discord). Chat there to give instructions to the agent.\n\n### Agents\nAn agent is an AI coding tool (e.g., Claude Code, Gemini, Cursor, Codex, etc.).\nOpenACP supports 28+ agents from the official ACP Registry (agentclientprotocol.com).\nYou can install multiple agents and choose which one to use per session.\nThe default agent is used when you don't specify one.\n\n### Agent Management\n- Browse agents: `/agents` in your chat platform or `openacp agents` in CLI\n- Install: tap the install button in /agents, or `openacp agents install <name>`\n- Uninstall: `openacp agents uninstall <name>`\n- Setup/login: `openacp agents run <name> -- <args>` (e.g., `openacp agents run gemini -- auth login`)\n- Details: `openacp agents info <name>` shows version, dependencies, and setup steps\n\nSome agents need additional setup before they can be used:\n- Claude: requires `claude login`\n- Gemini: requires `openacp agents run gemini -- auth login`\n- Codex: requires setting `OPENAI_API_KEY` environment variable\n- GitHub Copilot: requires `openacp agents run copilot -- auth login`\n\nAgents are installed in three ways depending on the agent:\n- **npx** \u2014 Node.js agents, downloaded automatically on first use\n- **uvx** \u2014 Python agents, downloaded automatically on first use\n- **binary** \u2014 Platform-specific binaries, downloaded to `~/.openacp/agents/`\n\n### Project Folder (Workspace)\nThe directory where the agent reads, writes, and runs code.\nWhen creating a session, you choose which folder the agent works in.\nYou can type a full path like `~/code/my-project` or just a name like `my-project` (it becomes `<base-dir>/my-project`).\n\n### System Topics\n- **Assistant** \u2014 Always-on helper that can answer questions, create sessions, check status, troubleshoot\n- **Notifications** \u2014 System alerts (permission requests, session errors, completions)\n\n---\n\n## Creating Sessions\n\n### From menu\nTap \uD83C\uDD95 New Session \u2192 choose agent (if multiple) \u2192 choose project folder \u2192 confirm\n\n### From command\n- `/new` \u2014 Interactive flow (asks agent + folder)\n- `/new claude ~/code/my-project` \u2014 Create directly with specific agent and folder\n\n### From Assistant topic\nJust ask: \"Create a session for my-project with claude\" \u2014 the assistant handles it\n\n### Quick new chat\n`/newchat` in a session topic \u2014 creates new session with same agent and folder as current one\n\n---\n\n## Working with Sessions\n\n### Chat\nType messages in the session topic. The agent responds with code changes, explanations, tool outputs.\n\n### What you see while the agent works\n- **\uD83D\uDCAD Thinking indicator** \u2014 Shows when the agent is reasoning, with elapsed time\n- **Text responses** \u2014 Streamed in real time, updated every few seconds\n- **Tool calls** \u2014 When the agent runs commands or edits files, you see tool name, input, status, and output\n- **\uD83D\uDCCB Plan card** \u2014 Visual task progress with completed/in-progress/pending items and progress bar\n- **\"View File\" / \"View Diff\" buttons** \u2014 Opens in browser with Monaco editor (requires tunnel)\n\n### Session lifecycle\n1. **Creating** \u2014 Topic created, agent spawning\n2. **Warming up** \u2014 Agent primes its cache (happens automatically, invisible to you)\n3. **Active** \u2014 Ready for your messages\n4. **Auto-naming** \u2014 After your first message, the session gets a descriptive name (agent summarizes in ~5 words). The topic title updates automatically.\n5. **Finished/Error** \u2014 Session completed or hit an error\n\n### Agent skills\nSome agents provide slash commands (e.g., /compact, /review). Available skills are pinned in the session topic.\n\n### Permission requests\nWhen the agent wants to run a command, it asks for permission.\nYou see buttons: \u2705 Allow, \u274C Reject (and sometimes \"Always Allow\").\nA notification also appears in the Notifications topic with a link to the request.\n\n### Bypass permissions\nAuto-approves ALL permission requests \u2014 the agent runs any command without asking.\n- Enable: `/enable_bypass` or tap the \u2620\uFE0F button in the session\n- Disable: `/disable_bypass` or tap the \uD83D\uDD10 button\n- \u26A0\uFE0F Use with caution \u2014 the agent can execute anything\n\n### Session timeout\nIdle sessions are automatically cancelled after a configurable timeout (default: 60 minutes).\nConfigure via `security.sessionTimeoutMinutes` in config.\n\n---\n\n## Session Transfer (Handoff)\n\n### Chat \u2192 Terminal\n1. Type `/handoff` in a session topic/thread\n2. You get a command like `claude --resume <SESSION_ID>`\n3. Copy and run it in your terminal \u2014 the session continues there with full conversation history\n\n### Terminal \u2192 Chat\n1. First time: run `openacp integrate claude` to install the handoff skill (one-time setup)\n2. In Claude Code, use the /openacp:handoff slash command\n3. The session appears as a new topic/thread and you can continue chatting there\n\n### How it works\n- The agent session ID is shared between platforms\n- Conversation history is preserved \u2014 pick up where you left off\n- The agent that supports resume (e.g., Claude with `--resume`) handles the actual transfer\n\n---\n\n## Managing Sessions\n\n### Status\n- `/status` \u2014 Shows active sessions count and details\n- Ask the Assistant: \"What sessions are running?\"\n\n### List all sessions\n- `/sessions` \u2014 Shows all sessions with status (active, finished, error)\n\n### Cancel\n- `/cancel` in a session topic \u2014 cancels that session\n- Ask the Assistant: \"Cancel the stuck session\"\n\n### Cleanup\n- From `/sessions` \u2192 tap cleanup buttons (finished, errors, all)\n- Ask the Assistant: \"Clean up old sessions\"\n\n---\n\n## Assistant Topic\n\nThe Assistant is an always-on AI helper in its own topic. It can:\n- Answer questions about OpenACP\n- Create sessions for you\n- Check status and health\n- Cancel sessions\n- Clean up old sessions\n- Troubleshoot issues\n- Manage configuration\n\nJust chat naturally: \"How do I create a session?\", \"What's the status?\", \"Something is stuck\"\n\n### Clear history\n`/clear` in the Assistant topic \u2014 resets the conversation\n\n---\n\n## System Commands\n\n| Command | Where | What it does |\n|---------|-------|-------------|\n| `/new [agent] [path]` | Anywhere | Create new session |\n| `/newchat` | Session topic | New session, same agent + folder |\n| `/cancel` | Session topic | Cancel current session |\n| `/status` | Anywhere | Show status |\n| `/sessions` | Anywhere | List all sessions |\n| `/agents` | Anywhere | Browse & install agents from ACP Registry |\n| `/install <name>` | Anywhere | Install an agent |\n| `/enable_bypass` | Session topic | Auto-approve all permissions |\n| `/disable_bypass` | Session topic | Restore permission prompts |\n| `/handoff` | Session topic | Transfer session to terminal |\n| `/clear` | Assistant topic | Clear assistant history |\n| `/menu` | Anywhere | Show action menu |\n| `/help` | Anywhere | Show help |\n| `/restart` | Anywhere | Restart OpenACP |\n| `/update` | Anywhere | Update to latest version |\n| `/integrate` | Anywhere | Manage agent integrations |\n\n---\n\n## Menu Buttons\n\n| Button | Action |\n|--------|--------|\n| \uD83C\uDD95 New Session | Create new session (interactive) |\n| \uD83D\uDCCB Sessions | List all sessions with cleanup options |\n| \uD83D\uDCCA Status | Show active/total session count |\n| \uD83E\uDD16 Agents | List available agents |\n| \uD83D\uDD17 Integrate | Manage agent integrations |\n| \u2753 Help | Show help text |\n| \uD83D\uDD04 Restart | Restart OpenACP |\n| \u2B06\uFE0F Update | Check and install updates |\n\n---\n\n## CLI Commands\n\n### Server\n- `openacp` \u2014 Start (uses configured mode: foreground or daemon)\n- `openacp start` \u2014 Start as background daemon\n- `openacp stop` \u2014 Stop daemon\n- `openacp status` \u2014 Show daemon status\n- `openacp logs` \u2014 Tail daemon logs\n- `openacp --foreground` \u2014 Force foreground mode (useful for debugging or containers)\n\n### Auto-start (run on boot)\n- macOS: installs a LaunchAgent in `~/Library/LaunchAgents/`\n- Linux: installs a systemd user service in `~/.config/systemd/user/`\n- Enabled automatically when you start the daemon. Remove with `openacp stop`.\n\n### Configuration\n- `openacp config` \u2014 Interactive config editor\n- `openacp reset` \u2014 Delete all data and start fresh\n\n### Agent Management (CLI)\n- `openacp agents` \u2014 List all agents (installed + available from ACP Registry)\n- `openacp agents install <name>` \u2014 Install an agent\n- `openacp agents uninstall <name>` \u2014 Remove an agent\n- `openacp agents info <name>` \u2014 Show details, dependencies, and setup guide\n- `openacp agents run <name> [-- args]` \u2014 Run agent CLI directly (for login, config, etc.)\n- `openacp agents refresh` \u2014 Force-refresh registry cache\n\n### Plugins\n- `openacp install <package>` \u2014 Install adapter plugin\n- `openacp uninstall <package>` \u2014 Remove adapter plugin\n- `openacp plugins` \u2014 List installed plugins\n\n### Integration\n- `openacp integrate <agent>` \u2014 Install agent integration (e.g., Claude handoff skill)\n- `openacp integrate <agent> --uninstall` \u2014 Remove integration\n\n### API (requires running daemon)\n`openacp api <command>` \u2014 Interact with running daemon:\n\n| Command | Description |\n|---------|-------------|\n| `status` | List active sessions |\n| `session <id>` | Session details |\n| `new <agent> <path>` | Create session |\n| `send <id> \"text\"` | Send prompt |\n| `cancel <id>` | Cancel session |\n| `bypass <id> on/off` | Toggle bypass permissions |\n| `topics [--status x,y]` | List topics |\n| `delete-topic <id> [--force]` | Delete topic |\n| `cleanup [--status x,y]` | Cleanup old topics |\n| `agents` | List agents |\n| `health` | System health |\n| `config` | Show config |\n| `config set <key> <value>` | Update config |\n| `adapters` | List adapters |\n| `tunnel` | Tunnel status |\n| `notify \"message\"` | Send notification |\n| `version` | Daemon version |\n| `restart` | Restart daemon |\n\n---\n\n## File Viewer (Tunnel)\n\nWhen tunnel is enabled, file edits and diffs get \"View\" buttons that open in your browser:\n- **Monaco Editor** \u2014 Full VS Code editor with syntax highlighting\n- **Diff viewer** \u2014 Side-by-side or inline comparison\n- **Line highlighting** \u2014 Click lines to highlight\n- Dark/light theme toggle\n\n### Setup\nEnable in config: set `tunnel.enabled` to `true`.\nProviders: Cloudflare (default, free), ngrok, bore, Tailscale Funnel.\n\n### Port Tunneling\n\nExpose any local port (dev servers, APIs, etc.) to the internet:\n\n**CLI commands** (agent can call these directly):\n- `openacp tunnel add <port> --label <name>` \u2014 Create tunnel to a local port\n- `openacp tunnel list` \u2014 List active tunnels\n- `openacp tunnel stop <port>` \u2014 Stop a tunnel\n- `openacp tunnel stop-all` \u2014 Stop all user tunnels\n\n**Telegram commands**:\n- `/tunnel <port> [label]` \u2014 Create tunnel\n- `/tunnels` \u2014 List active tunnels\n- `/tunnel stop <port>` \u2014 Stop tunnel\n\nExample: after starting a dev server on port 3000, run `openacp tunnel add 3000 --label my-app` to get a public URL.\n\n---\n\n## Configuration\n\nConfig file: `~/.openacp/config.json`\n\n### Channels\n- **channels.telegram.botToken** \u2014 Your Telegram bot token\n- **channels.telegram.chatId** \u2014 Your Telegram supergroup ID\n- **channels.discord.botToken** \u2014 Your Discord bot token\n- **channels.discord.guildId** \u2014 Your Discord server (guild) ID\n\n### Agents\n- **defaultAgent** \u2014 Which agent to use by default\n- Agents are managed via `/agents` (Telegram) or `openacp agents` (CLI)\n- Installed agents are stored in `~/.openacp/agents.json`\n- Agent list is fetched from the ACP Registry CDN and cached locally (24h)\n\n### Workspace\n- **workspace.baseDir** \u2014 Base directory for project folders (default: `~/openacp-workspace`)\n\n### Security\n- **security.allowedUserIds** \u2014 Restrict who can use the bot (empty = everyone)\n- **security.maxConcurrentSessions** \u2014 Max parallel sessions (default: 5)\n- **security.sessionTimeoutMinutes** \u2014 Auto-cancel idle sessions (default: 60)\n\n### Tunnel / File Viewer\n- **tunnel.enabled** \u2014 Enable file viewer tunnel\n- **tunnel.provider** \u2014 Tunnel provider: cloudflare (default, free), ngrok, bore, tailscale\n- **tunnel.port** \u2014 Local port for tunnel server (default: 3100)\n- **tunnel.auth.enabled** \u2014 Enable authentication for tunnel URLs\n- **tunnel.auth.token** \u2014 Auth token for tunnel access\n- **tunnel.storeTtlMinutes** \u2014 How long viewer links stay cached (default: 60)\n\n### Logging\n- **logging.level** \u2014 Log level: silent, debug, info, warn, error, fatal (default: info)\n- **logging.logDir** \u2014 Log directory (default: `~/.openacp/logs`)\n- **logging.maxFileSize** \u2014 Max log file size before rotation\n- **logging.maxFiles** \u2014 Max number of rotated log files\n- **logging.sessionLogRetentionDays** \u2014 Auto-delete old session logs (default: 30)\n\n### Data Retention\n- **sessionStore.ttlDays** \u2014 How long session records persist (default: 30). Old records are cleaned up automatically.\n\n### Environment variables\nOverride config with env vars:\n- `OPENACP_TELEGRAM_BOT_TOKEN`\n- `OPENACP_TELEGRAM_CHAT_ID`\n- `OPENACP_DISCORD_BOT_TOKEN`\n- `OPENACP_DISCORD_GUILD_ID`\n- `OPENACP_DEFAULT_AGENT`\n- `OPENACP_RUN_MODE` \u2014 foreground or daemon\n- `OPENACP_API_PORT` \u2014 API server port (default: 21420)\n- `OPENACP_TUNNEL_ENABLED`\n- `OPENACP_TUNNEL_PORT`\n- `OPENACP_TUNNEL_PROVIDER`\n- `OPENACP_LOG_LEVEL`\n- `OPENACP_LOG_DIR`\n- `OPENACP_DEBUG` \u2014 Sets log level to debug\n\n---\n\n## Troubleshooting\n\n### Session stuck / not responding\n- Check status: ask Assistant \"Is anything stuck?\"\n- Cancel and create new: `/cancel` then `/new`\n- Check system health: Assistant can run health check\n\n### Agent not found\n- Check available agents: `/agents` or `openacp agents`\n- Install missing agent: `openacp agents install <name>`\n- Some agents need login first: `openacp agents info <name>` to see setup steps\n- Run agent CLI for setup: `openacp agents run <name> -- <args>`\n\n### Permission request not showing\n- Check Notifications topic for the alert\n- Try `/enable_bypass` to auto-approve (if you trust the agent)\n\n### Session disappeared after restart\n- Sessions persist across restarts\n- Send a message in the old topic \u2014 it auto-resumes\n- If topic was deleted, the session record may still exist in status\n\n### Bot not responding at all\n- Check daemon: `openacp status`\n- Check logs: `openacp logs`\n- Restart: `openacp start` or `/restart`\n\n### Messages going to wrong topic\n- Each session is bound to a specific topic/thread\n- If you see messages appearing in the Assistant topic instead of the session topic, try creating a new session\n\n### Viewing logs\n- Session-specific logs: `~/.openacp/logs/sessions/`\n- System logs: `openacp logs` to tail live\n- Set `OPENACP_DEBUG=true` for verbose output\n\n---\n\n## Data & Storage\n\nAll data is stored in `~/.openacp/`:\n- `config.json` \u2014 Configuration\n- `agents.json` \u2014 Installed agents (managed by AgentCatalog)\n- `registry-cache.json` \u2014 Cached ACP Registry data (refreshes every 24h)\n- `agents/` \u2014 Downloaded binary agents\n- `sessions/` \u2014 Session records and state\n- `topics/` \u2014 Topic-to-session mappings\n- `logs/` \u2014 System and session logs\n- `plugins/` \u2014 Installed adapter plugins\n- `openacp.pid` \u2014 Daemon PID file\n\nSession records auto-cleanup: 30 days (configurable via `sessionStore.ttlDays`).\nSession logs auto-cleanup: 30 days (configurable via `logging.sessionLogRetentionDays`).\n";
3129
3218
 
3130
3219
  interface TelegramChannelConfig extends ChannelConfig {
3131
3220
  botToken: string;
@@ -3156,6 +3245,14 @@ declare class TelegramAdapter extends MessagingAdapter {
3156
3245
  private sessionTrackers;
3157
3246
  private callbackCache;
3158
3247
  private callbackCounter;
3248
+ /** Pending skill commands queued when session.threadId was not yet set */
3249
+ private _pendingSkillCommands;
3250
+ /** Control message IDs per session (for updating status text/buttons) */
3251
+ private controlMsgIds;
3252
+ /** Store control message ID in memory + persist to session record */
3253
+ private storeControlMsgId;
3254
+ /** Get control message ID (from Map, with fallback to session record) */
3255
+ private getControlMsgId;
3159
3256
  private getThreadId;
3160
3257
  private getOrCreateTracker;
3161
3258
  constructor(core: OpenACPCore, config: TelegramChannelConfig, saveTopicIds?: (updates: {
@@ -3197,6 +3294,12 @@ declare class TelegramAdapter extends MessagingAdapter {
3197
3294
  protected handleUsage(sessionId: string, content: OutgoingMessage, verbosity: DisplayVerbosity): Promise<void>;
3198
3295
  protected handleAttachment(sessionId: string, content: OutgoingMessage): Promise<void>;
3199
3296
  protected handleSessionEnd(sessionId: string, _content: OutgoingMessage): Promise<void>;
3297
+ protected handleConfigUpdate(sessionId: string, _content: OutgoingMessage): Promise<void>;
3298
+ /**
3299
+ * Edit the pinned control message to reflect current session state
3300
+ * (model, thought level, mode, bypass status).
3301
+ */
3302
+ updateControlMessage(sessionId: string): Promise<void>;
3200
3303
  protected handleError(sessionId: string, content: OutgoingMessage): Promise<void>;
3201
3304
  protected handleSystem(sessionId: string, content: OutgoingMessage): Promise<void>;
3202
3305
  sendPermissionRequest(sessionId: string, request: PermissionRequest): Promise<void>;
@@ -3205,6 +3308,8 @@ declare class TelegramAdapter extends MessagingAdapter {
3205
3308
  renameSessionThread(sessionId: string, newName: string): Promise<void>;
3206
3309
  deleteSessionThread(sessionId: string): Promise<void>;
3207
3310
  sendSkillCommands(sessionId: string, commands: AgentCommand[]): Promise<void>;
3311
+ /** Flush any skill commands that were queued before threadId was available */
3312
+ flushPendingSkillCommands(sessionId: string): Promise<void>;
3208
3313
  private resolveSessionId;
3209
3314
  private downloadTelegramFile;
3210
3315
  private handleIncomingMedia;
@@ -3214,4 +3319,4 @@ declare class TelegramAdapter extends MessagingAdapter {
3214
3319
  archiveSessionTopic(sessionId: string): Promise<void>;
3215
3320
  }
3216
3321
 
3217
- export { ActivityTracker, type AdapterCapabilities, type AgentCapabilities, AgentCatalog, type AgentCommand, type AgentDefinition, type AgentDistribution, type AgentEvent, AgentInstance, type AgentListItem, AgentManager, AgentStore, type AgentSwitchEntry, type ApiConfig, ApiServer, type Attachment, type AuthMethod, type AuthenticateRequest, type AvailabilityResult, BaseRenderer, type BridgeDeps, CONFIG_REGISTRY, ChannelAdapter, type ChannelConfig, type CleanupResult, type CommandArgs, type CommandDef, CommandRegistry, type CommandResponse, type Config, type ConfigFieldDef, ConfigManager$1 as ConfigManager, type ConfigOption, type ConfigSelectChoice, type ConfigSelectGroup, type ContentBlock, ContextManager, type ContextOptions, type ContextProvider, type ContextQuery, type ContextResult, type ContextService, type SessionInfo as ContextSessionInfo, type DeleteTopicResult, DisplaySpecBuilder, type DisplayVerbosity, DoctorEngine, type DoctorReport, DraftManager, EntireProvider, EventBus, type EventBusEvents, FileService, type FileServiceInterface, GroqSTT, type IChannelAdapter, type IRenderer, type IncomingMessage, type InstallContext, type InstallProgress, type InstallResult, type InstalledAgent, KIND_ICONS, type ListItem, type Logger$1 as Logger, type LoggingConfig, type McpServerConfig, type MenuOption, MessageTransformer, MessagingAdapter, type MessagingAdapterConfig, type MigrateContext, type ModelInfo, type NewSessionResponse, NotificationManager, type NotificationMessage, type NotificationService, OpenACPCore, type OpenACPPlugin, type OutgoingMessage, type OutputMode, OutputModeResolver, PRODUCT_GUIDE, type PendingFix, PermissionGate, type PermissionOption, type PermissionRequest, type PlanEntry, type PluginContext, type PluginPermission, type PluginStorage, PromptQueue, type PromptResponse, type RegistryAgent, type RegistryBinaryTarget, type RegistryDistribution, type RenderedMessage, SSEManager, STATUS_ICONS, type STTOptions, type STTProvider$1 as STTProvider, type STTResult, SecurityGuard, type SecurityService, SendQueue, Session, SessionBridge, type SessionCreateParams, type SessionEvents, SessionFactory, type SessionListItem, type SessionListResponse, type SessionListResult, SessionManager, type SessionMode, type SessionModeState, type SessionModelState, type SessionRecord, type SessionStatus, type SetConfigOptionValue, type SettingsAPI, type SideEffectDeps, type SpeechProviderConfig, SpeechService, type SpeechServiceConfig, type SpeechServiceInterface, StaticServer, StderrCapture, type StopReason, StreamAdapter, type TTSOptions, type TTSProvider$1 as TTSProvider, type TTSResult, TelegramAdapter, type TelegramPlatformData, type TerminalIO, ThoughtBuffer, type ThoughtDisplaySpec, type ToolCallMeta, ToolCallTracker, type ToolCardSnapshot, ToolCardState, type ToolCardStateConfig, type ToolDisplaySpec, type ToolEntry, ToolStateMap, type ToolUpdateMeta, type TopicInfo, TopicManager, type TunnelServiceInterface, TypedEmitter, type UsageConfig, type UsageRecord, type UsageRecordEvent, type UsageService, type ViewerLinks, cleanupOldSessionLogs, createChildLogger, createSessionLogger, expandHome, extractContentText, formatTokens, formatToolSummary, formatToolTitle, getConfigValue, getFieldDef, getPidPath, getSafeFields, getStatus, initLogger, installAutoStart, isAutoStartInstalled, isAutoStartSupported, isHotReloadable, log, nodeToWebReadable, nodeToWebWritable, progressBar, resolveOptions, resolveToolIcon, runConfigEditor, setLogLevel, shutdownLogger, splitMessage, startDaemon, stopDaemon, stripCodeFences, truncateContent, uninstallAutoStart };
3322
+ export { ActivityTracker, type AdapterCapabilities, type AgentCapabilities, AgentCatalog, type AgentCommand, type AgentDefinition, type AgentDistribution, type AgentEvent, AgentInstance, type AgentListItem, AgentManager, AgentStore, type AgentSwitchEntry, type ApiConfig, type ApiServerInstance, type ApiServerOptions, type ApiServerService, type Attachment, type AuthMethod, type AuthenticateRequest, type AvailabilityResult, BaseRenderer, type BridgeDeps, CONFIG_REGISTRY, ChannelAdapter, type ChannelConfig, type CleanupResult, type CommandArgs, type CommandDef, CommandRegistry, type CommandResponse, type Config, type ConfigFieldDef, ConfigManager$1 as ConfigManager, type ConfigOption, type ConfigSelectChoice, type ConfigSelectGroup, type ContentBlock, ContextManager, type ContextOptions, type ContextProvider, type ContextQuery, type ContextResult, type ContextService, type SessionInfo as ContextSessionInfo, type DeleteTopicResult, DisplaySpecBuilder, type DisplayVerbosity, DoctorEngine, type DoctorReport, DraftManager, EntireProvider, EventBus, type EventBusEvents, FileService, type FileServiceInterface, GroqSTT, type IChannelAdapter, type IRenderer, type IncomingMessage, type InstallContext, type InstallProgress, type InstallResult, type InstalledAgent, KIND_ICONS, type ListItem, type Logger$1 as Logger, type LoggingConfig, type McpServerConfig, type MenuOption, MessageTransformer, MessagingAdapter, type MessagingAdapterConfig, type MigrateContext, type ModelInfo, type NewSessionResponse, NotificationManager, type NotificationMessage, type NotificationService, OpenACPCore, type OpenACPPlugin, type OutgoingMessage, type OutputMode, OutputModeResolver, PRODUCT_GUIDE, type PendingFix, PermissionGate, type PermissionOption, type PermissionRequest, type PlanEntry, type PluginContext, type PluginPermission, type PluginStorage, PromptQueue, type PromptResponse, type RegistryAgent, type RegistryBinaryTarget, type RegistryDistribution, type RenderedMessage, SSEManager, STATUS_ICONS, type STTOptions, type STTProvider$1 as STTProvider, type STTResult, SecurityGuard, type SecurityService, SendQueue, Session, SessionBridge, type SessionCreateParams, type SessionEvents, SessionFactory, type SessionListItem, type SessionListResponse, type SessionListResult, SessionManager, type SessionMode, type SessionModeState, type SessionModelState, type SessionRecord, type SessionStatus, type SetConfigOptionValue, type SettingsAPI, type SideEffectDeps, type SpeechProviderConfig, SpeechService, type SpeechServiceConfig, type SpeechServiceInterface, StaticServer, StderrCapture, type StopReason, StreamAdapter, type TTSOptions, type TTSProvider$1 as TTSProvider, type TTSResult, TelegramAdapter, type TelegramPlatformData, type TerminalIO, ThoughtBuffer, type ThoughtDisplaySpec, type ToolCallMeta, ToolCallTracker, type ToolCardSnapshot, ToolCardState, type ToolCardStateConfig, type ToolDisplaySpec, type ToolEntry, ToolStateMap, type ToolUpdateMeta, type TopicInfo, TopicManager, type TunnelServiceInterface, TypedEmitter, type UsageConfig, type UsageRecord, type UsageRecordEvent, type UsageService, type ViewerLinks, cleanupOldSessionLogs, createApiServer, createApiServerService, createChildLogger, createSessionLogger, expandHome, extractContentText, formatTokens, formatToolSummary, formatToolTitle, getConfigValue, getFieldDef, getPidPath, getSafeFields, getStatus, initLogger, installAutoStart, isAutoStartInstalled, isAutoStartSupported, isHotReloadable, log, nodeToWebReadable, nodeToWebWritable, progressBar, resolveOptions, resolveToolIcon, runConfigEditor, setLogLevel, shutdownLogger, splitMessage, startDaemon, stopDaemon, stripCodeFences, truncateContent, uninstallAutoStart };