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.
- package/README.md +4 -1
- package/SOUL.md +51 -0
- package/bin/install.mjs +7 -1
- package/dist/copilot-instructions.md +15 -0
- package/dist/extension.mjs +823 -29
- package/dist/skills/tap-goal/SKILL.md +13 -2
- package/dist/skills/tap-loop/SKILL.md +6 -0
- package/dist/skills/tap-monitor/SKILL.md +19 -3
- package/dist/skills/tap-orchestrate/SKILL.md +81 -0
- package/dist/version.json +1 -1
- package/docs/adr/0001-persistent-config-default-ownership.md +33 -0
- package/docs/adr/0002-local-provider-gateway-runtime-security.md +36 -0
- package/docs/adr/0003-emitter-delivery-lifecycle.md +68 -0
- package/docs/adr/0004-persistent-config-canonical-streams.md +86 -0
- package/docs/adr/0005-provider-sdk-push-and-dynamic-tools.md +48 -0
- package/docs/adr/0006-command-emitter-cwd-workspace-boundary.md +46 -0
- package/docs/adr/0007-runtime-session-workspace-context.md +62 -0
- package/docs/evals.md +41 -0
- package/docs/evolution-of-tap-icon.html +989 -0
- package/docs/providers.md +242 -0
- package/docs/recipes/adaptive-agent.md +303 -0
- package/docs/recipes/agent-brainstorm/100-extension-ideas.md +288 -0
- package/docs/recipes/agent-brainstorm/deep-ideas.md +216 -0
- package/docs/recipes/ambient-guardian.md +314 -0
- package/docs/recipes/browser-bridge.md +162 -0
- package/docs/recipes/codex-goals-for-tap-goal.md +136 -0
- package/docs/recipes/copilot-sdk-canvas.md +147 -0
- package/docs/recipes/deferred-cognition.md +310 -0
- package/docs/recipes/provider-integration-patterns.md +93 -0
- package/docs/recipes/provider-interface-advanced.md +1364 -0
- package/docs/recipes/provider-interface-core-profile.md +568 -0
- package/docs/recipes/tap-control-plane-roadmap.md +60 -0
- package/docs/recipes/universal-tool-gateway.md +202 -0
- package/docs/reference.md +229 -0
- package/docs/use-cases.md +348 -0
- package/package.json +4 -1
- package/providers/detour/README.md +84 -0
- package/providers/detour/bridge.js +219 -0
- package/providers/detour/index.mjs +322 -0
- package/providers/detour/package-lock.json +577 -0
- package/providers/detour/package.json +19 -0
- package/providers/detour/scripts/build.mjs +31 -0
- package/providers/detour/src/bridge.js +256 -0
- package/providers/detour/src/contracts.js +40 -0
- package/providers/detour/src/inspector.js +260 -0
- package/providers/detour/src/inspector.test.mjs +53 -0
- package/providers/detour/src/panel.js +465 -0
- package/providers/detour/src/provider-core.js +233 -0
- package/providers/detour/src/provider-core.test.mjs +185 -0
- package/providers/detour/src/react-context-core.js +143 -0
- package/providers/detour/src/react-context.js +44 -0
- package/providers/detour/src/react-context.test.mjs +41 -0
- package/providers/templates/README.md +23 -0
- package/providers/templates/ci-review-provider.mjs +46 -0
- package/providers/templates/detour-workflow-provider.mjs +41 -0
- package/providers/templates/jira-github-provider.mjs +42 -0
- package/providers/templates/provider-utils.mjs +45 -0
- 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.
|