@polygraph/codex-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.
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +34 -11
- package/agents/polygraph-delegate-subagent.toml +27 -134
- package/hooks/agent-session-link.mjs +6 -1
- package/hooks/record-session-mapping.mjs +12 -9
- package/package.json +1 -1
- package/skills/await-polygraph-ci/SKILL.md +7 -7
- package/skills/polygraph/SKILL.md +24 -99
- package/skills/polygraph/reference/delegation.md +105 -0
- package/skills/session-start/SKILL.md +1 -1
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
|
|
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,158 +1,51 @@
|
|
|
1
1
|
name = "polygraph-delegate-subagent"
|
|
2
|
-
description = "
|
|
2
|
+
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."
|
|
3
3
|
developer_instructions = '''
|
|
4
4
|
# Polygraph Delegate Subagent
|
|
5
5
|
|
|
6
|
-
You
|
|
6
|
+
You wait for one Polygraph child agent to finish. You do not read its work.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
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.
|
|
9
9
|
|
|
10
|
-
## Input
|
|
10
|
+
## Input (from the main agent)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
| Parameter | Description |
|
|
13
|
+
| ----------- | ------------------------------------------------------------ |
|
|
14
|
+
| `sessionId` | The Polygraph session ID |
|
|
15
|
+
| `id` | The delegation id returned by `spawn_agent` (e.g. `frontend-1`) |
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
| ------------- | ---------------------------------------------------------------------------- |
|
|
16
|
-
| `sessionId` | The Polygraph session ID |
|
|
17
|
-
| `repo` | Repository to delegate to (e.g., `org/repo-name`) |
|
|
18
|
-
| `instruction` | The task instruction for the child agent |
|
|
19
|
-
| `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. |
|
|
20
|
-
| `context` | (Optional) Additional context to pass to the child agent |
|
|
17
|
+
## Loop
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
Call the `show_agent` tool with exactly these arguments, repeatedly:
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
- `sessionId`: the session ID
|
|
22
|
+
- `id`: the delegation id
|
|
23
|
+
- `waitForTransitionMs`: 300000
|
|
25
24
|
|
|
26
|
-
`
|
|
25
|
+
Never pass `tail`. Never call any other tool. Never read files, transcripts, or logs.
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
Each call blocks up to 5 minutes, then returns the child's `status` on `children[0]`:
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
sessionId: "<sessionId>",
|
|
33
|
-
repo: "<repo>",
|
|
34
|
-
instruction: "<instruction>",
|
|
35
|
-
role: "<role, if any>",
|
|
36
|
-
context: "<context>"
|
|
37
|
-
)
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
The call returns immediately — the child agent runs asynchronously.
|
|
41
|
-
|
|
42
|
-
**Polling with long-poll waits:**
|
|
43
|
-
|
|
44
|
-
Call `show_agent` in a loop, passing `waitForTransitionMs: 50000` on every call:
|
|
45
|
-
|
|
46
|
-
```
|
|
47
|
-
show_agent(
|
|
48
|
-
sessionId: "<sessionId>",
|
|
49
|
-
repo: "<repo>",
|
|
50
|
-
role: "<role, if any>",
|
|
51
|
-
waitForTransitionMs: 50000
|
|
52
|
-
)
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
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.
|
|
29
|
+
- `in-progress` or `created` → call `show_agent` again with the same arguments.
|
|
30
|
+
- `completed`, `failed`, `cancelled`, `input-required`, or `permission-required` → stop looping and exit with the message below.
|
|
56
31
|
|
|
57
|
-
|
|
32
|
+
If a call errors, retry it once. If it errors again, exit with the error text plus the delegation id and stop.
|
|
58
33
|
|
|
59
|
-
|
|
34
|
+
## Exit message (fixed template)
|
|
60
35
|
|
|
61
|
-
|
|
62
|
-
{ "taskId": "…", "message": "…", "status": "delegated" }
|
|
63
|
-
```
|
|
36
|
+
Return exactly this, filled in from the last `show_agent` response — nothing more:
|
|
64
37
|
|
|
65
|
-
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.
|
|
66
|
-
|
|
67
|
-
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:
|
|
68
|
-
|
|
69
|
-
- `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.
|
|
70
|
-
- `child.inputRequiredQuestion` — populated only when `child.status === 'input-required'`; contains the verbatim question the child agent has asked the parent.
|
|
71
|
-
- `child.lastOutputLines` — recent log tail (use for status narration; do not treat as an API surface).
|
|
72
|
-
- `child.repoFullName` — human-facing identifier for which repo is talking.
|
|
73
|
-
|
|
74
|
-
State machine:
|
|
75
|
-
|
|
76
|
-
1. `child.status === 'created'` or `'in-progress'` — child is still executing. Call `show_agent` (with `waitForTransitionMs: 50000`) again.
|
|
77
|
-
2. `child.status === 'input-required'` — child is paused waiting for parent input:
|
|
78
|
-
- Read `child.inputRequiredQuestion`.
|
|
79
|
-
- Surface this question verbatim to the parent/user: "The child agent in `{child.repoFullName}` needs input: {child.inputRequiredQuestion}".
|
|
80
|
-
- Wait for the parent/user to supply an answer.
|
|
81
|
-
- 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.
|
|
82
|
-
- Resume polling.
|
|
83
|
-
|
|
84
|
-
<!-- Claude and Codex parents handle permission gates via the native MCP elicitation dialog
|
|
85
|
-
rendered by polygraph-mcp's show_agent handler. The dialog targets the parent's main
|
|
86
|
-
thread, NOT this subagent. From this subagent's perspective the gate is transient: a
|
|
87
|
-
waited show_agent call returns immediately while the gate is open, but the parent's pick
|
|
88
|
-
resolves it and a later poll sees the child back in progress. Do nothing here. -->
|
|
89
|
-
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:
|
|
90
|
-
|
|
91
|
-
- **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.
|
|
92
|
-
- **Do NOT read `child.pendingPermission` as a question to answer or forward.** It is for inspection/logging only; it is not your input prompt.
|
|
93
|
-
- **Do NOT call any tool** (`spawn_agent`, `stop_agent`, `allow_agent`, `deny_agent`) to resolve it.
|
|
94
|
-
- 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.
|
|
95
|
-
|
|
96
|
-
4. `child.status === 'completed'` — child finished successfully. Read `child.lastOutputLines` for the most recent log tail and report outcome.
|
|
97
|
-
5. `child.status === 'failed'` — child failed. Read `child.lastOutputLines` for failure context and report the error.
|
|
98
|
-
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.
|
|
99
|
-
|
|
100
|
-
## Cancelling a running child
|
|
101
|
-
|
|
102
|
-
To cancel a running child mid-work, call `stop_agent` with the repo (and `role`, per the parameter rule above). Response:
|
|
103
|
-
|
|
104
|
-
```json
|
|
105
|
-
{
|
|
106
|
-
"taskId": "…",
|
|
107
|
-
"state": "cancelled",
|
|
108
|
-
"sessionPreserved": true,
|
|
109
|
-
"output": "…",
|
|
110
|
-
"message": "…"
|
|
111
|
-
}
|
|
112
38
|
```
|
|
39
|
+
Child agent <id> is done.
|
|
113
40
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
When the child agent reaches a terminal status, return a structured summary:
|
|
41
|
+
**Repo:** <repoFullName>
|
|
42
|
+
**Delegation id:** <id>
|
|
43
|
+
**Status:** <status>
|
|
119
44
|
|
|
45
|
+
Read the result with show_agent (id: "<id>").
|
|
120
46
|
```
|
|
121
|
-
## Polygraph Delegation Result
|
|
122
|
-
|
|
123
|
-
**Repo:** <repo>
|
|
124
|
-
**Role:** <role, or "default" when none was given>
|
|
125
|
-
**Status:** <success | failed | cancelled>
|
|
126
|
-
**Session ID:** <sessionId>
|
|
127
|
-
|
|
128
|
-
### Result
|
|
129
|
-
<result text drawn from child.lastOutputLines>
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Timeout
|
|
133
|
-
|
|
134
|
-
If polling exceeds **30 minutes**, return with a timeout status:
|
|
135
|
-
|
|
136
|
-
```
|
|
137
|
-
## Polygraph Delegation Result
|
|
138
|
-
|
|
139
|
-
**Repo:** <repo>
|
|
140
|
-
**Status:** timeout
|
|
141
|
-
**Session ID:** <sessionId>
|
|
142
|
-
**Elapsed:** <minutes>m
|
|
143
|
-
|
|
144
|
-
### Suggestions
|
|
145
|
-
- Check child agent status manually via `show_agent`
|
|
146
|
-
- Consider stopping the child agent via `stop_agent`
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
## Important Notes
|
|
150
47
|
|
|
151
|
-
-
|
|
48
|
+
For `input-required` or `permission-required`, replace "is done." with "needs attention." and keep everything else identical.
|
|
152
49
|
|
|
153
|
-
|
|
154
|
-
- Do NOT call `push_branch` or `create_pr` — those are the main agent's responsibility
|
|
155
|
-
- If `spawn_agent` fails, return the error immediately
|
|
156
|
-
- If `show_agent` returns an error, wait and retry (count as failed poll)
|
|
157
|
-
- After 5 consecutive poll failures, return with `status: error`
|
|
50
|
+
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`.
|
|
158
51
|
'''
|
|
@@ -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
|
-
|
|
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') {
|
|
@@ -27,15 +27,18 @@ export function main({
|
|
|
27
27
|
const link = buildCommandHookLink(payload, agentType, env);
|
|
28
28
|
if (!link) return false;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
@@ -137,7 +137,7 @@ For each repo with `ciStatus: FAILED`, branch on the PR's `ci` object from `show
|
|
|
137
137
|
- **If `pr.ci.cipeUrl` is non-null** → CIPE is authoritative. Delegate investigation using the Nx MCP `ci_information` tool (requires the Nx MCP server — see the prerequisite note above; if nx-mcp is unavailable, report the CIPE URL to the user, offer the install steps from the prerequisite section, and do NOT fetch the URL as a substitute).
|
|
138
138
|
- **If `pr.ci.cipeUrl` is null but `pr.ci.externalCIRuns` exists** → external CI only. Examine failed jobs from `pr.ci.externalCIRuns[].jobs` and use `get_ci_logs(sessionId, repositoryId, jobId)` (a polygraph-mcp tool) for log retrieval, passing `pr.repositoryId` and the failed job's `jobId` straight from the same PR object.
|
|
139
139
|
|
|
140
|
-
**Codex subagent wrapper:**
|
|
140
|
+
**Codex subagent wrapper:** Call the Polygraph MCP `spawn_agent` directly to start each investigation and collect its delegation id, then launch one Codex `spawn_agent` with `agent_type: "polygraph-delegate-subagent"` per id to wait on it, collecting them with `wait_agent`. Only the waiting belongs in the subagent; the unwaited `show_agent` read that returns each investigation is yours to make here.
|
|
141
141
|
|
|
142
142
|
1. Display known info from the PR's `ci` object before delegating:
|
|
143
143
|
|
|
@@ -159,15 +159,15 @@ For each repo with `ciStatus: FAILED`, branch on the PR's `ci` object from `show
|
|
|
159
159
|
|
|
160
160
|
Since `spawn_agent` is non-blocking, you can delegate to multiple failed repos in parallel.
|
|
161
161
|
|
|
162
|
-
3. **Monitor investigation progress** —
|
|
162
|
+
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`:
|
|
163
163
|
|
|
164
164
|
```
|
|
165
|
-
show_agent(sessionId: "<session-id>",
|
|
165
|
+
show_agent(sessionId: "<session-id>", id: "<delegation-id>")
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
-
|
|
168
|
+
`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.
|
|
169
169
|
|
|
170
|
-
4. Collect each child agent's response from
|
|
170
|
+
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.
|
|
171
171
|
|
|
172
172
|
5. Display failure summary for each repo:
|
|
173
173
|
|
|
@@ -203,8 +203,8 @@ For each repo with `ciStatus: FAILED`, branch on the PR's `ci` object from `show
|
|
|
203
203
|
- `cipeUrl` is a browser link for the user — never fetch, curl, WebFetch, or poll it (in the main agent or in child agents). CIPE data is only available via the Nx MCP `ci_information` tool.
|
|
204
204
|
- All heavy CI data inspection happens in child agents via `spawn_agent` to keep this context window clean.
|
|
205
205
|
|
|
206
|
-
- On Codex, the
|
|
206
|
+
- On Codex, the waiting runs inside `polygraph-delegate-subagent` and the main conversation uses `wait_agent` to collect it; `spawn_agent` and the unwaited `show_agent` read stay in the main conversation.
|
|
207
207
|
|
|
208
208
|
- 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.
|
|
209
|
-
- `spawn_agent` is **non-blocking** — it starts the child agent and returns immediately.
|
|
209
|
+
- `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.
|
|
210
210
|
- The `show_session` response is compact and safe to poll from the main agent.
|
|
@@ -6,21 +6,21 @@ description: Guidance for working with Polygraph sessions, shared/resumable agen
|
|
|
6
6
|
|
|
7
7
|
# Working with Polygraph
|
|
8
8
|
|
|
9
|
-
**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
|
|
9
|
+
**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).
|
|
10
10
|
|
|
11
11
|
## Critical Routing Rule (Codex Parent Conversation)
|
|
12
12
|
|
|
13
13
|
Read this before the tool table below — it determines which tools are yours to call directly.
|
|
14
14
|
|
|
15
|
-
- Codex `spawn_agent` ≠ Polygraph MCP `spawn_agent`. Codex `spawn_agent` launches the local custom subagents (`polygraph-init-subagent`, `polygraph-delegate-subagent`). The Polygraph MCP `spawn_agent`
|
|
15
|
+
- Codex `spawn_agent` ≠ Polygraph MCP `spawn_agent`. Codex `spawn_agent` launches the local custom subagents (`polygraph-init-subagent`, `polygraph-delegate-subagent`). The Polygraph MCP `spawn_agent` starts work inside another repository and returns a delegation id; calling it directly from this conversation is fine.
|
|
16
16
|
- **For new sessions:** call Codex `spawn_agent` with `agent_type: "polygraph-init-subagent"`. Do NOT call Polygraph MCP `list_repos` or `start_session` directly from this conversation.
|
|
17
17
|
- **For explicit repo additions to an existing session:** if the user gives exact refs by ID, short name, full name, GitHub `owner/repo` slug, or URL-like slug, call Polygraph MCP `add_repo` directly with those refs. Do NOT call `list_repos` or launch candidate discovery first.
|
|
18
|
-
- **For repo work:** call Codex `spawn_agent` with `agent_type: "polygraph-delegate-subagent"
|
|
18
|
+
- **For repo work:** call the Polygraph MCP `spawn_agent` directly to start the child and get a delegation id, then launch Codex `spawn_agent` with `agent_type: "polygraph-delegate-subagent"` to wait on that id and collect it with `wait_agent`. Waited `show_agent` polling belongs in that subagent; unwaited `show_agent` reads belong here. See `reference/delegation.md`.
|
|
19
19
|
- Do NOT pass `fork_context: true` to Codex `spawn_agent` when `agent_type` is a custom agent — Codex rejects it.
|
|
20
20
|
|
|
21
21
|
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.
|
|
22
22
|
|
|
23
|
-
**Polygraph operates on the current repo in place.** Starting
|
|
23
|
+
**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".
|
|
24
24
|
|
|
25
25
|
## Sandboxing in Polygraph Sessions
|
|
26
26
|
|
|
@@ -36,9 +36,10 @@ Polygraph functionality is available via both MCP tools and CLI commands. Use wh
|
|
|
36
36
|
| --- | --- | --- |
|
|
37
37
|
| `list_repos` | `polygraph repo list` | Discover candidate repositories. Candidate entries do not include repository descriptions; use `semanticQuery` for natural-language discovery. |
|
|
38
38
|
| `start_session` | `polygraph session start --repo <ids>` | Initialize a Polygraph session with selected repositories |
|
|
39
|
-
| `
|
|
40
|
-
| `
|
|
41
|
-
| `
|
|
39
|
+
| `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". |
|
|
40
|
+
| `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`. |
|
|
41
|
+
| `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`. |
|
|
42
|
+
| `stop_agent` | — | Cancel an in-progress child by delegation id; its session is preserved for later read-only context restoration. |
|
|
42
43
|
| `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. |
|
|
43
44
|
| `create_pr` | — | Create draft PRs with session metadata linking related PRs |
|
|
44
45
|
| `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. |
|
|
@@ -87,10 +88,10 @@ After logging in (or if logged in but no org is selected), use `polygraph accoun
|
|
|
87
88
|
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.
|
|
88
89
|
|
|
89
90
|
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.
|
|
90
|
-
1. **Delegate work to each repo** -
|
|
91
|
+
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.
|
|
91
92
|
|
|
92
|
-
4. **Monitor child agents** -
|
|
93
|
-
5. **Stop child agents** (if needed) - Use `stop_agent`
|
|
93
|
+
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.
|
|
94
|
+
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.
|
|
94
95
|
6. **Push branches** - Use `push_branch` after making commits. A required `description` must follow the Session Description Policy.
|
|
95
96
|
7. **Create draft PRs** - Use `create_pr` to create linked draft PRs. Always pass `description` following the Session Description Policy.
|
|
96
97
|
8. **Associate existing PRs** (optional) - Use `associate_pr` to link PRs created outside Polygraph.
|
|
@@ -106,6 +107,8 @@ There are three cases. Pick exactly one before calling any tool. The case labels
|
|
|
106
107
|
|
|
107
108
|
**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`.**
|
|
108
109
|
|
|
110
|
+
**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.**
|
|
111
|
+
|
|
109
112
|
**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.
|
|
110
113
|
|
|
111
114
|
**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`.
|
|
@@ -146,11 +149,11 @@ The subagent will:
|
|
|
146
149
|
|
|
147
150
|
Use this workflow when the user gives a Polygraph session ID and asks to understand, resume, inspect, or investigate prior work.
|
|
148
151
|
|
|
149
|
-
**Resume is not a work command.** If the user's intent is to resume, reconnect, or reconstruct a prior Polygraph session,
|
|
152
|
+
**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.
|
|
150
153
|
|
|
151
|
-
1. Fetch
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
+
1. Fetch session context by intent:
|
|
155
|
+
- 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.
|
|
156
|
+
- 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.
|
|
154
157
|
2. Treat the detailed output as authoritative context. It should include:
|
|
155
158
|
- `<summary>` — the session summary.
|
|
156
159
|
- `<repositories>` — relevant repos, including each repo's `<id>` and `<name>`.
|
|
@@ -192,88 +195,9 @@ Inspect the PR commits/diff and investigate the requested behavior. Report findi
|
|
|
192
195
|
Use this workflow when the user asks which Polygraph session produced, is behind, or changed a particular commit — or a particular line of code.
|
|
193
196
|
**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.
|
|
194
197
|
|
|
195
|
-
##
|
|
196
|
-
|
|
197
|
-
A repository in a session can host multiple child agents at once, distinguished by **role**:
|
|
198
|
-
|
|
199
|
-
- **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.
|
|
200
|
-
- **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.
|
|
201
|
-
- **Default role.** An omitted `role` means the default role: `spawn_agent` without `role` starts or follows up with the repo's default-role agent.
|
|
202
|
-
- **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>`.
|
|
203
|
-
|
|
204
|
-
## Simple tasks (fire-and-forget)
|
|
205
|
-
|
|
206
|
-
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.
|
|
207
|
-
|
|
208
|
-
**CRITICAL:** Routine Polygraph MCP `spawn_agent` and `show_agent` calls MUST run inside the custom Codex `polygraph-delegate-subagent`, not directly in the main conversation. Codex `spawn_agent` launches a local subagent; the Polygraph MCP `spawn_agent` starts work in another repository. Keeping the MCP delegate-and-poll loop inside `polygraph-delegate-subagent` prevents polling noise from filling the user's context.
|
|
209
|
-
|
|
210
|
-
1. For each repo, launch `polygraph-delegate-subagent` via Codex's `spawn_agent`:
|
|
211
|
-
|
|
212
|
-
```
|
|
213
|
-
spawn_agent(
|
|
214
|
-
agent_type: "polygraph-delegate-subagent",
|
|
215
|
-
message: """
|
|
216
|
-
Parameters:
|
|
217
|
-
- sessionId: "<session-id>"
|
|
218
|
-
- repo: "<org/repo-name>"
|
|
219
|
-
- instruction: "<the task instruction>"
|
|
220
|
-
- role: "<optional role>"
|
|
221
|
-
- context: "<optional context>"
|
|
222
|
-
|
|
223
|
-
Call the Polygraph MCP spawn_agent for the repo, then poll show_agent via chained waitForTransitionMs long-poll calls until terminal. Return a structured summary with repo, status, session ID, and result text.
|
|
224
|
-
"""
|
|
225
|
-
)
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
2. Delegate to multiple repos in parallel by launching multiple `polygraph-delegate-subagent` instances before waiting for results — one delegation per (repo, role) at a time.
|
|
229
|
-
3. The subagent watches `child.status` on its delegation's `children[]` entry — the one matching its repo and role — and exits when it sees a terminal status — typically `'completed'` or `'failed'` (and `'cancelled'` if it was stopped).
|
|
230
|
-
4. Collect completed results with `wait_agent` when the main flow needs them, then continue to `push_branch` + `create_pr`.
|
|
231
|
-
|
|
232
|
-
To debug a stuck subagent you can call `show_agent` as a one-off, but routine polling belongs in the background subagents.
|
|
233
|
-
|
|
234
|
-
Use Simple when the task is well-defined and the child will not need clarification.
|
|
235
|
-
|
|
236
|
-
## Multi-turn tasks (interactive)
|
|
237
|
-
|
|
238
|
-
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.
|
|
239
|
-
|
|
240
|
-
1. Call `spawn_agent` with the initial `instruction` (and optionally `role`). Parse the response:
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
{ "taskId": "…", "message": "…", "status": "delegated" }
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
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:
|
|
247
|
-
|
|
248
|
-
- `child.status` — one of `'created'`, `'in-progress'`, `'input-required'`, `'permission-required'`, `'completed'`, `'failed'`, `'cancelled'` (British double-L on `'cancelled'`).
|
|
249
|
-
- `child.inputRequiredQuestion` — populated only when `child.status === 'input-required'`.
|
|
250
|
-
- `child.lastOutputLines` — recent log tail.
|
|
251
|
-
- `child.repoFullName` — which repo is talking.
|
|
252
|
-
|
|
253
|
-
Drive the state machine:
|
|
254
|
-
|
|
255
|
-
- `child.status === 'in-progress'` or `'created'` — continue polling.
|
|
256
|
-
- `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.
|
|
257
|
-
- `child.status === 'completed'` — read `child.lastOutputLines`, proceed to `push_branch` + `create_pr`.
|
|
258
|
-
- `child.status === 'failed'` — read `child.lastOutputLines`, surface the failure.
|
|
259
|
-
- `child.status === 'cancelled'` — the child was stopped via `stop_agent`; see below.
|
|
260
|
-
- `child.status === 'permission-required'` — the child is waiting on a permission decision; see "Handling permission requests" below.
|
|
261
|
-
|
|
262
|
-
3. To abort mid-flight, call `stop_agent` with `{ sessionId, repo, role? }`. The response is:
|
|
263
|
-
|
|
264
|
-
```json
|
|
265
|
-
{
|
|
266
|
-
"taskId": "…",
|
|
267
|
-
"state": "cancelled",
|
|
268
|
-
"sessionPreserved": true,
|
|
269
|
-
"output": "…",
|
|
270
|
-
"message": "…"
|
|
271
|
-
}
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
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.
|
|
198
|
+
## Delegating to other repos
|
|
275
199
|
|
|
276
|
-
|
|
200
|
+
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.
|
|
277
201
|
|
|
278
202
|
<!-- Claude and Codex parents handle permission gates via the native MCP elicitation dialog
|
|
279
203
|
rendered by polygraph-mcp's show_agent handler. The dialog targets the parent harness's
|
|
@@ -375,15 +299,16 @@ If the session has a description timeline, also display:
|
|
|
375
299
|
|
|
376
300
|
## Best Practices
|
|
377
301
|
|
|
378
|
-
1. **
|
|
302
|
+
1. **Route waiting through Codex Polygraph subagents** — Use Codex `spawn_agent` with `agent_type: "polygraph-init-subagent"` to create new sessions, and `agent_type: "polygraph-delegate-subagent"` to wait on each delegation id. The Polygraph MCP `spawn_agent` and unwaited `show_agent` reads are yours to call directly; collect poller results with `wait_agent`.
|
|
379
303
|
|
|
380
|
-
1. **
|
|
304
|
+
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.
|
|
305
|
+
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
|
|
381
306
|
1. **Link PRs in descriptions** - Reference related PRs in each PR body
|
|
382
307
|
1. **Keep PRs as drafts** until all repos are ready
|
|
383
308
|
1. **Always pass `description`** when calling `create_pr`, `associate_pr`, or `update_session` — it is required and must follow the Session Description Policy
|
|
384
309
|
1. **Test integration** before marking PRs ready
|
|
385
310
|
1. **Coordinate merge order** if there are deployment dependencies
|
|
386
311
|
|
|
387
|
-
1. **NEVER
|
|
312
|
+
1. **NEVER run a waited `show_agent` loop in the main conversation**. Waiting MUST run inside `polygraph-delegate-subagent`.
|
|
388
313
|
|
|
389
|
-
1. **Use `stop_agent` to clean up** — Stop child agents that are stuck or no longer needed (pass
|
|
314
|
+
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.
|
|
@@ -35,7 +35,7 @@ For a fresh Codex Desktop conversation started with `/polygraph:session-start`,
|
|
|
35
35
|
|
|
36
36
|
Pick one path before using tools.
|
|
37
37
|
|
|
38
|
-
1. **Existing session with repos** - If a Polygraph session ID is already in scope and the user did not ask to add repositories,
|
|
38
|
+
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.
|
|
39
39
|
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.
|
|
40
40
|
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`.
|
|
41
41
|
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`.
|