runpane 2.4.182 → 2.4.183

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.
@@ -30,15 +30,48 @@ exports.RUNPANE_CONTRACT = {
30
30
  "yes": false,
31
31
  "verbose": false,
32
32
  "watch": {
33
- "format": "lines",
34
- "heartbeatSeconds": 60,
35
- "idleAfterMs": 600000,
36
- "settleMs": 0,
37
- "blockedSettleMs": 0,
38
- "minIntervalMs": 0,
39
- "idleBackoff": false,
40
- "agentsOnly": true,
41
- "includeHeldInputPresence": true
33
+ "follow": {
34
+ "summary": "What `runpane watch --follow` does with no other flags: quiet, batched updates and expiry after 3 hours with nothing happening. Individual duration flags override the defaults.",
35
+ "kinds": [
36
+ "agent.report",
37
+ "agent.ready",
38
+ "agent.blocked",
39
+ "agent.idle",
40
+ "panel.exited",
41
+ "panel.stopped",
42
+ "pane.gone",
43
+ "pane.associated",
44
+ "pane.detached",
45
+ "pr.conflicted",
46
+ "pr.checks",
47
+ "pr.merged",
48
+ "pr.closed",
49
+ "pr.head",
50
+ "pr.review",
51
+ "pr.comment",
52
+ "pr.stopped",
53
+ "github.unavailable",
54
+ "github.recovered"
55
+ ],
56
+ "readySettle": "3m",
57
+ "blockedSettle": "30s",
58
+ "minInterval": "10m",
59
+ "idleAfter": "10m",
60
+ "idleBackoff": true,
61
+ "expireAfter": "3h",
62
+ "heartbeat": "off",
63
+ "includeShells": false,
64
+ "heldInputDetection": true
65
+ },
66
+ "untilEvent": {
67
+ "summary": "What `runpane watch --until-event` does with no other flags: waits for the first event worth waking an agent for, prints it immediately without follow settling or batching, and exits 0.",
68
+ "kinds": [
69
+ "agent.report",
70
+ "agent.blocked",
71
+ "panel.exited",
72
+ "pane.gone"
73
+ ]
74
+ }
42
75
  }
43
76
  },
44
77
  "enums": {
@@ -390,7 +423,7 @@ exports.RUNPANE_CONTRACT = {
390
423
  "name": "watch",
391
424
  "summary": "Wait for workspace agent and Pane transitions using a daemon-held cursor.",
392
425
  "usage": [
393
- "runpane watch [--as <name>|--since <generation>] [--follow|--until-event] [--format <lines|json>] [--heartbeat <seconds>] [--idle-after <ms>] [--settle <ms>] [--blocked-settle <ms>] [--min-interval <ms>] [--idle-backoff] [--all-managed|--pane <id>|--session <id|name>] [--include-shells] [--self-test] [--quiet] [--kinds <kind,...>] [--repo <selector>] [--name-contains <text>] [--timeout-ms <ms>] [--from <now|earliest>] [--json] [--pane-dir <path>]"
426
+ "runpane watch [--follow|--until-event] [--session <id|name>|--pane <id>] [--json] [overrides]"
394
427
  ],
395
428
  "toolsets": [
396
429
  "agents"
@@ -1403,6 +1436,48 @@ exports.RUNPANE_CONTRACT = {
1403
1436
  "sessionResult"
1404
1437
  ],
1405
1438
  "localControl": true
1439
+ },
1440
+ {
1441
+ "name": "pr watch",
1442
+ "summary": "Register durable GitHub PR monitoring for a Pane.",
1443
+ "usage": [
1444
+ "runpane pr watch [--pane <id>] --url <PR URL> [--head <full-sha>] [--json] [--pane-dir <path>]"
1445
+ ],
1446
+ "toolsets": [
1447
+ "agents"
1448
+ ],
1449
+ "jsonSchemas": [
1450
+ "prWatchResult"
1451
+ ],
1452
+ "localControl": true
1453
+ },
1454
+ {
1455
+ "name": "pr unwatch",
1456
+ "summary": "Remove a Pane’s explicit PR registration.",
1457
+ "usage": [
1458
+ "runpane pr unwatch [--pane <id>] --url <PR URL> [--json] [--pane-dir <path>]"
1459
+ ],
1460
+ "toolsets": [
1461
+ "agents"
1462
+ ],
1463
+ "jsonSchemas": [
1464
+ "prWatchResult"
1465
+ ],
1466
+ "localControl": true
1467
+ },
1468
+ {
1469
+ "name": "pr list",
1470
+ "summary": "List PR registrations and stopped watches.",
1471
+ "usage": [
1472
+ "runpane pr list [--pane <id>] [--json] [--pane-dir <path>]"
1473
+ ],
1474
+ "toolsets": [
1475
+ "agents"
1476
+ ],
1477
+ "jsonSchemas": [
1478
+ "prWatchResult"
1479
+ ],
1480
+ "localControl": true
1406
1481
  }
1407
1482
  ],
1408
1483
  "flags": {
@@ -1684,35 +1759,35 @@ exports.RUNPANE_CONTRACT = {
1684
1759
  "value": "<pane-id>",
1685
1760
  "description": "Exclude a Pane from workspace watch results; repeatable."
1686
1761
  },
1687
- {
1688
- "name": "--format",
1689
- "value": "<lines|json>",
1690
- "description": "Watch output format; scoped to the watch command."
1691
- },
1692
1762
  {
1693
1763
  "name": "--heartbeat",
1694
- "value": "<seconds>",
1695
- "description": "Maximum healthy silence in follow mode; 0 disables heartbeats."
1764
+ "value": "<duration>",
1765
+ "description": "Print a HEARTBEAT line this often under --follow (a duration; default off)."
1696
1766
  },
1697
1767
  {
1698
1768
  "name": "--idle-after",
1699
- "value": "<milliseconds>",
1700
- "description": "Emit agent.idle after this READY duration; 0 disables idle events."
1769
+ "value": "<duration>",
1770
+ "description": "First IDLE reminder after a READY panel stays idle (a duration; default 10m under --follow; 0 turns IDLE off)."
1701
1771
  },
1702
1772
  {
1703
- "name": "--settle",
1704
- "value": "<milliseconds>",
1705
- "description": "Follow-only opt-in: emit READY only after the panel stays idle this long; a BUSY inside the window cancels it silently."
1773
+ "name": "--expire-after",
1774
+ "value": "<duration>",
1775
+ "description": "End a --follow watch after this long with nothing but IDLE, printing WATCH EXPIRED and exiting 0 (a duration; default 3h; 0 never expires)."
1776
+ },
1777
+ {
1778
+ "name": "--ready-settle",
1779
+ "value": "<duration>",
1780
+ "description": "Quiet time before READY under --follow (a duration; default 3m)."
1706
1781
  },
1707
1782
  {
1708
1783
  "name": "--blocked-settle",
1709
- "value": "<milliseconds>",
1710
- "description": "Follow-only opt-in: emit BLOCKED only after the panel stays blocked this long; an in-pane answer inside the window cancels it silently."
1784
+ "value": "<duration>",
1785
+ "description": "Time before BLOCKED under --follow (a duration; default 30s)."
1711
1786
  },
1712
1787
  {
1713
1788
  "name": "--min-interval",
1714
- "value": "<milliseconds>",
1715
- "description": "Follow-only opt-in: hold non-urgent lines and flush them together at most once per interval; BLOCKED, REPORT, PR CONFLICTED, and PR CHECKS FAILED bypass it."
1789
+ "value": "<duration>",
1790
+ "description": "Batch window for non-urgent lines under --follow (a duration; default 10m)."
1716
1791
  },
1717
1792
  {
1718
1793
  "name": "--body-file",
@@ -1818,6 +1893,11 @@ exports.RUNPANE_CONTRACT = {
1818
1893
  "name": "--effort",
1819
1894
  "value": "<level>",
1820
1895
  "description": "Reasoning effort for the receiving agent (minimal, low, medium, high, xhigh, max); overrides the destination text."
1896
+ },
1897
+ {
1898
+ "name": "--epoch",
1899
+ "value": "<epoch>",
1900
+ "description": "Journal epoch paired with --since; a mismatch emits _reset instead of silently skipping events."
1821
1901
  }
1822
1902
  ],
1823
1903
  "localBoolean": [
@@ -1893,18 +1973,10 @@ exports.RUNPANE_CONTRACT = {
1893
1973
  "name": "--include-held-input",
1894
1974
  "description": "Include up to 120 characters of unsubmitted composer text in ready events."
1895
1975
  },
1896
- {
1897
- "name": "--agents-only",
1898
- "description": "Limit workspace events to CLI agent panels."
1899
- },
1900
1976
  {
1901
1977
  "name": "--all-stopped",
1902
1978
  "description": "Resume every stopped agent panel in every non-archived Pane."
1903
1979
  },
1904
- {
1905
- "name": "--all-managed",
1906
- "description": "Watch every non-archived, non-hidden managed pane. This is the default scope; the flag states it explicitly and cannot be combined with --pane."
1907
- },
1908
1980
  {
1909
1981
  "name": "--include-shells",
1910
1982
  "description": "Include ordinary shell panels in follow mode."
@@ -1917,17 +1989,6 @@ exports.RUNPANE_CONTRACT = {
1917
1989
  "name": "--self-test",
1918
1990
  "description": "Probe the current daemon watch path without advancing a named cursor."
1919
1991
  },
1920
- {
1921
- "name": "--quiet",
1922
- "aliases": [
1923
- "--no-control-lines"
1924
- ],
1925
- "description": "Watch only: drop the WATCH OK, HEARTBEAT, and WATCH RECONNECTED control lines (_ok, _heartbeat, _reconnected in JSON). Errors, resets, and dropped counts always print."
1926
- },
1927
- {
1928
- "name": "--idle-backoff",
1929
- "description": "Follow-only opt-in: IDLE fires at --idle-after, then 30m, 1h, 3h, then daily per panel; any activity resets it."
1930
- },
1931
1992
  {
1932
1993
  "name": "--report",
1933
1994
  "description": "Prepare a redacted doctor report; external filing still requires --yes."
@@ -1986,7 +2047,7 @@ exports.RUNPANE_CONTRACT = {
1986
2047
  " runpane workspace list [--json]",
1987
2048
  " runpane workspace <machine> read|write|exec|<command>",
1988
2049
  " runpane watch --follow",
1989
- " runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone",
2050
+ " runpane watch --until-event",
1990
2051
  " runpane panes create --repo <selector> --name <name> --agent <codex|claude|cursor> [--base-branch <branch>] [--source user|agent] [--focus|--no-focus] [--wait-ready]",
1991
2052
  " runpane panes archive --pane <pane-id> [--source user|agent] [--force] [--remove-worktree] [--dry-run] --yes",
1992
2053
  " runpane panes archive --session <id|name> --merged [--remove-worktree] [--dry-run] --yes",
@@ -2236,54 +2297,60 @@ exports.RUNPANE_CONTRACT = {
2236
2297
  ],
2237
2298
  "watch": [
2238
2299
  "Usage:",
2239
- " runpane watch [--as <name>|--since <generation>] [--follow|--until-event] [options]",
2300
+ " runpane watch Print what changed since your cursor, then exit",
2301
+ " runpane watch --follow Keep watching, tuned for an agent watching unattended",
2302
+ " runpane watch --until-event Wait for one event worth waking for, print it, exit 0",
2303
+ "",
2304
+ "Waits for agent, Pane, panel, and PR events from the daemon without polling.",
2305
+ "",
2306
+ "What --follow does by default (see defaults.watch.follow in the contract):",
2307
+ " prints WATCH OK once when armed, then one line per event, with no heartbeats",
2308
+ " kinds: agent.report, agent.ready, agent.blocked, agent.idle, panel.exited, panel.stopped,",
2309
+ " pane.gone, pane.associated, pane.detached, pr.conflicted, pr.checks, pr.merged",
2310
+ " READY after 3m of quiet; BLOCKED after 30s; other lines batched at most every 10m",
2311
+ " (REPORT, BLOCKED, conflicts, and failed checks arrive at once)",
2312
+ " IDLE reminders at 10m, 30m, 1h, 3h, then daily",
2313
+ " ends with WATCH EXPIRED and exit 0 after 3h with nothing but IDLE",
2240
2314
  "",
2241
- "Waits for agent state, Pane lifecycle, and panel exit transitions without polling.",
2315
+ "What --until-event does by default: waits for agent.report, agent.blocked, panel.exited,",
2316
+ "or pane.gone, prints that one line, and exits 0. Run it as a background task, act on",
2317
+ "its line, then run it again; its named cursor means nothing is missed between runs.",
2318
+ "Single-event waits deliver immediately without follow settling or batching.",
2319
+ "--follow --since <generation> has no consumer name and bypasses settling and batching. Cadence requires a named follow consumer; --as cannot be combined with numeric --since.",
2242
2320
  "",
2243
2321
  "Options:",
2244
- " --as <name> Use a persisted daemon-held cursor",
2245
- " --since <generation> Use an explicit cursor instead",
2246
- " --from <now|earliest> Starting point for a new named cursor",
2247
- " --follow Continue waiting until interrupted",
2248
- " --until-event Exit 0 after the first event; --timeout-ms is the total deadline (exit 124)",
2249
- " --format <lines|json> Output format; lines is the follow default",
2250
- " --heartbeat <seconds> Healthy-silence bound; defaults to 60 under --follow",
2251
- " --idle-after <ms> Re-firing READY idle interval; defaults to 600000 under --follow",
2252
- " --settle <ms> Opt-in: emit READY only after this quiet window (--follow only)",
2253
- " --blocked-settle <ms> Opt-in: emit BLOCKED only after this window (--follow only)",
2254
- " --min-interval <ms> Opt-in: batch non-urgent lines per interval; BLOCKED, REPORT, and PR failures bypass (--follow only)",
2255
- " --idle-backoff Opt-in: IDLE at --idle-after, 30m, 1h, 3h, then daily (--follow only)",
2256
- " --all-managed Explicitly watch all managed panes",
2257
- " --include-shells Include ordinary shell panels",
2258
- " --agents-only Limit to CLI agent panels",
2259
- " --exclude-pane <id> Exclude a Pane; repeatable",
2260
- " --self-test Anonymous, read-only daemon path probe",
2261
- " --quiet Drop WATCH OK, HEARTBEAT, and WATCH RECONNECTED lines",
2262
- " --no-control-lines Alias for --quiet",
2263
- " --kinds <kind,...> Limit event kinds",
2264
- " --pane <id> Limit to a Pane; repeatable",
2265
- " --session <id|name> Follow every Pane in a named Session; associate/detach apply live",
2322
+ " --session <id|name> Watch every Pane in a Session; associate/detach apply live",
2323
+ " --pane <id> Watch one Pane; repeatable (default: every managed Pane)",
2324
+ " --exclude-pane <id> Leave out a Pane; repeatable",
2266
2325
  " --repo <selector> Limit to a saved repository",
2267
2326
  " --name-contains <text> Limit by Pane name substring",
2268
- " --timeout-ms <ms> Wait timeout; 0 polls once; the total deadline with --until-event",
2327
+ " --include-shells Include plain shell panels (default: agent panels only)",
2328
+ " --kinds <kind,...> Replace the default event kinds",
2329
+ " --json One JSON object per line",
2330
+ " --as <name> Name the cursor (default: from --session or this panel)",
2331
+ " --since <generation> Use an explicit cursor instead of a name",
2332
+ " --epoch <epoch> Pair with --since to detect journal resets",
2333
+ " --from <now|earliest> Where a new named cursor starts (default: now)",
2334
+ " --self-test Check the daemon watch path and exit",
2335
+ "",
2336
+ "Overrides (durations such as 30s, 10m, 3h; a bare number is milliseconds; 0 turns it off):",
2337
+ " --ready-settle <duration> Quiet time before READY (default 3m)",
2338
+ " --blocked-settle <duration> Time before BLOCKED (default 30s)",
2339
+ " --min-interval <duration> Batch window for non-urgent lines (default 10m)",
2340
+ " --idle-after <duration> First IDLE reminder (default 10m)",
2341
+ " --expire-after <duration> End after this long with nothing but IDLE (default 3h)",
2342
+ " --heartbeat <duration> Print a HEARTBEAT line this often (default off)",
2343
+ " --timeout-ms <duration> Per-request wait; with --until-event, the total deadline (exit 124)",
2269
2344
  " --limit <count> Maximum entries per response",
2270
- " --ack-now Use at-most-once named-cursor delivery",
2345
+ " --ack-now At-most-once cursor delivery",
2271
2346
  " --include-held-input Include unsubmitted composer text in JSON",
2272
- " --no-held-input Disable redacted STUCK detection",
2347
+ " --no-held-input Turn off STUCK detection",
2273
2348
  " --pane-dir <path> Connect to a specific Pane data directory",
2274
- " --json Alias for --format json",
2275
- "",
2276
- "Defaults are responsive: no settle, no batching, all kinds, IDLE every --idle-after. BUSY carries no action; drop it with --kinds.",
2277
- "agent.report (a worker ran `runpane report`) is opt-in: list it in --kinds. REPORT lines skip the --min-interval batch.",
2278
- "--follow and --until-event imply --agents-only, except for agent.report: a report from any panel, a shell tab included, still arrives.",
2279
- "Exit-to-wake (Claude Code background Bash, Codex, any harness that wakes when a process exits): runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone",
2280
- " Run it as a background task; when it exits, act on its line, then run it again. Never pipe --follow into grep -m1 or head to wait.",
2281
- "Unattended (Pane Chat default): --quiet --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
2282
- "User present: the same kinds with --settle 60000 --blocked-settle 15000 --min-interval 120000 and no --idle-backoff",
2283
- "Session orchestrator: runpane watch --session <id|name> --follow --quiet --json --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone,pane.associated,pane.detached,pr.conflicted,pr.checks,pr.merged --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
2284
- "Pane Chat arms those flags itself through its skill; pass them only for your own scripts.",
2285
- "STUCK means real unsubmitted composer text; an agent prompt suggestion never counts.",
2286
- "Dead watch: HEARTBEAT is proof of life only (--quiet drops it). Judge death by a non-zero exit or a WATCH ERROR line, not by silence. If the message says the cursor was superseded, keep the replacement monitor and do not re-arm the old one."
2349
+ "",
2350
+ "How a watch ends: WATCH EXPIRED (exit 0) means nothing happened; arm a new one with the next",
2351
+ "dispatch. A non-zero exit or a WATCH ERROR line means it failed: say so and re-arm once. Judge",
2352
+ "a watch by WATCH OK and these endings, never by silence. Never pipe --follow into grep -m1 or head.",
2353
+ "STUCK means real unsubmitted composer text; a prompt suggestion never counts."
2287
2354
  ],
2288
2355
  "panes create": [
2289
2356
  "Usage:",
@@ -3234,6 +3301,21 @@ exports.RUNPANE_CONTRACT = {
3234
3301
  "runpane sessions unpin --session <id|name> [--json] [--pane-dir <path>]",
3235
3302
  "",
3236
3303
  "Set the named Session pin star off. Repeated requests keep that state. Uses the same Session update path as the UI."
3304
+ ],
3305
+ "pr watch": [
3306
+ "runpane pr watch [--pane <id>] --url <PR URL> [--head <full-sha>] [--json] [--pane-dir <path>]",
3307
+ "",
3308
+ "Register durable GitHub PR monitoring for a Pane."
3309
+ ],
3310
+ "pr unwatch": [
3311
+ "runpane pr unwatch [--pane <id>] --url <PR URL> [--json] [--pane-dir <path>]",
3312
+ "",
3313
+ "Remove a Pane’s explicit PR registration."
3314
+ ],
3315
+ "pr list": [
3316
+ "runpane pr list [--pane <id>] [--json] [--pane-dir <path>]",
3317
+ "",
3318
+ "List PR registrations and stopped watches."
3237
3319
  ]
3238
3320
  },
3239
3321
  "pip": {
@@ -3256,7 +3338,7 @@ exports.RUNPANE_CONTRACT = {
3256
3338
  " runpane panes cost [--repo <selector>] [--pane <pane-id>] [--json]",
3257
3339
  " runpane workspace state [--repo <selector>] [--json]",
3258
3340
  " runpane watch --follow",
3259
- " runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone",
3341
+ " runpane watch --until-event",
3260
3342
  " runpane panes create --repo <selector> --name <name> --agent <codex|claude|cursor> [--base-branch <branch>] [--source user|agent] [--focus|--no-focus] [--wait-ready]",
3261
3343
  " runpane panes archive --pane <pane-id> [--source user|agent] [--force] [--remove-worktree] [--dry-run] --yes",
3262
3344
  " runpane panes archive --session <id|name> --merged [--remove-worktree] [--dry-run] --yes",
@@ -3490,54 +3572,60 @@ exports.RUNPANE_CONTRACT = {
3490
3572
  ],
3491
3573
  "watch": [
3492
3574
  "Usage:",
3493
- " runpane watch [--as <name>|--since <generation>] [--follow|--until-event] [options]",
3575
+ " runpane watch Print what changed since your cursor, then exit",
3576
+ " runpane watch --follow Keep watching, tuned for an agent watching unattended",
3577
+ " runpane watch --until-event Wait for one event worth waking for, print it, exit 0",
3578
+ "",
3579
+ "Waits for agent, Pane, panel, and PR events from the daemon without polling.",
3580
+ "",
3581
+ "What --follow does by default (see defaults.watch.follow in the contract):",
3582
+ " prints WATCH OK once when armed, then one line per event, with no heartbeats",
3583
+ " kinds: agent.report, agent.ready, agent.blocked, agent.idle, panel.exited, panel.stopped,",
3584
+ " pane.gone, pane.associated, pane.detached, pr.conflicted, pr.checks, pr.merged",
3585
+ " READY after 3m of quiet; BLOCKED after 30s; other lines batched at most every 10m",
3586
+ " (REPORT, BLOCKED, conflicts, and failed checks arrive at once)",
3587
+ " IDLE reminders at 10m, 30m, 1h, 3h, then daily",
3588
+ " ends with WATCH EXPIRED and exit 0 after 3h with nothing but IDLE",
3494
3589
  "",
3495
- "Waits for workspace transitions without polling.",
3590
+ "What --until-event does by default: waits for agent.report, agent.blocked, panel.exited,",
3591
+ "or pane.gone, prints that one line, and exits 0. Run it as a background task, act on",
3592
+ "its line, then run it again; its named cursor means nothing is missed between runs.",
3593
+ "Single-event waits deliver immediately without follow settling or batching.",
3594
+ "--follow --since <generation> has no consumer name and bypasses settling and batching. Cadence requires a named follow consumer; --as cannot be combined with numeric --since.",
3496
3595
  "",
3497
3596
  "Options:",
3498
- " --as <name>",
3499
- " --since <generation>",
3500
- " --from <now|earliest>",
3501
- " --follow",
3502
- " --until-event Exit 0 after the first event; --timeout-ms is the total deadline (exit 124)",
3503
- " --format <lines|json>",
3504
- " --heartbeat <seconds>",
3505
- " --idle-after <ms>",
3506
- " --settle <ms> Opt-in READY quiet window (--follow only)",
3507
- " --blocked-settle <ms> Opt-in BLOCKED window (--follow only)",
3508
- " --min-interval <ms> Opt-in batching; BLOCKED, REPORT, and PR failures bypass (--follow only)",
3509
- " --idle-backoff Opt-in IDLE backoff (--follow only)",
3510
- " --all-managed",
3511
- " --include-shells",
3512
- " --agents-only",
3513
- " --exclude-pane <id> Repeatable",
3514
- " --self-test",
3515
- " --quiet Drop OK, HEARTBEAT, and RECONNECTED lines",
3516
- " --no-control-lines Alias for --quiet",
3517
- " --kinds <kind,...>",
3518
- " --pane <id> Repeatable",
3519
- " --session <id|name> Follow a named Session's Panes; no re-arm",
3520
- " --repo <selector>",
3521
- " --name-contains <text>",
3522
- " --timeout-ms <ms> 0 polls once; the total deadline with --until-event",
3523
- " --limit <count>",
3524
- " --ack-now",
3525
- " --include-held-input",
3526
- " --no-held-input",
3597
+ " --session <id|name> Watch every Pane in a Session; associate/detach apply live",
3598
+ " --pane <id> Watch one Pane; repeatable (default: every managed Pane)",
3599
+ " --exclude-pane <id> Leave out a Pane; repeatable",
3600
+ " --repo <selector> Limit to a saved repository",
3601
+ " --name-contains <text> Limit by Pane name substring",
3602
+ " --include-shells Include plain shell panels (default: agent panels only)",
3603
+ " --kinds <kind,...> Replace the default event kinds",
3604
+ " --json One JSON object per line",
3605
+ " --as <name> Name the cursor (default: from --session or this panel)",
3606
+ " --since <generation> Use an explicit cursor instead of a name",
3607
+ " --epoch <epoch> Pair with --since to detect journal resets",
3608
+ " --from <now|earliest> Where a new named cursor starts (default: now)",
3609
+ " --self-test Check the daemon watch path and exit",
3610
+ "",
3611
+ "Overrides (durations such as 30s, 10m, 3h; a bare number is milliseconds; 0 turns it off):",
3612
+ " --ready-settle <duration> Quiet time before READY (default 3m)",
3613
+ " --blocked-settle <duration> Time before BLOCKED (default 30s)",
3614
+ " --min-interval <duration> Batch window for non-urgent lines (default 10m)",
3615
+ " --idle-after <duration> First IDLE reminder (default 10m)",
3616
+ " --expire-after <duration> End after this long with nothing but IDLE (default 3h)",
3617
+ " --heartbeat <duration> Print a HEARTBEAT line this often (default off)",
3618
+ " --timeout-ms <duration> Per-request wait; with --until-event, the total deadline (exit 124)",
3619
+ " --limit <count> Maximum entries per response",
3620
+ " --ack-now At-most-once cursor delivery",
3621
+ " --include-held-input Include unsubmitted composer text in JSON",
3622
+ " --no-held-input Turn off STUCK detection",
3527
3623
  " --pane-dir <path> Connect to a specific Pane data directory",
3528
- " --json Alias for --format json",
3529
- "",
3530
- "Defaults are responsive: no settle, no batching, all kinds, IDLE every --idle-after. BUSY carries no action; drop it with --kinds.",
3531
- "agent.report (a worker ran `runpane report`) is opt-in: list it in --kinds. REPORT lines skip the --min-interval batch.",
3532
- "--follow and --until-event imply --agents-only, except for agent.report: a report from any panel, a shell tab included, still arrives.",
3533
- "Exit-to-wake (Claude Code background Bash, Codex, any harness that wakes when a process exits): runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone",
3534
- " Run it as a background task; when it exits, act on its line, then run it again. Never pipe --follow into grep -m1 or head to wait.",
3535
- "Unattended (Pane Chat default): --quiet --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
3536
- "User present: the same kinds with --settle 60000 --blocked-settle 15000 --min-interval 120000 and no --idle-backoff",
3537
- "Session orchestrator: runpane watch --session <id|name> --follow --quiet --json --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone,pane.associated,pane.detached,pr.conflicted,pr.checks,pr.merged --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
3538
- "Pane Chat arms those flags itself through its skill; pass them only for your own scripts.",
3539
- "STUCK means real unsubmitted composer text; an agent prompt suggestion never counts.",
3540
- "Dead watch: HEARTBEAT is proof of life only (--quiet drops it). Judge death by a non-zero exit or a WATCH ERROR line, not by silence. If the message says the cursor was superseded, keep the replacement monitor and do not re-arm the old one."
3624
+ "",
3625
+ "How a watch ends: WATCH EXPIRED (exit 0) means nothing happened; arm a new one with the next",
3626
+ "dispatch. A non-zero exit or a WATCH ERROR line means it failed: say so and re-arm once. Judge",
3627
+ "a watch by WATCH OK and these endings, never by silence. Never pipe --follow into grep -m1 or head.",
3628
+ "STUCK means real unsubmitted composer text; a prompt suggestion never counts."
3541
3629
  ],
3542
3630
  "panes create": [
3543
3631
  "Usage:",
@@ -4409,6 +4497,21 @@ exports.RUNPANE_CONTRACT = {
4409
4497
  "python -m runpane sessions unpin --session <id|name> [--json] [--pane-dir <path>]",
4410
4498
  "",
4411
4499
  "Set the named Session pin star off. Repeated requests keep that state. Uses the same Session update path as the UI."
4500
+ ],
4501
+ "pr watch": [
4502
+ "runpane pr watch [--pane <id>] --url <PR URL> [--head <full-sha>] [--json] [--pane-dir <path>]",
4503
+ "",
4504
+ "Register durable GitHub PR monitoring for a Pane."
4505
+ ],
4506
+ "pr unwatch": [
4507
+ "runpane pr unwatch [--pane <id>] --url <PR URL> [--json] [--pane-dir <path>]",
4508
+ "",
4509
+ "Remove a Pane’s explicit PR registration."
4510
+ ],
4511
+ "pr list": [
4512
+ "runpane pr list [--pane <id>] [--json] [--pane-dir <path>]",
4513
+ "",
4514
+ "List PR registrations and stopped watches."
4412
4515
  ]
4413
4516
  }
4414
4517
  },
@@ -4490,11 +4593,6 @@ exports.RUNPANE_CONTRACT = {
4490
4593
  "runpane panels list --pane <pane-id> --json",
4491
4594
  "runpane panels output --panel <panel-id> --limit 200 --json",
4492
4595
  "printf 'Continue\\n' | runpane panels input --panel <panel-id> --input-file - --yes --json",
4493
- "runpane watch --self-test",
4494
- "runpane watch --follow",
4495
- "runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone",
4496
- "runpane watch --follow --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
4497
- "runpane watch --session <id|name> --follow --quiet --json --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone,pane.associated,pane.detached,pr.conflicted,pr.checks,pr.merged --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
4498
4596
  "runpane sessions list [--json] [--pane-dir <path>]",
4499
4597
  "runpane sessions create --from-json <path|-> [--json] [--pane-dir <path>]",
4500
4598
  "runpane sessions get --session <id|name> [--json] [--pane-dir <path>]",
@@ -4520,7 +4618,14 @@ exports.RUNPANE_CONTRACT = {
4520
4618
  "runpane links create --pane <pane-id> --json",
4521
4619
  "runpane panes git-status --pane <pane-id> --json",
4522
4620
  "runpane help",
4523
- "runpane <command> --help"
4621
+ "runpane <command> --help",
4622
+ "runpane watch --until-event",
4623
+ "runpane watch --follow",
4624
+ "runpane watch --session <id|name> --follow",
4625
+ "runpane watch --session <id|name> --follow --ready-settle 1m --blocked-settle 15s --min-interval 2m",
4626
+ "runpane pr watch [--pane <id>] --url <PR URL> [--head <full-sha>] [--json] [--pane-dir <path>]",
4627
+ "runpane pr unwatch [--pane <id>] --url <PR URL> [--json] [--pane-dir <path>]",
4628
+ "runpane pr list [--pane <id>] [--json] [--pane-dir <path>]"
4524
4629
  ],
4525
4630
  "commandDescriptions": [
4526
4631
  "`runpane` with no arguments and `runpane setup` open an interactive wizard when stdin and stdout are TTYs. The remote-host wizard asks only for a name, then runs interactive Tailscale setup with automatic port selection; explicit install daemon flags remain available for SSH and manual URLs. In non-interactive shells or CI, both forms must print help, common commands, and agent discovery hints, then exit successfully instead of waiting for input.",
@@ -4553,7 +4658,7 @@ exports.RUNPANE_CONTRACT = {
4553
4658
  "`runpane panes adopt --launch` accepts `--prompt`, `--prompt-file`, `--wait-ready`, and `--ready-timeout-ms`, and reports `readiness`, `initialInput`, and `nextCommand` like `panes create`. A prompt without `--launch` is an error rather than being dropped.",
4554
4659
  "If composer submission cannot be verified without risking a duplicate, the create item is unsuccessful with `initialInput.staged`, `initialInput.attempts`, `initialInput.blocked.kind: submission_unverified`, and an actionable `nextCommand`. The CLI-facing `--prompt` alias maps to this canonical `initialInput` result.",
4555
4660
  "When running from WSL while Pane is installed on Windows, the Linux wrapper may look for a missing `/tmp/pane-daemon.../daemon.sock` or resolve to a Windows shim such as Volta. In that case invoke the Windows wrapper through PowerShell from a Windows cwd, for example `powershell.exe -NoProfile -Command 'Set-Location $env:TEMP; runpane repos list --json'`.",
4556
- "`runpane watch` waits for workspace transitions from the daemon journal without polling. `--follow` keeps waiting and prints one line per event: READY, BLOCKED, IDLE, STUCK, NEW, GONE, EXIT, plus HEARTBEAT every 60 seconds as proof of life. Defaults are responsive: no settle, no batching, all kinds, IDLE every `--idle-after`. Expensive consumers opt into `--kinds` (drop `agent.busy`; BUSY carries no action), `--settle <ms>` (READY only after a quiet window; a BUSY inside it cancels the line), `--blocked-settle <ms>`, `--min-interval <ms>` (batch non-urgent lines; BLOCKED bypasses it), and `--idle-backoff` (10m, 30m, 1h, 3h, then daily). Two profiles cover orchestrators. Unattended: `runpane watch --follow --quiet --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff`, which budgets about 6 wake-ups per active pane per hour worst case, usually 1-3, and can deliver READY up to about 13 minutes late. User present: the same kinds with `--settle 60000 --blocked-settle 15000 --min-interval 120000` and no `--idle-backoff`, so READY arrives within about 3 minutes. Pane Chat arms it automatically through its skill; only your own scripts need the flags. STUCK means real unsubmitted composer text, never an agent prompt suggestion. `--quiet` (alias `--no-control-lines`) drops the WATCH OK, HEARTBEAT, and WATCH RECONNECTED control lines (`_ok`, `_heartbeat`, `_reconnected` in JSON); WATCH ERROR, RESET, and DROPPED (`_error`, `_reset`, `_dropped`) always print. Judge a dead watch by a non-zero exit or a WATCH ERROR line, not by silence. `--session <id|name>` follows every Pane associated with a named Session and re-reads membership on every read, so associate and detach need no re-arm; JOINED and LEFT (`pane.associated`, `pane.detached`) report membership changes. For Session members with an open PR, the daemon polls GitHub about every 3 minutes and reports `pr.conflicted` (`PR <pane-name> pane <pane-id> #<number> CONFLICTED`), `pr.checks` (`... CHECKS PASSED` or `... CHECKS FAILED <names>`), and `pr.merged` (`... MERGED`) on transitions only; list them in `--kinds`. In JSON, an entry with `replay: true` restates current state after a reset and is never READY. `--until-event` waits across daemon calls for the first matching event, prints it, and exits 0, so a harness that wakes an agent when a process exits (Claude Code background Bash, Codex) can wait with `runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone` and run it again after each event; the caller's own panel and a RESET never end the wait, and `--timeout-ms` becomes the total deadline (exit 124, `WATCH TIMEOUT`). Never pipe `--follow` into `grep -m1` or `head` to wait: `--follow` never exits on its own. `--follow` and `--until-event` imply `--agents-only`, except for `agent.report`, which arrives from any panel.",
4661
+ "`runpane watch` waits for agent, Pane, panel, and PR events from the daemon journal without polling. `--follow` keeps watching with defaults tuned for an agent watching unattended (quiet, batched, and it ends itself after 3 hours with nothing happening); individual duration flags override these defaults. Cadence requires a named follow consumer; `--follow --since <generation>` has no consumer name and bypasses settling and batching. `--until-event` waits for one event worth waking for, prints it immediately without follow settling or batching, and exits 0. The defaults are listed in the contract under defaults.watch.",
4557
4662
  "A watch releases its pending slot when its client socket or named pipe disconnects. Re-arming the same named cursor takes over the previous request; the superseded watcher exits with a WATCH ERROR instead of competing for the cursor. Anonymous concurrent watchers retain the eight-wait limit. Each request also has a 125-second lease (slightly longer than the maximum 120-second long poll), so an abandoned request is reclaimed even if no disconnect arrives.",
4558
4663
  "`sessions list` list durable named orchestration Sessions.",
4559
4664
  "`sessions create` create a durable named orchestration Session and its hidden terminal owner.",
@@ -4572,7 +4677,10 @@ exports.RUNPANE_CONTRACT = {
4572
4677
  "Commands with a contract `daemonAction` (the `panes` git, script, restore, and move commands, `folders list|create`, and `links open`) call the same Pane daemon channel as the matching button in the app and print `{ ok, data, error }`. Destructive ones add a pane:// `link` to review the Pane.",
4573
4678
  "`runpane links create` builds `pane://open?...` links; opening one in Pane selects what it names and never changes Pane state.",
4574
4679
  "`runpane docs search|read` search and read Pane docs, help, and installed Pane Chat skills offline. They ship in the npm package and the Pane app only.",
4575
- "`runpane handoff` hands a task to a fresh agent on this or another of your machines. The sending agent writes the note from `runpane handoff --template` (goal, current state, done and verified, in progress, next steps, decisions and constraints, open questions, how to verify, git state); `runpane handoff \"claude opus on parsas-macbook-pro\" --note-file ~/handoff.md` checks every section is filled in, requires the branch to be pushed (or pushes it with `--push`, never forcing), writes the note to that machine's `~/.pane/handoffs/` and starts the agent in a new Pane branched from the sender's branch. From a Session's terminal it skips git and creates a Session with the same name, goal and agent on the other machine instead. The receiver reports back to the sender's panel."
4680
+ "`runpane handoff` hands a task to a fresh agent on this or another of your machines. The sending agent writes the note from `runpane handoff --template` (goal, current state, done and verified, in progress, next steps, decisions and constraints, open questions, how to verify, git state); `runpane handoff \"claude opus on parsas-macbook-pro\" --note-file ~/handoff.md` checks every section is filled in, requires the branch to be pushed (or pushes it with `--push`, never forcing), writes the note to that machine's `~/.pane/handoffs/` and starts the agent in a new Pane branched from the sender's branch. From a Session's terminal it skips git and creates a Session with the same name, goal and agent on the other machine instead. The receiver reports back to the sender's panel.",
4681
+ "`runpane pr watch`: Register durable GitHub PR monitoring for a Pane.",
4682
+ "`runpane pr unwatch`: Remove a Pane’s explicit PR registration.",
4683
+ "`runpane pr list`: List PR registrations and stopped watches."
4576
4684
  ],
4577
4685
  "wrapperFlagNote": "The top-level `runpane --version` form prints the wrapper version. The install subcommand form `runpane install --version vX.Y.Z` selects a Pane release.",
4578
4686
  "localControlFlagNote": "`runpane doctor --json`, `runpane repos list`, `runpane panes ...`, and `runpane panels ...` commands use or describe the local framed daemon socket/pipe for a running Pane app. `--pane-dir` points the wrapper at a non-default Pane data directory, such as `PANE_DIR=~/.pane_test` in development. `runpane agent-context` is local/offline and can be used before Pane is running. `agent-context` and `version` accept and ignore `--pane-dir`, so one `--pane-dir` can be passed to every runpane command. In a Pane repository checkout, if `runpane` is not on PATH, build the local wrapper with `pnpm --filter runpane build` and run it with Node 22 or newer, for example `node packages/runpane/dist/cli.js doctor --json`. From WSL, if the user runs Windows Pane, call the Windows wrapper through `powershell.exe -NoProfile -Command 'Set-Location $env:TEMP; runpane ...'` so the command can reach the Windows named-pipe daemon and avoid UNC cwd issues.",
@@ -4677,21 +4785,18 @@ exports.RUNPANE_CONTRACT = {
4677
4785
  "--from",
4678
4786
  "earliest",
4679
4787
  "--follow",
4680
- "--format",
4681
- "lines",
4682
4788
  "--heartbeat",
4683
- "60",
4789
+ "60s",
4684
4790
  "--idle-after",
4685
4791
  "600000",
4686
- "--settle",
4792
+ "--ready-settle",
4687
4793
  "180000",
4688
4794
  "--blocked-settle",
4689
4795
  "30000",
4690
4796
  "--min-interval",
4691
4797
  "600000",
4692
- "--idle-backoff",
4693
- "--all-managed",
4694
- "--agents-only",
4798
+ "--expire-after",
4799
+ "10800000",
4695
4800
  "--exclude-pane",
4696
4801
  "pane-old",
4697
4802
  "--kinds",
@@ -5106,27 +5211,23 @@ exports.RUNPANE_CONTRACT = {
5106
5211
  [
5107
5212
  "watch",
5108
5213
  "--follow",
5109
- "--quiet",
5110
5214
  "--json",
5111
5215
  "--pane-dir",
5112
5216
  "/tmp/pane"
5113
5217
  ],
5114
5218
  [
5115
5219
  "watch",
5116
- "--follow",
5117
- "--no-control-lines",
5118
- "--all-managed"
5220
+ "--follow"
5119
5221
  ],
5120
5222
  [
5121
5223
  "watch",
5122
5224
  "--session",
5123
5225
  "__orchestration_session_1b4e28ba-2fa1-11d2-883f-0016d3cca427__",
5124
5226
  "--follow",
5125
- "--quiet",
5126
5227
  "--json",
5127
5228
  "--kinds",
5128
5229
  "agent.ready,agent.blocked,pane.associated,pane.detached,pr.conflicted,pr.checks,pr.merged",
5129
- "--settle",
5230
+ "--ready-settle",
5130
5231
  "180000"
5131
5232
  ],
5132
5233
  [
@@ -5453,12 +5554,93 @@ exports.RUNPANE_CONTRACT = {
5453
5554
  "--as",
5454
5555
  "orch",
5455
5556
  "--until-event",
5456
- "--quiet",
5457
5557
  "--json",
5458
5558
  "--kinds",
5459
5559
  "agent.report,agent.blocked,panel.exited,pane.gone",
5460
5560
  "--timeout-ms",
5461
5561
  "600000"
5562
+ ],
5563
+ [
5564
+ "pr",
5565
+ "watch",
5566
+ "--pane",
5567
+ "worker",
5568
+ "--url",
5569
+ "https://github.com/acme/app/pull/7"
5570
+ ],
5571
+ [
5572
+ "pr",
5573
+ "unwatch",
5574
+ "--pane",
5575
+ "worker",
5576
+ "--url",
5577
+ "https://github.com/acme/app/pull/7"
5578
+ ],
5579
+ [
5580
+ "pr",
5581
+ "list",
5582
+ "--pane",
5583
+ "worker"
5584
+ ],
5585
+ [
5586
+ "watch",
5587
+ "--follow",
5588
+ "--ready-settle",
5589
+ "5s",
5590
+ "--expire-after",
5591
+ "0"
5592
+ ],
5593
+ [
5594
+ "watch",
5595
+ "--until-event",
5596
+ "--timeout-ms",
5597
+ "30s"
5598
+ ],
5599
+ [
5600
+ "watch",
5601
+ "--timeout-ms",
5602
+ "off"
5603
+ ],
5604
+ [
5605
+ "watch",
5606
+ "--since",
5607
+ "42",
5608
+ "--epoch",
5609
+ "test-epoch",
5610
+ "--until-event",
5611
+ "--json"
5612
+ ],
5613
+ [
5614
+ "watch",
5615
+ "--follow",
5616
+ "--ready-settle",
5617
+ "5s",
5618
+ "--expire-after",
5619
+ "off"
5620
+ ],
5621
+ [
5622
+ "watch",
5623
+ "--follow",
5624
+ "--ready-settle",
5625
+ "5s",
5626
+ "--blocked-settle",
5627
+ "2s",
5628
+ "--min-interval",
5629
+ "1m",
5630
+ "--idle-after",
5631
+ "20m",
5632
+ "--expire-after",
5633
+ "off",
5634
+ "--heartbeat",
5635
+ "30s"
5636
+ ],
5637
+ [
5638
+ "watch",
5639
+ "--follow",
5640
+ "--since",
5641
+ "42",
5642
+ "--epoch",
5643
+ "test-epoch"
5462
5644
  ]
5463
5645
  ],
5464
5646
  "topLevelHelpIncludes": [
@@ -5884,38 +6066,92 @@ exports.RUNPANE_CONTRACT = {
5884
6066
  "watchDefaults": {
5885
6067
  "type": "object",
5886
6068
  "required": [
5887
- "heartbeatSeconds",
5888
- "idleAfterMs",
5889
- "settleMs",
5890
- "blockedSettleMs",
5891
- "minIntervalMs",
5892
- "idleBackoff",
5893
- "kinds"
6069
+ "follow",
6070
+ "untilEvent"
5894
6071
  ],
6072
+ "additionalProperties": false,
5895
6073
  "properties": {
5896
- "heartbeatSeconds": {
5897
- "type": "number"
5898
- },
5899
- "idleAfterMs": {
5900
- "type": "number"
5901
- },
5902
- "settleMs": {
5903
- "type": "number"
5904
- },
5905
- "blockedSettleMs": {
5906
- "type": "number"
5907
- },
5908
- "minIntervalMs": {
5909
- "type": "number"
5910
- },
5911
- "idleBackoff": {
5912
- "type": "boolean"
6074
+ "follow": {
6075
+ "type": "object",
6076
+ "additionalProperties": false,
6077
+ "required": [
6078
+ "summary",
6079
+ "kinds",
6080
+ "readySettle",
6081
+ "blockedSettle",
6082
+ "minInterval",
6083
+ "idleAfter",
6084
+ "idleBackoff",
6085
+ "expireAfter",
6086
+ "heartbeat",
6087
+ "includeShells",
6088
+ "heldInputDetection"
6089
+ ],
6090
+ "properties": {
6091
+ "summary": {
6092
+ "type": "string"
6093
+ },
6094
+ "kinds": {
6095
+ "type": "array",
6096
+ "items": {
6097
+ "type": "string"
6098
+ }
6099
+ },
6100
+ "readySettle": {
6101
+ "type": "string",
6102
+ "pattern": "^(off|[0-9]+(ms|s|m|h))$"
6103
+ },
6104
+ "blockedSettle": {
6105
+ "type": "string",
6106
+ "pattern": "^(off|[0-9]+(ms|s|m|h))$"
6107
+ },
6108
+ "minInterval": {
6109
+ "type": "string",
6110
+ "pattern": "^(off|[0-9]+(ms|s|m|h))$"
6111
+ },
6112
+ "idleAfter": {
6113
+ "type": "string",
6114
+ "pattern": "^(off|[0-9]+(ms|s|m|h))$"
6115
+ },
6116
+ "idleBackoff": {
6117
+ "type": "boolean"
6118
+ },
6119
+ "expireAfter": {
6120
+ "type": "string",
6121
+ "pattern": "^(off|[0-9]+(ms|s|m|h))$"
6122
+ },
6123
+ "heartbeat": {
6124
+ "type": "string",
6125
+ "pattern": "^(off|[0-9]+(ms|s|m|h))$"
6126
+ },
6127
+ "includeShells": {
6128
+ "type": "boolean"
6129
+ },
6130
+ "heldInputDetection": {
6131
+ "type": "boolean"
6132
+ }
6133
+ }
5913
6134
  },
5914
- "kinds": {
5915
- "const": "all"
6135
+ "untilEvent": {
6136
+ "type": "object",
6137
+ "required": [
6138
+ "summary",
6139
+ "kinds"
6140
+ ],
6141
+ "properties": {
6142
+ "summary": {
6143
+ "type": "string"
6144
+ },
6145
+ "kinds": {
6146
+ "type": "array",
6147
+ "items": {
6148
+ "type": "string"
6149
+ }
6150
+ }
6151
+ },
6152
+ "additionalProperties": false
5916
6153
  }
5917
- },
5918
- "additionalProperties": false
6154
+ }
5919
6155
  },
5920
6156
  "nextCommands": {
5921
6157
  "type": "array",
@@ -7203,7 +7439,14 @@ exports.RUNPANE_CONTRACT = {
7203
7439
  "pane.detached",
7204
7440
  "pr.conflicted",
7205
7441
  "pr.checks",
7206
- "pr.merged"
7442
+ "pr.merged",
7443
+ "pr.closed",
7444
+ "pr.head",
7445
+ "pr.review",
7446
+ "pr.comment",
7447
+ "pr.stopped",
7448
+ "github.unavailable",
7449
+ "github.recovered"
7207
7450
  ]
7208
7451
  },
7209
7452
  "paneId": {
@@ -7326,18 +7569,22 @@ exports.RUNPANE_CONTRACT = {
7326
7569
  "passed",
7327
7570
  "failed"
7328
7571
  ],
7329
- "description": "Settled result of a pr.checks entry: every check on pr.headOid finished."
7572
+ "description": "Failures are immediate; success requires complete nonempty required checks on the pinned head."
7330
7573
  },
7331
7574
  "failingChecks": {
7332
7575
  "type": "array",
7333
7576
  "items": {
7334
7577
  "type": "string"
7335
7578
  },
7336
- "description": "Up to five failing check names of a failed pr.checks entry."
7579
+ "description": "Newly failing check names on this head."
7337
7580
  },
7338
7581
  "report": {
7339
7582
  "$ref": "#/jsonSchemas/agentReport",
7340
7583
  "description": "Report of an agent.report entry; its summary is cut to 2,000 characters (the panel keeps up to 16,000)."
7584
+ },
7585
+ "epoch": {
7586
+ "type": "string",
7587
+ "description": "Added to watch --json NDJSON events. Persist with gen after processing for numeric rearm."
7341
7588
  }
7342
7589
  },
7343
7590
  "additionalProperties": false
@@ -7482,6 +7729,10 @@ exports.RUNPANE_CONTRACT = {
7482
7729
  },
7483
7730
  "idleBackoff": {
7484
7731
  "type": "boolean"
7732
+ },
7733
+ "epoch": {
7734
+ "type": "string",
7735
+ "description": "Expected epoch for since. Requires since and cannot be combined with as. Mismatch returns epoch-changed reset."
7485
7736
  }
7486
7737
  },
7487
7738
  "additionalProperties": false
@@ -7581,6 +7832,10 @@ exports.RUNPANE_CONTRACT = {
7581
7832
  },
7582
7833
  "nextCommand": {
7583
7834
  "type": "string"
7835
+ },
7836
+ "activityGeneration": {
7837
+ "type": "number",
7838
+ "description": "Latest meaningful activity in scope, including events hidden by output kinds or cadence. Natural expiry resets when it advances."
7584
7839
  }
7585
7840
  },
7586
7841
  "additionalProperties": false
@@ -10640,6 +10895,36 @@ exports.RUNPANE_CONTRACT = {
10640
10895
  }
10641
10896
  },
10642
10897
  "additionalProperties": false
10898
+ },
10899
+ "prWatchResult": {
10900
+ "type": "object",
10901
+ "required": [
10902
+ "ok"
10903
+ ],
10904
+ "properties": {
10905
+ "ok": {
10906
+ "const": true
10907
+ },
10908
+ "generation": {
10909
+ "type": "number"
10910
+ },
10911
+ "watch": {
10912
+ "type": "object"
10913
+ },
10914
+ "watches": {
10915
+ "type": "array",
10916
+ "items": {
10917
+ "type": "object"
10918
+ }
10919
+ },
10920
+ "removed": {
10921
+ "type": "boolean"
10922
+ },
10923
+ "epoch": {
10924
+ "type": "string"
10925
+ }
10926
+ },
10927
+ "additionalProperties": false
10643
10928
  }
10644
10929
  },
10645
10930
  "agentContext": {
@@ -10666,7 +10951,7 @@ exports.RUNPANE_CONTRACT = {
10666
10951
  "Use `runpane panels screen` for compact current state, including Claude and Codex composer state. Use `panels wait` for create-time readiness or text checks. Use `runpane watch --follow` to block on workspace transitions (READY, BLOCKED, IDLE, STUCK, EXIT) without polling, or `runpane watch --until-event` to exit on the first one. Use `panels submit` to send and submit a new turn. Use `panels submit-composer --strategy auto` only for a composer that was filled separately.",
10667
10952
  "Use `runpane panels input` only when exact bytes are required, such as Ctrl-C or handcrafted terminal input.",
10668
10953
  "Pane terminals draw inline images: sixel, iTerm2 inline images, and the kitty graphics protocol. Tools that need kitty graphics, such as terminal-browser and terminal-doom, run inside a Pane panel; `runpane doctor --json` reports the exact list under `terminal.graphicsProtocols`.",
10669
- "After creating Panes or sending terminal input, validate with `panels wait` or bounded `panels screen` before reporting success. For ongoing supervision, `runpane watch --follow` is the canonical monitor for a streaming reader. A harness that wakes only when a process exits (Claude Code background Bash, Codex) waits with `runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone` instead and runs it again after each event; never pipe `--follow` into `grep -m1` or `head`.",
10954
+ "After creating Panes or sending terminal input, validate with `panels wait` or bounded `panels screen`. For a harness that consumes streaming output, use `runpane watch --follow` (with `--session <id>` for a Session); its named consumer applies settling and batching. Adding numeric --since leaves it unnamed and bypasses cadence; --as cannot be combined with --since. A harness that wakes only when a process exits uses `runpane watch --as <name> --until-event --json` and runs it again after each event. Single-event waits deliver immediately without follow cadence. Never pipe `--follow` into `grep -m1` or `head`.",
10670
10955
  "When several agents share one resource, such as a test account, name a lock in their prompts: each worker runs `runpane lock acquire --name <name> --ttl 30m --wait 1800000 --json` before using it and `runpane lock release --name <name>` after."
10671
10956
  ],
10672
10957
  "detailCommand": "runpane agent-context --command <command> [--json]",
@@ -10941,17 +11226,13 @@ exports.RUNPANE_CONTRACT = {
10941
11226
  },
10942
11227
  {
10943
11228
  "name": "watch",
10944
- "summary": "Wait for workspace transitions (READY, BLOCKED, IDLE, STUCK, NEW, GONE, EXIT, STOPPED, JOINED, LEFT, PR) from the daemon journal without polling; responsive by default, with opt-in cadence flags for expensive consumers.",
11229
+ "summary": "Wait for workspace events from the durable daemon journal; follow uses conservative cadence and quiet expiry by default.",
10945
11230
  "arguments": [
10946
11231
  "--follow",
10947
11232
  "--until-event",
10948
11233
  "--session <id|name>",
10949
11234
  "--self-test",
10950
11235
  "--kinds <kind,...>",
10951
- "--settle <ms>",
10952
- "--blocked-settle <ms>",
10953
- "--min-interval <ms>",
10954
- "--idle-backoff",
10955
11236
  "--json"
10956
11237
  ]
10957
11238
  }
@@ -12806,135 +13087,130 @@ exports.RUNPANE_CONTRACT = {
12806
13087
  "watch": {
12807
13088
  "name": "watch",
12808
13089
  "summary": "Wait for workspace transitions using the daemon journal.",
12809
- "details": "`runpane watch --follow` is the responsive monitor: lines, 60-second heartbeats, IDLE every 10 minutes, managed-agent scope, redacted STUCK detection, no settle, no batching, all kinds. Expensive consumers (one wake-up per line) opt into the cadence flags. Pane Chat arms them automatically through its pane-orchestrator skill; only your own scripts need to pass them. Named cursors persist in the Pane data directory. Harnesses that wake an agent when a process exits (Claude Code background Bash, Codex) use `--until-event` instead: `runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone` exits 0 on the first event, and the agent runs it again after acting on it.",
13090
+ "details": "`runpane watch --follow` is the monitor for an agent: with no other flags it prints WATCH OK once, then one line per event (reports, ready, blocked, idle, exits, Session membership, and PR conflicts, checks, and merges), batches non-urgent lines, and ends itself with WATCH EXPIRED after 3 hours with nothing happening. Set individual duration flags to override these defaults. `--until-event` is for harnesses that wake an agent when a process exits (Claude Code background Bash, Codex): it prints the first event worth waking for immediately, without follow settling or batching, and exits 0, and its named cursor means nothing is missed between runs. Every default is listed in the contract under defaults.watch; each override flag replaces one value.",
12810
13091
  "requiresPaneDaemon": true,
12811
13092
  "mutates": false,
12812
13093
  "arguments": [
12813
13094
  {
12814
- "name": "--as",
12815
- "value": "<consumer-name>",
12816
- "required": false,
12817
- "description": "Use a persisted named cursor (at-least-once delivery): 1-128 letters, numbers, dots, underscores, or hyphens (daemons before this release accept 64). Default under --follow: session-<uuid> with --session (session-<name> for a name; shortened to session-<sha256 prefix> when longer than 64 characters), else PANE_PANEL_ID when set, shortened to panel-<sha256 prefix> when it is longer than 64 characters; otherwise anonymous."
12818
- },
12819
- {
12820
- "name": "--since",
12821
- "value": "<generation>",
13095
+ "name": "--follow",
12822
13096
  "required": false,
12823
- "description": "Use an explicit generation cursor instead of a name. Cannot be combined with --as, or with --settle, --blocked-settle, --min-interval."
13097
+ "description": "Keep watching, with the defaults in defaults.watch.follow: WATCH OK once, then one line per event; READY after 3m of quiet, BLOCKED after 30s, other lines batched at most every 10m (REPORT, BLOCKED, PR conflicts, and failed checks arrive at once); IDLE reminders at 10m, 30m, 1h, 3h, then daily; WATCH EXPIRED and exit 0 after 3h with nothing but IDLE."
12824
13098
  },
12825
13099
  {
12826
- "name": "--from",
13100
+ "name": "--until-event",
12827
13101
  "required": false,
12828
- "value": "<now|earliest>",
12829
- "description": "Starting point for a new named cursor. Default now (silent baseline); earliest replays the journal."
13102
+ "description": "Wait for the first event in defaults.watch.untilEvent.kinds (agent.report, agent.blocked, panel.exited, pane.gone), print that one line, and exit 0. Inside a Pane terminal the cursor defaults to until-<panel id>, so a re-run resumes where the last stopped. A RESET and the caller's own panel never end the wait. With --timeout-ms, exit 124 with WATCH TIMEOUT when the deadline passes. Cannot be combined with --follow or --self-test."
12830
13103
  },
12831
13104
  {
12832
- "name": "--follow",
13105
+ "name": "--session",
12833
13106
  "required": false,
12834
- "description": "Continue waiting until interrupted. Required by every cadence flag."
13107
+ "value": "<id|name>",
13108
+ "description": "Follow every Pane associated with a named Session (id or exact name). The daemon re-reads membership on every read, so a Pane associated after arming is included and a detached or archived Pane drops out without a re-arm. The Session's own orchestrator panels are never reported. Cannot be combined with --pane or --all-managed; an unknown Session fails."
12835
13109
  },
12836
13110
  {
12837
- "name": "--until-event",
13111
+ "name": "--pane",
12838
13112
  "required": false,
12839
- "description": "Wait across daemon calls (each capped at 120 s) for the first matching event, print it, and exit 0. Same scope defaults as --follow. A RESET and entries from the caller's own panel (PANE_PANEL_ID) never end the wait. Without --as inside a Pane terminal the cursor is until-<panel id>, apart from the panel's --follow cursor. --timeout-ms is the total deadline: exit 124 with WATCH TIMEOUT (_timeout). Cannot be combined with --follow, --self-test, or the cadence flags."
13113
+ "value": "<pane-id>",
13114
+ "description": "Limit to a Pane; repeatable. Default all managed panes. Cannot be combined with --session."
12840
13115
  },
12841
13116
  {
12842
- "name": "--format",
12843
- "value": "<lines|json>",
13117
+ "name": "--exclude-pane",
12844
13118
  "required": false,
12845
- "description": "Output format. Default lines in every mode; --json selects json."
13119
+ "value": "<pane-id>",
13120
+ "description": "Exclude a Pane; repeatable."
12846
13121
  },
12847
13122
  {
12848
- "name": "--heartbeat",
12849
- "value": "<seconds>",
13123
+ "name": "--repo",
12850
13124
  "required": false,
12851
- "description": "Healthy-silence bound; a HEARTBEAT line proves the watcher is alive. Default 60 under --follow, 0 otherwise. Any monitor that wakes an agent should pass --quiet, which drops it."
13125
+ "value": "<selector>",
13126
+ "description": "Limit to a saved repository (active, id, path, or name). Default all repositories."
12852
13127
  },
12853
13128
  {
12854
- "name": "--idle-after",
12855
- "value": "<milliseconds>",
13129
+ "name": "--name-contains",
12856
13130
  "required": false,
12857
- "description": "IDLE fires after a READY panel has stayed idle this long, then re-fires every multiple. Default 600000 under --follow, 0 otherwise."
13131
+ "value": "<text>",
13132
+ "description": "Limit by Pane name substring."
12858
13133
  },
12859
13134
  {
12860
- "name": "--settle",
12861
- "value": "<milliseconds>",
13135
+ "name": "--include-shells",
12862
13136
  "required": false,
12863
- "description": "Opt-in (follow only): emit READY only after the panel stays idle this long; a BUSY, BLOCKED, UNKNOWN, EXIT, or GONE inside the window cancels it silently. Default 0. Use when a pane flips idle/working while it waits on subagents; the unattended orchestrator value is 180000, the user-present value 60000."
13137
+ "description": "Include plain shell panels. Default: agent panels only. A report (agent.report) arrives from any panel either way."
12864
13138
  },
12865
13139
  {
12866
- "name": "--blocked-settle",
12867
- "value": "<milliseconds>",
13140
+ "name": "--kinds",
12868
13141
  "required": false,
12869
- "description": "Opt-in (follow only): emit BLOCKED only after this window; an in-pane answer inside it cancels the line. Default 0. The unattended orchestrator value is 30000, the user-present value 15000."
13142
+ "value": "<kind,...>",
13143
+ "description": "Replace the default event kinds: agent.ready, agent.busy, agent.blocked, agent.unknown, agent.idle, pane.created, pane.gone, panel.exited, panel.stopped, agent.report, pane.associated, pane.detached, pr.conflicted, pr.checks, pr.merged. Defaults: defaults.watch.follow.kinds under --follow, defaults.watch.untilEvent.kinds under --until-event, every kind for a one-shot read."
12870
13144
  },
12871
13145
  {
12872
- "name": "--min-interval",
12873
- "value": "<milliseconds>",
13146
+ "name": "--json",
12874
13147
  "required": false,
12875
- "description": "Opt-in (follow only): hold READY, IDLE, NEW, GONE, EXIT, and UNKNOWN lines and flush them together at most once per interval; BLOCKED, REPORT, pr.conflicted, and failed pr.checks bypass it and carry the held lines with them. Default 0. The unattended orchestrator value is 600000, the user-present value 120000."
13148
+ "description": "One JSON object per line. Control lines use kinds _ok, _heartbeat, _reconnected, _error, _reset, _dropped, _timeout, and _expired."
12876
13149
  },
12877
13150
  {
12878
- "name": "--idle-backoff",
13151
+ "name": "--as",
13152
+ "value": "<consumer-name>",
12879
13153
  "required": false,
12880
- "description": "Opt-in (follow only): IDLE fires at --idle-after, then 30m, 1h, 3h, then daily per panel; any activity resets the schedule. Default off (fixed multiples of --idle-after)."
13154
+ "description": "Name the cursor (at-least-once delivery): 1-128 letters, numbers, dots, underscores, or hyphens. Default under --follow: session-<uuid> with --session, else this panel (PANE_PANEL_ID, shortened to panel-<sha256 prefix> when longer than 64 characters); under --until-event: until-<panel id>; otherwise anonymous."
12881
13155
  },
12882
13156
  {
12883
- "name": "--kinds",
13157
+ "name": "--since",
13158
+ "value": "<generation>",
12884
13159
  "required": false,
12885
- "value": "<kind,...>",
12886
- "description": "Limit event kinds: agent.ready, agent.busy, agent.blocked, agent.unknown, agent.idle, pane.created, pane.gone, panel.exited, panel.stopped, agent.report, pane.associated, pane.detached, pr.conflicted, pr.checks, pr.merged. agent.report arrives only when listed explicitly. Default all, except that pane.associated, pane.detached, and the pr.* kinds arrive only when listed here or implied by --session, and panel.stopped arrives only when listed here; without it, a stopped agent panel reads as agent.unknown with running: false. Drop agent.busy for any consumer that acts on lines; BUSY carries no action."
13160
+ "description": "Use an explicit generation cursor instead of a name. Cannot be combined with --as or --follow."
12887
13161
  },
12888
13162
  {
12889
- "name": "--pane",
13163
+ "name": "--from",
12890
13164
  "required": false,
12891
- "value": "<pane-id>",
12892
- "description": "Limit to a Pane; repeatable. Default all managed panes. Cannot be combined with --session."
13165
+ "value": "<now|earliest>",
13166
+ "description": "Starting point for a new named cursor. Default now (silent baseline); earliest replays the journal."
12893
13167
  },
12894
13168
  {
12895
- "name": "--session",
13169
+ "name": "--self-test",
12896
13170
  "required": false,
12897
- "value": "<id|name>",
12898
- "description": "Follow every Pane associated with a named Session (id or exact name). The daemon re-reads membership on every read, so a Pane associated after arming is included and a detached or archived Pane drops out without a re-arm. The Session's own orchestrator panels are never reported. Cannot be combined with --pane or --all-managed; an unknown Session fails."
13171
+ "description": "Check the daemon watch path without moving any cursor, print WATCH OK, and exit 0."
12899
13172
  },
12900
13173
  {
12901
- "name": "--exclude-pane",
13174
+ "name": "--ready-settle",
13175
+ "value": "<duration>",
12902
13176
  "required": false,
12903
- "value": "<pane-id>",
12904
- "description": "Exclude a Pane; repeatable."
13177
+ "description": "Override: how long a panel must stay quiet before READY. Default 3m. A BUSY, BLOCKED, UNKNOWN, EXIT, or GONE inside the window cancels it. 0 turns it off."
12905
13178
  },
12906
13179
  {
12907
- "name": "--repo",
13180
+ "name": "--blocked-settle",
13181
+ "value": "<duration>",
12908
13182
  "required": false,
12909
- "value": "<selector>",
12910
- "description": "Limit to a saved repository (active, id, path, or name). Default all repositories."
13183
+ "description": "Override: how long before BLOCKED is printed; an in-pane answer inside it cancels the line. Default 30s. 0 turns it off."
12911
13184
  },
12912
13185
  {
12913
- "name": "--name-contains",
13186
+ "name": "--min-interval",
13187
+ "value": "<duration>",
12914
13188
  "required": false,
12915
- "value": "<text>",
12916
- "description": "Limit by Pane name substring."
13189
+ "description": "Override: batch READY, IDLE, NEW, GONE, EXIT, and UNKNOWN lines at most once per interval; REPORT, BLOCKED, pr.conflicted, and failed pr.checks arrive at once and carry the held lines. Default 10m. 0 turns batching off."
12917
13190
  },
12918
13191
  {
12919
- "name": "--agents-only",
13192
+ "name": "--idle-after",
13193
+ "value": "<duration>",
12920
13194
  "required": false,
12921
- "description": "Limit to CLI agent panels. Default on under --follow."
13195
+ "description": "Override: the first IDLE reminder after a READY panel stays idle. Default 10m. Later reminders back off to 30m, 1h, 3h, then daily. 0 turns IDLE off."
12922
13196
  },
12923
13197
  {
12924
- "name": "--all-managed",
13198
+ "name": "--expire-after",
13199
+ "value": "<duration>",
12925
13200
  "required": false,
12926
- "description": "Explicit spelling of the default scope: every non-archived, non-hidden managed Pane. Cannot be combined with --pane."
13201
+ "description": "Override: end the watch with WATCH EXPIRED and exit 0 after this long with no events but IDLE; any other event restarts the clock. Default 3h. 0 never expires."
12927
13202
  },
12928
13203
  {
12929
- "name": "--include-shells",
13204
+ "name": "--heartbeat",
13205
+ "value": "<duration>",
12930
13206
  "required": false,
12931
- "description": "Include ordinary shell panels."
13207
+ "description": "Override: print a HEARTBEAT line this often as proof of life. Default off; a monitor that wakes an agent should leave it off."
12932
13208
  },
12933
13209
  {
12934
13210
  "name": "--timeout-ms",
12935
13211
  "required": false,
12936
- "value": "<milliseconds>",
12937
- "description": "Wait timeout per request; 0 polls once. Default 60000, capped at 120000 (the follow loop re-issues requests). With --until-event it is the deadline for the whole wait instead."
13212
+ "value": "<duration>",
13213
+ "description": "Per-request wait in milliseconds; 0 reads once without waiting. Default 60000, capped at 120000 (the follow loop re-issues requests). With --until-event it is the deadline for the whole wait (exit 124 with WATCH TIMEOUT). Accepts ms/s/m/h units; bare values are milliseconds. 0/off is a nonblocking read only, and is rejected with --follow or --until-event."
12938
13214
  },
12939
13215
  {
12940
13216
  "name": "--limit",
@@ -12957,21 +13233,6 @@ exports.RUNPANE_CONTRACT = {
12957
13233
  "required": false,
12958
13234
  "description": "Disable redacted held-input detection under --follow: no STUCK lines, and no heldInputPresent in JSON."
12959
13235
  },
12960
- {
12961
- "name": "--self-test",
12962
- "required": false,
12963
- "description": "Probe the watch path anonymously without advancing a named cursor; run it before arming --follow."
12964
- },
12965
- {
12966
- "name": "--quiet",
12967
- "required": false,
12968
- "description": "Drop the control lines that only prove liveness: WATCH OK, HEARTBEAT, and WATCH RECONNECTED (_ok, _heartbeat, _reconnected in JSON). WATCH ERROR/_error, RESET/_reset, and DROPPED/_dropped always print, and --self-test still prints its WATCH OK result. Alias: --no-control-lines."
12969
- },
12970
- {
12971
- "name": "--json",
12972
- "required": false,
12973
- "description": "Emit structured NDJSON; held-input content remains opt-in."
12974
- },
12975
13236
  {
12976
13237
  "name": "--pane-dir",
12977
13238
  "value": "<path>",
@@ -12979,41 +13240,41 @@ exports.RUNPANE_CONTRACT = {
12979
13240
  "description": "Connect to a specific Pane data directory."
12980
13241
  },
12981
13242
  {
12982
- "name": "--pane-dir",
12983
- "value": "<path>",
12984
- "required": false,
12985
- "description": "Connect to a specific Pane data directory."
13243
+ "name": "--epoch",
13244
+ "value": "<epoch>",
13245
+ "description": "Journal epoch paired with --since; a mismatch emits _reset instead of silently skipping events.",
13246
+ "required": false
12986
13247
  }
12987
13248
  ],
12988
13249
  "examples": [
12989
13250
  "runpane watch --self-test",
13251
+ "runpane watch --until-event",
12990
13252
  "runpane watch --follow",
12991
- "runpane watch --as monitor --follow --quiet --json",
12992
- "runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone",
12993
- "runpane watch --follow --quiet --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
12994
- "runpane watch --follow --quiet --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 60000 --blocked-settle 15000 --min-interval 120000",
12995
- "runpane watch --session <id|name> --follow --quiet --json --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone,pane.associated,pane.detached,pr.conflicted,pr.checks,pr.merged --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff",
12996
- "runpane watch --session <id|name> --follow --quiet --json --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone,pane.associated,pane.detached,pr.conflicted,pr.checks,pr.merged --settle 60000 --blocked-settle 15000 --min-interval 120000"
13253
+ "runpane watch --session <id|name> --follow",
13254
+ "runpane watch --session <id|name> --follow --ready-settle 1m --blocked-settle 15s --min-interval 2m",
13255
+ "runpane watch --follow --json --kinds agent.report,agent.blocked"
12997
13256
  ],
12998
13257
  "notes": [
12999
- "Defaults stay responsive: no settle, no batching, all kinds, IDLE every --idle-after. Cadence flags are opt-in for expensive consumers and require --follow.",
13000
- "Unattended profile (what Pane Chat arms automatically, for overnight or background runs): runpane watch --self-test, then runpane watch --follow --quiet --kinds agent.ready,agent.blocked,agent.idle,panel.exited,pane.gone --settle 180000 --blocked-settle 30000 --min-interval 600000 --idle-backoff. Budget: about 6 wake-ups per active pane per hour worst case, usually 1-3; READY can arrive up to about 13 minutes after the turn ended.",
13001
- "User-present profile (a person is waiting on the result): the same command with --settle 60000 --blocked-settle 15000 --min-interval 120000 and no --idle-backoff. READY arrives within about 3 minutes of the turn ending.",
13002
- "BUSY carries no action; drop it with --kinds. HEARTBEAT is client-side proof of life and is never shaped by cadence flags; pass --quiet for any monitor that wakes an agent.",
13258
+ "Every default is in the contract under defaults.watch: follow (what --follow does) and untilEvent (what --until-event waits for). An override flag replaces one value; nothing else needs remembering.",
13259
+ "Arming: wait for WATCH OK before relying on a --follow monitor. It prints once when the watch is armed. Run the monitor under your harness's background monitor (one line = one notification).",
13260
+ "Ending: WATCH EXPIRED (exit 0) means nothing happened for --expire-after: say so, and arm a new watch with the next dispatch. A non-zero exit or a WATCH ERROR line means it failed: tell the user and re-arm once; if it dies again, file runpane doctor --report. If the message says the cursor was superseded, keep the replacement monitor and leave the old one stopped. Judge a watch by these lines, never by silence.",
13261
+ "--until-event prints exactly one line: the event (exit 0), WATCH TIMEOUT (exit 124, only with --timeout-ms), or WATCH ERROR (exit 2).",
13003
13262
  "STUCK means real unsubmitted composer text. An agent prompt suggestion (for example a grey Try \"...\" hint) never counts.",
13004
- "Dead watch: judge death by a non-zero exit or a WATCH ERROR line, not by silence. If the message says the cursor was superseded, keep the replacement monitor and do not re-arm the old one. Otherwise re-arm once; if it dies again, file runpane doctor --report. Stop the previous monitor before switching profiles.",
13005
- "Cadence state is held per named consumer; an anonymous --follow with a cadence flag names itself follow-<pid>. Held lines survive a reconnect of the same consumer; a changed filter re-delivers them under the new filter.",
13263
+ "Cadence state is held per named consumer. Without numeric --since, follow uses an explicit --as or assigns a Session, panel, or follow-<pid> name. With numeric --since it has no name and bypasses cadence; --as cannot be combined with --since. Held lines survive a reconnect of the same consumer; a changed filter re-delivers them under the new filter.",
13006
13264
  "Journal loss is surfaced through reset and dropped metadata.",
13007
13265
  "The daemon treats omitted idleAfterMs as disabled so older clients never receive agent.idle unexpectedly.",
13008
- "Control lines are not journal entries. JSON kinds and their lines-format text: _ok (WATCH OK, once when armed), _heartbeat (HEARTBEAT, every --heartbeat seconds), _reconnected (WATCH RECONNECTED, after a retried daemon error), _error (WATCH ERROR, a daemon error; the process exits 2 unless --follow or --until-event retries it), _reset (RESET, the journal restarted or the cursor fell behind; a fresh baseline follows), _dropped (DROPPED, entries lost to journal truncation), and _timeout (WATCH TIMEOUT after <ms>ms, the --until-event deadline passed; the process exits 124). --quiet drops _ok, _heartbeat, _reconnected, and _timeout; _error, _reset, and _dropped always print, except under --until-event, where --quiet drops _reset and _dropped too.",
13266
+ "Control lines are not journal entries. Lines text and JSON kind: WATCH OK (_ok, once when armed), HEARTBEAT (_heartbeat, only with --heartbeat), WATCH RECONNECTED (_reconnected, after a retried daemon error), WATCH ERROR (_error; exit 2 unless --follow or --until-event retries it), RESET (_reset, the journal restarted or the cursor fell behind; a fresh baseline follows), DROPPED (_dropped, entries lost to journal truncation), WATCH TIMEOUT (_timeout, --until-event deadline; exit 124), and WATCH EXPIRED (_expired, --expire-after passed; exit 0). Under --until-event, only _timeout and _error print.",
13009
13267
  "Under --follow, JSON entries for panels holding unsubmitted composer text carry heldInputPresent: true, the JSON equivalent of the STUCK line. Only typed composer text counts, the same reading as panels screen composer.hasUndeliveredText; an agent's dim or grey prompt suggestion or placeholder does not. --no-held-input turns this off.",
13010
13268
  "--session <id|name> scopes the watch to a named Session. The daemon resolves the Session once per request and its associated Panes on every read, so sessions associate and sessions detach take effect without a re-arm; archived and detached Panes drop out, and the Session's own orchestrator panels are excluded. Cadence state is keyed by the Session, not its current Panes, so membership changes keep held lines. A detached Pane's held lines are dropped.",
13011
13269
  "Under --session, pane.associated (JOINED <pane-name> pane <pane-id> session <session-id>) and pane.detached (LEFT ...) report membership changes; list them in --kinds next to the agent kinds. Other consumers receive them only when --kinds lists them, so older clients never see an unknown kind.",
13012
- "PR events (decision D7): about every 3 minutes (jittered) the daemon visits each Pane associated with a live Session (none: no gh at all), finds its PR by branch when no open PR is known yet (gh pr list --head, so unwatched workers' new and reopened PRs are found), runs gh pr view on an open PR, and appends pr.conflicted (PR <pane-name> pane <pane-id> #<number> CONFLICTED: the PR now conflicts with its base), pr.checks (... CHECKS PASSED, or CHECKS FAILED <up to five names>: every check on the head commit finished), and pr.merged (... MERGED). Entries carry pr: {number, url, headOid}, plus checks and failingChecks for pr.checks. They fire on transitions only: the first poll of a PR after the daemon starts only records its state, so re-check conflicts after a restart with gh pr view. pr.conflicted and failed pr.checks bypass --min-interval like BLOCKED. Like JOINED/LEFT, they reach a consumer only under --session or when --kinds lists them. Without gh, or while it is signed out or rate limited, polling backs off (up to an hour) and no PR entries arrive.",
13270
+ "PR watching: the daemon batches up to 25 direct GraphQL fingerprints per host every ~2 minutes. Full details are read on change or a 30-minute backstop. Existing Session branch discovery remains; standalone Panes register with pr watch --url <PR URL> [--head <full-sha>]. Immediate failures, required-check-aware success, head/review/comment/closed/merged events and GitHub outage/recovery events share durable baselines and the journal. A head pin prevents stale success, empty/partial checks never pass, and merge status BLOCKED/UNKNOWN conservatively withholds success. Rate limits honor server waits and reserve 10% for interactive use. Eight other errors or ten comment-only updates stop the watch with pr.stopped. See pr watch/unwatch/list.",
13013
13271
  "A daemon that predates --session returns no session field; runpane then fails with WATCH ERROR instead of watching every Pane.",
13014
13272
  "Replay: after a reset (RESET/_reset), the baseline restates current state. In JSON, those entries carry replay: true (and changedWhileAway: true after an epoch change). A replayed agent.ready is never READY: re-read runpane sessions overview instead of acting on it. Lines mode prints only CHANGED for them.",
13015
- "agent.report (REPORT <pane-name> pane <pane-id> panel <panel-id> <state> [pr#<n>] [<head>][: <question>]) is a worker's `runpane report`. It is opt-in: only a consumer that lists it in --kinds (or watches a Session) receives it. It passes --agents-only, which --follow and --until-event imply, whatever panel sent it: a shell tab or a wrapper launch whose agent is not yet detected included. It bypasses --min-interval, and orchestrators treat it as the completion signal; a READY without a report means look, and maybe nudge.",
13016
- "Exit-to-wake vs streaming: a harness that wakes an agent when a process exits (Claude Code background Bash, Codex) waits with runpane watch --as <name> --until-event --quiet --json --kinds agent.report,agent.blocked,panel.exited,pane.gone, run as a background task; when it exits, act on the printed line and run it again. The named cursor means nothing is missed between runs and nothing repeats. Streaming monitors (Pane Chat's Session watch, a Monitor tool) keep --follow. Never pipe --follow into grep -m1, head, or any other exit-on-match reader: --follow does not exit when its reader does until its next write."
13273
+ "agent.report (REPORT <pane-name> pane <pane-id> panel <panel-id> <state> [pr#<n>] [<head>][: <question>]) is a worker's `runpane report`. It is in the default kinds of --follow and --until-event, and arrives from any panel: a shell tab or a wrapper launch whose agent is not yet detected included. It bypasses --min-interval, and orchestrators treat it as the completion signal; a READY without a report means look, and maybe nudge.",
13274
+ "Never pipe --follow into grep -m1, head, or any other exit-on-match reader: it does not exit when its reader does until its next write. To wait for one event, use --until-event.",
13275
+ "Registration returns generation and epoch. Initially arm watch --until-event --since <generation> --epoch <epoch> --pane <id> --json --kinds pr.checks,pr.head,pr.comment,pr.review,pr.closed,pr.merged,pr.conflicted,pr.stopped,github.unavailable,github.recovered. Process every NDJSON event, then persist its gen and epoch for the next --since/--epoch; --limit 1 delivers one event per run. Never mix --as with --since/--epoch. On _reset or _dropped, resynchronize GitHub state, then use the control record generation and epoch; replay/baseline records are not new events. No merge or GitHub write is performed.",
13276
+ "--follow has one default cadence: READY 3m, BLOCKED 30s, non-urgent batches 10m, IDLE after 10m with backoff, expiry 3h, heartbeats off. Individual duration flags override it. --present and --profile were removed; use --ready-settle, --blocked-settle, and --min-interval for timing overrides.",
13277
+ "--until-event delivers matching events immediately without follow settling or batching. A harness that consumes a named follow stream gets its cadence; a process-exit waiter keeps immediate single-event semantics. --follow --since <generation> has no consumer name and bypasses settling and batching. Cadence requires a named follow consumer; --as cannot be combined with numeric --since."
13017
13278
  ]
13018
13279
  },
13019
13280
  "panes git-status": {
@@ -14105,7 +14366,7 @@ exports.RUNPANE_CONTRACT = {
14105
14366
  "notes": [
14106
14367
  "Inside a Pane terminal the report is for the current panel (PANE_SESSION_ID and PANE_PANEL_ID). Explicit --panel (with an optional --pane) wins; with neither, the command fails with a clear error.",
14107
14368
  "Each report replaces the panel's previous one and survives a Pane restart.",
14108
- "Watchers receive agent.report only when they list it in --kinds or watch a Session; it passes --agents-only from any panel, a shell tab included. REPORT lines skip the --min-interval batch.",
14369
+ "agent.report is in the default kinds of `runpane watch --follow` and `--until-event`, and arrives from any panel, a shell tab included. REPORT lines skip the --min-interval batch.",
14109
14370
  "The output says who received it: \"Delivered to N waiting watcher(s).\" counts watches waiting at that moment (liveWatchers in JSON). With no Session and nobody waiting it adds that the report is stored on the panel (runpane agents status --panel <id>) and that a named-cursor watch (runpane watch --as <name> --kinds agent.report) still receives it when it next waits. Zero is not a failure; the exit code is 0 either way."
14110
14371
  ]
14111
14372
  },
@@ -14792,6 +15053,132 @@ exports.RUNPANE_CONTRACT = {
14792
15053
  "notes": [
14793
15054
  "Uses the same Session update path as the UI. The requested pin state is idempotent."
14794
15055
  ]
15056
+ },
15057
+ "pr watch": {
15058
+ "name": "pr watch",
15059
+ "summary": "Register durable GitHub PR monitoring for a Pane.",
15060
+ "details": "Register durable GitHub PR monitoring for a Pane.",
15061
+ "requiresPaneDaemon": true,
15062
+ "mutates": true,
15063
+ "arguments": [
15064
+ {
15065
+ "name": "--pane",
15066
+ "value": "<id>",
15067
+ "required": false,
15068
+ "description": "Pane id; defaults to PANE_SESSION_ID."
15069
+ },
15070
+ {
15071
+ "name": "--url",
15072
+ "value": "<PR URL>",
15073
+ "required": true,
15074
+ "description": "Full HTTPS GitHub PR URL, including host, repository and number."
15075
+ },
15076
+ {
15077
+ "name": "--head",
15078
+ "value": "<full-sha>",
15079
+ "required": false,
15080
+ "description": "Pin success to this 40-character SHA; otherwise pin the first observed head."
15081
+ },
15082
+ {
15083
+ "name": "--json",
15084
+ "required": false,
15085
+ "description": "Print JSON (also the default for PR commands)."
15086
+ },
15087
+ {
15088
+ "name": "--pane-dir",
15089
+ "value": "<path>",
15090
+ "required": false,
15091
+ "description": "Pane data directory."
15092
+ }
15093
+ ],
15094
+ "examples": [
15095
+ "runpane pr watch [--pane <id>] --url <PR URL> [--head <full-sha>] [--json] [--pane-dir <path>]"
15096
+ ],
15097
+ "jsonSchemas": [
15098
+ "prWatchResult"
15099
+ ],
15100
+ "notes": [
15101
+ "Registration returns generation and epoch. Initially arm watch --until-event --since <generation> --epoch <epoch> --pane <id> --json --kinds pr.checks,pr.head,pr.comment,pr.review,pr.closed,pr.merged,pr.conflicted,pr.stopped,github.unavailable,github.recovered. Process every NDJSON event, then persist its gen and epoch for the next --since/--epoch; --limit 1 delivers one event per run. Never mix --as with --since/--epoch. On _reset or _dropped, resynchronize GitHub state, then use the control record generation and epoch; replay/baseline records are not new events. No merge or GitHub write is performed.",
15102
+ "Watches stop on merge/close, 8 non-rate-limit errors, or 10 comment-only updates. Archived/deleted Panes are removed. Re-register after resolving the reason. Session member PR discovery remains automatic."
15103
+ ]
15104
+ },
15105
+ "pr unwatch": {
15106
+ "name": "pr unwatch",
15107
+ "summary": "Remove a Pane’s explicit PR registration.",
15108
+ "details": "Remove a Pane’s explicit PR registration.",
15109
+ "requiresPaneDaemon": true,
15110
+ "mutates": true,
15111
+ "arguments": [
15112
+ {
15113
+ "name": "--pane",
15114
+ "value": "<id>",
15115
+ "required": false,
15116
+ "description": "Pane id; defaults to PANE_SESSION_ID."
15117
+ },
15118
+ {
15119
+ "name": "--url",
15120
+ "value": "<PR URL>",
15121
+ "required": true,
15122
+ "description": "Full HTTPS GitHub PR URL, including host, repository and number."
15123
+ },
15124
+ {
15125
+ "name": "--json",
15126
+ "required": false,
15127
+ "description": "Print JSON (also the default for PR commands)."
15128
+ },
15129
+ {
15130
+ "name": "--pane-dir",
15131
+ "value": "<path>",
15132
+ "required": false,
15133
+ "description": "Pane data directory."
15134
+ }
15135
+ ],
15136
+ "examples": [
15137
+ "runpane pr unwatch [--pane <id>] --url <PR URL> [--json] [--pane-dir <path>]"
15138
+ ],
15139
+ "jsonSchemas": [
15140
+ "prWatchResult"
15141
+ ],
15142
+ "notes": [
15143
+ "Registration returns generation and epoch. Initially arm watch --until-event --since <generation> --epoch <epoch> --pane <id> --json --kinds pr.checks,pr.head,pr.comment,pr.review,pr.closed,pr.merged,pr.conflicted,pr.stopped,github.unavailable,github.recovered. Process every NDJSON event, then persist its gen and epoch for the next --since/--epoch; --limit 1 delivers one event per run. Never mix --as with --since/--epoch. On _reset or _dropped, resynchronize GitHub state, then use the control record generation and epoch; replay/baseline records are not new events. No merge or GitHub write is performed.",
15144
+ "Watches stop on merge/close, 8 non-rate-limit errors, or 10 comment-only updates. Archived/deleted Panes are removed. Re-register after resolving the reason. Session member PR discovery remains automatic."
15145
+ ]
15146
+ },
15147
+ "pr list": {
15148
+ "name": "pr list",
15149
+ "summary": "List PR registrations and stopped watches.",
15150
+ "details": "List PR registrations and stopped watches.",
15151
+ "requiresPaneDaemon": true,
15152
+ "mutates": false,
15153
+ "arguments": [
15154
+ {
15155
+ "name": "--pane",
15156
+ "value": "<id>",
15157
+ "required": false,
15158
+ "description": "Pane id; defaults to PANE_SESSION_ID."
15159
+ },
15160
+ {
15161
+ "name": "--json",
15162
+ "required": false,
15163
+ "description": "Print JSON (also the default for PR commands)."
15164
+ },
15165
+ {
15166
+ "name": "--pane-dir",
15167
+ "value": "<path>",
15168
+ "required": false,
15169
+ "description": "Pane data directory."
15170
+ }
15171
+ ],
15172
+ "examples": [
15173
+ "runpane pr list [--pane <id>] [--json] [--pane-dir <path>]"
15174
+ ],
15175
+ "jsonSchemas": [
15176
+ "prWatchResult"
15177
+ ],
15178
+ "notes": [
15179
+ "Registration returns generation and epoch. Initially arm watch --until-event --since <generation> --epoch <epoch> --pane <id> --json --kinds pr.checks,pr.head,pr.comment,pr.review,pr.closed,pr.merged,pr.conflicted,pr.stopped,github.unavailable,github.recovered. Process every NDJSON event, then persist its gen and epoch for the next --since/--epoch; --limit 1 delivers one event per run. Never mix --as with --since/--epoch. On _reset or _dropped, resynchronize GitHub state, then use the control record generation and epoch; replay/baseline records are not new events. No merge or GitHub write is performed.",
15180
+ "Watches stop on merge/close, 8 non-rate-limit errors, or 10 comment-only updates. Archived/deleted Panes are removed. Re-register after resolving the reason. Session member PR discovery remains automatic."
15181
+ ]
14795
15182
  }
14796
15183
  },
14797
15184
  "managedBlock": [