copilot-tap-extension 2.0.7 → 2.0.9

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 (58) hide show
  1. package/README.md +4 -1
  2. package/SOUL.md +51 -0
  3. package/bin/install.mjs +7 -1
  4. package/dist/copilot-instructions.md +15 -0
  5. package/dist/extension.mjs +823 -29
  6. package/dist/skills/tap-goal/SKILL.md +13 -2
  7. package/dist/skills/tap-loop/SKILL.md +6 -0
  8. package/dist/skills/tap-monitor/SKILL.md +19 -3
  9. package/dist/skills/tap-orchestrate/SKILL.md +81 -0
  10. package/dist/version.json +1 -1
  11. package/docs/adr/0001-persistent-config-default-ownership.md +33 -0
  12. package/docs/adr/0002-local-provider-gateway-runtime-security.md +36 -0
  13. package/docs/adr/0003-emitter-delivery-lifecycle.md +68 -0
  14. package/docs/adr/0004-persistent-config-canonical-streams.md +86 -0
  15. package/docs/adr/0005-provider-sdk-push-and-dynamic-tools.md +48 -0
  16. package/docs/adr/0006-command-emitter-cwd-workspace-boundary.md +46 -0
  17. package/docs/adr/0007-runtime-session-workspace-context.md +62 -0
  18. package/docs/evals.md +41 -0
  19. package/docs/evolution-of-tap-icon.html +989 -0
  20. package/docs/providers.md +242 -0
  21. package/docs/recipes/adaptive-agent.md +303 -0
  22. package/docs/recipes/agent-brainstorm/100-extension-ideas.md +288 -0
  23. package/docs/recipes/agent-brainstorm/deep-ideas.md +216 -0
  24. package/docs/recipes/ambient-guardian.md +314 -0
  25. package/docs/recipes/browser-bridge.md +162 -0
  26. package/docs/recipes/codex-goals-for-tap-goal.md +136 -0
  27. package/docs/recipes/copilot-sdk-canvas.md +147 -0
  28. package/docs/recipes/deferred-cognition.md +310 -0
  29. package/docs/recipes/provider-integration-patterns.md +93 -0
  30. package/docs/recipes/provider-interface-advanced.md +1364 -0
  31. package/docs/recipes/provider-interface-core-profile.md +568 -0
  32. package/docs/recipes/tap-control-plane-roadmap.md +60 -0
  33. package/docs/recipes/universal-tool-gateway.md +202 -0
  34. package/docs/reference.md +229 -0
  35. package/docs/use-cases.md +348 -0
  36. package/package.json +4 -1
  37. package/providers/detour/README.md +84 -0
  38. package/providers/detour/bridge.js +219 -0
  39. package/providers/detour/index.mjs +322 -0
  40. package/providers/detour/package-lock.json +577 -0
  41. package/providers/detour/package.json +19 -0
  42. package/providers/detour/scripts/build.mjs +31 -0
  43. package/providers/detour/src/bridge.js +256 -0
  44. package/providers/detour/src/contracts.js +40 -0
  45. package/providers/detour/src/inspector.js +260 -0
  46. package/providers/detour/src/inspector.test.mjs +53 -0
  47. package/providers/detour/src/panel.js +465 -0
  48. package/providers/detour/src/provider-core.js +233 -0
  49. package/providers/detour/src/provider-core.test.mjs +185 -0
  50. package/providers/detour/src/react-context-core.js +143 -0
  51. package/providers/detour/src/react-context.js +44 -0
  52. package/providers/detour/src/react-context.test.mjs +41 -0
  53. package/providers/templates/README.md +23 -0
  54. package/providers/templates/ci-review-provider.mjs +46 -0
  55. package/providers/templates/detour-workflow-provider.mjs +41 -0
  56. package/providers/templates/jira-github-provider.mjs +42 -0
  57. package/providers/templates/provider-utils.mjs +45 -0
  58. package/providers/templates/sast-triage-provider.mjs +51 -0
@@ -0,0 +1,202 @@
1
+ # Recipe: Universal Tool Gateway — Dynamic Capabilities via Script Injection
2
+
3
+ ## The insight
4
+
5
+ Detour injects arbitrary JS into any web page. `session.registerTools()` adds tools to Copilot at runtime. Combine them: **any script, in any environment, can expose capabilities to Copilot dynamically.**
6
+
7
+ Instead of building separate extensions with hardcoded tools, tap becomes a **runtime tool gateway**. Connected clients announce tool definitions over WebSocket, tap materializes them in the Copilot session. When clients disconnect, their tools vanish.
8
+
9
+ ## Architecture
10
+
11
+ ```
12
+ ┌─────────────────────────────┐
13
+ │ Copilot CLI session │
14
+ │ │
15
+ │ ※ tap (tool gateway) │
16
+ │ session.registerTools(...) │
17
+ └──────────┬──────────────────┘
18
+ │ ws
19
+ ┌──────────▼──────────────────┐
20
+ │ Bridge Server │
21
+ │ ws://localhost:9400 │
22
+ └──┬──────┬──────┬──────┬─────┘
23
+ │ │ │ │
24
+ ┌──────▼┐ ┌───▼───┐ ┌▼────┐ ┌▼──────────┐
25
+ │Browser│ │Electron│ │Node │ │Any process │
26
+ │(Detour)│ │ app │ │script│ │with WS │
27
+ └───────┘ └───────┘ └─────┘ └───────────┘
28
+ ```
29
+
30
+ Every connected client is just a script that:
31
+ 1. Opens a WebSocket to the bridge
32
+ 2. Sends a `hello` with its tool definitions
33
+ 3. Handles tool invocations when Copilot calls them
34
+ 4. Optionally pushes events (→ tap emitter → Copilot session)
35
+
36
+ ## How tool definitions travel over the wire
37
+
38
+ ### Client announces capabilities
39
+
40
+ ```json
41
+ {
42
+ "type": "hello",
43
+ "role": "provider",
44
+ "name": "my-app",
45
+ "tools": [
46
+ {
47
+ "name": "app_get_user",
48
+ "description": "Get the currently logged-in user from the app",
49
+ "parameters": {
50
+ "type": "object",
51
+ "properties": {}
52
+ }
53
+ },
54
+ {
55
+ "name": "app_search",
56
+ "description": "Search the app's data",
57
+ "parameters": {
58
+ "type": "object",
59
+ "properties": {
60
+ "query": { "type": "string" }
61
+ },
62
+ "required": ["query"]
63
+ }
64
+ }
65
+ ]
66
+ }
67
+ ```
68
+
69
+ ### tap materializes tools in Copilot
70
+
71
+ When a provider connects, tap:
72
+ 1. Reads the tool definitions from the `hello` message
73
+ 2. Creates handler functions that route calls through the bridge
74
+ 3. Calls `session.registerTools([...existingTools, ...newTools])`
75
+ 4. Copilot immediately sees the new tools
76
+
77
+ When a provider disconnects, tap re-registers without those tools.
78
+
79
+ ### Copilot calls a dynamic tool
80
+
81
+ ```
82
+ User: "who is logged in?"
83
+
84
+ Copilot calls: app_get_user({})
85
+ → tap routes to bridge
86
+ → bridge routes to "my-app" provider
87
+ → provider executes, returns { user: "alice" }
88
+ → bridge routes response back
89
+ → tap returns result to Copilot
90
+
91
+ Copilot: "The logged-in user is alice."
92
+ ```
93
+
94
+ ## What this enables
95
+
96
+ ### Browser pages (via Detour)
97
+
98
+ Inject a bridge client script into any web page. The script announces tools based on what's available in that page's context.
99
+
100
+ ```js
101
+ // Injected into a React app — announces React-specific tools
102
+ bridge.announce({
103
+ tools: [
104
+ { name: "react_component_tree", description: "Get the React component tree" },
105
+ { name: "react_state", description: "Read React state for a component" },
106
+ { name: "page_screenshot", description: "Screenshot the viewport" }
107
+ ]
108
+ });
109
+ ```
110
+
111
+ ### Electron / desktop apps
112
+
113
+ Any Electron app can include a bridge client. Copilot gets tools to read app state, trigger actions, inspect UI.
114
+
115
+ ### Internal tools and dashboards
116
+
117
+ Inject a bridge client into your company's admin dashboard. Copilot can now query production data, check deployment status, read monitoring dashboards — all through dynamically registered tools.
118
+
119
+ ### CI/CD and DevOps
120
+
121
+ A bridge client in your CI pipeline announces build/deploy tools. Copilot can trigger deploys, check build status, read test results.
122
+
123
+ ### Other terminal processes
124
+
125
+ A Node script or Python process connects to the bridge and exposes domain-specific tools. A data pipeline announces query tools. A test runner announces result inspection tools.
126
+
127
+ ## Protocol
128
+
129
+ ### Messages
130
+
131
+ | Type | Direction | Purpose |
132
+ |---|---|---|
133
+ | `hello` | provider → bridge | Announce name + tool definitions |
134
+ | `tools.update` | provider → bridge | Update tool definitions (add/remove) |
135
+ | `tool.call` | bridge → provider | Copilot invoked a tool, execute it |
136
+ | `tool.result` | provider → bridge | Return tool execution result |
137
+ | `push` | provider → bridge | Unsolicited event (→ tap emitter) |
138
+ | `goodbye` | provider → bridge | Graceful disconnect, remove tools |
139
+
140
+ ### Tool call flow
141
+
142
+ ```
143
+ Copilot → tap handler → bridge.request("tool.call", { provider, tool, args })
144
+ → bridge routes to provider
145
+ → provider executes
146
+ → provider sends tool.result
147
+ → bridge routes back
148
+ → tap handler returns result to Copilot
149
+ ```
150
+
151
+ ## The bridge client SDK
152
+
153
+ A tiny JS module (~50 lines) that any script includes to become a provider:
154
+
155
+ ```js
156
+ import { createBridgeClient } from "copilot-bridge-client";
157
+
158
+ const bridge = createBridgeClient({
159
+ url: "ws://localhost:9400",
160
+ name: "my-app"
161
+ });
162
+
163
+ bridge.tool("get_user", "Get the current user", {}, async () => {
164
+ return { user: getCurrentUser() };
165
+ });
166
+
167
+ bridge.tool("search", "Search data", {
168
+ query: { type: "string" }
169
+ }, async ({ query }) => {
170
+ return await searchDatabase(query);
171
+ });
172
+
173
+ bridge.connect();
174
+ ```
175
+
176
+ That's it. Copilot now has `get_user` and `search` tools.
177
+
178
+ ## Implications
179
+
180
+ 1. **Extensions become optional.** Instead of packaging tools into a Copilot extension, any running process can expose tools dynamically.
181
+ 2. **Capabilities are composable.** Open a React app in Chrome → React tools appear. Start a deploy script → deploy tools appear. Tools come and go based on what's running.
182
+ 3. **Skills become injectable.** A provider can also send context/instructions along with tools, giving Copilot domain knowledge about how to use them.
183
+ 4. **tap becomes a tool multiplexer.** Its core job shifts from "emitter runtime" to "dynamic tool gateway that routes between Copilot and any connected service."
184
+
185
+ ## Relationship to MCP
186
+
187
+ This is the inverse of MCP (Model Context Protocol):
188
+ - **MCP**: Copilot connects to external servers that expose tools
189
+ - **Bridge**: External services connect to Copilot (via tap) and expose tools
190
+
191
+ The bridge pattern is more dynamic — tools appear and disappear based on runtime state. And the provider can be anything that runs JS (browser tab, Electron, Node, etc.) with zero infrastructure beyond the bridge relay.
192
+
193
+ ## Phased delivery
194
+
195
+ | Phase | Scope |
196
+ |---|---|
197
+ | **1. Bridge server + protocol** | WebSocket relay, hello/tool.call/tool.result messages |
198
+ | **2. tap integration** | Dynamic registerTools on provider connect/disconnect |
199
+ | **3. Bridge client SDK** | Tiny JS module for providers to include |
200
+ | **4. Detour recipe** | Example: inject bridge client into a web page via Detour |
201
+ | **5. Push events** | Provider → tap emitter pipeline for unsolicited events |
202
+ | **6. Context injection** | Providers send instructions/context alongside tools |
@@ -0,0 +1,229 @@
1
+ # Reference
2
+
3
+ Technical reference for copilot-tap-extension. For a quick overview, see the [README](../README.md).
4
+
5
+ ## Canonical vocabulary
6
+
7
+ These terms are used consistently across the code, tool descriptions, and config schema.
8
+
9
+ | Term | Meaning |
10
+ | --- | --- |
11
+ | `emitter` | A running background unit (CommandEmitter or PromptEmitter) |
12
+ | `stream` | Named EventStream storing accepted emitter output and notes |
13
+ | `sessionInjector` | Per-stream proactive delivery state |
14
+ | `eventFilter` | Ordered rule list on the emitter: `[{ match, outcome }]` |
15
+ | `emitterType` | Work source: `command` or `prompt` |
16
+ | `lifespan` | `temporary` (session-only) or `persistent` (written to config) |
17
+ | `ownership` | `userOwned` (protected) or `modelOwned` (agent can tune) |
18
+ | `runSchedule` | `continuous`, `timed`, or `oneTime` |
19
+ | `runInterval` | Repeat interval for timed work (e.g. `5m`, `2h`) |
20
+ | `autoStart` | Whether a persistent emitter starts automatically next session |
21
+
22
+ ## Emitter types
23
+
24
+ | Type | Definition | Best for |
25
+ | --- | --- | --- |
26
+ | **Continuous Command** | `command` only | Log tails, watch scripts, long-running jobs |
27
+ | **Timed Command** | `command` + `runInterval` | API polling, recurring validators |
28
+ | **One-shot Prompt** | `prompt` only | Background agent check (runs once) |
29
+ | **Timed Prompt** | `prompt` + `runInterval` | `/tap-loop`-style maintenance tasks |
30
+
31
+ ## Event outcomes
32
+
33
+ Each EventFilter rule maps a regex to one of four outcomes (first match wins):
34
+
35
+ | Outcome | Behavior |
36
+ | --- | --- |
37
+ | `drop` | Discard — does not enter the EventStream |
38
+ | `keep` | Store in the EventStream |
39
+ | `surface` | Keep + show in session timeline via `session.log()` |
40
+ | `inject` | Keep + surface + inject into Copilot via `session.send()` |
41
+
42
+ PromptEmitter events always inject (bypass the EventFilter).
43
+
44
+ ## Event processing pipeline
45
+
46
+ ```
47
+ Emitter output
48
+ |
49
+ v
50
+ EventFilter (first match wins)
51
+ |-- drop → discarded
52
+ |-- keep → stored in EventStream
53
+ |-- surface → stored + shown in timeline
54
+ +-- inject → stored + shown + injected into session
55
+ |
56
+ +-- no match → default: keep
57
+
58
+ PromptEmitter events always inject (bypass EventFilter)
59
+ ```
60
+
61
+ Start with no filter rules (keep-all bootstrap), observe the stream with `tap_stream_history`, then progressively add rules to drop noise and inject signal.
62
+
63
+ ## Tools
64
+
65
+ | Tool | What it does |
66
+ | --- | --- |
67
+ | `tap_start_emitter` | Start a background emitter (command, prompt, or timed) |
68
+ | `tap_stop_emitter` | Stop an emitter |
69
+ | `tap_list_emitters` | Show all running + persistent emitters |
70
+ | `tap_set_event_filter` | Update filter rules on a running emitter |
71
+ | `tap_stream_history` | Read recent EventStream entries |
72
+ | `tap_post` | Append a note to any EventStream |
73
+ | `tap_list_streams` | List all EventStreams + injector state |
74
+ | `tap_enable_injector` | Enable proactive session delivery |
75
+ | `tap_disable_injector` | Disable proactive delivery |
76
+ | `tap_open_diagnostics_canvas` | Open/focus the live diagnostics canvas |
77
+ | `tap_get_session_state` | Read current Copilot mode/model/tasks/schedules/canvas state |
78
+ | `tap_set_session_mode` | Guarded session mode switch with explicit confirmation |
79
+ | `tap_query_records` | Read structured tap records persisted in the session workspace |
80
+ | `tap_verify_goal_output` | Verify goal evidence from files, streams, or caller-supplied command evidence |
81
+ | `tap_audit_claims` | Audit structured goal claims against concrete evidence surfaces |
82
+
83
+ ## Diagnostics canvas
84
+
85
+ Tap declares an experimental Copilot SDK canvas named `tap-diagnostics`. Open it with:
86
+
87
+ ```text
88
+ tap_open_diagnostics_canvas
89
+ ```
90
+
91
+ The canvas is a bounded local flight recorder for:
92
+
93
+ - EventStreams and recent entries
94
+ - running and configured emitters
95
+ - EventFilter and SessionInjector state
96
+ - provider gateway state and registered tools
97
+ - notification queue state
98
+ - emitter-run traces and goal ledger streams
99
+ - current eval/control-plane evidence when available
100
+ - tap runtime logs
101
+ - recent SDK session events
102
+
103
+ The renderer is served from a loopback-only ephemeral HTTP server and is closed when the canvas instance closes. Sensitive gateway token values are redacted, and retained event payloads are bounded so the canvas stays responsive.
104
+
105
+ ## Loop semantics
106
+
107
+ The extension supports session-scoped timed schedules:
108
+
109
+ - `runInterval: "5m"` re-runs work on a fixed interval
110
+ - Commands with `runInterval` re-run after each completion
111
+ - Timed PromptEmitters fire immediately, then repeat on the interval
112
+ - Prompts without `runInterval` run once
113
+ - Timed schedules do not catch up missed runs
114
+ - If a timed PromptEmitter fires while the session is busy, that run defers to the next interval
115
+ - Persistent config restores the emitter next session, but this is not a durable cloud scheduler
116
+
117
+ The repo also ships a **`tap-loop` skill** (`src/skills/tap-loop`) for quick setup:
118
+
119
+ ```text
120
+ /tap-loop 5m check the deploy
121
+ ```
122
+
123
+ For long-horizon work, the repo also ships a **`tap-goal` skill**
124
+ (`src/skills/tap-goal`). A goal is explicit, status/control commands are
125
+ user-owned, and completion should only be reported when the objective is
126
+ actually achieved. In ※ tap it is implemented as a PromptEmitter with a required
127
+ iteration budget. Conservative goals use `every="idle"`; autopilot-compatible
128
+ goals use a timed backoff schedule such as `everySchedule=["2m","5m","10m"]` so
129
+ the objective can keep nudging the session while Copilot may stay busy.
130
+
131
+ A strong tap goal follows the Codex-style goal contract: outcome, verification
132
+ surface, constraints, boundaries, iteration policy, and blocked stop condition.
133
+ Each iteration self-steers by reading its own emitter state, posts a structured
134
+ iteration record to its EventStream, validates against concrete evidence when
135
+ relevant, and shifts into wrap-up mode when the remaining budget is low. Budget
136
+ exhaustion is a handoff state, not success:
137
+
138
+ ```text
139
+ /tap-goal migrate the repo to the new API and keep going until tests pass
140
+ ```
141
+
142
+ ## Ownership model
143
+
144
+ Ownership lives on the EventEmitter only. EventStream and SessionInjector are derived.
145
+
146
+ - **`userOwned`** — protected; the model must pass `transferOwnership=true` to override
147
+ - **`modelOwned`** — safe for the model to adjust during the session
148
+
149
+ ## Usage patterns
150
+
151
+ ### Watch something temporarily
152
+
153
+ ```
154
+ lifespan="temporary"
155
+ subscribe=true
156
+ EventFilter: [{ "match": "error|warning|ready", "outcome": "inject" }, { "match": ".*", "outcome": "drop" }]
157
+ ```
158
+
159
+ ### Persistent deploy watcher
160
+
161
+ ```
162
+ lifespan="persistent"
163
+ autoStart=true
164
+ ownership="userOwned"
165
+ ```
166
+
167
+ ### Let the model tune noise
168
+
169
+ Use `ownership="modelOwned"` on a temporary emitter so the agent can adjust the EventFilter during the task.
170
+
171
+ ## Example config
172
+
173
+ ```json
174
+ {
175
+ "streams": [
176
+ {
177
+ "name": "ops",
178
+ "description": "Operational events from background emitters",
179
+ "sessionInjector": {
180
+ "enabled": true,
181
+ "delivery": "important",
182
+ "ownership": "userOwned"
183
+ }
184
+ },
185
+ {
186
+ "name": "repo-maintenance",
187
+ "description": "Prompt-based maintenance loop",
188
+ "sessionInjector": {
189
+ "enabled": true,
190
+ "delivery": "important",
191
+ "ownership": "userOwned"
192
+ }
193
+ }
194
+ ],
195
+ "emitters": [
196
+ {
197
+ "name": "heartbeat",
198
+ "description": "Demo background status stream",
199
+ "command": "node ./examples/heartbeat.mjs",
200
+ "stream": "ops",
201
+ "autoStart": true,
202
+ "includeStderr": true,
203
+ "ownership": "userOwned",
204
+ "eventFilter": [
205
+ { "match": "booting", "outcome": "drop" },
206
+ { "match": "ready|healthy", "outcome": "surface" },
207
+ { "match": "warning|error", "outcome": "inject" },
208
+ { "match": ".*", "outcome": "keep" }
209
+ ]
210
+ },
211
+ {
212
+ "name": "repo-maintenance",
213
+ "description": "Prompt-based loop for repo health checks",
214
+ "prompt": "Check whether there are new failing runs, PR review comments, or issue escalations worth addressing. Summarize only actionable changes.",
215
+ "runInterval": "15m",
216
+ "stream": "repo-maintenance",
217
+ "autoStart": false,
218
+ "ownership": "userOwned"
219
+ }
220
+ ]
221
+ }
222
+ ```
223
+
224
+ ## Limits
225
+
226
+ - Extension-level approximation, not a native runtime primitive.
227
+ - In-memory state resets on `/clear`.
228
+ - Notifications are batched to reduce spam.
229
+ - Emitters run with the extension's trust level — only run commands you trust.