@polygraph/claude-plugin 0.4.47 → 0.4.49

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polygraph",
3
- "version": "0.4.47",
3
+ "version": "0.4.49",
4
4
  "description": "AI agent skills and subagents for Polygraph sessions, repository context, and coordination",
5
5
  "author": {
6
6
  "name": "Narwhal Technologies Inc",
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <h1 align="center">Polygraph Skills</h1>
9
9
 
10
10
  <p align="center">
11
- AI agent skills and subagents for <a href="https://trypolygraph.com/">Polygraph</a> — the meta-harness for maximum agentic autonomy, giving agents visibility across every repo and memory that survives every session.
11
+ AI agent skills and subagents for <a href="https://trypolygraph.com/">Polygraph</a> — the agent-agnostic meta-harness for maximum agentic autonomy, giving agents visibility across every repo and memory that survives every session.
12
12
  </p>
13
13
 
14
14
  <p align="center">
@@ -23,7 +23,7 @@
23
23
 
24
24
  ## What is Polygraph?
25
25
 
26
- Polygraph is a meta-harness for maximum agentic autonomy. It works with the agents you already use and gives them what they're missing: visibility across every repo boundary, and memory that survives every session. Agents discover how repositories relate, coordinate changes across them, and hand off or resume work later with repos, branches, PRs, and logs all preserved.
26
+ Polygraph is the agent-agnostic meta-harness for maximum agentic autonomy. It works with the agents you already use and gives them what they're missing: visibility across every repo boundary, and memory that survives every session. Agents discover how repositories relate, coordinate changes across them, and hand off or resume work later with repos, branches, PRs, and logs all preserved.
27
27
 
28
28
  ## Setup
29
29
 
@@ -35,19 +35,42 @@ polygraph config
35
35
 
36
36
  It detects your AI agent — Claude Code, Codex, OpenCode, and more — and installs the Polygraph skills and subagents for it. Re-run it any time to add another agent or update an existing install.
37
37
 
38
+ ### Codex: raising the long-poll ceiling
39
+
40
+ The delegate subagent polls child agents with `show_agent(waitForTransitionMs: 300000)` — a single 5-minute wait instead of a stream of short polls, which is what keeps delegation cheap. Claude Code and OpenCode honor the full wait. Codex does not by default.
41
+
42
+ Codex caps every MCP tool call at `tool_timeout_sec = 60`, so long polls effectively cap at 50s. Nothing the plugin ships can change that: a plugin's `.mcp.json` has no timeout field, and `tool_timeout_sec` is only read from `~/.codex/config.toml`. Polling still works at 50s — the MCP resolves the real ceiling per client and returns early rather than erroring — you just pay for more round trips.
43
+
44
+ To get the full 5-minute wait under Codex, set **both**:
45
+
46
+ 1. `tool_timeout_sec` in `~/.codex/config.toml`:
47
+
48
+ ```toml
49
+ [mcp_servers.polygraph]
50
+ tool_timeout_sec = 360
51
+ ```
52
+
53
+ 2. `POLYGRAPH_MCP_MAX_WAIT_MS` in that server's env, if you launch it from the CLI:
54
+
55
+ ```sh
56
+ POLYGRAPH_MCP_MAX_WAIT_MS=300000
57
+ ```
58
+
59
+ Neither is required — they are a throughput optimization, not a correctness fix.
60
+
38
61
  ## Skills
39
62
 
40
- - **polygraph** — Comprehensive guidance for Polygraph sessions: shared context, repository graph visibility, PR/CI state, delegation, and session management
41
- - **adversarial-review** — Second-opinion review of a session's work by independent reviewer agents, one per repo, running under a read-only `reviewer` role
42
- - **await-polygraph-ci** — Wait for CI pipelines to settle across all repos in a session, investigate failures, and present fix options
43
- - **get-latest-ci** — One-shot fetch of the latest CI pipeline execution for the current branch
44
- - **session-debrief** — Analyze the raw logs of past Polygraph sessions and produce structured, rank-ordered debriefs for use in a different session
63
+ - **polygraph** — Comprehensive guidance for Polygraph sessions: shared context, repository graph visibility, PR/CI state, delegation, and session management.
64
+ - **adversarial-review** — Second-opinion review of a session's work by independent reviewer agents, one per repo, running under a read-only `reviewer` role.
65
+ - **await-polygraph-ci** — Wait for CI pipelines to settle across all repos in a session, investigate failures, and present fix options.
66
+ - **get-latest-ci** — One-shot fetch of the latest CI pipeline execution for the current branch.
67
+ - **session-debrief** — Analyze the raw logs of past Polygraph sessions and produce structured, rank-ordered debriefs for use in a different session.
45
68
 
46
69
  ## Agents
47
70
 
48
- - **polygraph-init-subagent** — Discovers candidate repositories and initializes a Polygraph session
49
- - **polygraph-delegate-subagent** — Delegates work to a child agent in another repository, polls for completion
50
- - **session-debrief** — Analyzes the raw logs of past Polygraph sessions and returns a structured, rank-ordered debrief for the current task
71
+ - **polygraph-init-subagent** — Discovers candidate repositories and initializes a Polygraph session.
72
+ - **polygraph-delegate-subagent** — Delegates work to a child agent in another repository, polls for completion.
73
+ - **session-debrief** — Analyzes the raw logs of past Polygraph sessions and returns a structured, rank-ordered debrief for the current task.
51
74
 
52
75
  ## Development
53
76
 
@@ -71,7 +94,7 @@ For the strictest release flow, do not allow direct `npm publish` for the truste
71
94
 
72
95
  ## Learn More
73
96
 
74
- - **[Polygraph](https://trypolygraph.com/)** — The meta-harness for maximum agentic autonomy
97
+ - **[Polygraph](https://trypolygraph.com/)** — The agent-agnostic meta-harness for maximum agentic autonomy
75
98
  - **[@polygraph/mcp](https://www.npmjs.com/package/@polygraph/mcp)** — The MCP server that powers Polygraph tools
76
99
  ## License
77
100
 
@@ -1,167 +1,57 @@
1
1
  ---
2
2
 
3
3
  name: polygraph-delegate-subagent
4
- description: Delegates work to a child agent in another repository via Polygraph, polls for completion, and returns a structured summary. Runs in the background.
4
+ description: Waits for one Polygraph child agent (addressed by delegation id) to finish or need attention, then exits with a short fixed message. Never reads logs. Runs in the background.
5
5
  model: haiku
6
6
  tools:
7
- - mcp__plugin_polygraph_polygraph-mcp__spawn_agent
8
7
  - mcp__plugin_polygraph_polygraph-mcp__show_agent
9
- - mcp__plugin_polygraph_polygraph-mcp__stop_agent
10
- - Bash
11
8
 
12
9
  ---
13
10
 
14
11
  # Polygraph Delegate Subagent
15
12
 
16
- You are a Polygraph delegation subagent. Your job is to delegate work to a child agent in another repository, poll for completion, and return a structured summary.
13
+ You wait for one Polygraph child agent to finish. You do not read its work.
17
14
 
18
- You run in the background. The main agent checks your output file for progress.
15
+ The main agent already spawned the child via `spawn_agent` and received a delegation id. Your entire job is to poll that id until the child needs attention, then exit with a short fixed message. The main agent reads the child's result itself — every log line you echo is duplicated tokens, so you never fetch logs.
19
16
 
20
- ## Input Parameters (from Main Agent)
17
+ ## Input (from the main agent)
21
18
 
22
- The main agent provides these parameters in the prompt:
19
+ | Parameter | Description |
20
+ | ----------- | ------------------------------------------------------------ |
21
+ | `sessionId` | The Polygraph session ID |
22
+ | `id` | The delegation id returned by `spawn_agent` (e.g. `frontend-1`) |
23
23
 
24
- | Parameter | Description |
25
- | ------------- | ---------------------------------------------------------------------------- |
26
- | `sessionId` | The Polygraph session ID |
27
- | `repo` | Repository to delegate to (e.g., `org/repo-name`) |
28
- | `instruction` | The task instruction for the child agent |
29
- | `role` | (Optional) Agent slot within the repo; omit for the default role and never invent one. Pass the SAME role on every `spawn_agent`/`show_agent`/`stop_agent` call for this delegation. |
30
- | `context` | (Optional) Additional context to pass to the child agent |
24
+ ## Loop
31
25
 
32
- ## Delegating work
26
+ Call the `show_agent` tool with exactly these arguments, repeatedly:
33
27
 
34
- Call the `spawn_agent` tool to start a child agent on the repo or to send a follow-up to an active task. Follow-up routing is automatic per (repo, role): if that (repo, role) already has an active child task (working or paused on input), the orchestrator delivers your `instruction` to that task as a follow-up message rather than starting a second run; otherwise it starts a new child run. A repo therefore has at most one active child per role.
28
+ - `sessionId`: the session ID
29
+ - `id`: the delegation id
30
+ - `waitForTransitionMs`: 300000
35
31
 
36
- `repo` must be a repository other than the one the parent agent is working in — never delegate into the parent's own repo.
32
+ Never pass `tail`. Never call any other tool. Never read files, transcripts, or logs.
37
33
 
38
- **Resume/reconstruction is read-only.** If the parent asks you to resume, reconnect, restore, or reconstruct a preserved session without an explicit new change request from the user, do not call `spawn_agent` to continue work. Use `show_agent` only as needed to read status/log context, return a concise restoration summary, and stop. After resuming, wait for explicit user instructions before any child agent makes changes.
34
+ Each call blocks up to 5 minutes, then returns the child's `status` on `children[0]`:
39
35
 
40
- ```
41
- spawn_agent(
42
- sessionId: "<sessionId>",
43
- repo: "<repo>",
44
- instruction: "<instruction>",
45
- role: "<role, if any>",
46
- context: "<context>"
47
- )
48
- ```
49
-
50
- The call returns immediately — the child agent runs asynchronously.
51
-
52
- **Polling with long-poll waits:**
53
-
54
- Call `show_agent` in a loop, passing `waitForTransitionMs: 50000` on every call:
55
-
56
- ```
57
- show_agent(
58
- sessionId: "<sessionId>",
59
- repo: "<repo>",
60
- role: "<role, if any>",
61
- waitForTransitionMs: 50000
62
- )
63
- ```
64
-
65
- Each call blocks up to ~50 seconds and resolves within ~1 second of a state change. It returns immediately if the child is already terminal, `input-required`, or `permission-required`. Call it back-to-back in a loop.
36
+ - `in-progress` or `created` → call `show_agent` again with the same arguments.
37
+ - `completed`, `failed`, `cancelled`, `input-required`, or `permission-required` → stop looping and exit with the message below.
66
38
 
67
- ## Polling the child (multi-turn + input-required)
39
+ If a call errors, retry it once. If it errors again, exit with the error text plus the delegation id and stop.
68
40
 
69
- After calling `spawn_agent`, parse the structured JSON response:
41
+ ## Exit message (fixed template)
70
42
 
71
- ```json
72
- { "taskId": "…", "message": "…", "status": "delegated" }
73
- ```
43
+ Return exactly this, filled in from the last `show_agent` response — nothing more:
74
44
 
75
- Then poll `show_agent` in a loop with `waitForTransitionMs: 50000`. **Do not pass a `tail` argument** — the tool's default is sized for status polling. Only set `tail` if you have a specific reason (e.g., the default truncated output you actually need to inspect, or you are hunting for an earlier failure that scrolled off). Never ratchet `tail` upward across polls; that is what causes the polling loop to flood your context window.
76
-
77
- The response's `children[]` array has one entry per agent matching your query. Find YOUR delegation's entry (match on `role`; absent means the default role) and inspect:
78
-
79
- - `child.status` — an AcpRunStatus value: one of `'created'`, `'in-progress'`, `'input-required'`, `'permission-required'`, `'completed'`, `'failed'`, `'cancelled'` (British double-L on `'cancelled'`). Note `'permission-required'` and `'input-required'` are DIFFERENT states handled by different cases below — do not conflate them.
80
- - `child.inputRequiredQuestion` — populated only when `child.status === 'input-required'`; contains the verbatim question the child agent has asked the parent.
81
- - `child.lastOutputLines` — recent log tail (use for status narration; do not treat as an API surface).
82
- - `child.repoFullName` — human-facing identifier for which repo is talking.
83
-
84
- State machine:
85
-
86
- 1. `child.status === 'created'` or `'in-progress'` — child is still executing. Call `show_agent` (with `waitForTransitionMs: 50000`) again.
87
- 2. `child.status === 'input-required'` — child is paused waiting for parent input:
88
- - Read `child.inputRequiredQuestion`.
89
- - Surface this question verbatim to the parent/user: "The child agent in `{child.repoFullName}` needs input: {child.inputRequiredQuestion}".
90
- - Wait for the parent/user to supply an answer.
91
- - Call `spawn_agent` again with the same `repo`, the same `role`, and `instruction: <the answer>` — the orchestrator routes it to that (repo, role)'s active task automatically.
92
- - Resume polling.
93
-
94
- <!-- Claude and Codex parents handle permission gates via the native MCP elicitation dialog
95
- rendered by polygraph-mcp's show_agent handler. The dialog targets the parent's main
96
- thread, NOT this subagent. From this subagent's perspective the gate is transient: a
97
- waited show_agent call returns immediately while the gate is open, but the parent's pick
98
- resolves it and a later poll sees the child back in progress. Do nothing here. -->
99
- 3. `child.status === 'permission-required'` — the child opened a permission gate. **This is NOT `input-required`. Do not treat it like case 2.** The parent's native MCP elicitation dialog already renders the prompt in the parent's own UI and routes the decision back to the child through `polygraph-mcp`. Your only job is to stay out of the way and keep polling:
100
-
101
- - **Do NOT return, finish, summarize, relay, or surface this to the parent.** Do NOT describe the child as "needing input", "awaiting approval", "asking for permission", or anything that would make the parent prompt the user — the parent already has its own dialog. Returning here is the bug this case exists to prevent.
102
- - **Do NOT read `child.pendingPermission` as a question to answer or forward.** It is for inspection/logging only; it is not your input prompt.
103
- - **Do NOT call any tool** (`spawn_agent`, `stop_agent`, `allow_agent`, `deny_agent`) to resolve it.
104
- - Treat `permission-required` **like `in-progress`**: just call `show_agent` (with `waitForTransitionMs`) again — the parent's dialog resolves it. Only at a terminal state do you return, per the cases below.
105
-
106
- 4. `child.status === 'completed'` — child finished successfully. Read `child.lastOutputLines` for the most recent log tail and report outcome.
107
- 5. `child.status === 'failed'` — child failed. Read `child.lastOutputLines` for failure context and report the error.
108
- 6. `child.status === 'cancelled'` — child was stopped via `stop_agent`. Its session is preserved for later context restoration. Do not restart or continue work from that preserved session unless the user explicitly asks for changes.
109
-
110
- ## Cancelling a running child
111
-
112
- To cancel a running child mid-work, call `stop_agent` with the repo (and `role`, per the parameter rule above). Response:
113
-
114
- ```json
115
- {
116
- "taskId": "…",
117
- "state": "cancelled",
118
- "sessionPreserved": true,
119
- "output": "…",
120
- "message": "…"
121
- }
122
45
  ```
46
+ Child agent <id> is done.
123
47
 
124
- Because `sessionPreserved: true`, the session can be restored later for context. After resuming, do not call `spawn_agent` to continue prior work or make changes until the user explicitly asks for changes.
125
-
126
- ## Returning the summary
127
-
128
- When the child agent reaches a terminal status, return a structured summary:
48
+ **Repo:** <repoFullName>
49
+ **Delegation id:** <id>
50
+ **Status:** <status>
129
51
 
52
+ Read the result with show_agent (id: "<id>").
130
53
  ```
131
- ## Polygraph Delegation Result
132
-
133
- **Repo:** <repo>
134
- **Role:** <role, or "default" when none was given>
135
- **Status:** <success | failed | cancelled>
136
- **Session ID:** <sessionId>
137
-
138
- ### Result
139
- <result text drawn from child.lastOutputLines>
140
- ```
141
-
142
- ## Timeout
143
-
144
- If polling exceeds **30 minutes**, return with a timeout status:
145
-
146
- ```
147
- ## Polygraph Delegation Result
148
-
149
- **Repo:** <repo>
150
- **Status:** timeout
151
- **Session ID:** <sessionId>
152
- **Elapsed:** <minutes>m
153
-
154
- ### Suggestions
155
- - Check child agent status manually via `show_agent`
156
- - Consider stopping the child agent via `stop_agent`
157
- ```
158
-
159
- ## Important Notes
160
54
 
161
- - You run in the background write clear status lines so the main agent can parse your output file
55
+ For `input-required` or `permission-required`, replace "is done." with "needs attention." and keep everything else identical.
162
56
 
163
- - Do NOT make decisions about the work only delegate and monitor
164
- - Do NOT call `push_branch` or `create_pr` — those are the main agent's responsibility
165
- - If `spawn_agent` fails, return the error immediately
166
- - If `show_agent` returns an error, wait and retry (count as failed poll)
167
- - After 5 consecutive poll failures, return with `status: error`
57
+ Do not summarize, quote, or describe the child's work. Do not include log lines. The main agent reads the result itself via `show_agent`.
@@ -0,0 +1,85 @@
1
+ import { spawnSync } from 'node:child_process';
2
+
3
+ function nonEmptyString(value) {
4
+ return typeof value === 'string' && value.trim() ? value : undefined;
5
+ }
6
+
7
+ function isManagedChildEnvironment(env) {
8
+ return Boolean(env && Object.hasOwn(env, 'POLYGRAPH_CHILD_AGENT'));
9
+ }
10
+
11
+ export function buildFinalizeAgentSessionArgs({
12
+ agentType,
13
+ agentSessionId,
14
+ cwd,
15
+ transcriptPath,
16
+ source,
17
+ }) {
18
+ const harnessSession = nonEmptyString(agentSessionId);
19
+ const hookSource = nonEmptyString(source);
20
+ if (agentType !== 'claude') throw new Error(`Unsupported agent type: ${agentType}`);
21
+ if (!harnessSession) throw new Error('agentSessionId is required');
22
+ if (!hookSource) throw new Error('source is required');
23
+
24
+ const args = [
25
+ '_finalize-agent-session',
26
+ '--agent-type',
27
+ agentType,
28
+ '--agent-session-id',
29
+ harnessSession,
30
+ ];
31
+
32
+ const workingDirectory = nonEmptyString(cwd);
33
+ if (workingDirectory) args.push('--cwd', workingDirectory);
34
+
35
+ const transcript = nonEmptyString(transcriptPath);
36
+ if (transcript) args.push('--transcript-path', transcript);
37
+
38
+ args.push('--source', hookSource);
39
+ return args;
40
+ }
41
+
42
+ export function finalizeAgentSession(claim, spawn = spawnSync, env = process.env) {
43
+ if (isManagedChildEnvironment(env)) return false;
44
+
45
+ const command = nonEmptyString(env?.POLYGRAPH_CLI) ?? 'polygraph';
46
+ const commandEnv = { ...env };
47
+ delete commandEnv.POLYGRAPH_SESSION_ID;
48
+ delete commandEnv.POLYGRAPH_CAPTURE_TOKEN;
49
+
50
+ const result = spawn(command, buildFinalizeAgentSessionArgs(claim), {
51
+ encoding: 'utf8',
52
+ env: commandEnv,
53
+ stdio: ['ignore', 'ignore', 'pipe'],
54
+ });
55
+
56
+ if (result?.error) throw result.error;
57
+ if (result?.status !== 0) {
58
+ const detail = nonEmptyString(result?.stderr);
59
+ throw new Error(
60
+ `polygraph _finalize-agent-session exited with status ${String(result?.status)}` +
61
+ (detail ? `: ${detail}` : '')
62
+ );
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
+ export function buildCommandHookFinalize(payload, agentType, env = process.env) {
69
+ if (!payload || typeof payload !== 'object') return undefined;
70
+ if (isManagedChildEnvironment(env)) return undefined;
71
+ if (agentType !== 'claude' || payload.hook_event_name !== 'SessionEnd') {
72
+ return undefined;
73
+ }
74
+
75
+ const agentSessionId = nonEmptyString(payload.session_id);
76
+ if (!agentSessionId) return undefined;
77
+
78
+ return {
79
+ agentType,
80
+ agentSessionId,
81
+ cwd: nonEmptyString(payload.cwd),
82
+ transcriptPath: nonEmptyString(payload.transcript_path),
83
+ source: 'hook',
84
+ };
85
+ }
@@ -102,7 +102,12 @@ export function buildCommandHookLink(payload, agentType, env = process.env) {
102
102
 
103
103
  if (payload.hook_event_name === 'SessionStart') {
104
104
  const polygraphSessionId = nonEmptyString(env.POLYGRAPH_SESSION_ID);
105
- return polygraphSessionId ? { ...common, polygraphSessionId } : undefined;
105
+ if (polygraphSessionId) return { ...common, polygraphSessionId };
106
+
107
+ // Ordinary sessions of every supported harness are eligible for
108
+ // speculative capture, so later session searches can find them even when
109
+ // the session was not launched with Polygraph session evidence.
110
+ return AGENT_TYPES.has(agentType) ? common : undefined;
106
111
  }
107
112
 
108
113
  if (payload.hook_event_name === 'PostToolUse') {
@@ -0,0 +1,56 @@
1
+ import { readFileSync, realpathSync } from 'node:fs';
2
+ import { fileURLToPath } from 'node:url';
3
+
4
+ import {
5
+ buildCommandHookFinalize,
6
+ finalizeAgentSession,
7
+ } from './agent-session-finalize.mjs';
8
+ import { logHookFailure } from './agent-session-link.mjs';
9
+
10
+ function readPayload() {
11
+ try {
12
+ const raw = readFileSync(0, 'utf8');
13
+ return raw ? JSON.parse(raw) : undefined;
14
+ } catch {
15
+ return undefined;
16
+ }
17
+ }
18
+
19
+ export function main({
20
+ payload = readPayload(),
21
+ agentType = process.argv[2],
22
+ env = process.env,
23
+ spawn,
24
+ } = {}) {
25
+ try {
26
+ const finalize = buildCommandHookFinalize(payload, agentType, env);
27
+ if (!finalize) return false;
28
+ return finalizeAgentSession(
29
+ {
30
+ ...finalize,
31
+ cwd: finalize.cwd ?? process.cwd(),
32
+ },
33
+ spawn,
34
+ env
35
+ );
36
+ } catch (error) {
37
+ logHookFailure(`${agentType || 'unknown'}:finalize-agent-session`, error, {
38
+ hookEventName: payload?.hook_event_name,
39
+ agentSessionId: payload?.session_id,
40
+ });
41
+ return false;
42
+ }
43
+ }
44
+
45
+ function isMainModule() {
46
+ if (!process.argv[1]) return false;
47
+ try {
48
+ return realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url));
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+
54
+ if (isMainModule()) {
55
+ main();
56
+ }
package/hooks/hooks.json CHANGED
@@ -33,7 +33,18 @@
33
33
  },
34
34
  {
35
35
  "type": "command",
36
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/record-session-mapping.mjs claude"
36
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/record-session-mapping.mjs claude",
37
+ "async": true
38
+ }
39
+ ]
40
+ }
41
+ ],
42
+ "SessionEnd": [
43
+ {
44
+ "hooks": [
45
+ {
46
+ "type": "command",
47
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/finalize-agent-session.mjs claude"
37
48
  }
38
49
  ]
39
50
  }
@@ -27,15 +27,18 @@ export function main({
27
27
  const link = buildCommandHookLink(payload, agentType, env);
28
28
  if (!link) return false;
29
29
 
30
- return linkAgentSession(
31
- {
32
- ...link,
33
- pid,
34
- cwd: link.cwd ?? process.cwd(),
35
- },
36
- spawn,
37
- env
38
- );
30
+ const claim = {
31
+ ...link,
32
+ cwd: link.cwd ?? process.cwd(),
33
+ };
34
+ // Claude lifecycle hooks deliberately forward only the exact harness
35
+ // identity, transcript, cwd, and hook source. PID is not identity and can
36
+ // be stale by the time an asynchronous SessionStart hook runs.
37
+ if (!(agentType === 'claude' && payload?.hook_event_name === 'SessionStart')) {
38
+ claim.pid = pid;
39
+ }
40
+
41
+ return linkAgentSession(claim, spawn, env);
39
42
  } catch (error) {
40
43
  logHookFailure(`${agentType || 'unknown'}:link-agent-session`, error, {
41
44
  hookEventName: payload?.hook_event_name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polygraph/claude-plugin",
3
- "version": "0.4.47",
3
+ "version": "0.4.49",
4
4
  "description": "AI agent skills and subagents for Polygraph sessions, repository context, and coordination",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -166,15 +166,15 @@ For each repo with `ciStatus: FAILED`, branch on the PR's `ci` object from `show
166
166
 
167
167
  Since `spawn_agent` is non-blocking, you can delegate to multiple failed repos in parallel.
168
168
 
169
- 3. **Monitor investigation progress** — poll `show_agent` to wait for each child agent to complete:
169
+ 3. **Monitor investigation progress** — launch one background `polygraph-delegate-subagent` per delegation id and let it do the waiting. When it exits, read that investigation with a single unwaited `show_agent`:
170
170
 
171
171
  ```
172
- show_agent(sessionId: "<session-id>", repo: "frontend")
172
+ show_agent(sessionId: "<session-id>", id: "<delegation-id>")
173
173
  ```
174
174
 
175
- Poll until the child agent's status indicates completion (pass the same `role` you spawned with, if any). Use the `tail` parameter to retrieve recent output lines containing the investigation results.
175
+ `result.text` is the child's investigation summary. Never run a waited `show_agent` loop here, and do not pass `tail` unless `result.text` alone is insufficient.
176
176
 
177
- 4. Collect each child agent's response from the status output. If a child agent fails or gets stuck, use `stop_agent` to terminate it and skip that repo.
177
+ 4. Collect each child agent's response from its unwaited `show_agent` read. If a child agent fails or gets stuck, use `stop_agent` with its delegation id to terminate it and skip that repo.
178
178
 
179
179
  5. Display failure summary for each repo:
180
180
 
@@ -211,5 +211,5 @@ For each repo with `ciStatus: FAILED`, branch on the PR's `ci` object from `show
211
211
  - All heavy CI data inspection happens in child agents via `spawn_agent` to keep this context window clean.
212
212
 
213
213
  - Child agents can use `get_ci_logs` to save CI job logs to local files, but ONLY when no CIPE exists for the PR (`pr.ci.cipeUrl` is null). When a CIPE exists, logs come from the CIPE system via the Nx MCP `ci_information` tool. Job IDs come from `pr.ci.externalCIRuns[].jobs[].jobId` in the `show_session` response. The tool returns a file path (`logFile`) and size (`sizeBytes`) — use the `Read` tool to examine the log content. Logs can be large (100KB+), so only fetch logs for failed or relevant jobs.
214
- - `spawn_agent` is **non-blocking** — it starts the child agent and returns immediately. Use `show_agent` to poll for results and `stop_agent` to terminate stuck agents.
214
+ - `spawn_agent` is **non-blocking** — it starts the child agent and returns a delegation id immediately. Waiting on that id belongs in a background poller subagent; an unwaited `show_agent` by id returns the result, and `stop_agent` by id terminates a stuck agent.
215
215
  - The `show_session` response is compact and safe to poll from the main agent.
@@ -9,11 +9,11 @@ allowed-tools:
9
9
 
10
10
  # Working with Polygraph
11
11
 
12
- **IMPORTANT:** Polygraph keeps local clones only for *other* repositories in the session. NEVER `cd` into those clones or access their files directly — work in other repositories ALWAYS happens through the Polygraph MCP `spawn_agent` tool, invoked via background `polygraph-delegate-subagent` Tasks.
12
+ **IMPORTANT:** Polygraph keeps local clones only for *other* repositories in the session. NEVER `cd` into those clones or access their files directly — work in other repositories ALWAYS happens through the Polygraph MCP `spawn_agent` tool. Before delegating anything, read [`reference/delegation.md`](reference/delegation.md).
13
13
 
14
14
  Polygraph connects repos and the agent work happening across them. Its central artifact is the session, which groups the repos, branches, PRs, and CI status for one piece of work and can be shared and resumed: use it to coordinate changes across multiple repos or in a single repoto share the session URL with collaborators, hand off progress via the session description, resume prior work, and watch CI across the session's PRs.
15
15
 
16
- **Polygraph operates on the current repo in place.** Starting or joining a session never clones or modifies the repository you are in — you keep working in your real working directory, and `push_branch` pushes your local commits from that checkout. Only *other* repos are worked on in separate Polygraph-managed clones via `spawn_agent`.
16
+ **Polygraph operates on the current repo in place.** Starting a session never clones or modifies the repository you are in — you keep working in your real working directory, and `push_branch` pushes your local commits from that checkout. Only *other* repos are worked on in separate Polygraph-managed clones via `spawn_agent`. Resuming is the one qualified case: a `resume_session` with the explicit `reset` consent force-switches branches inside the session's materialized repositories, and can force-move the current working tree when it is itself one of them. Repositories outside the session folder are never modified. Full contract under "Explore an Existing Session".
17
17
 
18
18
  ## Sandboxing in Polygraph Sessions
19
19
 
@@ -29,9 +29,10 @@ Polygraph functionality is available via both MCP tools and CLI commands. Use wh
29
29
  | --- | --- | --- |
30
30
  | `list_repos` | `polygraph repo list` | Discover candidate repositories. Candidate entries do not include repository descriptions; use `semanticQuery` for natural-language discovery. |
31
31
  | `start_session` | `polygraph session start --repo <ids>` | Initialize a Polygraph session with selected repositories |
32
- | `spawn_agent` | | Start a child task, or send a follow-up to an active task, in another repository. A repeat call for the same (repo, role) is delivered to that task as a follow-up; otherwise a new child starts. Roles and resume behavior are under "Multi-turn tasks". |
33
- | `show_agent` | — | Poll one repo's child status (one repo per call; `role` narrows to that agent). Status enum and the poll/state-machine flow are under "Multi-turn tasks". |
34
- | `stop_agent` | — | Cancel an in-progress child; its session is preserved for later read-only context restoration. |
32
+ | `resume_session` | `polygraph session resume --session <id> --json` | Join an existing session from this conversation: a tracked adoption that performs the full reconstruct. On divergence it stays local by default; `reset` is an explicit, destructive opt-in. Divergence and post-join behavior are under "Explore an Existing Session". |
33
+ | `spawn_agent` | — | Start a child task, or send a follow-up to an active task, in another repository; returns a delegation id. A repeat call for the same (repo, role) is delivered to that task as a follow-up; otherwise a new child starts. See `reference/delegation.md`. |
34
+ | `show_agent` | — | Poll by repo or delegation id; unwaited reads return the child's result. Waited calls are for the poller subagent, not the main conversation. See `reference/delegation.md`. |
35
+ | `stop_agent` | — | Cancel an in-progress child by delegation id; its session is preserved for later read-only context restoration. |
35
36
  | `push_branch` | — | Push a local git branch to the remote repository. For the repo you are in, this pushes from your current checkout. Requires a session description. |
36
37
  | `create_pr` | — | Create draft PRs with session metadata linking related PRs |
37
38
  | `show_session` | `polygraph session show <id> [--details]` | Query status of the current session. Use details when session summary, repo IDs, PR URLs, and PR descriptions are needed. |
@@ -51,7 +52,7 @@ Polygraph functionality is available via both MCP tools and CLI commands. Use wh
51
52
  | `select_account` | `polygraph account select` | Select the organization that future commands run against |
52
53
  | `whoami` | `polygraph whoami` | Show current auth status and org |
53
54
 
54
- **Delegation rules:** `list_repos` and `start_session` MUST be called via the `polygraph-init-subagent` as described in the "Initialize or Join Polygraph Session" section. Direct `add_repo` is allowed only when the user provides exact repo refs for an existing session. `spawn_agent` and `show_agent` MUST ALWAYS be called via background Task subagents (`run_in_background: true`) as described in the delegation sections belowNEVER call them directly in the main conversation. The subagents are plugin-namespaced: pass `subagent_type: "polygraph:polygraph-init-subagent"` / `"polygraph:polygraph-delegate-subagent"`; fall back to the bare name only if the namespaced form is not found.
55
+ **Delegation rules:** `list_repos` and `start_session` MUST be called via the `polygraph-init-subagent` as described in the "Initialize or Join Polygraph Session" section. Direct `add_repo` is allowed only when the user provides exact repo refs for an existing session. `spawn_agent` is a fast, non-blocking call and IS allowed directly in the main conversation — it returns a delegation id. Waited `show_agent` POLLING must run in a background Task subagent (`run_in_background: true`), never inline. One-off unwaited `show_agent` reads in the main conversation are fine and expected that is how you read a child's result. See [`reference/delegation.md`](reference/delegation.md). The subagents are plugin-namespaced: pass `subagent_type: "polygraph:polygraph-init-subagent"` / `"polygraph:polygraph-delegate-subagent"`; fall back to the bare name only if the namespaced form is not found.
55
56
 
56
57
  ## CLI Statefulness
57
58
 
@@ -80,10 +81,10 @@ After logging in (or if logged in but no org is selected), use `polygraph accoun
80
81
  The delegate/monitor/stop steps apply only when working across repos. A single-repo session skips them and still benefits from shared progress, resume, and CI visibility.
81
82
 
82
83
  0. **Initialize or join Polygraph session** - If you were spawned inside an existing session (the startup banner names a session ID), reuse it. Call `show_session` first; if it already has repos and the user did not ask to add more, you're done. If the user asks to add exact repo refs, call `add_repo` directly and skip candidate discovery. If the session has no repos and no exact refs were provided, launch the `polygraph-init-subagent` with that `sessionId` so it discovers candidates and uses `add_repo` (NOT `start_session`). Only when there is no session ID at all should the init subagent create a new session.
83
- 1. **Delegate work to each repo** - Use the `polygraph-delegate-subagent` to start child agents. Delegate only to *other* repos — never to the repo you are in; work on it directly (your regular subagents are fine for local work — only Polygraph delegation is reserved for other repos). Parallel delegation across repos is encouraged. Choose the Simple (fire-and-forget) or Multi-turn (interactive) pattern described below based on whether the child may need clarification.
84
+ 1. **Delegate work to each repo** - Call `spawn_agent` for each repo to get a delegation id, then launch one background `polygraph-delegate-subagent` per id to wait on it. With the default role, delegate only to *other* repos — never to the repo you are in; work on it directly (your regular subagents are fine for local work — only Polygraph delegation is reserved for other repos). Delegating into the repo you are in is allowed only with an explicit non-default `role`. Parallel delegation across repos is encouraged. Read [`reference/delegation.md`](reference/delegation.md) before delegating.
84
85
 
85
- 4. **Monitor child agents** - Use `show_agent` to poll one repo's children (`repo` is required; pass `role` to narrow to one agent) and read each entry's `status` and `lastOutputLines` from the `children[]` array.
86
- 5. **Stop child agents** (if needed) - Use `stop_agent` (with `role` when targeting a non-default agent) to cancel an in-progress child agent. The agent's session is preserved for later read-only context restoration; after a resume, wait for explicit user instructions before making changes.
86
+ 4. **Monitor child agents** - Let the background poller subagent do the waiting. When it exits, read that child's answer with a single unwaited `show_agent(sessionId, id)` `result.text` is the child's final message.
87
+ 5. **Stop child agents** (if needed) - Use `stop_agent` with the delegation id to cancel an in-progress child agent. The agent's session is preserved for later read-only context restoration; after a resume, wait for explicit user instructions before making changes.
87
88
  6. **Push branches** - Use `push_branch` after making commits. A required `description` must follow the Session Description Policy.
88
89
  7. **Create draft PRs** - Use `create_pr` to create linked draft PRs. Always pass `description` following the Session Description Policy.
89
90
  8. **Associate existing PRs** (optional) - Use `associate_pr` to link PRs created outside Polygraph.
@@ -99,6 +100,8 @@ There are three cases. Pick exactly one before calling any tool. The case labels
99
100
 
100
101
  **Hard rule: if a session ID is already in scope (e.g., the startup banner says "You're in Polygraph session …", or the user passed one or you are provided one by a reminder hook), that session ID is authoritative for this entire conversation. NEVER call `start_session` — doing so creates a brand-new session and orphans the one the parent harness is pointed at. Reuse the existing session via `show_session` and, if needed, `add_repo`.**
101
102
 
103
+ **Hard rule: before doing ad-hoc work in another repository, or reading a session's context untracked, ask whether an existing session already covers this work. If one might and its session ID is not in scope, ask the user for it — never fall back to an ad-hoc clone. To WORK in a session's context, join it via `resume_session` — a tracked adoption. To only read or summarize, `show_session` remains the read path.**
104
+
102
105
  **Case A — Existing session, already has repos.** Call `show_session` directly with the known session ID. Skip the init subagent entirely, show the session details (format below), and proceed.
103
106
 
104
107
  **Case B — Existing session, no repos yet (or user wants to add more).** If the user gives exact repo refs by ID, short name, full name, GitHub `owner/repo` slug, or URL-like slug, call `add_repo(sessionId, repoIds: [...])` directly with those refs. Do NOT call `list_repos`, do NOT ask for candidates, and do NOT launch the init subagent just to resolve those refs. If the user wants discovery/filtering instead, launch the `polygraph-init-subagent`, passing both the existing `sessionId` and `userContext`. The subagent will discover candidates, select relevant repositories, and call `add_repo` against the existing session — it will NOT call `start_session`.
@@ -139,11 +142,11 @@ The subagent will:
139
142
 
140
143
  Use this workflow when the user gives a Polygraph session ID and asks to understand, resume, inspect, or investigate prior work.
141
144
 
142
- **Resume is not a work command.** If the user's intent is to resume, reconnect, or reconstruct a prior Polygraph session, fetch and summarize the restored context, then stop. Do not edit files, push branches, add repos, delegate new work, or continue previous changes until the user explicitly asks for changes. Treat "resume" as context restoration followed by waiting for user instructions.
145
+ **Resume is not a work command.** If the user's intent is to resume, reconnect, or reconstruct a prior Polygraph session, join it via `resume_session`, summarize the restored context it returns, then stop. Do not edit files, push branches, add repos, delegate new work, or continue previous changes until the user explicitly asks for changes. Recording the join in the session's history is not making changes to the work. Treat "resume" as context restoration followed by waiting for user instructions.
143
146
 
144
- 1. Fetch detailed session context:
145
- - Prefer `show_session` with `details: true`
146
- - Otherwise run `polygraph session show --details <session-id>`.
147
+ 1. Fetch session context by intent:
148
+ - To continue work in the session from this conversation, join it via `resume_session` (CLI: `polygraph session resume --session <id> --json`) — a tracked adoption that performs the full reconstruct; the restored session context comes back in the tool result. On a divergent session the join stays on the local conversation by default and returns the divergence evidence; adopting the selected path requires the explicit `reset` consent, a destructive opt-in that force-switches branches in the session's materialized repositories (discarding uncommitted tracked changes there, untracked files survive) and deletes this machine's local session logs.
149
+ - To only read, inspect, or summarize without joining, prefer `show_session` with `details: true`; otherwise run `polygraph session show --details <session-id>`. This is the read-only path and records nothing.
147
150
  2. Treat the detailed output as authoritative context. It should include:
148
151
  - `<summary>` — the session summary.
149
152
  - `<repositories>` — relevant repos, including each repo's `<id>` and `<name>`.
@@ -185,71 +188,9 @@ Inspect the PR commits/diff and investigate the requested behavior. Report findi
185
188
  Use this workflow when the user asks which Polygraph session produced, is behind, or changed a particular commit — or a particular line of code.
186
189
  **Read [`reference/session-by-commit.md`](reference/session-by-commit.md) before running any lookup.** That reference file holds clear, reliable steps for answering questions related to this.
187
190
 
188
- ## Agent roles
189
-
190
- A repository in a session can host multiple child agents at once, distinguished by **role**:
191
-
192
- - **Omit by default.** Set a `role` only when the user very explicitly asked for a named one, or when a skill the user invoked prescribes one (e.g. `adversarial-review` uses `reviewer`). Never pick one yourself.
193
- - **Purpose.** Roles let independent streams of work run concurrently in one repo — e.g. a default agent implementing a feature while a `reviewer` or `ci-investigator` runs alongside. Each (repo, role) pair has at most one active child.
194
- - **Default role.** An omitted `role` means the default role: `spawn_agent` without `role` starts or follows up with the repo's default-role agent.
195
- - **Logs.** Only default-role agents upload logs to the cloud and appear in the multiplexed log stream (`polygraph session logs`). Inspect non-default agents locally with `polygraph agent attach --role <role>`.
196
-
197
- ## Simple tasks (fire-and-forget)
198
-
199
- Use this pattern when the task is well-defined and the child is not expected to need clarification. It is a single-round delegation: kick it off, poll until terminal, then push branch + create PR.
200
-
201
- Delegate through a background `Task` subagent rather than calling `spawn_agent`/`show_agent` in the main conversation — direct calls flood the context with polling noise. This is a hard requirement, not a suggestion.
202
-
203
- 1. Launch one background `Task` per repo with `subagent_type: "polygraph:polygraph-delegate-subagent"` and `run_in_background: true`, passing `sessionId`, `repo`, `instruction`, and optional `role`/`context`. The subagent calls `spawn_agent`, then polls `show_agent` via chained `waitForTransitionMs` long-poll calls until terminal.
204
- 2. Delegate to several repos in parallel by launching multiple background Tasks at once — one delegation per (repo, role). Read their output files later for progress.
205
- 3. Each subagent watches `child.status` on its `children[]` entry (matching its repo and role) and exits at a terminal status — `'completed'`, `'failed'`, or `'cancelled'`.
206
- 4. Once all report terminal, continue to `push_branch` + `create_pr`.
207
-
208
- To debug a stuck subagent you can call `show_agent` as a one-off, but routine polling belongs in the background subagents.
209
-
210
- Use Simple when the task is well-defined and the child will not need clarification.
211
-
212
- ## Multi-turn tasks (interactive)
213
-
214
- Use this pattern when the child may need clarification, the task is exploratory, or interactive collaboration is desired. The orchestrator exposes paused children via the `'input-required'` status.
215
-
216
- 1. Call `spawn_agent` with the initial `instruction` (and optionally `role`). Parse the response:
217
-
218
- ```json
219
- { "taskId": "…", "message": "…", "status": "delegated" }
220
- ```
221
-
222
- 2. Poll `show_agent` via chained `waitForTransitionMs` long-poll calls (`repo` is required; pass the same `role` you spawned with to narrow to that agent). The response shape is `{ children: PolygraphChildStatusItem[] }` with one entry per matching agent in that repo. On your delegation's entry, inspect:
223
-
224
- - `child.status` — one of `'created'`, `'in-progress'`, `'input-required'`, `'permission-required'`, `'completed'`, `'failed'`, `'cancelled'` (British double-L on `'cancelled'`).
225
- - `child.inputRequiredQuestion` — populated only when `child.status === 'input-required'`.
226
- - `child.lastOutputLines` — recent log tail.
227
- - `child.repoFullName` — which repo is talking.
228
-
229
- Drive the state machine:
230
-
231
- - `child.status === 'in-progress'` or `'created'` — continue polling.
232
- - `child.status === 'input-required'` — read `child.inputRequiredQuestion`, surface it to the user verbatim (e.g. "The child agent in `{child.repoFullName}` needs input: {child.inputRequiredQuestion}"), get the answer, then call `spawn_agent` again with the same `repo`, the same `role`, and `instruction: <answer>` — it is routed to that (repo, role)'s active task automatically. Continue polling.
233
- - `child.status === 'completed'` — read `child.lastOutputLines`, proceed to `push_branch` + `create_pr`.
234
- - `child.status === 'failed'` — read `child.lastOutputLines`, surface the failure.
235
- - `child.status === 'cancelled'` — the child was stopped via `stop_agent`; see below.
236
- - `child.status === 'permission-required'` — the child is waiting on a permission decision; see "Handling permission requests" below.
237
-
238
- 3. To abort mid-flight, call `stop_agent` with `{ sessionId, repo, role? }`. The response is:
239
-
240
- ```json
241
- {
242
- "taskId": "…",
243
- "state": "cancelled",
244
- "sessionPreserved": true,
245
- "output": "…",
246
- "message": "…"
247
- }
248
- ```
249
-
250
- Because `sessionPreserved: true`, the stopped agent's session can be restored later for context. After resuming, do not make changes or continue prior work until the user explicitly asks for changes.
191
+ ## Delegating to other repos
251
192
 
252
- Use Multi-turn when the child may need clarification, the task is exploratory, or interactive collaboration is desired. Otherwise use Simple.
193
+ Working across more than one repo, or delegating any task? Read [`reference/delegation.md`](reference/delegation.md) first — required. Delegation is the only way to act on other repos, and the reference holds the contract that keeps it cheap and trackable: skipping it leads to re-pasting briefs, polling in the main conversation, and touching other repos' clones directly — each of which burns tokens or breaks session tracking.
253
194
 
254
195
  <!-- Claude and Codex parents handle permission gates via the native MCP elicitation dialog
255
196
  rendered by polygraph-mcp's show_agent handler. The dialog targets the parent harness's
@@ -351,13 +292,14 @@ If the session has a description timeline, also display:
351
292
 
352
293
  ## Best Practices
353
294
 
354
- 1. **Delegate via background subagents** — run every `spawn_agent`/`show_agent` through `Task(run_in_background: true)`; direct calls flood the context with polling noise.
295
+ 1. **Wait in background subagents** — `spawn_agent` is fine to call directly, but every waited `show_agent` poll belongs in a `Task(run_in_background: true)`; inline polling floods the context with status noise.
355
296
 
356
- 1. **Poll child status before proceeding** — Always verify child agents have reached a terminal `child.status` (`'completed'`, `'failed'`, or `'cancelled'`) via `show_agent` before pushing branches or creating PRs
297
+ 1. **Read each result once** — when a poller exits, read that child with a single unwaited `show_agent(sessionId, id)`; `result.text` is the child's final message. Only reach for an explicit `tail` if that is not enough.
298
+ 1. **Poll child status before proceeding** — Always verify child agents have reached a terminal `child.status` (`'completed'`, `'failed'`, or `'cancelled'`) before pushing branches or creating PRs
357
299
  1. **Link PRs in descriptions** - Reference related PRs in each PR body
358
300
  1. **Keep PRs as drafts** until all repos are ready
359
301
  1. **Always pass `description`** when calling `create_pr`, `associate_pr`, or `update_session` — it is required and must follow the Session Description Policy
360
302
  1. **Test integration** before marking PRs ready
361
303
  1. **Coordinate merge order** if there are deployment dependencies
362
304
 
363
- 1. **Use `stop_agent` to clean up** — Stop child agents that are stuck or no longer needed (pass `role` to target a non-default agent). The child's session is preserved (`sessionPreserved: true`) so the context can be restored later, but after resuming you must wait for explicit user instructions before making changes.
305
+ 1. **Use `stop_agent` to clean up** — Stop child agents that are stuck or no longer needed (pass the delegation id). The child's session is preserved (`sessionPreserved: true`) so the context can be restored later, but after resuming you must wait for explicit user instructions before making changes.
@@ -0,0 +1,105 @@
1
+ # Delegation Reference
2
+
3
+ Delegation is the only way to act on a repository other than the one you are in. Polygraph keeps local clones of the other repos in the session, but they are not yours to touch: never `cd` into them, read their files, or run git in them. Everything happens through `spawn_agent` and `show_agent`.
4
+
5
+ The flow is **pointer-based**. `spawn_agent` hands you a delegation id. A cheap background subagent watches that id and tells you when it stops moving. You then read the answer yourself, once. Nothing re-pastes the brief, and no log lines pass through a middleman.
6
+
7
+ ## The delegation id
8
+
9
+ `spawn_agent` returns a short id (e.g. `frontend-1`) that names one child run. It is the handle for everything afterwards — polling, reading the result, following up, stopping. The id pins the repo AND the role, so once you have it you never re-specify either.
10
+
11
+ Keep every id you are given. Losing one means falling back to `repo` + `role` lookups, which are ambiguous the moment a repo hosts more than one agent.
12
+
13
+ ## Spawning
14
+
15
+ Call `spawn_agent` directly from the main conversation. This is a fast, non-blocking call that returns an id — it is not polling and does not belong in a subagent.
16
+
17
+ ```
18
+ spawn_agent(
19
+ sessionId: "<sessionId>",
20
+ repo: "<org/repo-name>",
21
+ instruction: "<the task instruction>",
22
+ role: "<optional role>",
23
+ context: "<optional context>"
24
+ )
25
+ ```
26
+
27
+ Write the instruction as if to a competent engineer who cannot see your conversation: state the goal, the constraints, and what "done" looks like. The child has its own repo and its own context; it inherits nothing from yours.
28
+
29
+ Delegate to several repos in parallel by calling `spawn_agent` once per repo before waiting on any of them.
30
+
31
+ **Own-repo rule.** With the default role, `repo` must be a repository other than the one you are working in — never delegate into your own repo with the default role; work on it directly (ordinary local subagents are fine for that). Delegating into your own repo IS allowed with an explicit non-default `role`, because each (repo, role) pair is a separate agent slot and the child then runs alongside your own default-role work without colliding with it.
32
+
33
+ ## Waiting
34
+
35
+ For each id, launch one background poller subagent whose entire job is to block until that child stops moving. Give it the `sessionId` and the `id`, and nothing else.
36
+
37
+ - **Claude Code** — a background `Task` with `subagent_type: "polygraph:polygraph-delegate-subagent"`, `run_in_background: true`, and description `Delegate to <repo>`. Fall back to the bare agent name only if the namespaced form is not found.
38
+ - **OpenCode** — invoke `@polygraph-delegate-subagent`.
39
+ - **Codex** — launch `agent_type: "polygraph-delegate-subagent"` via Codex's own `spawn_agent`, and collect it with `wait_agent`.
40
+
41
+ The poller has exactly one tool and cannot read logs. It exits with a few lines naming the repo, the id, and the final status. That message is a doorbell, not a report — it tells you the child is worth reading, and nothing about what the child did.
42
+
43
+ **Routine polling never happens in the main conversation.** A waited `show_agent` loop run inline floods your context with status noise and is the single largest avoidable cost in a multi-repo session. That is what the poller exists to absorb.
44
+
45
+ ## Reading the result
46
+
47
+ When a poller exits, read the child's answer yourself with a single **unwaited** `show_agent` — no `waitForTransitionMs`, no `tail`:
48
+
49
+ ```
50
+ show_agent(sessionId: "<sessionId>", id: "<id>")
51
+ ```
52
+
53
+ `result.text` is the child's final message: what it did, what it found, what it wants you to know. This is the payload. Read it once, in the main conversation, and act on it.
54
+
55
+ One-off unwaited reads like this are cheap and expected inline. It is the *waiting* that belongs in a subagent, not the reading.
56
+
57
+ ## When the result is not enough
58
+
59
+ Only if `result.text` is missing, truncated, or the child failed in a way you cannot explain from it:
60
+
61
+ - Pass an explicit `tail` to `show_agent` to pull recent log lines.
62
+ - Page further back with the `page` param: `tail: 5, page: 2` returns the 5 lines before the newest 5, `page: 3` the window before that.
63
+ - If the user wants to watch the run live, point them at `polygraph agent attach <repo>` (plus `--role <role>` for a non-default agent) — an interactive terminal view for humans, not a command for you to run.
64
+
65
+ These are deliberate, targeted follow-ups. None of them belongs in a polling loop, and none of them is a reason to go looking at transcript files, `~/.polygraph/sessions`, or anything a harness saved to disk because a tool result was too large. `show_agent` is the supported interface.
66
+
67
+ ## Follow-ups
68
+
69
+ To send a child more work, call `spawn_agent` again with the SAME `repo` and the SAME `role`. If that (repo, role) still has a live task — working, or paused waiting on you — the orchestrator delivers your instruction to it as a follow-up instead of starting a second run. A (repo, role) pair therefore has at most one active child at a time.
70
+
71
+ A follow-up returns a **new** delegation id, linked to the previous one by a `continues` reference. The new id is the live handle: poll it, read it, follow up on it. The old id still addresses the earlier turn if you need to look back at it.
72
+
73
+ After a follow-up, launch a fresh poller subagent for the new id. The old poller has already exited; it does not resume.
74
+
75
+ ## Input-required
76
+
77
+ When a child needs an answer from you, it stops and the poller exits with status `input-required` and "needs attention."
78
+
79
+ Read the child with an unwaited `show_agent` as usual. `inputRequiredQuestion` carries the child's verbatim question. Surface it to the user as the child asked it — do not paraphrase or answer on the user's behalf — then send the answer back as an ordinary follow-up `spawn_agent` for the same (repo, role). Poll the new id it returns.
80
+
81
+ `permission-required` is a different state and is not yours to resolve here; see "Handling permission requests" in the skill.
82
+
83
+ ## Roles
84
+
85
+ A repository in a session can host several child agents at once, distinguished by **role**:
86
+
87
+ - **Omit by default.** Set a `role` only when the user very explicitly asked for a named one, or when a skill the user invoked prescribes one (e.g. `adversarial-review` uses `reviewer`). Never invent one.
88
+ - **Purpose.** Roles let independent streams of work run concurrently in one repo — a default agent implementing a feature while a `reviewer` or `ci-investigator` runs alongside. Each (repo, role) pair has at most one active child.
89
+ - **Default role.** An omitted `role` means the default role: `spawn_agent` without `role` starts or follows up with that repo's default-role agent.
90
+ - **Ids pin the role.** A delegation id already identifies one (repo, role) pair, so `show_agent` and `stop_agent` by id need no `role` argument. Pass `role` only when addressing an agent by `repo` instead of by id.
91
+ - **Logs.** Only default-role agents upload logs to the cloud and appear in the multiplexed stream (`polygraph session logs`). A non-default agent's transcript stays on this machine — the user can watch it with `polygraph agent attach <repo> --role <role>`.
92
+
93
+ ## Stopping
94
+
95
+ Cancel a running child by id:
96
+
97
+ ```
98
+ stop_agent(sessionId: "<sessionId>", id: "<id>")
99
+ ```
100
+
101
+ The response reports `sessionPreserved: true`: the stopped agent's session is kept so its context can be restored later. Restoring is read-only. After a resume, do not continue the prior work or make further changes until the user explicitly asks for them.
102
+
103
+ ## Before publishing
104
+
105
+ Every delegation must reach a terminal status — `completed`, `failed`, or `cancelled` — before you push branches or open PRs. A poller exiting on `input-required` is not terminal; it means the child is still waiting on you.
@@ -32,7 +32,7 @@ For a fresh Codex Desktop conversation started with `/polygraph:session-start`,
32
32
 
33
33
  Pick one path before using tools.
34
34
 
35
- 1. **Existing session with repos** - If a Polygraph session ID is already in scope and the user did not ask to add repositories, call `show_session` to inspect it and print the session details. Do not create a new session.
35
+ 1. **Existing session with repos** - If a Polygraph session ID is already in scope and the user did not ask to add repositories, pick by intent: to continue the session's work from this conversation, join it via `resume_session`; to only inspect it, call `show_session` and print the session details. Do not create a new session.
36
36
  2. **Existing session, adding exact repository refs** - If a session ID is in scope and the user provided exact refs by ID, short name, full name, GitHub `owner/repo` slug, URL-like slug, or MCP resource syntax, call `add_repo(sessionId, repoIds: [...])` directly. Do not run discovery.
37
37
  3. **Existing session, discovery needed** - If a session ID is in scope but repos need to be discovered or selected, launch `polygraph-init-subagent` with the existing `sessionId` and `userContext`. The subagent must use `add_repo`, not `start_session`.
38
38
  4. **No session in scope** - Launch `polygraph-init-subagent` with `userContext` and no `sessionId`. The subagent will discover related repositories when needed and call `start_session`.