pi-amq 0.1.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 (264) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/LICENSE +21 -0
  3. package/README.md +92 -0
  4. package/THIRD_PARTY_NOTICES.md +33 -0
  5. package/dist/extension/guidance.d.ts +25 -0
  6. package/dist/extension/guidance.d.ts.map +1 -0
  7. package/dist/extension/guidance.js +121 -0
  8. package/dist/extension/guidance.js.map +1 -0
  9. package/dist/extension/guide.d.ts +30 -0
  10. package/dist/extension/guide.d.ts.map +1 -0
  11. package/dist/extension/guide.js +128 -0
  12. package/dist/extension/guide.js.map +1 -0
  13. package/dist/extension/index.d.ts +4 -0
  14. package/dist/extension/index.d.ts.map +1 -0
  15. package/dist/extension/index.js +6 -0
  16. package/dist/extension/index.js.map +1 -0
  17. package/dist/extension/runtime.d.ts +48 -0
  18. package/dist/extension/runtime.d.ts.map +1 -0
  19. package/dist/extension/runtime.js +1095 -0
  20. package/dist/extension/runtime.js.map +1 -0
  21. package/dist/extension/tools.d.ts +12 -0
  22. package/dist/extension/tools.d.ts.map +1 -0
  23. package/dist/extension/tools.js +198 -0
  24. package/dist/extension/tools.js.map +1 -0
  25. package/dist/extension/worker-runtime.d.ts +34 -0
  26. package/dist/extension/worker-runtime.d.ts.map +1 -0
  27. package/dist/extension/worker-runtime.js +974 -0
  28. package/dist/extension/worker-runtime.js.map +1 -0
  29. package/dist/extension/worker-tools.d.ts +140 -0
  30. package/dist/extension/worker-tools.d.ts.map +1 -0
  31. package/dist/extension/worker-tools.js +217 -0
  32. package/dist/extension/worker-tools.js.map +1 -0
  33. package/dist/integration/confirmed-bind-onboarding.d.ts +44 -0
  34. package/dist/integration/confirmed-bind-onboarding.d.ts.map +1 -0
  35. package/dist/integration/confirmed-bind-onboarding.js +199 -0
  36. package/dist/integration/confirmed-bind-onboarding.js.map +1 -0
  37. package/dist/integration/fleet-view.d.ts +66 -0
  38. package/dist/integration/fleet-view.d.ts.map +1 -0
  39. package/dist/integration/fleet-view.js +238 -0
  40. package/dist/integration/fleet-view.js.map +1 -0
  41. package/dist/integration/fresh-session-dispatcher.d.ts +66 -0
  42. package/dist/integration/fresh-session-dispatcher.d.ts.map +1 -0
  43. package/dist/integration/fresh-session-dispatcher.js +103 -0
  44. package/dist/integration/fresh-session-dispatcher.js.map +1 -0
  45. package/dist/integration/launch-preview.d.ts +31 -0
  46. package/dist/integration/launch-preview.d.ts.map +1 -0
  47. package/dist/integration/launch-preview.js +325 -0
  48. package/dist/integration/launch-preview.js.map +1 -0
  49. package/dist/integration/transport-port.d.ts +39 -0
  50. package/dist/integration/transport-port.d.ts.map +1 -0
  51. package/dist/integration/transport-port.js +2 -0
  52. package/dist/integration/transport-port.js.map +1 -0
  53. package/dist/integration/worker-completion-notifier.d.ts +22 -0
  54. package/dist/integration/worker-completion-notifier.d.ts.map +1 -0
  55. package/dist/integration/worker-completion-notifier.js +81 -0
  56. package/dist/integration/worker-completion-notifier.js.map +1 -0
  57. package/dist/transport/amq-cli-adapter.d.ts +41 -0
  58. package/dist/transport/amq-cli-adapter.d.ts.map +1 -0
  59. package/dist/transport/amq-cli-adapter.js +291 -0
  60. package/dist/transport/amq-cli-adapter.js.map +1 -0
  61. package/dist/transport/amq-message.d.ts +47 -0
  62. package/dist/transport/amq-message.d.ts.map +1 -0
  63. package/dist/transport/amq-message.js +173 -0
  64. package/dist/transport/amq-message.js.map +1 -0
  65. package/dist/transport/amq-operations.d.ts +105 -0
  66. package/dist/transport/amq-operations.d.ts.map +1 -0
  67. package/dist/transport/amq-operations.js +320 -0
  68. package/dist/transport/amq-operations.js.map +1 -0
  69. package/dist/transport/atomic-json.d.ts +18 -0
  70. package/dist/transport/atomic-json.d.ts.map +1 -0
  71. package/dist/transport/atomic-json.js +90 -0
  72. package/dist/transport/atomic-json.js.map +1 -0
  73. package/dist/transport/binding-resolver.d.ts +46 -0
  74. package/dist/transport/binding-resolver.d.ts.map +1 -0
  75. package/dist/transport/binding-resolver.js +389 -0
  76. package/dist/transport/binding-resolver.js.map +1 -0
  77. package/dist/transport/bridge-journal.d.ts +100 -0
  78. package/dist/transport/bridge-journal.d.ts.map +1 -0
  79. package/dist/transport/bridge-journal.js +459 -0
  80. package/dist/transport/bridge-journal.js.map +1 -0
  81. package/dist/transport/dlq-retry-audit.d.ts +28 -0
  82. package/dist/transport/dlq-retry-audit.d.ts.map +1 -0
  83. package/dist/transport/dlq-retry-audit.js +66 -0
  84. package/dist/transport/dlq-retry-audit.js.map +1 -0
  85. package/dist/transport/inbox-pump.d.ts +81 -0
  86. package/dist/transport/inbox-pump.d.ts.map +1 -0
  87. package/dist/transport/inbox-pump.js +558 -0
  88. package/dist/transport/inbox-pump.js.map +1 -0
  89. package/dist/transport/index.d.ts +12 -0
  90. package/dist/transport/index.d.ts.map +1 -0
  91. package/dist/transport/index.js +12 -0
  92. package/dist/transport/index.js.map +1 -0
  93. package/dist/transport/presence-projector.d.ts +29 -0
  94. package/dist/transport/presence-projector.d.ts.map +1 -0
  95. package/dist/transport/presence-projector.js +73 -0
  96. package/dist/transport/presence-projector.js.map +1 -0
  97. package/dist/transport/priority-delivery-scheduler.d.ts +85 -0
  98. package/dist/transport/priority-delivery-scheduler.d.ts.map +1 -0
  99. package/dist/transport/priority-delivery-scheduler.js +210 -0
  100. package/dist/transport/priority-delivery-scheduler.js.map +1 -0
  101. package/dist/transport/receiver-lease.d.ts +102 -0
  102. package/dist/transport/receiver-lease.d.ts.map +1 -0
  103. package/dist/transport/receiver-lease.js +778 -0
  104. package/dist/transport/receiver-lease.js.map +1 -0
  105. package/dist/transport/runtime-status.d.ts +48 -0
  106. package/dist/transport/runtime-status.d.ts.map +1 -0
  107. package/dist/transport/runtime-status.js +71 -0
  108. package/dist/transport/runtime-status.js.map +1 -0
  109. package/dist/workflow/atomic-storage.d.ts +12 -0
  110. package/dist/workflow/atomic-storage.d.ts.map +1 -0
  111. package/dist/workflow/atomic-storage.js +104 -0
  112. package/dist/workflow/atomic-storage.js.map +1 -0
  113. package/dist/workflow/budget.d.ts +18 -0
  114. package/dist/workflow/budget.d.ts.map +1 -0
  115. package/dist/workflow/budget.js +74 -0
  116. package/dist/workflow/budget.js.map +1 -0
  117. package/dist/workflow/index.d.ts +17 -0
  118. package/dist/workflow/index.d.ts.map +1 -0
  119. package/dist/workflow/index.js +17 -0
  120. package/dist/workflow/index.js.map +1 -0
  121. package/dist/workflow/lifecycle-projection.d.ts +18 -0
  122. package/dist/workflow/lifecycle-projection.d.ts.map +1 -0
  123. package/dist/workflow/lifecycle-projection.js +22 -0
  124. package/dist/workflow/lifecycle-projection.js.map +1 -0
  125. package/dist/workflow/model-fallback.d.ts +2 -0
  126. package/dist/workflow/model-fallback.d.ts.map +1 -0
  127. package/dist/workflow/model-fallback.js +17 -0
  128. package/dist/workflow/model-fallback.js.map +1 -0
  129. package/dist/workflow/pi-session-fork.d.ts +18 -0
  130. package/dist/workflow/pi-session-fork.d.ts.map +1 -0
  131. package/dist/workflow/pi-session-fork.js +80 -0
  132. package/dist/workflow/pi-session-fork.js.map +1 -0
  133. package/dist/workflow/run-artifacts.d.ts +5 -0
  134. package/dist/workflow/run-artifacts.d.ts.map +1 -0
  135. package/dist/workflow/run-artifacts.js +25 -0
  136. package/dist/workflow/run-artifacts.js.map +1 -0
  137. package/dist/workflow/run-cleanup.d.ts +70 -0
  138. package/dist/workflow/run-cleanup.d.ts.map +1 -0
  139. package/dist/workflow/run-cleanup.js +222 -0
  140. package/dist/workflow/run-cleanup.js.map +1 -0
  141. package/dist/workflow/run-gate.d.ts +43 -0
  142. package/dist/workflow/run-gate.d.ts.map +1 -0
  143. package/dist/workflow/run-gate.js +70 -0
  144. package/dist/workflow/run-gate.js.map +1 -0
  145. package/dist/workflow/run-store.d.ts +208 -0
  146. package/dist/workflow/run-store.d.ts.map +1 -0
  147. package/dist/workflow/run-store.js +2965 -0
  148. package/dist/workflow/run-store.js.map +1 -0
  149. package/dist/workflow/spawn-confirmation.d.ts +44 -0
  150. package/dist/workflow/spawn-confirmation.d.ts.map +1 -0
  151. package/dist/workflow/spawn-confirmation.js +228 -0
  152. package/dist/workflow/spawn-confirmation.js.map +1 -0
  153. package/dist/workflow/system-process-inspector.d.ts +9 -0
  154. package/dist/workflow/system-process-inspector.d.ts.map +1 -0
  155. package/dist/workflow/system-process-inspector.js +129 -0
  156. package/dist/workflow/system-process-inspector.js.map +1 -0
  157. package/dist/workflow/worker-child-extension.d.ts +3 -0
  158. package/dist/workflow/worker-child-extension.d.ts.map +1 -0
  159. package/dist/workflow/worker-child-extension.js +182 -0
  160. package/dist/workflow/worker-child-extension.js.map +1 -0
  161. package/dist/workflow/worker-gate.d.ts +74 -0
  162. package/dist/workflow/worker-gate.d.ts.map +1 -0
  163. package/dist/workflow/worker-gate.js +165 -0
  164. package/dist/workflow/worker-gate.js.map +1 -0
  165. package/dist/workflow/worker-launch.d.ts +65 -0
  166. package/dist/workflow/worker-launch.d.ts.map +1 -0
  167. package/dist/workflow/worker-launch.js +338 -0
  168. package/dist/workflow/worker-launch.js.map +1 -0
  169. package/dist/workflow/worker-model-catalog.d.ts +36 -0
  170. package/dist/workflow/worker-model-catalog.d.ts.map +1 -0
  171. package/dist/workflow/worker-model-catalog.js +105 -0
  172. package/dist/workflow/worker-model-catalog.js.map +1 -0
  173. package/dist/workflow/worker-process-supervisor.d.ts +110 -0
  174. package/dist/workflow/worker-process-supervisor.d.ts.map +1 -0
  175. package/dist/workflow/worker-process-supervisor.js +1188 -0
  176. package/dist/workflow/worker-process-supervisor.js.map +1 -0
  177. package/dist/workflow/worker-prompt-capsule.d.ts +32 -0
  178. package/dist/workflow/worker-prompt-capsule.d.ts.map +1 -0
  179. package/dist/workflow/worker-prompt-capsule.js +52 -0
  180. package/dist/workflow/worker-prompt-capsule.js.map +1 -0
  181. package/dist/workflow/worker-role-presets.d.ts +38 -0
  182. package/dist/workflow/worker-role-presets.d.ts.map +1 -0
  183. package/dist/workflow/worker-role-presets.js +201 -0
  184. package/dist/workflow/worker-role-presets.js.map +1 -0
  185. package/dist/workflow/workflow-doctor.d.ts +57 -0
  186. package/dist/workflow/workflow-doctor.d.ts.map +1 -0
  187. package/dist/workflow/workflow-doctor.js +289 -0
  188. package/dist/workflow/workflow-doctor.js.map +1 -0
  189. package/dist/workflow/workflow-lease.d.ts +125 -0
  190. package/dist/workflow/workflow-lease.d.ts.map +1 -0
  191. package/dist/workflow/workflow-lease.js +689 -0
  192. package/dist/workflow/workflow-lease.js.map +1 -0
  193. package/dist/workflow/workflow-types.d.ts +335 -0
  194. package/dist/workflow/workflow-types.d.ts.map +1 -0
  195. package/dist/workflow/workflow-types.js +11 -0
  196. package/dist/workflow/workflow-types.js.map +1 -0
  197. package/dist/workflow/workspace-paths.d.ts +4 -0
  198. package/dist/workflow/workspace-paths.d.ts.map +1 -0
  199. package/dist/workflow/workspace-paths.js +22 -0
  200. package/dist/workflow/workspace-paths.js.map +1 -0
  201. package/docs/conformance.md +116 -0
  202. package/docs/demo-transcript.md +34 -0
  203. package/docs/design.md +1481 -0
  204. package/docs/operator-guide.md +100 -0
  205. package/extension/guidance.ts +162 -0
  206. package/extension/guide.ts +155 -0
  207. package/extension/index.ts +8 -0
  208. package/extension/runtime.ts +1224 -0
  209. package/extension/tools.ts +221 -0
  210. package/extension/worker-runtime.ts +1150 -0
  211. package/extension/worker-tools.ts +276 -0
  212. package/integration/confirmed-bind-onboarding.ts +296 -0
  213. package/integration/fleet-view.ts +286 -0
  214. package/integration/fresh-session-dispatcher.ts +166 -0
  215. package/integration/launch-preview.ts +396 -0
  216. package/integration/transport-port.ts +29 -0
  217. package/integration/worker-completion-notifier.ts +98 -0
  218. package/package.json +55 -0
  219. package/roles/oracle.md +9 -0
  220. package/roles/researcher.md +8 -0
  221. package/roles/reviewer.md +9 -0
  222. package/roles/scout.md +8 -0
  223. package/roles/worker.md +10 -0
  224. package/scripts/demo-two-terminal.sh +59 -0
  225. package/skills/coordination-patterns/SKILL.md +41 -0
  226. package/skills/coordination-patterns/references/supervised-worker-fan-out.md +12 -0
  227. package/skills/coordination-patterns/references/today-mail-team.md +36 -0
  228. package/transport/amq-cli-adapter.ts +381 -0
  229. package/transport/amq-message.ts +218 -0
  230. package/transport/amq-operations.ts +482 -0
  231. package/transport/atomic-json.ts +115 -0
  232. package/transport/binding-resolver.ts +565 -0
  233. package/transport/bridge-journal.ts +588 -0
  234. package/transport/dlq-retry-audit.ts +97 -0
  235. package/transport/inbox-pump.ts +729 -0
  236. package/transport/index.ts +11 -0
  237. package/transport/presence-projector.ts +104 -0
  238. package/transport/priority-delivery-scheduler.ts +328 -0
  239. package/transport/receiver-lease.ts +945 -0
  240. package/transport/runtime-status.ts +112 -0
  241. package/workflow/.gitkeep +1 -0
  242. package/workflow/atomic-storage.ts +120 -0
  243. package/workflow/budget.ts +90 -0
  244. package/workflow/index.ts +16 -0
  245. package/workflow/lifecycle-projection.ts +37 -0
  246. package/workflow/model-fallback.ts +17 -0
  247. package/workflow/pi-session-fork.ts +96 -0
  248. package/workflow/run-artifacts.ts +36 -0
  249. package/workflow/run-cleanup.ts +315 -0
  250. package/workflow/run-gate.ts +122 -0
  251. package/workflow/run-store.ts +3890 -0
  252. package/workflow/spawn-confirmation.ts +305 -0
  253. package/workflow/system-process-inspector.ts +142 -0
  254. package/workflow/worker-child-extension.ts +193 -0
  255. package/workflow/worker-gate.ts +238 -0
  256. package/workflow/worker-launch.ts +485 -0
  257. package/workflow/worker-model-catalog.ts +165 -0
  258. package/workflow/worker-process-supervisor.ts +1504 -0
  259. package/workflow/worker-prompt-capsule.ts +81 -0
  260. package/workflow/worker-role-presets.ts +241 -0
  261. package/workflow/workflow-doctor.ts +400 -0
  262. package/workflow/workflow-lease.ts +905 -0
  263. package/workflow/workflow-types.ts +367 -0
  264. package/workflow/workspace-paths.ts +32 -0
@@ -0,0 +1,276 @@
1
+ import type {
2
+ AgentToolResult,
3
+ ExtensionAPI,
4
+ ExtensionContext,
5
+ } from "@earendil-works/pi-coding-agent";
6
+ import { Type, type Static } from "typebox";
7
+
8
+ const Difficulty = Type.Union([
9
+ Type.Literal("easy"),
10
+ Type.Literal("medium"),
11
+ Type.Literal("hard"),
12
+ ]);
13
+ const Channel = Type.Union([Type.Literal("json-result"), Type.Literal("rpc")]);
14
+ const SideEffectPolicy = Type.Union([
15
+ Type.Literal("none"),
16
+ Type.Literal("idempotent"),
17
+ Type.Literal("reconcilable"),
18
+ Type.Literal("external"),
19
+ ]);
20
+ const WorkerContext = Type.Union([Type.Literal("fresh"), Type.Literal("fork")]);
21
+ const ReportField = Type.Object({
22
+ type: Type.Union([
23
+ Type.Literal("string"), Type.Literal("number"), Type.Literal("boolean"),
24
+ Type.Literal("object"), Type.Literal("array"), Type.Literal("null"),
25
+ ]),
26
+ });
27
+ const ReportSchema = Type.Object({
28
+ schema: Type.Literal(1),
29
+ required: Type.Array(Type.String()),
30
+ properties: Type.Record(Type.String(), ReportField),
31
+ });
32
+ const AcceptanceCheck = Type.Object({
33
+ command: Type.String(),
34
+ args: Type.Array(Type.String()),
35
+ });
36
+ const IntegerBudgetLimit = Type.Union([
37
+ Type.Object({ kind: Type.Literal("limited"), value: Type.Integer({ minimum: 1 }) }),
38
+ Type.Object({ kind: Type.Literal("unlimited") }),
39
+ ]);
40
+ const NumberBudgetLimit = Type.Union([
41
+ Type.Object({ kind: Type.Literal("limited"), value: Type.Number({ exclusiveMinimum: 0 }) }),
42
+ Type.Object({ kind: Type.Literal("unlimited") }),
43
+ ]);
44
+ const WorkerBudget = Type.Object({
45
+ max_attempts: IntegerBudgetLimit,
46
+ max_wall_clock_ms: IntegerBudgetLimit,
47
+ max_tokens: IntegerBudgetLimit,
48
+ max_cost_usd: NumberBudgetLimit,
49
+ max_output_bytes: IntegerBudgetLimit,
50
+ });
51
+ const Model = Type.Object({
52
+ provider: Type.String(),
53
+ id: Type.String(),
54
+ thinking_level: Type.Optional(Type.String()),
55
+ });
56
+ const Workspace = Type.Object({
57
+ path: Type.String(),
58
+ mode: Type.Union([Type.Literal("read-only"), Type.Literal("mutating")]),
59
+ worktree_path: Type.Optional(Type.String()),
60
+ });
61
+ const ContentRef = Type.Object({
62
+ kind: Type.Union([Type.Literal("file"), Type.Literal("session"), Type.Literal("artifact")]),
63
+ value: Type.String(),
64
+ hash: Type.Optional(Type.String()),
65
+ });
66
+
67
+ const SpawnParameters = Type.Object({
68
+ request_key: Type.String(),
69
+ difficulty: Type.Optional(Difficulty),
70
+ role: Type.Optional(Type.String({ minLength: 1 })),
71
+ audits_worker_id: Type.Optional(Type.String()),
72
+ continue_worker_id: Type.Optional(Type.String()),
73
+ context: Type.Optional(WorkerContext),
74
+ goal: Type.String(),
75
+ acceptance: Type.Array(Type.String(), { minItems: 1 }),
76
+ constraints: Type.Array(Type.String()),
77
+ budget: WorkerBudget,
78
+ model: Type.Optional(Model),
79
+ fallback_models: Type.Optional(Type.Array(Model, { maxItems: 4 })),
80
+ capabilities: Type.Optional(Type.Array(Type.String())),
81
+ workspace: Workspace,
82
+ context_refs: Type.Array(ContentRef),
83
+ channel: Channel,
84
+ side_effect_policy: SideEffectPolicy,
85
+ report_schema: Type.Optional(ReportSchema),
86
+ requires_human_approval: Type.Optional(Type.Boolean()),
87
+ acceptance_check: Type.Optional(AcceptanceCheck),
88
+ });
89
+ const ListParameters = Type.Object({
90
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
91
+ });
92
+ const CollectParameters = Type.Object({
93
+ worker_ids: Type.Array(Type.String(), { minItems: 1, maxItems: 32 }),
94
+ structured_reports: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
95
+ });
96
+ const CancelParameters = Type.Object({ worker_id: Type.String(), reason: Type.String() });
97
+ const SteerParameters = Type.Object({
98
+ worker_id: Type.String(),
99
+ message: Type.String(),
100
+ correlation: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
101
+ });
102
+ const ReviseParameters = Type.Object({
103
+ worker_id: Type.String(),
104
+ correlation: Type.Optional(Type.String({ minLength: 1, maxLength: 256 })),
105
+ goal: Type.Optional(Type.String()),
106
+ acceptance: Type.Optional(Type.Array(Type.String(), { minItems: 1 })),
107
+ constraints: Type.Optional(Type.Array(Type.String())),
108
+ report_schema: Type.Optional(ReportSchema),
109
+ requires_human_approval: Type.Optional(Type.Boolean()),
110
+ acceptance_check: Type.Optional(AcceptanceCheck),
111
+ });
112
+ const CleanupRunsParameters = Type.Object({
113
+ older_than_ms: Type.Integer({ minimum: 1 }),
114
+ dry_run: Type.Optional(Type.Boolean()),
115
+ yes: Type.Optional(Type.Boolean()),
116
+ });
117
+ const WorkflowDoctorParameters = Type.Object({});
118
+
119
+ export type SpawnWorkerToolParameters = Static<typeof SpawnParameters>;
120
+ export type ListWorkersToolParameters = Static<typeof ListParameters>;
121
+ export type CollectWorkerToolParameters = Static<typeof CollectParameters>;
122
+ export type CancelWorkerToolParameters = Static<typeof CancelParameters>;
123
+ export type SteerWorkerToolParameters = Static<typeof SteerParameters>;
124
+ export type ReviseContractToolParameters = Static<typeof ReviseParameters>;
125
+ export type CleanupRunsToolParameters = Static<typeof CleanupRunsParameters>;
126
+
127
+ export interface WorkerToolProvider {
128
+ spawn(
129
+ params: SpawnWorkerToolParameters,
130
+ signal: AbortSignal | undefined,
131
+ ctx: ExtensionContext,
132
+ ): Promise<unknown>;
133
+ list(params: ListWorkersToolParameters, ctx: ExtensionContext): Promise<unknown>;
134
+ collect(
135
+ params: CollectWorkerToolParameters,
136
+ signal: AbortSignal | undefined,
137
+ ctx: ExtensionContext,
138
+ ): Promise<unknown>;
139
+ cancel(
140
+ params: CancelWorkerToolParameters,
141
+ signal: AbortSignal | undefined,
142
+ ctx: ExtensionContext,
143
+ ): Promise<unknown>;
144
+ steer(
145
+ params: SteerWorkerToolParameters,
146
+ signal: AbortSignal | undefined,
147
+ ctx: ExtensionContext,
148
+ ): Promise<unknown>;
149
+ reviseContract(
150
+ params: ReviseContractToolParameters,
151
+ signal: AbortSignal | undefined,
152
+ ctx: ExtensionContext,
153
+ ): Promise<unknown>;
154
+ cleanupRuns(
155
+ params: CleanupRunsToolParameters,
156
+ signal: AbortSignal | undefined,
157
+ ctx: ExtensionContext,
158
+ ): Promise<unknown>;
159
+ doctorWorkflow(signal: AbortSignal | undefined, ctx: ExtensionContext): Promise<unknown>;
160
+ }
161
+
162
+ export function registerWorkerTools(pi: ExtensionAPI, provider: WorkerToolProvider): void {
163
+ pi.registerTool({
164
+ name: "spawn_worker",
165
+ label: "Spawn Worker",
166
+ description: "Create one durable worker contract and start its supervised process, optionally continuing a proven-dead terminal worker session.",
167
+ promptSnippet: "Spawn a bounded supervised worker",
168
+ promptGuidelines: [
169
+ "Choose scout, worker, reviewer, oracle, or researcher; user role declarations may override these builtins.",
170
+ "Worker context defaults to fresh; context=fork copies the driver current leaf into an independent child session without changing the parent.",
171
+ "fallback_models is an ordered bounded chain used only for child launch-validation or provider auth, quota, and capacity failures.",
172
+ "Explicit difficulty and capabilities override role defaults. A reviewer auditing a named worker is floored to that worker's model capability.",
173
+ "When tier selection is unavailable or uncertain, omit model so the current model is used.",
174
+ "Keep acceptance criteria falsifiable and grant only the required workspace and capabilities.",
175
+ "Use continue_worker_id only for deliberate continuation after the prior worker is terminal and proven dead; the new request remains a fresh contract.",
176
+ ],
177
+ parameters: SpawnParameters,
178
+ async execute(_id, params, signal, _onUpdate, ctx) {
179
+ return invoke(() => provider.spawn(params, signal, ctx));
180
+ },
181
+ });
182
+
183
+ pi.registerTool({
184
+ name: "list_workers",
185
+ label: "List Workers",
186
+ description: "List the current run's bounded durable worker, Gate, revision, and stall projections.",
187
+ parameters: ListParameters,
188
+ async execute(_id, params, _signal, _onUpdate, ctx) {
189
+ return invoke(() => provider.list(params, ctx));
190
+ },
191
+ });
192
+
193
+ pi.registerTool({
194
+ name: "collect",
195
+ label: "Collect Workers",
196
+ description: "Observe worker candidates and Gate facts without treating collection itself as done.",
197
+ parameters: CollectParameters,
198
+ async execute(_id, params, signal, _onUpdate, ctx) {
199
+ return invoke(() => provider.collect(params, signal, ctx));
200
+ },
201
+ });
202
+
203
+ pi.registerTool({
204
+ name: "cancel",
205
+ label: "Cancel Worker",
206
+ description: "Record the sole cancellation verb before signaling the supervised process group.",
207
+ parameters: CancelParameters,
208
+ async execute(_id, params, signal, _onUpdate, ctx) {
209
+ return invoke(() => provider.cancel(params, signal, ctx));
210
+ },
211
+ });
212
+
213
+ pi.registerTool({
214
+ name: "steer",
215
+ label: "Steer Worker",
216
+ description: "Deliver already-authorized new input to an RPC worker; waking a typed wait requires its exact correlation.",
217
+ parameters: SteerParameters,
218
+ async execute(_id, params, signal, _onUpdate, ctx) {
219
+ return invoke(() => provider.steer(params, signal, ctx));
220
+ },
221
+ });
222
+
223
+ pi.registerTool({
224
+ name: "revise_contract",
225
+ label: "Revise Worker Contract",
226
+ description: "Record an immutable contract revision and deliver it at the next inference boundary; waking a typed wait requires its exact correlation.",
227
+ parameters: ReviseParameters,
228
+ async execute(_id, params, signal, _onUpdate, ctx) {
229
+ return invoke(() => provider.reviseContract(params, signal, ctx));
230
+ },
231
+ });
232
+
233
+ pi.registerTool({
234
+ name: "cleanup_runs",
235
+ label: "Cleanup Workflow Runs",
236
+ description: "Preview or explicitly delete old terminal workflow runs, worker sessions, and artifacts.",
237
+ promptGuidelines: [
238
+ "Dry-run is the default. Set dry_run=false only when deletion is intended.",
239
+ "TUI deletion always requires user confirmation; headless deletion also requires yes=true.",
240
+ ],
241
+ parameters: CleanupRunsParameters,
242
+ async execute(_id, params, signal, _onUpdate, ctx) {
243
+ return invoke(() => provider.cleanupRuns(params, signal, ctx));
244
+ },
245
+ });
246
+
247
+ pi.registerTool({
248
+ name: "workflow_doctor",
249
+ label: "Workflow Doctor",
250
+ description: "Read-only workflow diagnostics for run ledgers, processes, leases, sessions, and disk usage.",
251
+ parameters: WorkflowDoctorParameters,
252
+ async execute(_id, _params, signal, _onUpdate, ctx) {
253
+ return invoke(() => provider.doctorWorkflow(signal, ctx));
254
+ },
255
+ });
256
+ }
257
+
258
+ async function invoke(operation: () => Promise<unknown>): Promise<AgentToolResult<unknown>> {
259
+ try {
260
+ const details = await operation();
261
+ return rendered(details);
262
+ } catch (error) {
263
+ return rendered({ error: { category: "worker-runtime", message: toError(error).message } });
264
+ }
265
+ }
266
+
267
+ function rendered(details: unknown): AgentToolResult<unknown> {
268
+ return {
269
+ content: [{ type: "text", text: JSON.stringify(details, null, 2) }],
270
+ details,
271
+ };
272
+ }
273
+
274
+ function toError(error: unknown): Error {
275
+ return error instanceof Error ? error : new Error(String(error));
276
+ }
@@ -0,0 +1,296 @@
1
+ import { isAbsolute, normalize } from "node:path";
2
+
3
+ import type { AmqCommandResult, AmqRunOptions } from "../transport/amq-cli-adapter.js";
4
+ import {
5
+ BindingResolver,
6
+ type BindingProjectionV1,
7
+ type BindingResolution,
8
+ } from "../transport/binding-resolver.js";
9
+
10
+ interface JsonRunner {
11
+ runJson<T = unknown>(
12
+ argv: readonly string[],
13
+ options?: AmqRunOptions<T>,
14
+ ): Promise<AmqCommandResult<T>>;
15
+ }
16
+
17
+ export interface ConfirmedBindRequest {
18
+ readonly projectDirectory: string;
19
+ readonly root: string;
20
+ readonly handle: string;
21
+ readonly agents: readonly string[];
22
+ readonly environment?: Readonly<NodeJS.ProcessEnv>;
23
+ }
24
+
25
+ export interface ConfirmedBindPreview {
26
+ readonly kind: "existing" | "bootstrap";
27
+ readonly projectDirectory: string;
28
+ readonly root: string;
29
+ readonly session: string;
30
+ readonly project: string;
31
+ readonly handle: string;
32
+ readonly agents: readonly string[];
33
+ readonly command: readonly string[];
34
+ readonly commandText: string;
35
+ readonly effects: readonly string[];
36
+ }
37
+
38
+ export type ConfirmedBindResult =
39
+ | { readonly kind: "cancelled"; readonly diagnostic: string }
40
+ | { readonly kind: "refused"; readonly diagnostic: string }
41
+ | { readonly kind: "bound"; readonly binding: BindingProjectionV1 };
42
+
43
+ interface WhoAgent {
44
+ readonly handle: string;
45
+ }
46
+
47
+ interface WhoSession {
48
+ readonly name: string;
49
+ readonly agents: readonly WhoAgent[];
50
+ }
51
+
52
+ interface WhoProjection {
53
+ readonly initialized: boolean;
54
+ readonly sessions: readonly WhoSession[];
55
+ }
56
+
57
+ interface EnvPreview {
58
+ readonly root: string;
59
+ readonly session: string;
60
+ readonly project: string;
61
+ }
62
+
63
+ /** Owns the confirmation-gated first-run boundary; it never guesses authority. */
64
+ export class ConfirmedBindOnboarding {
65
+ readonly #runner: JsonRunner;
66
+ readonly #resolver: BindingResolver;
67
+
68
+ constructor(runner: JsonRunner, resolver = new BindingResolver(runner)) {
69
+ this.#runner = runner;
70
+ this.#resolver = resolver;
71
+ }
72
+
73
+ async preview(request: ConfirmedBindRequest): Promise<ConfirmedBindPreview> {
74
+ const projectDirectory = absolute(request.projectDirectory, "project directory");
75
+ const root = absolute(request.root, "AMQ root");
76
+ const handle = agentHandle(request.handle, "chosen handle");
77
+ const requestedAgents = roster(request.agents, handle);
78
+ const environment = request.environment ?? process.env;
79
+ const who = await this.#runner.runJson(
80
+ ["who", "--root", root],
81
+ { env: commandEnvironment(environment), parse: parseWho },
82
+ );
83
+
84
+ if (who.ok && who.json?.initialized) {
85
+ const existingAgents = unique(who.json.sessions.flatMap((session) =>
86
+ session.agents.map((agent) => agent.handle),
87
+ ));
88
+ if (!existingAgents.includes(handle)) {
89
+ throw new Error(
90
+ `Chosen handle ${handle} is not provisioned in the existing AMQ roster: ${existingAgents.join(",")}`,
91
+ );
92
+ }
93
+ const live = await this.#runner.runJson(
94
+ ["env", "--root", root, "--me", handle],
95
+ { env: commandEnvironment(environment), parse: parseEnvPreview },
96
+ );
97
+ if (!live.ok || !live.json) {
98
+ throw new Error(literalFailure(live, "Existing AMQ root could not be inspected"));
99
+ }
100
+ return {
101
+ kind: "existing",
102
+ projectDirectory,
103
+ root: live.json.root,
104
+ session: live.json.session,
105
+ project: live.json.project,
106
+ handle,
107
+ agents: existingAgents,
108
+ command: [],
109
+ commandText: "No initialization command; verify and bind the selected existing mailbox",
110
+ effects: ["Persist pi-amq-binding/v1 only after live mailbox verification"],
111
+ };
112
+ }
113
+ if (!(who.ok && who.json && !who.json.initialized) && !bootstrapEligible(who)) {
114
+ throw new Error(literalFailure(who, "AMQ roster discovery failed"));
115
+ }
116
+
117
+ const command = ["amq", "coop", "init", "--root", root, "--agents", requestedAgents.join(",")];
118
+ return {
119
+ kind: "bootstrap",
120
+ projectDirectory,
121
+ root,
122
+ session: "",
123
+ project: projectDirectory,
124
+ handle,
125
+ agents: requestedAgents,
126
+ command,
127
+ commandText: command.map(shellWord).join(" "),
128
+ effects: [
129
+ `Create the physical AMQ root at ${root}`,
130
+ `Provision the complete roster: ${requestedAgents.join(",")}`,
131
+ `Write ${projectDirectory}/.amqrc`,
132
+ `Update ${projectDirectory}/.gitignore when needed`,
133
+ ],
134
+ };
135
+ }
136
+
137
+ async confirm(
138
+ preview: ConfirmedBindPreview,
139
+ confirmed: boolean,
140
+ environment: Readonly<NodeJS.ProcessEnv> = process.env,
141
+ ): Promise<ConfirmedBindResult> {
142
+ if (!confirmed) return { kind: "cancelled", diagnostic: "AMQ bind cancelled; no state changed" };
143
+
144
+ if (preview.kind === "bootstrap") {
145
+ const initialized = await this.#runner.runJson(
146
+ preview.command.slice(1),
147
+ { cwd: preview.projectDirectory, env: commandEnvironment(environment) },
148
+ );
149
+ if (!initialized.ok) {
150
+ return {
151
+ kind: "refused",
152
+ diagnostic: literalFailure(initialized, "AMQ roster bootstrap failed; pi-amq remains dormant"),
153
+ };
154
+ }
155
+ }
156
+
157
+ const resolution = await this.#resolver.resolve({
158
+ flags: { handle: preview.handle, root: preview.root },
159
+ environment: clearInheritedBinding(environment),
160
+ });
161
+ if (resolution.kind !== "bound") return refusedResolution(resolution);
162
+ if (resolution.binding.root !== preview.root || resolution.binding.handle !== preview.handle) {
163
+ return {
164
+ kind: "refused",
165
+ diagnostic: "Post-provision binding differs from the confirmed root or handle; pi-amq remains dormant",
166
+ };
167
+ }
168
+
169
+ const who = await this.#runner.runJson(
170
+ ["who", "--root", preview.root],
171
+ { env: commandEnvironment(environment), parse: parseWho },
172
+ );
173
+ if (!who.ok || !who.json?.initialized) {
174
+ return {
175
+ kind: "refused",
176
+ diagnostic: literalFailure(who, "Post-provision roster verification failed; pi-amq remains dormant"),
177
+ };
178
+ }
179
+ const verifiedAgents = unique(who.json.sessions.flatMap((session) =>
180
+ session.agents.map((agent) => agent.handle),
181
+ ));
182
+ if (!sameRoster(verifiedAgents, preview.agents)) {
183
+ return {
184
+ kind: "refused",
185
+ diagnostic: `Post-provision roster mismatch: confirmed ${preview.agents.join(",")}; live ${verifiedAgents.join(",")}`,
186
+ };
187
+ }
188
+ return { kind: "bound", binding: resolution.binding };
189
+ }
190
+ }
191
+
192
+ export function parseRosterInput(value: string, handle: string): readonly string[] {
193
+ return roster(value.split(","), agentHandle(handle, "chosen handle"));
194
+ }
195
+
196
+ function parseWho(value: unknown): WhoProjection {
197
+ if (value === null) return { initialized: false, sessions: [] };
198
+ if (!Array.isArray(value)) throw new Error("amq who --json must return an array");
199
+ const sessions = value.map((rawSession, sessionIndex) => {
200
+ const session = record(rawSession, `amq who session ${sessionIndex}`);
201
+ if (!Array.isArray(session.agents)) throw new Error("amq who session agents must be an array");
202
+ return {
203
+ name: stringField(session, "name"),
204
+ agents: session.agents.map((rawAgent, agentIndex) => {
205
+ const agent = record(rawAgent, `amq who agent ${agentIndex}`);
206
+ return { handle: agentHandle(stringField(agent, "handle"), "AMQ roster handle") };
207
+ }),
208
+ };
209
+ });
210
+ return { initialized: true, sessions };
211
+ }
212
+
213
+ function parseEnvPreview(value: unknown): EnvPreview {
214
+ const object = record(value, "amq env --json");
215
+ return {
216
+ root: absolute(stringField(object, "root"), "live AMQ root"),
217
+ session: stringField(object, "session_name"),
218
+ project: stringField(object, "project"),
219
+ };
220
+ }
221
+
222
+ function bootstrapEligible(result: AmqCommandResult<unknown>): boolean {
223
+ return result.category === "not-found"
224
+ || (result.category === "error" && /(?:not initialized|missing|not found|no such file)/i.test(result.stderr));
225
+ }
226
+
227
+ function refusedResolution(resolution: Exclude<BindingResolution, { readonly kind: "bound" }>): ConfirmedBindResult {
228
+ return {
229
+ kind: "refused",
230
+ diagnostic: `${resolution.diagnostic}; pi-amq remains dormant`,
231
+ };
232
+ }
233
+
234
+ function roster(values: readonly string[], handle: string): readonly string[] {
235
+ const normalized = values
236
+ .flatMap((value) => value.split(","))
237
+ .map((value) => value.trim())
238
+ .filter((value) => value.length > 0)
239
+ .map((value) => agentHandle(value, "roster handle"));
240
+ return unique([...normalized, handle, "user"]);
241
+ }
242
+
243
+ function unique(values: readonly string[]): readonly string[] {
244
+ return [...new Set(values)].sort((left, right) => left.localeCompare(right));
245
+ }
246
+
247
+ function sameRoster(left: readonly string[], right: readonly string[]): boolean {
248
+ const normalizedLeft = unique(left);
249
+ const normalizedRight = unique(right);
250
+ return normalizedLeft.length === normalizedRight.length
251
+ && normalizedLeft.every((value, index) => value === normalizedRight[index]);
252
+ }
253
+
254
+ function absolute(value: string, name: string): string {
255
+ if (!isAbsolute(value)) throw new TypeError(`${name} must be absolute`);
256
+ return normalize(value);
257
+ }
258
+
259
+ function agentHandle(value: string, name: string): string {
260
+ const normalized = value.trim();
261
+ if (!/^[a-z0-9][a-z0-9._-]*$/.test(normalized)) {
262
+ throw new TypeError(`${name} must use lowercase AMQ handle syntax`);
263
+ }
264
+ return normalized;
265
+ }
266
+
267
+ function clearInheritedBinding(environment: Readonly<NodeJS.ProcessEnv>): NodeJS.ProcessEnv {
268
+ const cleared: NodeJS.ProcessEnv = { ...environment };
269
+ for (const key of ["AM_ROOT", "AM_ROOT_ID", "AM_BASE_ROOT", "AM_BASE_ROOT_ID", "AM_SESSION", "AM_ME"]) {
270
+ delete cleared[key];
271
+ }
272
+ return cleared;
273
+ }
274
+
275
+ function commandEnvironment(environment: Readonly<NodeJS.ProcessEnv>): NodeJS.ProcessEnv {
276
+ return { ...environment };
277
+ }
278
+
279
+ function shellWord(value: string): string {
280
+ return /^[A-Za-z0-9_./,:=-]+$/.test(value) ? value : `'${value.replaceAll("'", `'\"'\"'`)}'`;
281
+ }
282
+
283
+ function literalFailure(result: AmqCommandResult<unknown>, fallback: string): string {
284
+ return result.stderr.trim() || result.jsonError?.message || result.processError?.message || fallback;
285
+ }
286
+
287
+ function record(value: unknown, name: string): Record<string, unknown> {
288
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${name} must be an object`);
289
+ return value as Record<string, unknown>;
290
+ }
291
+
292
+ function stringField(object: Record<string, unknown>, key: string): string {
293
+ const value = object[key];
294
+ if (typeof value !== "string") throw new Error(`${key} must be a string`);
295
+ return value;
296
+ }