aibroker 0.54.0 → 0.55.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +3 -0
  2. package/dist/a2a/agentish-extension.d.ts +111 -0
  3. package/dist/a2a/agentish-extension.d.ts.map +1 -0
  4. package/dist/a2a/agentish-extension.js +124 -0
  5. package/dist/a2a/agentish-extension.js.map +1 -0
  6. package/dist/a2a/client.d.ts +45 -0
  7. package/dist/a2a/client.d.ts.map +1 -0
  8. package/dist/a2a/client.js +112 -0
  9. package/dist/a2a/client.js.map +1 -0
  10. package/dist/a2a/exposure.d.ts +24 -0
  11. package/dist/a2a/exposure.d.ts.map +1 -0
  12. package/dist/a2a/exposure.js +70 -0
  13. package/dist/a2a/exposure.js.map +1 -0
  14. package/dist/a2a/schema/types.d.ts +153 -0
  15. package/dist/a2a/schema/types.d.ts.map +1 -0
  16. package/dist/a2a/schema/types.js +28 -0
  17. package/dist/a2a/schema/types.js.map +1 -0
  18. package/dist/a2a/schema/validate.d.ts +27 -0
  19. package/dist/a2a/schema/validate.d.ts.map +1 -0
  20. package/dist/a2a/schema/validate.js +190 -0
  21. package/dist/a2a/schema/validate.js.map +1 -0
  22. package/dist/a2a/server.d.ts +73 -0
  23. package/dist/a2a/server.d.ts.map +1 -0
  24. package/dist/a2a/server.js +325 -0
  25. package/dist/a2a/server.js.map +1 -0
  26. package/dist/a2a/tasks.d.ts +81 -0
  27. package/dist/a2a/tasks.d.ts.map +1 -0
  28. package/dist/a2a/tasks.js +148 -0
  29. package/dist/a2a/tasks.js.map +1 -0
  30. package/dist/agentish/index.d.ts +80 -0
  31. package/dist/agentish/index.d.ts.map +1 -0
  32. package/dist/agentish/index.js +263 -0
  33. package/dist/agentish/index.js.map +1 -0
  34. package/dist/daemon/a2a-cli.d.ts +46 -0
  35. package/dist/daemon/a2a-cli.d.ts.map +1 -0
  36. package/dist/daemon/a2a-cli.js +385 -0
  37. package/dist/daemon/a2a-cli.js.map +1 -0
  38. package/dist/daemon/agentish-cli.d.ts +17 -0
  39. package/dist/daemon/agentish-cli.d.ts.map +1 -0
  40. package/dist/daemon/agentish-cli.js +137 -0
  41. package/dist/daemon/agentish-cli.js.map +1 -0
  42. package/dist/daemon/agentish-stats.d.ts +65 -0
  43. package/dist/daemon/agentish-stats.d.ts.map +1 -0
  44. package/dist/daemon/agentish-stats.js +125 -0
  45. package/dist/daemon/agentish-stats.js.map +1 -0
  46. package/dist/daemon/agentish.d.ts +13 -0
  47. package/dist/daemon/agentish.d.ts.map +1 -0
  48. package/dist/daemon/agentish.js +13 -0
  49. package/dist/daemon/agentish.js.map +1 -0
  50. package/dist/daemon/cli.js +12 -1
  51. package/dist/daemon/cli.js.map +1 -1
  52. package/dist/daemon/core-handlers.d.ts.map +1 -1
  53. package/dist/daemon/core-handlers.js +38 -0
  54. package/dist/daemon/core-handlers.js.map +1 -1
  55. package/dist/daemon/manage.d.ts +62 -0
  56. package/dist/daemon/manage.d.ts.map +1 -1
  57. package/dist/daemon/manage.js +198 -4
  58. package/dist/daemon/manage.js.map +1 -1
  59. package/dist/daemon/screenshot.d.ts +13 -0
  60. package/dist/daemon/screenshot.d.ts.map +1 -1
  61. package/dist/daemon/screenshot.js +60 -29
  62. package/dist/daemon/screenshot.js.map +1 -1
  63. package/dist/daemon/todoist-webhook.d.ts +25 -2
  64. package/dist/daemon/todoist-webhook.d.ts.map +1 -1
  65. package/dist/daemon/todoist-webhook.js +136 -17
  66. package/dist/daemon/todoist-webhook.js.map +1 -1
  67. package/dist/mcp/index.js +41 -1
  68. package/dist/mcp/index.js.map +1 -1
  69. package/package.json +1 -1
@@ -19,6 +19,18 @@
19
19
  * carries one-shot instructions from the operator into the next arming, and it
20
20
  * says what it did. Everything requiring judgement stays with the person.
21
21
  */
22
+ /**
23
+ * Whether the pane is showing a Claude Code permission/approval prompt.
24
+ *
25
+ * Kept deliberately narrow. A busy pane and a stuck one both look the same
26
+ * to a change-hash — nothing moves — so telling them apart has to come from
27
+ * the actual words on screen, and the words checked here are ones a genuine
28
+ * permission dialog produces and very little else does. A background-agent
29
+ * status line ("Waiting for 1 background agent to finish") or a bare shell
30
+ * prompt must both read as false here, or every long tool call starts
31
+ * looking like a stuck session and the manager stops arming real work.
32
+ */
33
+ export declare function pendingPrompt(content: string): boolean;
22
34
  export interface ManagedSession {
23
35
  sessionId: string;
24
36
  /** Human name at the time of starting, for logs only — sessions get renamed. */
@@ -56,6 +68,35 @@ export interface ManagedSession {
56
68
  clearAfterHandover?: boolean;
57
69
  /** Consecutive armings typed but never seen to land. Bounded — see ARM_ATTEMPTS. */
58
70
  armFails?: number;
71
+ /**
72
+ * Consecutive arm-failure CAPS crossed, never reset by hitting the cap.
73
+ *
74
+ * `armFails` resets to 0 the instant it reaches ARM_ATTEMPTS, which is right
75
+ * for the backoff mechanics but wrong for detecting a genuinely stuck
76
+ * session: a content-agnostic "are we stuck" check keyed off `armFails`
77
+ * never sees anything but 0..ARM_ATTEMPTS-1. This survives that reset, so
78
+ * `blockedReason` has something durable to look at.
79
+ */
80
+ armFailStreak?: number;
81
+ /**
82
+ * When arming first crossed ARM_ATTEMPTS and the manager gave up typing at
83
+ * this session. Distinct from `blockedSince` (when the operator was told):
84
+ * this is set the moment the streak crosses the cap, `blockedSince` only
85
+ * once the pane has also gone static long enough to confirm it.
86
+ */
87
+ stuckSince?: number;
88
+ /**
89
+ * When this session was first seen BLOCKED — waiting on a permission
90
+ * prompt or similar, not merely idle. Kept across ticks so the operator
91
+ * can be told how long it has actually been stuck, not just that it is.
92
+ * Cleared the moment it stops looking blocked, so a later block re-alerts
93
+ * fresh rather than inheriting a stale duration.
94
+ */
95
+ blockedSince?: number;
96
+ /** When the operator was last alerted about this BLOCKED session — the
97
+ * dedup clock. See BLOCKED_REALERT_MS: without this, every 20-second tick
98
+ * the pane still shows the prompt would be its own alert. */
99
+ blockedAlertedAt?: number;
59
100
  /** When a handover was last obtained, so it is not demanded every tick. */
60
101
  handoverDoneAt?: number;
61
102
  /** The context reading when it was obtained, so "how much work since" is
@@ -303,6 +344,27 @@ export declare function screenLease(m: ManagedSession, now: number): {
303
344
  until: number;
304
345
  since: number;
305
346
  } | null;
347
+ /**
348
+ * Why a managed session should be treated as BLOCKED — waiting on a human
349
+ * decision arming cannot supply — rather than merely idle or slow.
350
+ *
351
+ * Two paths, checked in this order because they answer different questions.
352
+ * The fast path reads the words on screen and needs nothing else: a
353
+ * permission prompt IS a permission prompt the instant it appears, however
354
+ * recently the pane last changed. The slow path exists for what the fast one
355
+ * cannot see — a native macOS dialog steals keyboard focus and leaves no
356
+ * trace in the terminal's own buffer — so it infers the same state from
357
+ * arming's own repeated failure instead: ARM_ATTEMPTS have already come back
358
+ * unconfirmed AND the screen has still not moved since. Neither signal alone
359
+ * would be trustworthy (a slow turn fails to arm too; a quiet pane is often
360
+ * just a quiet pane) but together they are the two ways this actually
361
+ * happens.
362
+ *
363
+ * Returns the reason string rather than a boolean because the caller puts it
364
+ * straight into the operator alert — computing it a second time to describe
365
+ * what the first computation found is how the two descriptions drift apart.
366
+ */
367
+ export declare function blockedReason(m: Pick<ManagedSession, "armFailStreak" | "stuckSince" | "lastChangeAt">, content: string, now: number): string | null;
306
368
  export declare function startManagerLoop(): void;
307
369
  export interface ManageResult {
308
370
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/daemon/manage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAuMH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;6DACyD;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;uEACmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kFAC8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;OAUG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE;QACN,uEAAuE;QACvE,KAAK,EAAE,MAAM,CAAC;QACd,oEAAoE;QACpE,OAAO,EAAE,MAAM,CAAC;QAChB,2DAA2D;QAC3D,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,2EAA2E;QAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AA0MD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB/D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAE,IAAiB,GAAG,MAAM,CAUnF;AA4LD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkC3F;AA8PD;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAyCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,SAAa,GAAG,MAAM,CAa3D;AAED,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,IAAI,SAAa,GAAG,MAAM,CAQ7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,SAAY,GAAG,MAAM,CAE5D;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,SAAY,GAAG,MAAM,CAE9D;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAa,GAAG,IAAI,CAQxE;AAaD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAgBnF;AAOD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,IAAiB,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAuBrD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAgBvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAwBR;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASnG;AA0vBD,wBAAgB,gBAAgB,IAAI,IAAI,CASvC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwkBjG"}
1
+ {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/daemon/manage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAqOH;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4DAA4D;IAC5D,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;6DACyD;IACzD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;kEAE8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;uEACmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;;;;;;;;;;;OAeG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;kFAC8E;IAC9E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;;;;;;;OAUG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE;QACN,uEAAuE;QACvE,KAAK,EAAE,MAAM,CAAC;QACd,oEAAoE;QACpE,OAAO,EAAE,MAAM,CAAC;QAChB,2DAA2D;QAC3D,MAAM,EAAE,OAAO,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,2EAA2E;QAC3E,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,SAAS,EAAE,MAAM,CAAC;CACnB;AA0MD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB/D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAE,IAAiB,GAAG,MAAM,CAUnF;AA0ND;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAkC3F;AA8PD;;;;;;;;;;;;GAYG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5C;AAyCD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,SAAa,GAAG,MAAM,CAa3D;AAED,6DAA6D;AAC7D,wBAAgB,mBAAmB,CAAC,IAAI,SAAa,GAAG,MAAM,CAQ7D;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,SAAY,GAAG,MAAM,CAE5D;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,SAAY,GAAG,MAAM,CAE9D;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAa,GAAG,IAAI,CAQxE;AAaD;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,GAAG,IAAI,CAgBnF;AAOD;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,IAAiB,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAuBrD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAgBvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,GACjB,MAAM,CAwBR;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASnG;AAkLD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,GAAG,YAAY,GAAG,cAAc,CAAC,EACxE,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,GACV,MAAM,GAAG,IAAI,CAUf;AAmqBD,wBAAgB,gBAAgB,IAAI,IAAI,CASvC;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,OAAO,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwkBjG"}
@@ -29,8 +29,10 @@ import { typeIntoSession } from "../transport/sync-facade.js";
29
29
  import { discoverLiveSessions } from "../core/session-discovery.js";
30
30
  import { hasPailotClients } from "../adapters/pailot/gateway.js";
31
31
  import { getAibpBridge } from "../core/state.js";
32
+ import { captureSessionPng } from "./screenshot.js";
32
33
  import { listDialogs, answerDialog } from "./dialogs.js";
33
34
  import { readControls, grantUntil, returnToOperator, verdict as screenVerdict, describeControls, screenGrantedClause, } from "./pointer-controls.js";
35
+ import { AG2_SPEC } from "./agentish.js";
34
36
  const STATE_FILE = join(homedir(), ".aibroker", "managers.json");
35
37
  /** How often every manager is looked at. Cheap: one content read per managed session. */
36
38
  const TICK_MS = 20_000;
@@ -108,6 +110,32 @@ const HANDOVER_GRACE_MS = 6 * 60_000;
108
110
  * leans towards firing.
109
111
  */
110
112
  const STUCK_AFTER_MS = 12 * 60_000;
113
+ /**
114
+ * How long a session may sit with arming failing before it is treated as
115
+ * BLOCKED rather than merely slow to confirm.
116
+ *
117
+ * This is the content-agnostic path, for the case the fast prompt-text match
118
+ * cannot see: a native macOS dialog (a permission or security escalation
119
+ * that even bypass mode raises) steals keyboard focus without leaving any
120
+ * trace in the pane it sits in front of. Nothing distinguishes that from an
121
+ * ordinary slow turn except that arming keeps failing — so this fires only
122
+ * once BOTH are true: ARM_ATTEMPTS have already failed, and the screen still
123
+ * has not moved since. Three minutes, not twelve, because a session already
124
+ * failing to arm is already a worse sign than a merely quiet one.
125
+ */
126
+ const BLOCKED_STATIC_MS = 3 * 60_000;
127
+ /**
128
+ * How long to wait before re-alerting the operator about a BLOCKED session.
129
+ *
130
+ * The overnight failure this exists for produced the same log line every
131
+ * tick for four hours with nobody told. The opposite fault — a buzz every
132
+ * 20 seconds for as long as the dialog sits there — is nearly as bad, since
133
+ * an alert that repeats every tick teaches its reader to swipe it away
134
+ * unread. Fifteen minutes says it once, then again if it is still true a
135
+ * while later, which is the shape a "you need to look at this" message
136
+ * should have.
137
+ */
138
+ const BLOCKED_REALERT_MS = 15 * 60_000;
111
139
  /**
112
140
  * The startup banner, which is the pane's only POSITIVE evidence of a clear.
113
141
  *
@@ -190,6 +218,23 @@ const OUT_OF_GOAL = [
190
218
  /goal not achieved/i,
191
219
  /could not achieve the goal/i,
192
220
  ];
221
+ /**
222
+ * Whether the pane is showing a Claude Code permission/approval prompt.
223
+ *
224
+ * Kept deliberately narrow. A busy pane and a stuck one both look the same
225
+ * to a change-hash — nothing moves — so telling them apart has to come from
226
+ * the actual words on screen, and the words checked here are ones a genuine
227
+ * permission dialog produces and very little else does. A background-agent
228
+ * status line ("Waiting for 1 background agent to finish") or a bare shell
229
+ * prompt must both read as false here, or every long tool call starts
230
+ * looking like a stuck session and the manager stops arming real work.
231
+ */
232
+ export function pendingPrompt(content) {
233
+ return (/Do you want to proceed\?/i.test(content) ||
234
+ /tell Claude what to do differently/i.test(content) ||
235
+ /❯\s*\d+\.\s*(Yes|No)\b/.test(content) ||
236
+ /Yes,\s*and don'?t ask again/i.test(content));
237
+ }
193
238
  /**
194
239
  * A duration nobody can print nonsense from.
195
240
  *
@@ -552,6 +597,36 @@ function reportToOperator(text) {
552
597
  return;
553
598
  alertOperator(text);
554
599
  }
600
+ /**
601
+ * Alert the operator about a BLOCKED session, with a screenshot if one can
602
+ * be had.
603
+ *
604
+ * A picture of the actual prompt is worth having — "blocked" alone does not
605
+ * say whether it is a two-line yes/no or a wall of text — but capturing one
606
+ * means stealing the operator's window focus for a moment (see
607
+ * captureSessionPng), so it is only attempted at all when a phone is
608
+ * actually there to receive it. Any failure along the way — no client
609
+ * connected, the window could not be found, the capture itself errored —
610
+ * falls back to the plain text alert rather than saying nothing.
611
+ */
612
+ async function alertBlocked(m, caption) {
613
+ if (hasPailotClients()) {
614
+ try {
615
+ const shot = await captureSessionPng(m.sessionId);
616
+ if (shot) {
617
+ getAibpBridge()?.routeToMobile(m.sessionId, caption, "IMAGE", {
618
+ imageBase64: shot.buffer.toString("base64"),
619
+ mimeType: shot.mime,
620
+ });
621
+ return;
622
+ }
623
+ }
624
+ catch (e) {
625
+ log(`[manage:${m.name}] blocked-alert screenshot failed — ${e.message}`);
626
+ }
627
+ }
628
+ alertOperator(caption);
629
+ }
555
630
  /**
556
631
  * A note that is ALSO worth a buzz on the phone.
557
632
  *
@@ -1232,7 +1307,15 @@ function goalText(m) {
1232
1307
  const lease = screenLease(m, Date.now());
1233
1308
  return lease ? screenGrantedClause(lease.until, !rules) : "";
1234
1309
  })();
1235
- return composeGoal(m.objective, rules, hands, extra, standingRulesSource());
1310
+ // AG2 goes first, ahead of the objective and the standing rules, on every
1311
+ // arming — not once at setup. A managed session works unattended for hours
1312
+ // and a reminder given only at the start does not survive a compaction or a
1313
+ // `/clear`. This is folded into composeGoal's own objective argument, not
1314
+ // spliced in afterwards, so the ordering it produces (task, then rules,
1315
+ // then right-now context) still holds with AG2 simply riding in front of
1316
+ // the task. What a managed session sends the operator, and what it commits
1317
+ // to git, stays prose either way — AG2 is only how agents talk to agents.
1318
+ return composeGoal(`${AG2_SPEC} ${m.objective}`, rules, hands, extra, standingRulesSource());
1236
1319
  }
1237
1320
  /**
1238
1321
  * Did it land? Look for the goal's own words in the transcript.
@@ -1329,6 +1412,38 @@ async function arm(m, reason) {
1329
1412
  notify(m, `typed but the objective's own words never appeared — treating as NOT armed (${reason})`);
1330
1413
  return false;
1331
1414
  }
1415
+ /**
1416
+ * Why a managed session should be treated as BLOCKED — waiting on a human
1417
+ * decision arming cannot supply — rather than merely idle or slow.
1418
+ *
1419
+ * Two paths, checked in this order because they answer different questions.
1420
+ * The fast path reads the words on screen and needs nothing else: a
1421
+ * permission prompt IS a permission prompt the instant it appears, however
1422
+ * recently the pane last changed. The slow path exists for what the fast one
1423
+ * cannot see — a native macOS dialog steals keyboard focus and leaves no
1424
+ * trace in the terminal's own buffer — so it infers the same state from
1425
+ * arming's own repeated failure instead: ARM_ATTEMPTS have already come back
1426
+ * unconfirmed AND the screen has still not moved since. Neither signal alone
1427
+ * would be trustworthy (a slow turn fails to arm too; a quiet pane is often
1428
+ * just a quiet pane) but together they are the two ways this actually
1429
+ * happens.
1430
+ *
1431
+ * Returns the reason string rather than a boolean because the caller puts it
1432
+ * straight into the operator alert — computing it a second time to describe
1433
+ * what the first computation found is how the two descriptions drift apart.
1434
+ */
1435
+ export function blockedReason(m, content, now) {
1436
+ if (pendingPrompt(content))
1437
+ return "permission prompt";
1438
+ const quietFor = now - (m.lastChangeAt ?? now);
1439
+ // Stuck = we tried to arm repeatedly (streak survived the cap reset) AND,
1440
+ // now that we have stopped typing, the pane has gone static. The static
1441
+ // check matters because our own arming used to keep the pane changing.
1442
+ if (m.stuckSince !== undefined && quietFor >= BLOCKED_STATIC_MS) {
1443
+ return `stuck ${Math.round((now - m.stuckSince) / 60_000)}m, input not accepted`;
1444
+ }
1445
+ return null;
1446
+ }
1332
1447
  function reasonToArm(m, content, now) {
1333
1448
  if (m.paused)
1334
1449
  return null;
@@ -1459,8 +1574,16 @@ async function tick() {
1459
1574
  * turn, costing nothing; against a dead one it is the whole recovery. The
1460
1575
  * asymmetry is the argument — and it is why this fires on a signal as crude
1461
1576
  * as "nothing changed", which no more precise test would improve on.
1577
+ *
1578
+ * Excluded once `stuckSince` is set: that means arming has already been
1579
+ * tried and failed enough to be classed as stuck, and this backstop's
1580
+ * response — type into it, and count the typing itself as a change — is
1581
+ * exactly the loop the blocked-alert exists to stop. Firing here would
1582
+ * reset `lastChangeAt` right when the blocked check needs it to keep
1583
+ * climbing, silently clearing the confirmed-blocked state without the
1584
+ * session having moved at all.
1462
1585
  */
1463
- if (!m.paused && !m.handoverAskedAt && now - m.lastChangeAt > STUCK_AFTER_MS) {
1586
+ if (!m.paused && !m.handoverAskedAt && !m.stuckSince && now - m.lastChangeAt > STUCK_AFTER_MS) {
1464
1587
  notify(m, `nothing has moved on that screen for ${minutesSince(m.lastChangeAt, now)} — arming, because a managed session is never meant to be still this long`);
1465
1588
  // Counted as a change so a session that stays stuck is not re-armed every
1466
1589
  // tick: this is a recovery, and a recovery that repeats is a loop.
@@ -1836,6 +1959,65 @@ async function tick() {
1836
1959
  continue;
1837
1960
  }
1838
1961
  }
1962
+ /**
1963
+ * BLOCKED: waiting on a human decision that arming cannot supply.
1964
+ *
1965
+ * This is the fix for a session that hit a permission or security
1966
+ * prompt overnight — even bypass mode escalates some of these — and sat
1967
+ * answered by nobody for four hours while the manager kept typing the
1968
+ * goal at it and logging "will retry". Typing MORE text at a modal does
1969
+ * not help; at best it queues, at worst it lands somewhere the operator
1970
+ * never sees. So this is checked BEFORE any arm attempt below, and it
1971
+ * never answers the prompt itself — only says, loudly, that it exists.
1972
+ *
1973
+ * Gated on holding a screen grant (`lease`, computed above) because
1974
+ * confirming a screenshot requires bringing that iTerm2 window to the
1975
+ * front via AppleScript, which steals whatever window currently has the
1976
+ * operator's focus. Without a grant that is not this manager's window to
1977
+ * take, so a session in that state gets no special handling here and
1978
+ * simply keeps going through the ordinary arm path below — worse than
1979
+ * ideal, but never worse than before this existed.
1980
+ */
1981
+ if (!m.paused && lease) {
1982
+ const blocked = blockedReason(m, content, now);
1983
+ if (blocked) {
1984
+ if (!m.blockedAlertedAt || now - m.blockedAlertedAt >= BLOCKED_REALERT_MS) {
1985
+ const quietMin = Math.round(Math.max(0, now - m.lastChangeAt) / 60_000);
1986
+ m.blockedSince ??= now;
1987
+ m.blockedAlertedAt = now;
1988
+ const caption = `⚠️ ${m.name} looks blocked (${blocked}) — no change for ${quietMin}m. ` +
1989
+ `Needs your approve/decline; I will not answer it.`;
1990
+ void alertBlocked(m, caption);
1991
+ note(m, `blocked (${blocked}) — alerted the operator, not arming`);
1992
+ dirty = true;
1993
+ }
1994
+ continue;
1995
+ }
1996
+ // Recovery. Gated on having ALREADY confirmed-and-alerted a block —
1997
+ // not merely on armFailStreak/stuckSince being set — because quietFor
1998
+ // is, by construction, still under BLOCKED_STATIC_MS for the entire
1999
+ // ramp from "stuck" to "confirmed blocked": clearing on that alone
2000
+ // would wipe stuckSince on the very next tick, every time, and the
2001
+ // alert below would never get a chance to fire. Once blockedSince
2002
+ // exists, though, BLOCKED_STATIC_MS of true silence already elapsed,
2003
+ // so a later drop in quietFor is unambiguously the session moving on
2004
+ // its own, not an artifact of this manager's own last typed attempt.
2005
+ if (m.blockedSince !== undefined || m.blockedAlertedAt !== undefined) {
2006
+ m.armFailStreak = 0;
2007
+ delete m.stuckSince;
2008
+ delete m.blockedSince;
2009
+ delete m.blockedAlertedAt;
2010
+ dirty = true;
2011
+ }
2012
+ }
2013
+ // Once arming has failed enough to be considered stuck, stop typing into
2014
+ // the session — the only way BLOCKED_STATIC_MS of true quiet is ever
2015
+ // reached is if this manager is not the one refreshing the pane every
2016
+ // REARM_COOLDOWN_MS. This is what actually fixes the overnight bug: the
2017
+ // old code kept re-arming (and re-spamming) forever because each retype
2018
+ // reset lastChangeAt and the freeze detector never got a quiet window.
2019
+ if (m.stuckSince !== undefined)
2020
+ continue;
1839
2021
  if (now - m.lastRearmAt < REARM_COOLDOWN_MS)
1840
2022
  continue;
1841
2023
  const reason = reasonToArm(m, content, now);
@@ -1862,14 +2044,26 @@ async function tick() {
1862
2044
  dirty = true;
1863
2045
  if (armed) {
1864
2046
  m.armFails = 0;
2047
+ m.armFailStreak = 0;
2048
+ delete m.stuckSince;
2049
+ delete m.blockedSince;
2050
+ delete m.blockedAlertedAt;
1865
2051
  continue;
1866
2052
  }
1867
2053
  m.armFails = (m.armFails ?? 0) + 1;
2054
+ m.armFailStreak = (m.armFailStreak ?? 0) + 1;
1868
2055
  if (m.armFails >= ARM_ATTEMPTS) {
1869
2056
  m.armFails = 0;
1870
2057
  m.lastRearmAt = now;
1871
- notify(m, `typed the goal ${ARM_ATTEMPTS} times without being able to confirm it landed — stopping, so it is not queued again. ` +
1872
- `The session is usually mid-turn when this happens; it stays managed and will arm at the next real lapse.`);
2058
+ m.stuckSince ??= now;
2059
+ // No operator push here. Pushing on every cap is the 144-alerts-overnight
2060
+ // bug: this fires again every ~2-4 minutes for as long as arming keeps
2061
+ // failing, and it is content-agnostic — it cannot tell "mid-turn, will
2062
+ // clear itself" from "stuck on a modal for hours". The blocked check
2063
+ // above owns the operator alert now: it has `armFailStreak`/`stuckSince`
2064
+ // (which survive this reset) plus a static-pane confirmation, and its
2065
+ // own BLOCKED_REALERT_MS dedup. This stays log-only.
2066
+ note(m, `arming keeps failing (streak ${m.armFailStreak}) — holding off; the blocked check owns the operator alert now`);
1873
2067
  }
1874
2068
  }
1875
2069
  if (dirty)