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,147 @@
1
+ # Copilot SDK canvas surfaces
2
+
3
+ These notes reflect the canvas API surface found in the local Copilot CLI 1.0.61 SDK. The canvas APIs are marked experimental in the SDK types, so treat exact names and runtime behavior as subject to change.
4
+
5
+ ## What a canvas is
6
+
7
+ A canvas is an extension-owned UI surface declared through the Copilot SDK:
8
+
9
+ ```js
10
+ import { joinSession, createCanvas } from "@github/copilot-sdk/extension";
11
+
12
+ await joinSession({
13
+ canvases: [
14
+ createCanvas({
15
+ id: "tap-dashboard",
16
+ displayName: "Tap Dashboard",
17
+ description: "Inspect live tap streams and emitter state.",
18
+ open: async (ctx) => ({
19
+ title: "Tap Dashboard",
20
+ status: `Instance ${ctx.instanceId}`,
21
+ url: "http://127.0.0.1:5173/",
22
+ }),
23
+ }),
24
+ ],
25
+ });
26
+ ```
27
+
28
+ The extension process is the live provider for that canvas. The runtime routes `canvas.open`, `canvas.close`, and `canvas.action.invoke` requests back into the SDK process, and the SDK dispatches them to the handlers bound by `createCanvas`.
29
+
30
+ Use canvases when a workflow needs a persistent visual panel, inspector, or control surface instead of plain text tool output. Examples that fit tap well: stream dashboards, emitter graphs, PR-review status boards, browser-debug panels, and interactive incident timelines.
31
+
32
+ ## Declaration shape
33
+
34
+ `createCanvas(options)` returns a `Canvas` that can be passed in `joinSession({ canvases })`.
35
+
36
+ | Field | Required | Notes |
37
+ | --- | --- | --- |
38
+ | `id` | yes | Provider-local canvas id, unique within the declaring extension connection. |
39
+ | `displayName` | yes | Human-readable name shown in discovery/UI chrome. |
40
+ | `description` | yes | Short single-sentence description shown to the agent. |
41
+ | `inputSchema` | no | JSON Schema for the `open` input payload. |
42
+ | `actions` | no | Agent/host-invocable actions for an open instance. |
43
+ | `open(ctx)` | yes | Called when the host or agent opens/focuses an instance. |
44
+ | `onClose(ctx)` | no | Called when an instance is closed; use it to release resources. |
45
+
46
+ Action names are unique within the canvas and must not start with `canvas.` because that prefix is reserved for lifecycle verbs.
47
+
48
+ ## Open lifecycle
49
+
50
+ The host or agent opens a canvas with a `canvasId`, stable caller-supplied `instanceId`, and optional `extensionId` when multiple providers declare the same `canvasId`. The SDK calls:
51
+
52
+ ```js
53
+ open: async ({ sessionId, extensionId, canvasId, instanceId, input, host }) => {
54
+ return {
55
+ title: "Rendered title",
56
+ status: "ready",
57
+ url: "http://127.0.0.1:49152/",
58
+ };
59
+ }
60
+ ```
61
+
62
+ The response may include:
63
+
64
+ | Field | Meaning |
65
+ | --- | --- |
66
+ | `url` | Web renderer URL for the host to render. Optional for native canvases. |
67
+ | `title` | Title shown in host chrome. |
68
+ | `status` | Provider-supplied status text shown in host chrome. |
69
+
70
+ Re-opening the same `instanceId` is idempotent and focuses/reuses the existing panel. Open snapshots and `session.canvas.opened` events include `reopen: true` when the notification represents such a reopen.
71
+
72
+ The CLI canvas scaffold uses a practical default for web canvases: start a loopback HTTP server on port `0`, let the OS choose a free ephemeral port, keep one server per `instanceId`, and close that server from `onClose` so ports do not leak.
73
+
74
+ ## Actions
75
+
76
+ Canvas actions let the agent or host interact with an already-open instance:
77
+
78
+ ```js
79
+ createCanvas({
80
+ id: "tap-dashboard",
81
+ displayName: "Tap Dashboard",
82
+ description: "Inspect live tap streams and emitter state.",
83
+ actions: [
84
+ {
85
+ name: "refresh_stream",
86
+ description: "Refresh the stream snapshot shown in the canvas.",
87
+ inputSchema: {
88
+ type: "object",
89
+ properties: {
90
+ limit: { type: "integer", minimum: 1, maximum: 100 },
91
+ },
92
+ },
93
+ handler: async ({ instanceId, input }) => {
94
+ return { ok: true, instanceId, limit: input?.limit ?? 25 };
95
+ },
96
+ },
97
+ ],
98
+ open: async () => ({ title: "Tap Dashboard", url: "http://127.0.0.1:5173/" }),
99
+ });
100
+ ```
101
+
102
+ At the model/tool layer, actions are discovered through `list_canvas_capabilities` and invoked through `invoke_canvas_action`. At the SDK RPC layer, renderer-capable clients can call `session.rpc.canvas.invokeAction(...)`.
103
+
104
+ ## Renderer and RPC APIs
105
+
106
+ The SDK exposes an experimental `session.rpc.canvas` API for SDK clients that can render or coordinate canvases:
107
+
108
+ | Method | Purpose |
109
+ | --- | --- |
110
+ | `list()` | List canvas declarations available in the session. |
111
+ | `listOpen()` | List currently open canvas instances. |
112
+ | `open({ canvasId, instanceId, input, extensionId? })` | Open or focus an instance. |
113
+ | `close({ instanceId })` | Close an open instance. |
114
+ | `invokeAction({ instanceId, actionName, input })` | Invoke an action on an open instance. |
115
+
116
+ Set `requestCanvasRenderer: true` only for SDK session clients that can display canvases. That opt-in surfaces the model tools `list_canvas_capabilities`, `open_canvas`, and `invoke_canvas_action`. Extension canvases generated by `extensions_manage({ kind: "canvas" })` only declare `canvases`; they do not set `requestCanvasRenderer`.
117
+
118
+ For resumed SDK sessions, `openCanvases` can be supplied with the prior open-instance snapshot so the runtime can rehydrate canvas state without re-opening everything manually.
119
+
120
+ ## Events and capability signals
121
+
122
+ Canvas-related session events are transient (`ephemeral: true`):
123
+
124
+ | Event | Key payload |
125
+ | --- | --- |
126
+ | `capabilities.changed` | `data.ui.canvases` indicates whether canvas rendering is supported. |
127
+ | `session.canvas.registry_changed` | Current canvas declarations: ids, display names, descriptions, input schemas, and actions. |
128
+ | `session.canvas.opened` | Open instance snapshot: `instanceId`, `canvasId`, `extensionId`, `url`, `title`, `status`, `input`, `reopen`, and `availability`. |
129
+
130
+ Open instances report `availability: "ready"` while the provider connection is live and `"stale"` if the provider has gone away. In stale/unavailable cases, action routing can fail until the agent re-opens the canvas or the provider reconnects.
131
+
132
+ ## Tap integration notes
133
+
134
+ Tap's external provider protocol is intentionally not the Copilot SDK. External tap providers can register tools, update tools, and push/keep/surface/inject events, but they cannot declare Copilot SDK canvases over the WebSocket provider protocol today.
135
+
136
+ To add a canvas-backed tap experience, implement the canvas in the tap extension layer with `createCanvas`, or add an explicit gateway/protocol extension that maps provider UI declarations into SDK canvases. Keep provider-side browser or local services behind loopback URLs and pass only the URL/title/status through the canvas `open` response.
137
+
138
+ Tap now includes a built-in example: the `tap-diagnostics` canvas. It is declared by the extension and can be opened with `tap_open_diagnostics_canvas`. The canvas serves a loopback-only renderer and streams bounded diagnostics snapshots over SSE.
139
+
140
+ ## Best practices
141
+
142
+ 1. Keep renderer servers on loopback (`127.0.0.1`) and use ephemeral ports unless the port is user-configured.
143
+ 2. Key per-instance resources by `instanceId` and release them in `onClose`.
144
+ 3. Validate `open` and action inputs with JSON Schema; avoid broad casts or unstructured payloads.
145
+ 4. Keep action names stable, descriptive, and free of the reserved `canvas.` prefix.
146
+ 5. Treat all canvas APIs as experimental and guard optional host capabilities such as `host?.capabilities?.canvases`.
147
+ 6. Use `session.log()` for extension diagnostics; do not write diagnostics to stdout because stdout is reserved for JSON-RPC.
@@ -0,0 +1,310 @@
1
+ # Recipe: Deferred Cognition — The AI Schedules Work for Its Future Self
2
+
3
+ ## The insight
4
+
5
+ You're investigating a problem. You hit a wall — the data you need doesn't exist yet (logs rotate at midnight, a deploy hasn't finished, a test suite is running). Today you'd set a calendar reminder, context-switch, and come back later having forgotten half of what you figured out.
6
+
7
+ Deferred cognition means the AI captures its current investigation state — hypothesis, evidence gathered, what was ruled out, what to check next — and creates a persistent PromptEmitter that fires on the next session start. When you open Copilot tomorrow, it picks up mid-thought.
8
+
9
+ This isn't session resumption (which replays the conversation). It's the AI **deliberately planning future work**, with a specific prompt about what to do when it wakes up.
10
+
11
+ ## Why skills can't do this
12
+
13
+ 1. A skill can't write itself. Deferred cognition generates a prompt at runtime based on the current investigation state.
14
+ 2. A skill can't schedule itself. The persistent PromptEmitter fires automatically on next session start.
15
+ 3. A skill doesn't carry state. The deferred prompt includes specific hypothesis, file paths, line numbers, evidence collected — all from the current session.
16
+ 4. A skill can't span sessions. This bridges the gap between "what I know now" and "what I need to do later."
17
+
18
+ ## Architecture
19
+
20
+ ```
21
+ Session N (today)
22
+
23
+
24
+ You say: "check the rotated logs tomorrow and continue"
25
+
26
+
27
+ tap captures current investigation state:
28
+ • Files examined (from onPostToolUse history)
29
+ • Hypothesis (from assistant.message history)
30
+ • What was ruled out (from conversation)
31
+ • What to check next (from your instruction)
32
+
33
+
34
+ Creates persistent PromptEmitter:
35
+ name: "deferred-investigation-auth-timeout"
36
+ schedule: oneTime (fires once on next session start)
37
+ autoStart: true
38
+ prompt: <generated investigation prompt>
39
+
40
+
41
+ Saved to tap.config.json
42
+
43
+ ═══════════════════════════════════════════════
44
+
45
+ Session N+1 (tomorrow)
46
+
47
+
48
+ onSessionStart → persistent emitters auto-start
49
+
50
+
51
+ PromptEmitter fires immediately:
52
+ "Continue investigating the auth-timeout issue from yesterday.
53
+
54
+ State from previous session:
55
+ - Hypothesis: Connection pool exhaustion in auth-service
56
+ - Evidence: Error rate correlates with batch job at 23:45
57
+ - Checked: src/auth/pool.ts (pool size is 10, seems low)
58
+ - Ruled out: DNS resolution (dig shows normal latency)
59
+ - Next step: Check the rotated logs at /var/log/auth/
60
+ for connection refused errors between 23:45-00:15
61
+ - Also check: Has the batch job's connection count changed
62
+ since PR #289 merged last Tuesday?
63
+
64
+ Read the relevant files, check the logs, and report
65
+ what you find."
66
+
67
+
68
+ Copilot picks up the investigation mid-thought.
69
+ The PromptEmitter is oneTime → auto-removes after firing.
70
+ ```
71
+
72
+ ## Components
73
+
74
+ ### 1. State capture tool
75
+
76
+ A new tap tool that captures the current investigation state:
77
+
78
+ ```js
79
+ {
80
+ name: "tap_defer",
81
+ description:
82
+ "Defer work to a future session. Captures the current investigation " +
83
+ "state and creates a persistent prompt that fires on next session start. " +
84
+ "Use when the user wants to continue later, wait for something, or " +
85
+ "schedule future work.",
86
+ parameters: {
87
+ type: "object",
88
+ properties: {
89
+ what: {
90
+ type: "string",
91
+ description:
92
+ "What to do in the future session — the specific task or check"
93
+ },
94
+ when: {
95
+ type: "string",
96
+ description:
97
+ "When to fire: 'next-session' (default), or a delay like '6h', '1d'"
98
+ }
99
+ },
100
+ required: ["what"]
101
+ },
102
+ handler: async ({ what, when }, invocation) => {
103
+ // Gather context from the current session
104
+ const sessionHistory = await session.getMessages();
105
+ const recentFiles = extractRecentFiles(sessionHistory);
106
+ const hypothesis = extractHypothesis(sessionHistory);
107
+ const ruledOut = extractRuledOut(sessionHistory);
108
+
109
+ // Generate the deferred prompt
110
+ const deferredPrompt = await generateDeferredPrompt({
111
+ task: what,
112
+ files: recentFiles,
113
+ hypothesis,
114
+ ruledOut,
115
+ sessionTranscriptSummary: summarizeRecent(sessionHistory, 20)
116
+ });
117
+
118
+ // Create a persistent one-time emitter
119
+ const emitterName = `deferred-${normalizeName(what).slice(0, 30)}`;
120
+ await supervisor.start({
121
+ name: emitterName,
122
+ prompt: deferredPrompt,
123
+ scope: "persistent",
124
+ runSchedule: "oneTime",
125
+ autoStart: true,
126
+ managedBy: "modelOwned"
127
+ });
128
+
129
+ return `Deferred to ${when || "next session"}: "${what}"\n` +
130
+ `Emitter '${emitterName}' will fire on next session start ` +
131
+ `with full investigation context.`;
132
+ }
133
+ }
134
+ ```
135
+
136
+ ### 2. Prompt generation
137
+
138
+ The deferred prompt is generated by a PromptEmitter that summarizes the current session:
139
+
140
+ ```js
141
+ async function generateDeferredPrompt({ task, files, hypothesis, ruledOut, sessionTranscriptSummary }) {
142
+ const prompt = `Continue this investigation from a previous session.
143
+
144
+ ## Task
145
+ ${task}
146
+
147
+ ## State from previous session
148
+ ${hypothesis ? `- Hypothesis: ${hypothesis}` : ""}
149
+ ${files.length ? `- Files examined: ${files.join(", ")}` : ""}
150
+ ${ruledOut.length ? `- Ruled out: ${ruledOut.join("; ")}` : ""}
151
+
152
+ ## Previous session summary
153
+ ${sessionTranscriptSummary}
154
+
155
+ ## Instructions
156
+ Pick up where this left off. Read the relevant files, perform the
157
+ check described in the task, and report findings. If the hypothesis
158
+ is confirmed, suggest a fix. If not, update the hypothesis.`;
159
+
160
+ return prompt;
161
+ }
162
+ ```
163
+
164
+ ### 3. Auto-cleanup
165
+
166
+ The emitter is `oneTime` with `autoStart: true`. After it fires:
167
+ - The PromptEmitter runs once on session start
168
+ - Copilot processes it and responds
169
+ - The emitter marks itself completed and removes from persistent config
170
+ - No cleanup needed
171
+
172
+ ### 4. Deferred chain (optional)
173
+
174
+ If the future session also hits a wall, the AI can defer again:
175
+
176
+ ```
177
+ Session N: "check logs tomorrow" → deferred
178
+ Session N+1: checks logs, finds a clue, needs metric data that's being aggregated
179
+ → "check the hourly aggregation after it runs at 6am" → deferred again
180
+ Session N+2: checks aggregation, finds root cause, suggests fix
181
+ ```
182
+
183
+ Each deferral carries forward the accumulated context from all previous sessions. The investigation builds across days without losing state.
184
+
185
+ ## Use cases
186
+
187
+ ### "Wait for the deploy to finish"
188
+
189
+ ```
190
+ > I need to check if the new config works in production.
191
+ > The deploy won't finish for another 20 minutes and
192
+ > I need to switch to something else.
193
+
194
+ Copilot calls tap_defer({
195
+ what: "Check if auth-service in production is using the new
196
+ Redis connection pool settings from PR #312. Verify
197
+ by checking the pod env vars and the connection count
198
+ in the Redis dashboard.",
199
+ when: "next-session"
200
+ })
201
+
202
+ > Deferred to next session. I'll check the deploy results
203
+ > when you come back.
204
+ ```
205
+
206
+ ### "Monitor overnight"
207
+
208
+ ```
209
+ > The memory leak reproduces after ~8 hours of uptime.
210
+ > The service was restarted at 4pm. Defer checking the
211
+ > heap stats to tomorrow morning.
212
+
213
+ Copilot calls tap_defer({
214
+ what: "Check heap memory stats for auth-service. It was
215
+ restarted at 4pm yesterday. If uptime is now ~16h,
216
+ check if RSS has grown linearly. Compare to the
217
+ baseline of 512MB at startup. Check /debug/pprof/heap
218
+ if available.",
219
+ when: "next-session"
220
+ })
221
+ ```
222
+
223
+ ### "Continue this research"
224
+
225
+ ```
226
+ > I'm evaluating whether to migrate from Express to Fastify.
227
+ > I've compared routing and middleware so far. Defer the
228
+ > rest — I need to check plugin ecosystem and benchmarks.
229
+
230
+ Copilot calls tap_defer({
231
+ what: "Continue the Express→Fastify migration evaluation.
232
+ Already compared: routing (Fastify wins on speed),
233
+ middleware (Express has more ecosystem).
234
+ Still need to evaluate: plugin compatibility for
235
+ our 12 Express middlewares, benchmark with our
236
+ actual API routes, and migration effort estimate.",
237
+ when: "next-session"
238
+ })
239
+ ```
240
+
241
+ ## Protocol
242
+
243
+ ### Deferred emitter in tap.config.json
244
+
245
+ ```json
246
+ {
247
+ "emitters": [
248
+ {
249
+ "name": "deferred-check-auth-deploy",
250
+ "prompt": "Continue investigating the auth-timeout issue...",
251
+ "runSchedule": "oneTime",
252
+ "autoStart": true,
253
+ "ownership": "modelOwned",
254
+ "lifespan": "persistent",
255
+ "metadata": {
256
+ "deferredAt": "2026-04-26T14:30:00Z",
257
+ "deferredFrom": "session-abc123",
258
+ "task": "Check if auth-service is using new Redis pool settings"
259
+ }
260
+ }
261
+ ]
262
+ }
263
+ ```
264
+
265
+ ### Lifecycle
266
+
267
+ ```
268
+ tap_defer() called
269
+
270
+
271
+ Generate prompt from session state
272
+
273
+
274
+ Write to tap.config.json as persistent oneTime emitter
275
+
276
+
277
+ Session ends normally
278
+
279
+ ═══ time passes ═══
280
+
281
+
282
+ New session starts → onSessionStart loads persistent config
283
+
284
+
285
+ Deferred emitter fires (session.send with the prompt)
286
+
287
+
288
+ Copilot processes the deferred work
289
+
290
+
291
+ Emitter completes → removed from persistent config
292
+ ```
293
+
294
+ ## Phased delivery
295
+
296
+ | Phase | Scope |
297
+ |---|---|
298
+ | **1. tap_defer tool** | Capture task + basic context, create persistent oneTime emitter |
299
+ | **2. State extraction** | Use session.getMessages() to extract hypothesis, files, ruled-out from conversation |
300
+ | **3. Prompt generation** | PromptEmitter that distills session into a rich deferred prompt |
301
+ | **4. Deferred chains** | Carry forward accumulated context across multiple deferrals |
302
+ | **5. Scheduled deferral** | Support `when: "6h"` with delayed autoStart (timed persistent emitter) |
303
+
304
+ ## Open questions
305
+
306
+ - **Context budget** — how much session history to include in the deferred prompt? Too little loses context, too much wastes tokens.
307
+ - **Relevance decay** — a deferral from 2 weeks ago may no longer be relevant. Auto-expire?
308
+ - **Multiple deferrals** — what if you defer 5 things? Queue them? Fire all at session start?
309
+ - **User confirmation** — should deferred prompts fire silently or announce themselves? "You deferred 2 tasks from yesterday. Running them now."
310
+ - **Cross-repo** — deferred work is stored in tap.config.json which is repo-scoped. What about repo-independent deferrals?
@@ -0,0 +1,93 @@
1
+ # Provider integration patterns
2
+
3
+ These recipes turn external workflow systems into tap-operable signals and tools.
4
+ They are intentionally provider-shaped: each integration exposes a small local
5
+ service or WebSocket provider, then tap uses EventStreams, EventFilters, goals,
6
+ and diagnostics to keep the agent connected to the workflow.
7
+
8
+ ## Shared shape
9
+
10
+ 1. Provider authenticates to the external system.
11
+ 2. Provider registers focused tools through the tap provider gateway.
12
+ 3. Provider exposes or emits normalized events with stable fields.
13
+ 4. tap filters events into `keep`, `surface`, or `inject`.
14
+ 5. High-value events can start or steer a `/tap-goal` or `/tap-orchestrate`
15
+ workflow.
16
+
17
+ Prefer structured JSON lines for provider output:
18
+
19
+ ```json
20
+ {"type":"ci.failure","repo":"owner/name","runUrl":"...","branch":"main","severity":"high"}
21
+ ```
22
+
23
+ This makes EventFilter rules stable and auditable.
24
+
25
+ Dependency-free template providers live in `providers/templates/`:
26
+
27
+ - `ci-review-provider.mjs`
28
+ - `jira-github-provider.mjs`
29
+ - `sast-triage-provider.mjs`
30
+ - `detour-workflow-provider.mjs`
31
+
32
+ ## Jira + GitHub
33
+
34
+ Inspired by the Codex Jira/GitHub automation pattern:
35
+
36
+ - Jira label or automation rule triggers a provider event.
37
+ - Provider tools:
38
+ - `jira_get_issue`
39
+ - `jira_transition_issue`
40
+ - `jira_post_comment`
41
+ - `github_create_pr`
42
+ - A goal completes only when the EventStream ledger includes:
43
+ - Jira issue key
44
+ - branch name
45
+ - commit SHA
46
+ - PR URL
47
+ - Jira status transition
48
+
49
+ ## CI auto-fix
50
+
51
+ Inspired by the Codex GitHub Actions auto-fix pattern:
52
+
53
+ - CommandEmitter or provider watches failed workflow runs.
54
+ - Failure events surface or inject with run URL and failing job.
55
+ - A repair goal uses the failing output as the verification surface.
56
+ - Completion requires a successful verification command and a traceable branch
57
+ or PR.
58
+
59
+ ## Code review
60
+
61
+ Inspired by the Codex SDK code-review pattern:
62
+
63
+ - Provider or skill runs a structured review command.
64
+ - Findings use stable fields:
65
+ - title
66
+ - body
67
+ - confidence score
68
+ - priority
69
+ - file path
70
+ - line range
71
+ - P0/P1 findings should inject; P2/P3 findings should surface or keep.
72
+
73
+ ## SAST triage
74
+
75
+ Inspired by the GitLab security-quality pattern:
76
+
77
+ - Ingest SAST JSON as structured provider events.
78
+ - Deduplicate by `(CWE, sink/function, file:line)`.
79
+ - Rank by exploitability and business risk.
80
+ - Process one finding per goal iteration.
81
+ - Completion requires either a validated patch or an explicit blocked reason.
82
+
83
+ ## Browser / Detour workflows
84
+
85
+ Use Detour for browser-page instrumentation and tap for agent-side orchestration:
86
+
87
+ - Detour injects browser bridge code.
88
+ - Provider exposes a local API for page events.
89
+ - CommandEmitter polls the provider and normalizes events.
90
+ - tap goals or monitors react to stable event types.
91
+
92
+ Do not mutate Detour source for tap-specific workflows; use injectable scripts
93
+ and provider-side adapters.