@chrisromp/copilot-bridge 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +4 -0
  2. package/config.sample.json +20 -0
  3. package/dist/config.d.ts +9 -1
  4. package/dist/config.d.ts.map +1 -1
  5. package/dist/config.js +117 -0
  6. package/dist/config.js.map +1 -1
  7. package/dist/core/bridge-docs.d.ts +17 -0
  8. package/dist/core/bridge-docs.d.ts.map +1 -0
  9. package/dist/core/bridge-docs.js +790 -0
  10. package/dist/core/bridge-docs.js.map +1 -0
  11. package/dist/core/bridge.d.ts +14 -1
  12. package/dist/core/bridge.d.ts.map +1 -1
  13. package/dist/core/bridge.js +22 -2
  14. package/dist/core/bridge.js.map +1 -1
  15. package/dist/core/command-handler.d.ts +17 -4
  16. package/dist/core/command-handler.d.ts.map +1 -1
  17. package/dist/core/command-handler.js +255 -52
  18. package/dist/core/command-handler.js.map +1 -1
  19. package/dist/core/model-fallback.d.ts +2 -2
  20. package/dist/core/model-fallback.d.ts.map +1 -1
  21. package/dist/core/model-fallback.js +11 -4
  22. package/dist/core/model-fallback.js.map +1 -1
  23. package/dist/core/quiet-mode.d.ts +14 -0
  24. package/dist/core/quiet-mode.d.ts.map +1 -0
  25. package/dist/core/quiet-mode.js +49 -0
  26. package/dist/core/quiet-mode.js.map +1 -0
  27. package/dist/core/session-manager.d.ts +53 -3
  28. package/dist/core/session-manager.d.ts.map +1 -1
  29. package/dist/core/session-manager.js +430 -30
  30. package/dist/core/session-manager.js.map +1 -1
  31. package/dist/index.js +437 -41
  32. package/dist/index.js.map +1 -1
  33. package/dist/state/store.d.ts +1 -0
  34. package/dist/state/store.d.ts.map +1 -1
  35. package/dist/state/store.js +13 -2
  36. package/dist/state/store.js.map +1 -1
  37. package/dist/types.d.ts +29 -0
  38. package/dist/types.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/templates/admin/AGENTS.md +56 -0
  41. package/templates/agents/AGENTS.md +20 -0
@@ -0,0 +1,790 @@
1
+ /**
2
+ * Bridge documentation content for the fetch_copilot_bridge_documentation tool.
3
+ * Each topic returns focused markdown content with source pointers.
4
+ */
5
+ import { createLogger } from '../logger.js';
6
+ import { getConfig } from '../config.js';
7
+ import * as fs from 'node:fs';
8
+ import * as path from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+ const log = createLogger('bridge-docs');
11
+ const TOPICS = [
12
+ 'overview', 'commands', 'config', 'mcp', 'permissions', 'workspaces',
13
+ 'hooks', 'skills', 'inter-agent', 'scheduling', 'providers', 'troubleshooting', 'status',
14
+ ];
15
+ export function isValidTopic(topic) {
16
+ return TOPICS.includes(topic);
17
+ }
18
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
19
+ let _cachedVersion = null;
20
+ function getVersion() {
21
+ if (_cachedVersion)
22
+ return _cachedVersion;
23
+ try {
24
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '../../package.json'), 'utf8'));
25
+ let version = pkg.version ?? 'unknown';
26
+ const gitDir = path.join(__dirname, '../../.git');
27
+ if (fs.existsSync(gitDir)) {
28
+ try {
29
+ const head = fs.readFileSync(path.join(gitDir, 'HEAD'), 'utf8').trim();
30
+ if (head.startsWith('ref: ')) {
31
+ const refPath = path.join(gitDir, head.slice(5));
32
+ if (fs.existsSync(refPath)) {
33
+ const hash = fs.readFileSync(refPath, 'utf8').trim().slice(0, 7);
34
+ version += ` (${hash})`;
35
+ }
36
+ }
37
+ else {
38
+ version += ` (${head.slice(0, 7)})`;
39
+ }
40
+ }
41
+ catch { /* best-effort */ }
42
+ }
43
+ _cachedVersion = version;
44
+ return version;
45
+ }
46
+ catch {
47
+ return 'unknown';
48
+ }
49
+ }
50
+ function topicList() {
51
+ return `Available topics: ${TOPICS.map(t => `\`${t}\``).join(', ')}
52
+
53
+ Call with a specific topic for focused information, e.g. \`fetch_copilot_bridge_documentation({ topic: "commands" })\``;
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Topic: overview
57
+ // ---------------------------------------------------------------------------
58
+ function topicOverview() {
59
+ return `# copilot-bridge Overview
60
+
61
+ copilot-bridge connects GitHub Copilot CLI sessions to messaging platforms (Mattermost, Slack). It runs as a background service, managing one Copilot session per chat channel.
62
+
63
+ ## Key Features
64
+
65
+ - **Multi-bot support** — multiple bot identities, each with its own workspace, AGENTS.md, and MCP servers
66
+ - **Streaming responses** — edit-in-place messages with throttled updates
67
+ - **Slash commands** — \`/model\`, \`/new\`, \`/status\`, \`/context\`, etc. (use \`fetch_copilot_bridge_documentation({ topic: "commands" })\` for full list)
68
+ - **Permission system** — interactive prompts, autopilot mode, persistent rules
69
+ - **MCP servers** — auto-loads from plugins, user config, and workspace config
70
+ - **Skills** — skill directories discovered from standard Copilot locations
71
+ - **Hooks** — preToolUse/postToolUse shell hooks for custom logic
72
+ - **Inter-agent communication** — bots can query each other via \`ask_agent\` tool
73
+ - **Task scheduling** — cron and one-off scheduled prompts
74
+ - **Model fallback** — automatic failover on model capacity/availability errors
75
+ - **BYOK providers** — bring your own API keys for external model providers (Ollama, Azure, OpenAI-compatible)
76
+ - **Infinite sessions** — SDK-managed context compaction for long conversations
77
+
78
+ ## Architecture
79
+
80
+ 1. Channel adapter receives platform message, normalizes to \`InboundMessage\`
81
+ 2. Messages serialized per-channel via promise chains
82
+ 3. \`SessionManager\` creates/resumes Copilot sessions via \`CopilotBridge\` (SDK wrapper)
83
+ 4. SDK events flow back through formatters → streaming handler → platform
84
+
85
+ ## Source
86
+
87
+ - Repository: https://github.com/ChrisRomp/copilot-bridge
88
+ - Docs: \`docs/\` directory in the repository
89
+ - Key files: \`src/index.ts\` (orchestrator), \`src/core/session-manager.ts\` (session lifecycle), \`src/core/bridge.ts\` (SDK wrapper)`;
90
+ }
91
+ // ---------------------------------------------------------------------------
92
+ // Topic: commands
93
+ // ---------------------------------------------------------------------------
94
+ function topicCommands() {
95
+ return `# Slash Commands
96
+
97
+ Commands are intercepted by the bridge before reaching the Copilot session. The agent does not see these commands. Use \`/help all\` for the complete list — below are the most commonly used commands.
98
+
99
+ ## Session Management
100
+ | Command | Description |
101
+ |---------|-------------|
102
+ | \`/new\` | Create a new session (destroys current) |
103
+ | \`/stop\`, \`/cancel\` | Stop the current task |
104
+ | \`/reload\` | Re-attach session (re-reads AGENTS.md, config, MCP) |
105
+ | \`/reload config\` | Hot-reload bridge config file |
106
+ | \`/resume [id]\` | Resume a past session (no args = list recent) |
107
+
108
+ ## Model & Agent
109
+ | Command | Description |
110
+ |---------|-------------|
111
+ | \`/model [name]\` | Show available models or switch model |
112
+ | \`/model <provider>:<model>\` | Switch to a BYOK provider model |
113
+ | \`/provider\` | List configured BYOK providers |
114
+ | \`/provider test <name>\` | Test provider connectivity |
115
+ | \`/agent <name>\` | Switch to a named agent persona |
116
+ | \`/agents\` | List available agent definitions |
117
+ | \`/reasoning [level]\` | Set reasoning effort (low/medium/high/xhigh) |
118
+
119
+ ## Permissions & Mode
120
+ | Command | Description |
121
+ |---------|-------------|
122
+ | \`/approve\` | Approve pending permission request |
123
+ | \`/deny\` | Deny pending permission request |
124
+ | \`/yolo\` | Toggle auto-approve all permissions |
125
+ | \`/autopilot\`, \`/auto\` | Toggle autonomous mode |
126
+ | \`/plan [on|off|show|summary|clear]\` | Toggle plan mode. On entry, surfaces existing plan if found |
127
+ | \`/implement [yolo|interactive]\` | Start implementing the current plan. Default: autopilot. \`yolo\`: autopilot + auto-approve. \`interactive\`: step-by-step |
128
+ | \`/always approve|deny <pattern>\` | Persist a permission rule |
129
+ | \`/rules\` | List all stored permission rules |
130
+
131
+ ## Information
132
+ | Command | Description |
133
+ |---------|-------------|
134
+ | \`/status\` | Show session info, model, mode, context usage |
135
+ | \`/context\` | Show context window usage |
136
+ | \`/verbose\` | Toggle verbose tool output |
137
+ | \`/mcp\` | Show loaded MCP servers and their source |
138
+ | \`/skills\`, \`/tools\` | Show available skills, enable/disable |
139
+ | \`/schedule\`, \`/tasks\` | List scheduled tasks |
140
+ | \`/help [all]\` | Show common commands (or all) |
141
+
142
+ ## Display
143
+ | Command | Description |
144
+ |---------|-------------|
145
+ | \`/streamer-mode\`, \`/on-air\` | Toggle sensitive data redaction |
146
+
147
+ ## Source
148
+ - Command parsing: \`src/core/command-handler.ts\`
149
+ - Command dispatch: \`src/index.ts\` (handleInboundMessage)`;
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // Topic: config
153
+ // ---------------------------------------------------------------------------
154
+ function topicConfig(isAdmin) {
155
+ const editNote = isAdmin
156
+ ? 'Config file: `~/.copilot-bridge/config.json` (or `COPILOT_BRIDGE_CONFIG` env var)'
157
+ : 'Config changes require editing `~/.copilot-bridge/config.json`. Ask your administrator or the user to make config modifications.';
158
+ return `# Configuration
159
+
160
+ ${editNote}
161
+
162
+ ## Key Top-Level Settings
163
+
164
+ | Field | Type | Description |
165
+ |-------|------|-------------|
166
+ | \`platforms\` | object | Platform configs keyed by name (e.g., \`"mattermost": { url, bots }\`) |
167
+ | \`channels\` | array | Per-channel config entries (each has \`id\`, \`platform\`, \`bot\`, etc.) |
168
+ | \`defaults\` | object | Default values for channel settings |
169
+ | \`logLevel\` | string | \`"debug"\`, \`"info"\`, \`"warn"\`, \`"error"\` |
170
+ | \`infiniteSessions\` | boolean | Enable SDK context compaction (default: false) |
171
+ | \`permissions\` | object | Permission rules (allow/deny patterns) |
172
+ | \`interAgent\` | object | Inter-agent communication settings |
173
+ | \`providers\` | object | BYOK provider configs (use \`fetch_copilot_bridge_documentation({ topic: "providers" })\` for details) |
174
+
175
+ ## Defaults Section (per-channel overridable)
176
+
177
+ | Field | Default | Description |
178
+ |-------|---------|-------------|
179
+ | \`model\` | \`"claude-sonnet-4.6"\` | Default model |
180
+ | \`verbose\` | \`false\` | Show tool call details |
181
+ | \`permissionMode\` | \`"interactive"\` | Permission handling mode |
182
+ | \`fallbackModels\` | \`[]\` | Model fallback chain |
183
+
184
+ ## Channel Entries
185
+
186
+ Each entry in the \`channels\` array configures one channel:
187
+ \`\`\`json
188
+ {
189
+ "channels": [
190
+ {
191
+ "id": "channel-id-from-platform",
192
+ "platform": "mattermost",
193
+ "bot": "copilot",
194
+ "name": "My Project",
195
+ "workingDirectory": "/path/to/workspace",
196
+ "model": "claude-opus-4.6"
197
+ }
198
+ ]
199
+ }
200
+ \`\`\`
201
+
202
+ ## Hot-Reloadable Settings
203
+
204
+ These apply immediately on \`/reload config\`: \`defaults\`, \`permissions\`, \`interAgent\`, \`channels\`.
205
+ Top-level settings like \`infiniteSessions\` take effect at next session create/resume.
206
+
207
+ ## Source
208
+ - Config loading/validation: \`src/config.ts\`
209
+ - Sample: \`config.sample.json\`
210
+ - Docs: \`docs/configuration.md\``;
211
+ }
212
+ // ---------------------------------------------------------------------------
213
+ // Topic: mcp
214
+ // ---------------------------------------------------------------------------
215
+ function topicMcp() {
216
+ return `# MCP Server Configuration
217
+
218
+ MCP (Model Context Protocol) servers provide external tools to agents. Loaded in three layers:
219
+
220
+ 1. **Plugins** (\`~/.copilot/installed-plugins/**/.mcp.json\`) — lowest priority
221
+ 2. **User config** (\`~/.copilot/mcp-config.json\`) — overrides plugins
222
+ 3. **Workspace config** (\`<workspace>/mcp-config.json\`) — highest priority, per-bot
223
+
224
+ ## Format
225
+
226
+ \`\`\`json
227
+ {
228
+ "mcpServers": {
229
+ "my-server": {
230
+ "type": "stdio",
231
+ "command": "node",
232
+ "args": ["/path/to/server.js"]
233
+ }
234
+ }
235
+ }
236
+ \`\`\`
237
+
238
+ ## Working Directory
239
+
240
+ Local MCP servers automatically run with \`cwd\` set to the bot's workspace. Override with explicit \`cwd\` in config.
241
+
242
+ ## Environment Variables
243
+
244
+ Workspace \`.env\` vars are injected into every local MCP server's \`env\` field. Use \`\${VAR}\` in config to remap variable names.
245
+
246
+ Priority: explicit \`env\` in config > \`.env\` values.
247
+
248
+ ## Troubleshooting
249
+
250
+ - **Server not loading**: Check \`~/.copilot-bridge/copilot-bridge.log\` for MCP startup errors
251
+ - **Tools not visible**: Server may start but fail to connect to backend (missing env vars). Run \`/reload\` after fixing.
252
+ - **Wrong server version**: \`npx\` caches — use \`npx --yes package@latest\` to force update
253
+ - **\`/mcp\` command**: Shows all loaded servers and which layer they came from
254
+
255
+ ## Source
256
+ - Server resolution: \`src/core/session-manager.ts\` (resolveMcpServers, loadWorkspaceMcpServers, mergeMcpServers)
257
+ - Docs: \`docs/workspaces.md\` (MCP section)`;
258
+ }
259
+ // ---------------------------------------------------------------------------
260
+ // Topic: permissions
261
+ // ---------------------------------------------------------------------------
262
+ function topicPermissions() {
263
+ return `# Permission System
264
+
265
+ The bridge controls which tools agents can use. Permissions are resolved in order:
266
+
267
+ 1. **Hardcoded safety denies** (cannot override)
268
+ 2. **Autopilot/yolo mode** (auto-approve if enabled)
269
+ 3. **Config deny rules** (from \`permissions.deny\`)
270
+ 4. **Config allow rules** (from \`permissions.allow\`)
271
+ 5. **SQLite stored rules** (from \`/always approve\` or \`/always deny\`)
272
+ 6. **Interactive prompt** (asks user in chat)
273
+
274
+ ## Config Rules
275
+
276
+ \`\`\`json
277
+ {
278
+ "permissions": {
279
+ "allow": ["read", "shell(ls)", "shell(cat)", "vault-search"],
280
+ "deny": ["shell(rm)", "shell(git push)"],
281
+ "allowPaths": [],
282
+ "allowUrls": ["docs.github.com"]
283
+ }
284
+ }
285
+ \`\`\`
286
+
287
+ ## Pattern Syntax
288
+
289
+ | Pattern | Matches |
290
+ |---------|---------|
291
+ | \`"read"\` | All file reads |
292
+ | \`"write"\` | All file writes |
293
+ | \`"shell"\` | All shell commands |
294
+ | \`"shell(ls)"\` | The \`ls\` command specifically |
295
+ | \`"shell(git push)"\` | \`git push\` with any args |
296
+ | \`"mcp-server-name"\` | All tools from that MCP server |
297
+ | \`"mcp-server(tool)"\` | Specific MCP tool |
298
+
299
+ ## User Commands
300
+
301
+ | Command | Effect |
302
+ |---------|--------|
303
+ | \`/approve\` | Approve current pending request |
304
+ | \`/deny\` | Deny current pending request |
305
+ | \`/yolo\` | Toggle auto-approve all |
306
+ | \`/autopilot\` | Toggle autonomous mode (auto-approve + continues without user input) |
307
+ | \`/always approve <pattern>\` | Persist an allow rule |
308
+ | \`/always deny <pattern>\` | Persist a deny rule |
309
+ | \`/rules\` | View all stored rules |
310
+ | \`/rules clear\` | Clear all stored rules |
311
+
312
+ ## Source
313
+ - Permission resolution: \`src/core/session-manager.ts\` (handlePermissionRequest)
314
+ - Access control: \`src/core/access-control.ts\`
315
+ - Docs: \`docs/configuration.md\` (Permissions section)`;
316
+ }
317
+ // ---------------------------------------------------------------------------
318
+ // Topic: workspaces
319
+ // ---------------------------------------------------------------------------
320
+ function topicWorkspaces() {
321
+ return `# Workspaces
322
+
323
+ Each bot has a dedicated workspace directory (default: \`~/.copilot-bridge/workspaces/<bot-name>/\`).
324
+
325
+ ## Structure
326
+
327
+ \`\`\`
328
+ <workspace>/
329
+ ├── AGENTS.md # Agent instructions (read by Copilot CLI)
330
+ ├── .env # Environment variables (secrets, API tokens)
331
+ ├── mcp-config.json # Workspace-specific MCP servers (optional)
332
+ ├── agents/ # Named agent personas (*.agent.md)
333
+ ├── .github/skills/ # Project-level skills
334
+ └── .agents/skills/ # Legacy skill location
335
+ \`\`\`
336
+
337
+ ## .env Files
338
+
339
+ - Loaded into shell environment at session start
340
+ - Injected into MCP server \`env\` fields automatically
341
+ - **Never read or display .env contents** — secrets must stay out of chat context
342
+
343
+ ## Agent Personas
344
+
345
+ Files in \`<workspace>/agents/*.agent.md\` define named personas. Users switch with \`/agent <name>\`.
346
+
347
+ ## Workspace Resolution
348
+
349
+ 1. Channel-specific \`workingDirectory\` in config
350
+ 2. Bot-specific workspace: \`~/.copilot-bridge/workspaces/<bot-name>/\`
351
+ 3. Fallback to bridge cwd
352
+
353
+ ## Additional Paths
354
+
355
+ Admins can grant bots access to folders outside their workspace via the \`grant_path_access\` API.
356
+
357
+ ## Source
358
+ - Workspace resolution: \`src/core/session-manager.ts\` (resolveWorkingDirectory)
359
+ - Docs: \`docs/workspaces.md\``;
360
+ }
361
+ // ---------------------------------------------------------------------------
362
+ // Topic: hooks
363
+ // ---------------------------------------------------------------------------
364
+ function topicHooks() {
365
+ return `# Hooks
366
+
367
+ Hooks run shell commands at session lifecycle points. They can allow, deny, or prompt for tool usage.
368
+
369
+ ## Configuration
370
+
371
+ Hooks are defined in \`hooks.json\` files, discovered in order (lowest → highest priority):
372
+ 1. Plugin hooks (\`~/.copilot/installed-plugins/**/hooks.json\`)
373
+ 2. User hooks (\`~/.copilot/hooks.json\`)
374
+ 3. Workspace hooks (\`<workspace>/.github/hooks/hooks.json\`, \`<workspace>/.github/hooks.json\`, or \`<workspace>/hooks.json\`) — requires \`allowWorkspaceHooks: true\` in config
375
+
376
+ ## Format
377
+
378
+ \`\`\`json
379
+ {
380
+ "version": 1,
381
+ "hooks": {
382
+ "preToolUse": [
383
+ {
384
+ "type": "command",
385
+ "bash": "./scripts/guard.sh",
386
+ "timeoutSec": 5
387
+ }
388
+ ]
389
+ }
390
+ }
391
+ \`\`\`
392
+
393
+ ## Hook Types
394
+
395
+ | Type | When | Can Control? |
396
+ |------|------|-------------|
397
+ | \`preToolUse\` | Before tool execution | Yes — allow/deny/ask |
398
+ | \`postToolUse\` | After tool execution | No |
399
+
400
+ ## Hook Script I/O
401
+
402
+ - **Input**: JSON on stdin with \`toolName\`, \`toolArgs\`, \`sessionId\`
403
+ - **Output**: JSON on stdout with \`permissionDecision\` (\`"allow"\`, \`"deny"\`, \`"ask"\`)
404
+ - **Important**: \`toolArgs\` may be a JSON string (not object) — parse with \`jq '(.toolArgs | if type == "string" then fromjson else . end)'\`
405
+
406
+ ## Performance
407
+
408
+ Hooks fire on **every tool call** — keep scripts fast. Use low \`timeoutSec\` (5s) and early-exit for non-matching tools.
409
+
410
+ ## Source
411
+ - Hook loading: \`src/core/hooks-loader.ts\`
412
+ - Hook integration: \`src/core/session-manager.ts\` (resolveHooks, wrapHooksWithAsk)
413
+ - Docs: \`docs/configuration.md\` (Hooks section)`;
414
+ }
415
+ // ---------------------------------------------------------------------------
416
+ // Topic: skills
417
+ // ---------------------------------------------------------------------------
418
+ function topicSkills() {
419
+ return `# Skills
420
+
421
+ Skills are prompt-based capabilities discovered from standard Copilot directory conventions.
422
+
423
+ ## Discovery Paths (checked in order)
424
+
425
+ 1. \`~/.copilot/skills/\` — user-level
426
+ 2. \`~/.agents/skills/\` — user-level (alternate)
427
+ 3. \`<workspace>/.github/skills/\` — project-level (standard)
428
+ 4. \`<workspace>/.agents/skills/\` — project-level (legacy)
429
+ 5. \`~/.copilot/installed-plugins/**/skills/\` — plugin skills
430
+
431
+ Each subdirectory in these locations is a skill. Skills contain a \`SKILL.md\` file with instructions.
432
+
433
+ ## Managing Skills
434
+
435
+ | Command | Effect |
436
+ |---------|--------|
437
+ | \`/skills\` | List all discovered skills and their status |
438
+ | \`/skills enable <name>\` | Enable a disabled skill |
439
+ | \`/skills disable <name>\` | Disable a skill for this channel |
440
+
441
+ Disabled skills are persisted per-channel in SQLite and passed to the SDK as \`disabledSkills\`.
442
+
443
+ ## Source
444
+ - Skill discovery: \`src/core/session-manager.ts\` (discoverSkillDirectories)
445
+ - Skill toggle: \`src/core/command-handler.ts\` (/skills command)
446
+ - State: \`src/state/store.ts\` (channel_prefs.disabled_skills)`;
447
+ }
448
+ // ---------------------------------------------------------------------------
449
+ // Topic: inter-agent
450
+ // ---------------------------------------------------------------------------
451
+ function topicInterAgent() {
452
+ return `# Inter-Agent Communication
453
+
454
+ Bots can query each other via the \`ask_agent\` tool, creating ephemeral sessions.
455
+
456
+ ## Configuration
457
+
458
+ \`\`\`json
459
+ {
460
+ "interAgent": {
461
+ "enabled": true,
462
+ "defaultTimeout": 60,
463
+ "maxTimeout": 300,
464
+ "maxDepth": 3,
465
+ "allow": {
466
+ "bot-a": { "canCall": ["bot-b"], "canBeCalledBy": ["bot-b"] },
467
+ "bot-b": { "canCall": ["bot-a"], "canBeCalledBy": ["bot-a"] },
468
+ "helper": { "canCall": [], "canBeCalledBy": ["*"] }
469
+ }
470
+ }
471
+ }
472
+ \`\`\`
473
+
474
+ ## How It Works
475
+
476
+ 1. Agent calls \`ask_agent({ target: "other-bot", message: "question" })\`
477
+ 2. Bridge validates allowlist and depth limits
478
+ 3. Creates ephemeral session for target bot with its own workspace/AGENTS.md
479
+ 4. Sends message, collects response, tears down session
480
+ 5. Returns response to calling agent
481
+
482
+ ## Tool Parameters
483
+
484
+ | Param | Required | Description |
485
+ |-------|----------|-------------|
486
+ | \`target\` | Yes | Bot name to ask |
487
+ | \`message\` | Yes | Question or request |
488
+ | \`agent\` | No | Specific agent persona |
489
+ | \`timeout\` | No | Timeout in seconds |
490
+ | \`autopilot\` | No | Auto-approve tools in target session |
491
+ | \`denyTools\` | No | Tools to deny in target session |
492
+ | \`grantTools\` | No | Tools to pre-approve |
493
+
494
+ ## Safety
495
+
496
+ - **Allowlist**: Both caller and target must be configured
497
+ - **Depth limit**: Prevents infinite chains (default: 3)
498
+ - **Visited set**: Same bot can't be called twice in a chain
499
+ - **Audit**: All calls logged to SQLite \`agent_calls\` table
500
+
501
+ ## Source
502
+ - Inter-agent logic: \`src/core/inter-agent.ts\`
503
+ - Ephemeral sessions: \`src/core/session-manager.ts\` (executeEphemeralCall)
504
+ - Docs: \`docs/configuration.md\` (Inter-agent section)`;
505
+ }
506
+ // ---------------------------------------------------------------------------
507
+ // Topic: scheduling
508
+ // ---------------------------------------------------------------------------
509
+ function topicScheduling() {
510
+ return `# Task Scheduling
511
+
512
+ The \`schedule\` tool allows creating recurring (cron) or one-off scheduled tasks.
513
+
514
+ ## How It Works
515
+
516
+ Tasks are stored in SQLite and checked by a timer. When a task fires, it sends the configured prompt to the bot's channel as if a user sent it.
517
+
518
+ ## Creating Tasks
519
+
520
+ Via the \`schedule\` tool:
521
+ - **Recurring**: \`schedule({ action: "create", prompt: "Check for updates", cron: "0 9 * * 1-5", timezone: "America/Los_Angeles" })\`
522
+ - **One-off**: \`schedule({ action: "create", prompt: "Remind me about the meeting", run_at: "2026-03-20T15:00:00Z" })\`
523
+
524
+ ## Managing Tasks
525
+
526
+ | Action | Description |
527
+ |--------|-------------|
528
+ | \`create\` | Create a new task (requires \`prompt\` + \`cron\` or \`run_at\`) |
529
+ | \`list\` | List all tasks for this channel |
530
+ | \`cancel\` | Cancel a task by ID |
531
+ | \`pause\` | Pause a recurring task |
532
+ | \`resume\` | Resume a paused task |
533
+
534
+ User command: \`/schedule\` or \`/tasks\` lists all scheduled tasks.
535
+
536
+ ## Timezone
537
+
538
+ - \`cron\` expressions are evaluated in the specified \`timezone\` (default: UTC)
539
+ - \`run_at\` is always ISO 8601 (include \`Z\` suffix for UTC)
540
+ - Display uses the task's timezone
541
+
542
+ ## Source
543
+ - Schedule tool: \`src/core/session-manager.ts\` (buildScheduleToolDef)
544
+ - Job storage/execution: \`src/core/scheduler.ts\``;
545
+ }
546
+ // ---------------------------------------------------------------------------
547
+ // Topic: providers
548
+ // ---------------------------------------------------------------------------
549
+ function topicProviders(isAdmin) {
550
+ const adminNote = isAdmin
551
+ ? `\n\n## Managing Providers (Admin)
552
+
553
+ As the admin agent, you can add/remove/edit providers in \`config.json\`:
554
+
555
+ 1. **Always back up first**: \`cp ~/.copilot-bridge/config.json ~/.copilot-bridge/config.json.bak.$(date +%s)\`
556
+ 2. Edit the \`"providers"\` key in config.json
557
+ 3. Run \`/reload config\` in the target channel to apply (no restart needed)
558
+
559
+ ### Adding a Provider
560
+ Add an entry under \`"providers"\` with: \`type\`, \`baseUrl\`, optional \`apiKeyEnv\`, and \`models\` array.
561
+
562
+ ### Removing a Provider
563
+ Delete the provider key from \`"providers"\`. Channels using that provider will fall back to Copilot on next session create.
564
+
565
+ ### Validating
566
+ After editing, the user can run \`/provider test <name>\` to verify connectivity.`
567
+ : '\n\nTo add or remove providers, ask the admin bot or edit `config.json` directly, then `/reload config`.';
568
+ return `# BYOK Providers
569
+
570
+ Bring Your Own Key (BYOK) lets you use external model providers alongside GitHub Copilot models. Supported backends: OpenAI-compatible APIs, Azure OpenAI, Ollama, vLLM, and any OpenAI-compatible endpoint.
571
+
572
+ ## Config Schema
573
+
574
+ Providers are configured under the \`"providers"\` key in \`config.json\`:
575
+
576
+ \`\`\`json
577
+ {
578
+ "providers": {
579
+ "ollama": {
580
+ "type": "openai",
581
+ "baseUrl": "http://localhost:11434/v1",
582
+ "models": [
583
+ { "id": "qwen3:8b", "name": "Qwen 3 8B" }
584
+ ]
585
+ },
586
+ "work-azure": {
587
+ "type": "azure",
588
+ "baseUrl": "https://myco.openai.azure.com",
589
+ "apiKeyEnv": "AZURE_OPENAI_KEY",
590
+ "azure": { "apiVersion": "2024-10-21" },
591
+ "models": [
592
+ { "id": "gpt-4o", "name": "GPT-4o" },
593
+ { "id": "gpt-5.2-codex", "name": "GPT-5.2 Codex", "wireApi": "responses" }
594
+ ]
595
+ }
596
+ }
597
+ }
598
+ \`\`\`
599
+
600
+ ### Provider Fields
601
+
602
+ | Field | Required | Description |
603
+ |-------|----------|-------------|
604
+ | \`type\` | no | \`"openai"\` (default, OpenAI-compatible), \`"azure"\`, or \`"anthropic"\` |
605
+ | \`baseUrl\` | yes | API endpoint URL |
606
+ | \`apiKeyEnv\` | no | Environment variable holding the API key (omit for keyless, e.g., local Ollama) |
607
+ | \`wireApi\` | no | Default wire protocol for all models: \`"completions"\` (default) or \`"responses"\`. Can be overridden per model. |
608
+ | \`azure\` | azure only | \`{ "apiVersion": "..." }\` |
609
+ | \`models\` | yes | Array of model entries. Each has \`id\` (required), \`name\`, and optional \`wireApi\` override. |
610
+
611
+ ### Wire API & Model Compatibility
612
+
613
+ \`wireApi\` can be set at provider level (applies to all models) or per model (overrides provider default).
614
+
615
+ - \`"completions"\` (Chat Completions API) — works with most models: GPT-4o, GPT-4.1, Llama, Phi, Qwen, etc.
616
+ - \`"responses"\` (Responses API) — **required** for Codex models (\`gpt-5.x-codex-*\`)
617
+ - Models must support **structured function calling** (OpenAI-compatible \`tool_calls\`). Models that emit tool calls as raw text (e.g., DeepSeek, some smaller models) will not work correctly.
618
+
619
+ ## Commands
620
+
621
+ | Command | Description |
622
+ |---------|-------------|
623
+ | \`/provider\` | List all configured providers |
624
+ | \`/provider test <name>\` | Test connectivity and validate models |
625
+ | \`/model\` | Lists all models grouped by provider |
626
+ | \`/model <provider>\` | Filter to one provider's models |
627
+ | \`/model <provider>:<model>\` | Switch to a specific provider model |
628
+
629
+ ## Model Resolution
630
+
631
+ - \`/model qwen3:8b\` — bare ID resolves Copilot first, then BYOK providers
632
+ - \`/model ollama:qwen3:8b\` — provider-prefixed ID targets that provider directly
633
+ - Provider names are case-insensitive; model IDs split on the first colon only
634
+
635
+ ## Session Behavior
636
+
637
+ - Switching between Copilot and BYOK (or between BYOK providers) creates a fresh session
638
+ - Switching models within the same provider reuses the existing session
639
+ - BYOK models are excluded from automatic fallback chains (only Copilot models auto-fallback)
640
+ - Provider config changes apply on \`/reload config\` — no bridge restart needed
641
+ ${adminNote}
642
+
643
+ ## Troubleshooting
644
+
645
+ ### Azure 404 Not Found
646
+
647
+ The SDK constructs Azure URLs as: \`{baseUrl}/openai/deployments/{model.id}/{endpoint}?api-version={apiVersion}\`
648
+
649
+ - If \`baseUrl\` contains \`/openai/\`, it's used as-is; otherwise the SDK strips to origin and appends \`/openai\`
650
+ - \`model.id\` becomes the deployment name in the URL — must match your Azure deployment exactly
651
+ - \`apiVersion\` defaults to \`"2024-10-21"\` if omitted; check Azure portal for supported versions
652
+
653
+ Common fixes:
654
+ 1. Use just the host for \`baseUrl\`: \`https://myco.openai.azure.com\` (no \`/v1\` or \`/openai/deployments/...\`)
655
+ 2. Ensure \`model.id\` matches the Azure deployment name exactly
656
+ 3. Set \`azure.apiVersion\` explicitly if the default doesn't work
657
+
658
+ ### Auth Header Mismatch
659
+
660
+ Azure uses \`api-key\` header, OpenAI uses \`Authorization: Bearer\`. Set \`type: "azure"\` for Azure endpoints — default \`"openai"\` sends the wrong header.
661
+
662
+ ### Tools Not Executing
663
+
664
+ If the agent outputs raw XML/JSON instead of running tools, the model doesn't support structured function calling. Use GPT-4o, GPT-4.1, Llama 3.3, Phi-4, or Qwen 3 instead of DeepSeek or small fine-tuned models.
665
+
666
+ ## Source
667
+ - Provider config/validation: \`src/config.ts\`
668
+ - Model resolution: \`src/core/command-handler.ts\` (resolveModel, parseProviderModel)
669
+ - Provider routing: \`src/core/session-manager.ts\` (createNewSession, switchModel)
670
+ - Docs: \`docs/byok.md\``;
671
+ }
672
+ // ---------------------------------------------------------------------------
673
+ // Topic: troubleshooting
674
+ // ---------------------------------------------------------------------------
675
+ function topicTroubleshooting(isAdmin) {
676
+ const logNote = isAdmin
677
+ ? 'Logs are at `~/.copilot-bridge/copilot-bridge.log`. Use `grep` to search for relevant errors.'
678
+ : 'Ask the user or admin to check `~/.copilot-bridge/copilot-bridge.log` for error details.';
679
+ return `# Troubleshooting
680
+
681
+ ${logNote}
682
+
683
+ ## Common Issues
684
+
685
+ ### MCP server not loading
686
+ - Check logs for MCP startup errors
687
+ - Verify env vars in workspace \`.env\`
688
+ - Run \`/reload\` after config changes
689
+ - Use \`/mcp\` to see loaded servers and their source
690
+
691
+ ### Permission prompt not appearing
692
+ - Check if \`/yolo\` or \`/autopilot\` is enabled (\`/status\` shows this)
693
+ - Check \`permissions\` config for matching allow rules
694
+ - Stored rules from \`/always approve\` may be auto-allowing
695
+
696
+ ### Model errors / fallback
697
+ - The bridge auto-falls back on capacity/availability errors
698
+ - "Failed to get response" with "Unknown error" = transient API issue (not model-specific, no fallback)
699
+ - Use \`/model\` to check current model and available alternatives
700
+
701
+ ### Stale session
702
+ - \`/new\` creates a fresh session
703
+ - \`/reload\` re-attaches the same session with updated config
704
+ - After bridge restart, sessions auto-resume
705
+
706
+ ### Context window full
707
+ - \`/context\` shows current usage
708
+ - Enable \`infiniteSessions\` in config for automatic compaction
709
+ - \`/new\` starts fresh
710
+
711
+ ### Hooks not firing
712
+ - Check hook script permissions (\`chmod +x\`)
713
+ - Workspace hooks need \`allowWorkspaceHooks: true\` in config
714
+ - Hook timeout may be too short (check \`timeoutSec\`)
715
+
716
+ ## Filing Issues
717
+
718
+ Report bugs at: https://github.com/ChrisRomp/copilot-bridge/issues
719
+
720
+ Use the repository's issue templates:
721
+ - **Bug report**: Summary, Steps to Reproduce, Expected/Actual Behavior
722
+ - **Feature request**: Summary, Motivation, Proposed Solution
723
+
724
+ Always include:
725
+ - Bridge version (\`/status\` or \`fetch_copilot_bridge_documentation({ topic: "status" })\`)
726
+ - Relevant log excerpts (redact any unique identifiers, tokens, or URLs)
727
+ - Platform (macOS/Linux) and Node.js version
728
+
729
+ ## Source
730
+ - Issue templates: \`.github/ISSUE_TEMPLATE/\`
731
+ - Model fallback: \`src/core/model-fallback.ts\`
732
+ - Loop detection: \`src/core/loop-detector.ts\``;
733
+ }
734
+ function topicStatus(ctx) {
735
+ const version = getVersion();
736
+ const config = getConfig();
737
+ const lines = ['# Bridge Status (Live)', ''];
738
+ lines.push(`- **Version**: ${version}`);
739
+ lines.push(`- **Platforms**: ${Object.keys(config.platforms).join(', ') || 'none'}`);
740
+ lines.push(`- **Log level**: ${config.logLevel ?? 'info'}`);
741
+ lines.push(`- **Infinite sessions**: ${config.infiniteSessions ? 'enabled' : 'disabled'}`);
742
+ if (ctx.model)
743
+ lines.push(`- **Current model**: ${ctx.model}`);
744
+ if (ctx.sessionId)
745
+ lines.push(`- **Session ID**: \`${ctx.sessionId}\``);
746
+ // Configured bots (from platforms)
747
+ const bots = new Set();
748
+ for (const platform of Object.values(config.platforms)) {
749
+ if (platform.bots) {
750
+ for (const name of Object.keys(platform.bots))
751
+ bots.add(name);
752
+ }
753
+ }
754
+ if (bots.size > 0) {
755
+ lines.push(`- **Configured bots**: ${[...bots].join(', ')}`);
756
+ }
757
+ lines.push('');
758
+ lines.push('## Source');
759
+ lines.push('- Config: `src/config.ts`');
760
+ lines.push('- Session manager: `src/core/session-manager.ts`');
761
+ return lines.join('\n');
762
+ }
763
+ export function getBridgeDocs(req) {
764
+ if (!req.topic) {
765
+ return `# copilot-bridge Documentation
766
+
767
+ Use this tool with a \`topic\` parameter to get focused information.
768
+
769
+ ${topicList()}`;
770
+ }
771
+ if (!isValidTopic(req.topic)) {
772
+ return `Unknown topic: "${req.topic}"\n\n${topicList()}`;
773
+ }
774
+ switch (req.topic) {
775
+ case 'overview': return topicOverview();
776
+ case 'commands': return topicCommands();
777
+ case 'config': return topicConfig(req.isAdmin);
778
+ case 'mcp': return topicMcp();
779
+ case 'permissions': return topicPermissions();
780
+ case 'workspaces': return topicWorkspaces();
781
+ case 'hooks': return topicHooks();
782
+ case 'skills': return topicSkills();
783
+ case 'inter-agent': return topicInterAgent();
784
+ case 'scheduling': return topicScheduling();
785
+ case 'providers': return topicProviders(req.isAdmin);
786
+ case 'troubleshooting': return topicTroubleshooting(req.isAdmin);
787
+ case 'status': return topicStatus({ channelId: req.channelId, model: req.model, sessionId: req.sessionId });
788
+ }
789
+ }
790
+ //# sourceMappingURL=bridge-docs.js.map