vinta-ai-maestro 0.7.0-alpha21 → 0.7.0-alpha22

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 (127) hide show
  1. package/README.md +42 -22
  2. package/dist/cli/halt.js +185 -0
  3. package/dist/cli/halt.js.map +1 -0
  4. package/dist/cli/index.js +32 -7
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/cli/logs.js +133 -0
  7. package/dist/cli/logs.js.map +1 -0
  8. package/dist/cli/policy.js +191 -0
  9. package/dist/cli/policy.js.map +1 -0
  10. package/dist/cli/purge.js +15 -3
  11. package/dist/cli/purge.js.map +1 -1
  12. package/dist/cli/run.js +231 -105
  13. package/dist/cli/run.js.map +1 -1
  14. package/dist/cli/serve.js +104 -262
  15. package/dist/cli/serve.js.map +1 -1
  16. package/dist/cli/status.js +189 -0
  17. package/dist/cli/status.js.map +1 -0
  18. package/dist/daemon/api.js +46 -0
  19. package/dist/daemon/api.js.map +1 -1
  20. package/dist/daemon/control.js.map +1 -1
  21. package/dist/daemon/proxy.js +130 -0
  22. package/dist/daemon/proxy.js.map +1 -0
  23. package/dist/daemon/schemas.js +1 -1
  24. package/dist/daemon/schemas.js.map +1 -1
  25. package/dist/daemon/server.js +37 -1
  26. package/dist/daemon/server.js.map +1 -1
  27. package/dist/doctor/index.js +36 -6
  28. package/dist/doctor/index.js.map +1 -1
  29. package/dist/gates/runner.js +17 -0
  30. package/dist/gates/runner.js.map +1 -1
  31. package/dist/job/job.js +178 -0
  32. package/dist/job/job.js.map +1 -0
  33. package/dist/journal/journal.js +4 -2
  34. package/dist/journal/journal.js.map +1 -1
  35. package/dist/postmortem/postmortem.js +5 -1
  36. package/dist/postmortem/postmortem.js.map +1 -1
  37. package/dist/run/start.js +8 -0
  38. package/dist/run/start.js.map +1 -1
  39. package/dist/scheduler/scheduler.js +128 -4
  40. package/dist/scheduler/scheduler.js.map +1 -1
  41. package/dist/system-one/adapter.js +10 -0
  42. package/dist/system-one/adapter.js.map +1 -1
  43. package/dist/system-one/config.js +7 -1
  44. package/dist/system-one/config.js.map +1 -1
  45. package/dist/system-one/http.js +8 -0
  46. package/dist/system-one/http.js.map +1 -1
  47. package/dist/system-one/index.js +1 -1
  48. package/dist/system-one/index.js.map +1 -1
  49. package/dist/ui/assets/{angular-html-CRxtcu8T.js → angular-html--rod_uFH.js} +1 -1
  50. package/dist/ui/assets/{angular-ts-YNySLBa9.js → angular-ts-CH4AhDr9.js} +1 -1
  51. package/dist/ui/assets/{apl-C5XxFwl2.js → apl-CaWMi3_O.js} +1 -1
  52. package/dist/ui/assets/{astro-Cl27tLte.js → astro-65q_izSI.js} +1 -1
  53. package/dist/ui/assets/{blade-4DvMBPB7.js → blade-BwQnvQGM.js} +1 -1
  54. package/dist/ui/assets/{c-MKR_TARB.js → c-Ym8Nhq5w.js} +1 -1
  55. package/dist/ui/assets/{chapel-D1cEsFmo.js → chapel-DEt1k_nb.js} +1 -1
  56. package/dist/ui/assets/{cobol-FqyqpV-Z.js → cobol-C7uzupvl.js} +1 -1
  57. package/dist/ui/assets/{coffee-DaQH_cnd.js → coffee-DOZQfojQ.js} +1 -1
  58. package/dist/ui/assets/{cpp-CDg7r5Lm.js → cpp-BtyhwYIb.js} +1 -1
  59. package/dist/ui/assets/{crystal-DdeNtPaD.js → crystal-CrqkYiNi.js} +1 -1
  60. package/dist/ui/assets/{css-7_Zq6chi.js → css-C8oBROxa.js} +1 -1
  61. package/dist/ui/assets/{edge-BY-k2s7F.js → edge-sFmhfyIP.js} +1 -1
  62. package/dist/ui/assets/{elixir-3ED3OKOH.js → elixir-BM9jh3e3.js} +1 -1
  63. package/dist/ui/assets/{elm-DVdSluoB.js → elm-BETVX7DL.js} +1 -1
  64. package/dist/ui/assets/{erb-Bq5Dc-e6.js → erb-BDyKRm-4.js} +1 -1
  65. package/dist/ui/assets/{git-rebase-BpW9Ws96.js → git-rebase-DAcGcSet.js} +1 -1
  66. package/dist/ui/assets/{glimmer-js-Bp-SmLHE.js → glimmer-js-BatXXwpe.js} +1 -1
  67. package/dist/ui/assets/{glimmer-ts-M5xKg4QH.js → glimmer-ts-Bo1DlScE.js} +1 -1
  68. package/dist/ui/assets/{glsl-DmXsA1Di.js → glsl-BqkyX5Iz.js} +1 -1
  69. package/dist/ui/assets/{graphql-LX-8XoeY.js → graphql-BkAH8Crt.js} +1 -1
  70. package/dist/ui/assets/{hack-DrC6kjZ-.js → hack-BAy_rDSy.js} +1 -1
  71. package/dist/ui/assets/{haml-Cs9eCa_K.js → haml-NAdeHrqL.js} +1 -1
  72. package/dist/ui/assets/{handlebars-knh5cKv7.js → handlebars-DP0nKrnR.js} +1 -1
  73. package/dist/ui/assets/{html-Bj-aBVNp.js → html-C-Hz7c0Z.js} +1 -1
  74. package/dist/ui/assets/{html-derivative-CiiCAeKi.js → html-derivative-CJndNfHU.js} +1 -1
  75. package/dist/ui/assets/{http-DF47-qRx.js → http-CoSnU61Z.js} +1 -1
  76. package/dist/ui/assets/{hurl-Cai6Oj1b.js → hurl-BKpQxBZx.js} +1 -1
  77. package/dist/ui/assets/{index-LzKXLqvN.js → index-DVILQrp_.js} +85 -85
  78. package/dist/ui/assets/{java-CgXVugkp.js → java-ClhzGlNg.js} +1 -1
  79. package/dist/ui/assets/{javascript-Br7K4LwF.js → javascript-DQKc5wHM.js} +1 -1
  80. package/dist/ui/assets/{jinja-CYOzT9zf.js → jinja-CM6c-Rc_.js} +1 -1
  81. package/dist/ui/assets/{jison-DYMbT0wb.js → jison-DEvrAQvy.js} +1 -1
  82. package/dist/ui/assets/{json-Vju3cCl7.js → json-COx1UCVY.js} +1 -1
  83. package/dist/ui/assets/{json-text-editor-9yQRL-wq.js → json-text-editor-CgieRwCY.js} +1 -1
  84. package/dist/ui/assets/{jsx-BJ2SfNc2.js → jsx-Dq4g_gGM.js} +1 -1
  85. package/dist/ui/assets/{julia-Bl5QJcj1.js → julia-_M8UpEBR.js} +1 -1
  86. package/dist/ui/assets/{just-BSgRkB_b.js → just-SEENIkfF.js} +1 -1
  87. package/dist/ui/assets/{latex-9nJBPEWl.js → latex-z11sRY1e.js} +1 -1
  88. package/dist/ui/assets/{liquid-BB_V_UwS.js → liquid-7qBozupt.js} +1 -1
  89. package/dist/ui/assets/{lua-DklLSFpq.js → lua-efJ4NR0B.js} +1 -1
  90. package/dist/ui/assets/{marko-9qUza2Cy.js → marko-CfHkKZQs.js} +1 -1
  91. package/dist/ui/assets/{mdc-CyuS771N.js → mdc-BFzMhqRK.js} +1 -1
  92. package/dist/ui/assets/{nginx-4PCMUjXv.js → nginx-Ba1yATRU.js} +1 -1
  93. package/dist/ui/assets/{nim-BGtJj58_.js → nim-D9tR0l59.js} +1 -1
  94. package/dist/ui/assets/{org-BZTxzmqq.js → org-BOYaWmQ6.js} +1 -1
  95. package/dist/ui/assets/{perl-DnYlfNV7.js → perl-Bhdbf0pR.js} +1 -1
  96. package/dist/ui/assets/{php-BjK_5VzK.js → php-DGidydcD.js} +1 -1
  97. package/dist/ui/assets/{pug-BxtgFz0R.js → pug-A8eoEM0H.js} +1 -1
  98. package/dist/ui/assets/{qml-D2-GrqTo.js → qml-C2Blb4M7.js} +1 -1
  99. package/dist/ui/assets/{r-DYmTOMm-.js → r-kHJ9J7di.js} +1 -1
  100. package/dist/ui/assets/{razor-D-GkAGbA.js → razor-DRWMcd86.js} +1 -1
  101. package/dist/ui/assets/{regexp-BqEDcLm3.js → regexp-DSa9yWx_.js} +1 -1
  102. package/dist/ui/assets/{rst-CZdKMzIl.js → rst-DFFsPn-_.js} +1 -1
  103. package/dist/ui/assets/{ruby-DVnXlHSi.js → ruby-QmGD3U24.js} +1 -1
  104. package/dist/ui/assets/{sas-DaOpt0VL.js → sas-Cu9PDsjp.js} +1 -1
  105. package/dist/ui/assets/{scss-KndlTnGC.js → scss-BT4OjSCC.js} +1 -1
  106. package/dist/ui/assets/{shellscript-vpjNsx9D.js → shellscript-CLSCFAsj.js} +1 -1
  107. package/dist/ui/assets/{shellsession-BiAZj2V1.js → shellsession-CwZ_5n4W.js} +1 -1
  108. package/dist/ui/assets/{soy-DHlNGgnB.js → soy-ofP5hQkZ.js} +1 -1
  109. package/dist/ui/assets/{sql-DGabU7xY.js → sql-j0ru169V.js} +1 -1
  110. package/dist/ui/assets/{stata-kbiiK5op.js → stata-v3x2Y_5m.js} +1 -1
  111. package/dist/ui/assets/{surrealql-CImshJdi.js → surrealql-Bmbpksc8.js} +1 -1
  112. package/dist/ui/assets/{svelte-DoeuXk_R.js → svelte-BuXIEx69.js} +1 -1
  113. package/dist/ui/assets/{templ-5tZza6px.js → templ-F72RdV4h.js} +1 -1
  114. package/dist/ui/assets/{tex-DO9W0uTa.js → tex-BdlHb9hn.js} +1 -1
  115. package/dist/ui/assets/{ts-tags-BRigjIub.js → ts-tags-DPrkt62A.js} +1 -1
  116. package/dist/ui/assets/{tsx-Ddc_hGks.js → tsx-DZLkwBdm.js} +1 -1
  117. package/dist/ui/assets/{twig-BJPQXv7X.js → twig-DTmppqKc.js} +1 -1
  118. package/dist/ui/assets/{typescript-BO9qTW9s.js → typescript-DJcPZKIN.js} +1 -1
  119. package/dist/ui/assets/{typst-BKKO1Q3c.js → typst-BiL5yhyo.js} +1 -1
  120. package/dist/ui/assets/{vue-Bem1c9GL.js → vue-Bzrf-J5S.js} +1 -1
  121. package/dist/ui/assets/{vue-html-Ss5ojtBB.js → vue-html-DbFRWAKl.js} +1 -1
  122. package/dist/ui/assets/{vue-vine-BNczxZ-v.js → vue-vine-C4Oz1MOg.js} +1 -1
  123. package/dist/ui/assets/{xml-C64yarKu.js → xml-mRmLJidt.js} +1 -1
  124. package/dist/ui/assets/{xsl-BTsmbzwy.js → xsl-DjiVjdxI.js} +1 -1
  125. package/dist/ui/assets/{yaml-D1GFx-Qg.js → yaml-Yth9CZzj.js} +1 -1
  126. package/dist/ui/index.html +1 -1
  127. package/package.json +1 -1
package/README.md CHANGED
@@ -60,7 +60,7 @@ pnpm install
60
60
  That is the development setup. To *use* it, there is no clone at all — the package is published:
61
61
 
62
62
  ```bash
63
- npx vinta-ai-maestro@alpha serve
63
+ npx vinta-ai-maestro@alpha ui
64
64
  ```
65
65
 
66
66
  `@alpha` while the only published versions are pre-releases. Plain `npx vinta-ai-maestro` resolves the `latest` dist-tag, which is correct once a stable exists.
@@ -80,15 +80,20 @@ The published binary is a different file — `dist/cli/bin.js`, plain JavaScript
80
80
 
81
81
  Every command runs against a project checkout — your project, not this one. `--repo <dir>` names it; with no flag it is the current directory.
82
82
 
83
- ## The six commands
83
+ ## The commands
84
84
 
85
85
  | Command | What it does |
86
86
  |---|---|
87
87
  | `doctor <workflow.json> [--repo <dir>] [--resume <run-id>]` | Preflights every check a run depends on and exits non-zero if a run cannot start. `--resume` asks the question for `run --resume <run-id>`: that run's own lanes are holding its phase branches on purpose, and are not leftovers to clear. |
88
88
  | `simulate <workflow.json>` | Projects the schedule without running it — wall clock, critical path, pool contention. Spawns no agent. |
89
- | `serve [--repo <dir>] [--host <host>] [--port <n>]` | Starts the daemon and prints the URL to open. Its editor edits `<repo>/ai-plans/*.workflow.json`. |
90
- | `run <workflow.json> [--repo <dir>] [--host <host>] [--port <n>]` | Starts the daemon *and* executes the workflow. Exits when the run ends. |
91
- | `purge [run-id] [--repo <dir>] [--yes] [--dry-run]` | Deletes run state under `.vinta-ai-maestro/runs/`. |
89
+ | `run <workflow.json> [--repo <dir>]` | Starts the workflow as a **background job** and returns once it is under way. The run does not need the terminal: close it and the run carries on. `--foreground` hosts it in this process instead and exits when it ends, for CI. |
90
+ | `run --resume <run-id>` | Picks an interrupted or paused run back up, as a background job. |
91
+ | `status [run-id] [--json]` | Every run in the project and what it is doing — or one run, phase by phase. A run whose job died without ending it shows as `interrupted`. |
92
+ | `logs <run-id> [-f] [-n <lines>]` | The run's job log: what `run` used to print to the terminal, plus its daemon's records. `-f` follows it until the job exits. |
93
+ | `pause <run-id> [--wait]` | Nothing new starts; each running phase finishes the step it is in, then the job exits. The run is `paused`, its lanes intact, and `run --resume` continues it. |
94
+ | `stop <run-id> [--wait]` | Kills every live agent turn and gate now. The run is `cancelled` and cannot be resumed. |
95
+ | `ui [--repo <dir>] [--host <host>] [--port <n>]` | Serves the browser UI for every run in the project and prints the URL to open. Runs are not hosted here — close it whenever you like. A run started from its editor is launched as a background job. `serve` is the same command. |
96
+ | `purge [run-id] [--repo <dir>] [--yes] [--dry-run]` | Deletes run state under `.vinta-ai-maestro/runs/`. A run whose job is still running is kept. |
92
97
  | `with <resource> -- <cmd>` | Inside an agent turn, waits for a semaphore resource, runs the command, and releases it. The live run supplies its daemon connection through the lane environment. |
93
98
  | `gate <gate-id>` | Inside an agent turn, asks the daemon to run one of the plan's declared gates against this turn's lane. The daemon holds the gate's resources and caches the result; the CLI exits with the gate's exit code. |
94
99
 
@@ -96,6 +101,14 @@ There is also `judge-hook`, which is internal: it is the hook `--permission judg
96
101
 
97
102
  `--port` defaults to `0`, an OS-assigned port printed with the URL. `--host` defaults to `127.0.0.1` — see [The URL is the credential](#the-url-is-the-credential). `vinta-ai-maestro <command> --help` prints the command's own options.
98
103
 
104
+ ### A run is a background job
105
+
106
+ `run` hands the run to a detached copy of itself — the run's *job* — and returns as soon as the job says the run has started. The job is the run's only host: it holds the scheduler, the agents, and a loopback API the agents call back into (`with`, `gate`). It writes `.vinta-ai-maestro/runs/<run-id>/job.json` — its pid, that API's address and its token — and every other command finds the run through that file. It serves no UI.
107
+
108
+ `ui` is the browser's way in. It reads every run's history from the journal, and for a run whose job is alive it forwards that run's requests and socket to the job, so the live graph, steering, answering a question and taking over a terminal all work as if the two were one process. Close `ui` and the runs carry on; open it tomorrow and it shows them where they got to.
109
+
110
+ A run ends one of four ways, and the journal says which: it settles (`done` or `failed`), it is paused (`paused`, resumable), it is stopped (`cancelled`, final), or its job is killed by a signal (`failed`, resumable — exactly what closing the terminal used to do). `status` shows a run whose job vanished without writing any of those — a reboot, a `kill -9` — as `interrupted`, and `run --resume` picks it up.
111
+
99
112
  The daemon-facing commands use three exit codes, so a script can tell the cases
100
113
  apart: `0` success, `1` the command ran and the answer was no, `2` the command
101
114
  line was wrong. `with` and `gate` pass through the exit code of the thing they
@@ -304,10 +317,10 @@ A repository with two phases that depend on nothing, so both belong to wave 1 an
304
317
 
305
318
  Scope it to what your phases actually need; this is the permissive end.
306
319
 
307
- **3. Review and approve it in the editor.** `serve` opens the daemon with no run attached. Its Editor lists every `ai-plans/*.workflow.json` in the project — the file you just wrote, or the one `plan-feature` wrote — and saving writes back to that same file:
320
+ **3. Review and approve it in the editor.** `ui` serves the browser UI for the project. Its Editor lists every `ai-plans/*.workflow.json` in the project — the file you just wrote, or the one `plan-feature` wrote — and saving writes back to that same file:
308
321
 
309
322
  ```console
310
- $ vinta-ai-maestro serve
323
+ $ vinta-ai-maestro ui
311
324
  vinta-ai-maestro: daemon listening on http://127.0.0.1:52218
312
325
  Open this URL. It carries the access token, so treat it as a secret:
313
326
  http://127.0.0.1:52218/?token=<the-token-printed-here>
@@ -370,13 +383,16 @@ Both phases start at `0s` — that is the parallelism the plan claimed, confirme
370
383
 
371
384
  ```console
372
385
  $ vinta-ai-maestro run ai-plans/widget-tags.workflow.json
373
- vinta-ai-maestro: daemon listening on http://127.0.0.1:52765
374
- Open this URL. It carries the access token, so treat it as a secret:
375
- http://127.0.0.1:52765/?token=<the-token-printed-here>
376
- vinta-ai-maestro: run widget-tags-mtvodosx started (2 nodes).
386
+ vinta-ai-maestro: starting run widget-tags-mtvodosx in the background…
387
+ vinta-ai-maestro: run widget-tags-mtvodosx started (job pid 48213).
388
+ status vinta-ai-maestro status widget-tags-mtvodosx
389
+ logs vinta-ai-maestro logs widget-tags-mtvodosx --follow
390
+ ui vinta-ai-maestro ui
391
+ pause vinta-ai-maestro pause widget-tags-mtvodosx
392
+ stop vinta-ai-maestro stop widget-tags-mtvodosx
377
393
  ```
378
394
 
379
- The daemon comes up before the first node dispatches, so you can open the URL and watch. Both phases are assigned a lane immediately and implement concurrently, each in its own worktree under `.vinta-ai-maestro/lanes/`, on its own branch cut from `base_branch`:
395
+ The command returns; the run does not need this terminal. The preflight is the job's — if it refuses, `run` prints the report and says the run did not start. Watch it from the terminal with `status` and `logs -f`, or keep `ui` open in another one and follow it in the browser. Both phases are assigned a lane immediately and implement concurrently, each in its own worktree under `.vinta-ai-maestro/lanes/`, on its own branch cut from `base_branch`:
380
396
 
381
397
  ```console
382
398
  $ git branch
@@ -388,7 +404,7 @@ $ git branch
388
404
  wt/widget-tags-mtvodosx-lane-2
389
405
  ```
390
406
 
391
- `plan/…/phase-<id>` is the phase's own branch; `wt/…` are the branches the lane and integration worktrees are checked out on. When the run ends, read [What this walkthrough has and has not been run against](#what-this-walkthrough-has-and-has-not-been-run-against) before you read the last two lines it prints.
407
+ `plan/…/phase-<id>` is the phase's own branch; `wt/…` are the branches the lane and integration worktrees are checked out on. Need the machine back for an hour? `vinta-ai-maestro pause <run-id>` lets the phases finish the step they are in and ends the job; `vinta-ai-maestro run --resume <run-id>` carries on later. When the run ends, read [What this walkthrough has and has not been run against](#what-this-walkthrough-has-and-has-not-been-run-against) before you read the last two lines it prints.
392
408
 
393
409
  **7. Read what happened, then clean up.** A finished run leaves its worktrees, branches and databases in place on purpose — they are the evidence. The post-mortem is written at the end and is what `plan-feature` reads before drawing the next feature's graph:
394
410
 
@@ -420,6 +436,8 @@ Everything a run writes lives inside the project, never in a global cache direct
420
436
  logs/daemon.<n>.ndjson # rotations, oldest pruned past five
421
437
  lanes/<lane>/ # the lane worktrees, and .templates/ for forked DBs
422
438
  runs/<run-id>/
439
+ job.json # while the job runs: pid, API address, token (0600)
440
+ job.log # the job's console, every attempt appended
423
441
  workflow.json # the snapshot frozen at run start
424
442
  postmortem.json # written once the run has ended
425
443
  nodes/<node-id>/
@@ -448,10 +466,10 @@ It removes the run *directory* — snapshot, transcripts, raw streams, gate logs
448
466
 
449
467
  Transcripts say what the *agents* did. This says what the *daemon* did — which is the half you need when the answer is "nothing happened", "it stopped", or "the process is gone".
450
468
 
451
- It is a file and a view, and they show the same records:
469
+ It is a file and a view, and they show the same records. Every process writes to the same file — each run's job and `ui` — and every record carries its `pid`. For one run's job alone, `vinta-ai-maestro logs <run-id>` is shorter:
452
470
 
453
471
  ```console
454
- $ vinta-ai-maestro serve
472
+ $ vinta-ai-maestro ui
455
473
  vinta-ai-maestro: daemon listening on http://127.0.0.1:52765
456
474
  Open this URL. It carries the access token, so treat it as a secret:
457
475
  http://127.0.0.1:52765/?token=<the-token-printed-here>
@@ -505,9 +523,9 @@ Everything other than `message` is **identifiers only** — the rule `flow.db` f
505
523
  Flags:
506
524
 
507
525
  ```console
508
- $ vinta-ai-maestro serve --log-level debug # adds a line per request and per socket
509
- $ vinta-ai-maestro serve --log-stderr # also print to the terminal, one line each
510
- $ vinta-ai-maestro serve --log-detail kind # drop error messages, keep kinds and frames
526
+ $ vinta-ai-maestro ui --log-level debug # adds a line per request and per socket
527
+ $ vinta-ai-maestro ui --log-stderr # also print to the terminal, one line each
528
+ $ vinta-ai-maestro ui --log-detail kind # drop error messages, keep kinds and frames
511
529
  ```
512
530
 
513
531
  `run` accepts the same three, and writes to the same file.
@@ -526,6 +544,8 @@ Open this URL. It carries the access token, so treat it as a secret:
526
544
 
527
545
  That line is the only place in this package's output where the token ever appears. It is not in the "listening on" line, not in warnings, not in errors — so pasting a log into a ticket is safe, and pasting *that* URL into a ticket publishes the run.
528
546
 
547
+ Each run's job has a token of its own, which it is never printed: it is written to that run's `job.json`, readable by your user only, and removed when the job ends. `ui` reads it to forward a live run's traffic, and the browser never sees it — its requests carry `ui`'s token, which the job would refuse. Treat `job.json` like the URL: it is access to the run.
548
+
529
549
  **`--host` is explicit and warned about.** The default bind is `127.0.0.1`. Any other value makes the daemon reachable from other machines, and the daemon prints a warning naming the host — on stderr, where it cannot be mistaken for part of the URL. Anyone who can reach the daemon and holds the token can drive the run: there is no per-user access control, by design. Prefer an SSH port-forward to `--host` for a daemon on a bigger box.
530
550
 
531
551
  ## Harnesses
@@ -544,7 +564,7 @@ Point the adapter at a specific binary with an environment variable, which overr
544
564
 
545
565
  ### What an agent may do in its lane
546
566
 
547
- `--permission <ask|auto|full|judged>` on `run` and `serve`, defaulting to **`auto`**: the agent works unattended inside its own lane, which is what a lane is for.
567
+ `--permission <ask|auto|full|judged>` on `run` and `ui` (for the runs it starts), defaulting to **`auto`**: the agent works unattended inside its own lane, which is what a lane is for.
548
568
 
549
569
  **The operator sets this, never the workflow document.** It is an argument to the command rather than a field in the JSON, and deliberately so — the document is committed and shared, and a file in a repository should not be able to tell someone else's machine to run agents without approvals. A plan may say which model writes a phase; it may not say how much of a stranger's filesystem that model gets.
550
570
 
@@ -555,7 +575,7 @@ Point the adapter at a specific binary with an environment variable, which overr
555
575
  | `full` | `--allow-dangerously-skip-permissions --permission-mode bypassPermissions` | `--dangerously-bypass-approvals-and-sandbox` |
556
576
  | `judged` | as `full`, plus a `PreToolUse` hook that asks a System One classifier about each judged call | refused by `doctor` |
557
577
 
558
- `ask` is the CLIs' own default and the one to avoid headlessly: nothing answers a permission prompt in a `run`. The request surfaces as a `permission_request` event and the transcript renders it, but no reply is ever sent — so the agent reports a blocked working directory and the phase fails having written nothing. Use it with `serve` and a human watching, or not at all.
578
+ `ask` is the CLIs' own default and the one to avoid headlessly: nothing answers a permission prompt in a `run`. The request surfaces as a `permission_request` event and the transcript renders it, but no reply is ever sent — so the agent reports a blocked working directory and the phase fails having written nothing. Use it with `ui` open and a human watching, or not at all.
559
579
 
560
580
  `full` is available and is not the default. Both vendors describe their equivalent as being for sandboxes with no internet access, and a lane is not that — it has the network and whatever credentials the machine holds.
561
581
 
@@ -567,7 +587,7 @@ A lane is still a worktree of your repository, so committed settings travel into
567
587
 
568
588
  ## System One classifiers
569
589
 
570
- `--system-one <config.json>` on `run`, `serve` and `doctor` points a run at a fast classifier — one that answers yes/no or scores a fixed set of labels, and writes nothing. The file is the operator's and never part of the plan:
590
+ `--system-one <config.json>` on `run`, `ui` and `doctor` points a run at a fast classifier — one that answers yes/no or scores a fixed set of labels, and writes nothing. The file is the operator's and never part of the plan:
571
591
 
572
592
  ```jsonc
573
593
  {
@@ -579,7 +599,7 @@ A lane is still a worktree of your repository, so committed settings travel into
579
599
  }
580
600
  ```
581
601
 
582
- `http` POSTs `{ kind, question, labels, input }` and reads `{ scores }` or `{ yes }` back, with the key from the environment variable you name (removed from the daemon's environment once read, so agents never see it). `command` runs a local program with the same JSON on stdin and stdout — the choice when repository content must not leave the machine, because a hosted classifier receives diffs, gate logs and shell commands.
602
+ `http` POSTs `{ kind, question, labels, input }` and reads `{ scores }` or `{ yes }` back, with the key from the environment variable you name (removed from the daemon's environment once read, so agents never see it). Before a run starts, the preflight asks the classifier one synthetic question, so a missing or rejected key, or an unreachable URL, stops the run at minute zero instead of at the first gate. Set `"probe": false` to skip it. `doctor` also warns about an `http` adapter with no `api_key_env`, since that sends no `Authorization` header. `command` runs a local program with the same JSON on stdin and stdout — the choice when repository content must not leave the machine, because a hosted classifier receives diffs, gate logs and shell commands.
583
603
 
584
604
  A plan can then declare **judge gates** next to its command gates:
585
605
 
@@ -0,0 +1,185 @@
1
+ /**
2
+ * `vinta-ai-maestro pause <runId>` and `vinta-ai-maestro stop <runId>`.
3
+ *
4
+ * Both end a run before its DAG does, and both reach it the same way: through
5
+ * the run's own job, found by its `job.json` and asked over the API its agents
6
+ * already use. Not by signal — a signal means "interrupted" to the job, and on
7
+ * Windows there is no signal to send that the job could catch at all.
8
+ *
9
+ * - **pause** drains. No new phase starts, each running phase finishes the step
10
+ * it is in, and the job exits with the run `paused` and every lane in place.
11
+ * `run --resume` picks it up.
12
+ * - **stop** kills: every live agent turn and every running gate ends now, and
13
+ * the run is `cancelled`, which `--resume` refuses.
14
+ *
15
+ * Both return as soon as the job has the request; `--wait` stays until it has
16
+ * exited. A pause can take as long as the longest step in flight.
17
+ *
18
+ * **When there is no job to ask.** A run whose job died is already as
19
+ * resumable as a pause would make it, so `pause` says so and changes nothing.
20
+ * `stop` still has something to do — make the run final — and does it
21
+ * directly in the journal. A job that is alive and not answering is the one
22
+ * case `stop` ends by force: its process tree is signalled, and the
23
+ * cancellation is written once it is gone.
24
+ */
25
+ import { existsSync } from 'node:fs';
26
+ import { resolve } from 'node:path';
27
+ import { parseArgs } from 'node:util';
28
+ import { isAlive, liveJob } from '../job/job.js';
29
+ import { openJournal } from '../journal/journal.js';
30
+ import { killTree } from '../platform/platform.js';
31
+ import { FAILED, OK, USAGE } from './io.js';
32
+ import { storeFor } from './paths.js';
33
+ import { isResumable, runState } from './status.js';
34
+ export const PAUSE_USAGE = `usage: vinta-ai-maestro pause <runId> [--repo <dir>] [--wait]
35
+
36
+ Pauses a running run. Nothing new starts; each running phase finishes the
37
+ step it is in — the agent turn or gate under way — and then the run's job
38
+ exits, leaving every lane as it was. Resume it with
39
+ \`vinta-ai-maestro run --resume <runId>\`.
40
+
41
+ --repo <dir> The project. Defaults to the current directory.
42
+ --wait Return once the job has exited, not once it has the request.`;
43
+ export const STOP_USAGE = `usage: vinta-ai-maestro stop <runId> [--repo <dir>] [--wait]
44
+
45
+ Stops a run for good. Every live agent turn and every running gate is
46
+ killed, and the run ends "cancelled": it cannot be resumed. Lanes and
47
+ branches are left in place, as for any finished run. A paused or
48
+ interrupted run can be stopped too, which makes it final.
49
+
50
+ --repo <dir> The project. Defaults to the current directory.
51
+ --wait Return once the job has exited, not once it has the request.`;
52
+ export const pauseCommand = (argv, io, deps = {}) => haltCommand('pause', argv, io, deps);
53
+ export const stopCommand = (argv, io, deps = {}) => haltCommand('stop', argv, io, deps);
54
+ async function haltCommand(mode, argv, io, deps) {
55
+ const usage = mode === 'pause' ? PAUSE_USAGE : STOP_USAGE;
56
+ let parsed;
57
+ try {
58
+ parsed = parseArgs({
59
+ args: [...argv],
60
+ options: { repo: { type: 'string' }, wait: { type: 'boolean' } },
61
+ allowPositionals: true,
62
+ });
63
+ }
64
+ catch {
65
+ io.err(usage);
66
+ return USAGE;
67
+ }
68
+ const runId = parsed.positionals[0];
69
+ if (runId === undefined || parsed.positionals.length > 1) {
70
+ io.err(usage);
71
+ return USAGE;
72
+ }
73
+ const repoPath = resolve(parsed.values.repo ?? process.cwd());
74
+ if (!existsSync(storeFor(repoPath))) {
75
+ io.err(`vinta-ai-maestro: no run "${runId}" in ${repoPath}`);
76
+ return FAILED;
77
+ }
78
+ const alive = deps.alive ?? isAlive;
79
+ const sleep = deps.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
80
+ const journal = openJournal(repoPath);
81
+ try {
82
+ const row = journal.run(runId);
83
+ if (row === undefined) {
84
+ io.err(`vinta-ai-maestro: no run "${runId}" in ${repoPath}`);
85
+ return FAILED;
86
+ }
87
+ const job = liveJob(repoPath, runId, alive);
88
+ const state = runState(row, job);
89
+ if (job === null)
90
+ return haltUnhosted(mode, journal, runId, state, io);
91
+ const asked = await ask(job, mode, deps.fetch ?? fetch);
92
+ if (asked === 'unreachable') {
93
+ if (mode === 'pause') {
94
+ io.err(`vinta-ai-maestro: run ${runId}'s job (pid ${job.pid}) is not answering. ` +
95
+ `\`vinta-ai-maestro stop ${runId}\` ends it regardless.`);
96
+ return FAILED;
97
+ }
98
+ return await forceStop(journal, runId, job, io, { alive, sleep, kill: deps.kill ?? signalTree });
99
+ }
100
+ if (asked === 'refused') {
101
+ io.err(`vinta-ai-maestro: run ${runId} is ${state}; there is nothing to ${mode}.`);
102
+ return FAILED;
103
+ }
104
+ io.out(mode === 'pause'
105
+ ? `vinta-ai-maestro: pausing run ${runId}. Running phases finish their current step, then the job exits.`
106
+ : `vinta-ai-maestro: stopping run ${runId}.`);
107
+ if (parsed.values.wait !== true) {
108
+ io.out(`vinta-ai-maestro: follow it with: vinta-ai-maestro logs ${runId} --follow`);
109
+ return OK;
110
+ }
111
+ while (alive(job.pid))
112
+ await sleep(250);
113
+ const ended = journal.run(runId)?.status ?? 'unknown';
114
+ io.out(`vinta-ai-maestro: run ${runId} is ${ended}.`);
115
+ return OK;
116
+ }
117
+ finally {
118
+ journal.close();
119
+ }
120
+ }
121
+ /** The run's own API, asked. `refused` is a 409: the run is not running. */
122
+ async function ask(job, mode, fetchImpl) {
123
+ try {
124
+ const response = await fetchImpl(`${job.url}/api/runs/${encodeURIComponent(job.runId)}/${mode}`, {
125
+ method: 'POST',
126
+ headers: { authorization: `Bearer ${job.token}`, 'content-type': 'application/json' },
127
+ body: '{}',
128
+ signal: AbortSignal.timeout(10_000),
129
+ });
130
+ if (response.ok)
131
+ return 'accepted';
132
+ return response.status === 409 ? 'refused' : 'unreachable';
133
+ }
134
+ catch {
135
+ return 'unreachable';
136
+ }
137
+ }
138
+ function haltUnhosted(mode, journal, runId, state, io) {
139
+ if (state === 'done' || state === 'cancelled') {
140
+ io.err(`vinta-ai-maestro: run ${runId} already ended (${state}).`);
141
+ return FAILED;
142
+ }
143
+ if (mode === 'pause') {
144
+ // Nothing is running it, so there is nothing to drain — and it is already
145
+ // exactly as resumable as a pause would leave it.
146
+ io.err(`vinta-ai-maestro: run ${runId} is ${state} — nothing is running it, so there is nothing to pause.`);
147
+ if (isResumable(state))
148
+ io.err(`vinta-ai-maestro: resume it with: vinta-ai-maestro run --resume ${runId}`);
149
+ return FAILED;
150
+ }
151
+ journal.append({ runId, type: 'run_ended', payload: { status: 'cancelled' } });
152
+ io.out(`vinta-ai-maestro: run ${runId} cancelled (it was ${state}). It can no longer be resumed.`);
153
+ return OK;
154
+ }
155
+ /**
156
+ * A job that is alive and will not answer: ended from outside. SIGTERM first —
157
+ * the job's own handler records the run as interrupted — then SIGKILL if it
158
+ * will not go, and the cancellation written over whatever it managed to say.
159
+ */
160
+ async function forceStop(journal, runId, job, io, deps) {
161
+ io.err(`vinta-ai-maestro: run ${runId}'s job (pid ${job.pid}) is not answering; ending it.`);
162
+ deps.kill(job.pid, 'SIGTERM');
163
+ for (let waited = 0; waited < 10_000 && deps.alive(job.pid); waited += 250)
164
+ await deps.sleep(250);
165
+ if (deps.alive(job.pid))
166
+ deps.kill(job.pid, 'SIGKILL');
167
+ journal.append({ runId, type: 'run_ended', payload: { status: 'cancelled' } });
168
+ io.out(`vinta-ai-maestro: run ${runId} cancelled.`);
169
+ // Agents and gates lead process groups of their own, which a job killed from
170
+ // outside never got the chance to end.
171
+ io.err('vinta-ai-maestro: agent or gate processes it started may still be running.');
172
+ return OK;
173
+ }
174
+ /** The job leads its own session (it was spawned detached), so its group is its tree. */
175
+ function signalTree(pid, signal) {
176
+ if (!killTree(pid, signal)) {
177
+ try {
178
+ process.kill(pid, signal);
179
+ }
180
+ catch {
181
+ // Already gone.
182
+ }
183
+ }
184
+ }
185
+ //# sourceMappingURL=halt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"halt.js","sourceRoot":"","sources":["../../src/cli/halt.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * The `vinta-ai-maestro` command line.
3
3
  *
4
- * Seven subcommands, dispatched by hand. `node:util`'s `parseArgs` does the flag
4
+ * A dozen subcommands, dispatched by hand. `node:util`'s `parseArgs` does the flag
5
5
  * parsing inside each one, and nothing else does any: a framework here would be
6
6
  * a dependency, a plugin lifecycle and a help renderer bought to replace a
7
- * switch statement over seven strings.
7
+ * switch statement over a dozen strings.
8
8
  *
9
9
  * `main` returns an exit code rather than calling `process.exit`. That is what
10
10
  * makes the commands testable without a built binary, and it keeps the one
@@ -18,10 +18,13 @@
18
18
  import { OK, USAGE, processIo } from './io.js';
19
19
  import { DOCTOR_USAGE, doctorCommand } from './doctor.js';
20
20
  import { GATE_USAGE, gateCommand } from './gate.js';
21
+ import { PAUSE_USAGE, STOP_USAGE, pauseCommand, stopCommand } from './halt.js';
22
+ import { LOGS_USAGE, logsCommand } from './logs.js';
21
23
  import { PURGE_USAGE, purgeCommand } from './purge.js';
22
24
  import { RUN_USAGE, runCommand } from './run.js';
23
25
  import { SERVE_USAGE, serveCommand } from './serve.js';
24
26
  import { SIMULATE_USAGE, simulateCommand } from './simulate.js';
27
+ import { STATUS_USAGE, statusCommand } from './status.js';
25
28
  import { WITH_USAGE, withCommand } from './with.js';
26
29
  import { judgeHookCommandMain } from '../system-one/hook.js';
27
30
  export const HELP = `vinta-ai-maestro — code-orchestrated parallel execution of a plan.
@@ -32,11 +35,16 @@ usage: vinta-ai-maestro <command> [options]
32
35
  non-zero if a run cannot start.
33
36
  simulate <workflow.json> Project the schedule without running it: wall
34
37
  clock, critical path and pool contention.
35
- serve Start the daemon and print the URL to open.
36
- Runs submitted to it outlive the terminal that
37
- submitted them.
38
- run <workflow.json> Start the daemon and execute the workflow.
39
- run --resume <run-id> Pick an interrupted run back up where it stopped.
38
+ run <workflow.json> Start the workflow as a background job and
39
+ return. The run outlives this terminal.
40
+ run --resume <run-id> Pick an interrupted or paused run back up.
41
+ status [run-id] What every run — or one run — is doing.
42
+ logs <run-id> [-f] A run's job log; -f follows it to the end.
43
+ pause <run-id> Let running steps finish, then stop. Resumable.
44
+ stop <run-id> Kill the run now. Final.
45
+ ui Serve the browser UI for every run and print the
46
+ URL to open. Closing it leaves runs running.
47
+ (\`serve\` is the same command.)
40
48
  purge [run-id] Delete run state under .vinta-ai-maestro/ — transcripts
41
49
  and gate logs hold repository contents verbatim.
42
50
  with <resource> -- <cmd> Run a command while holding a live run's resource.
@@ -58,7 +66,12 @@ const USAGES = {
58
66
  doctor: DOCTOR_USAGE,
59
67
  simulate: SIMULATE_USAGE,
60
68
  serve: SERVE_USAGE,
69
+ ui: SERVE_USAGE,
61
70
  run: RUN_USAGE,
71
+ status: STATUS_USAGE,
72
+ logs: LOGS_USAGE,
73
+ pause: PAUSE_USAGE,
74
+ stop: STOP_USAGE,
62
75
  purge: PURGE_USAGE,
63
76
  with: WITH_USAGE,
64
77
  gate: GATE_USAGE,
@@ -86,9 +99,18 @@ export async function main(argv, io = processIo()) {
86
99
  case 'simulate':
87
100
  return await simulateCommand(rest, io);
88
101
  case 'serve':
102
+ case 'ui':
89
103
  return await serveCommand(rest, io);
90
104
  case 'run':
91
105
  return await runCommand(rest, io);
106
+ case 'status':
107
+ return await statusCommand(rest, io);
108
+ case 'logs':
109
+ return await logsCommand(rest, io);
110
+ case 'pause':
111
+ return await pauseCommand(rest, io);
112
+ case 'stop':
113
+ return await stopCommand(rest, io);
92
114
  case 'purge':
93
115
  return await purgeCommand(rest, io);
94
116
  case 'with':
@@ -123,6 +145,9 @@ export { doctorCommand } from './doctor.js';
123
145
  export { simulateCommand } from './simulate.js';
124
146
  export { serveCommand, announce } from './serve.js';
125
147
  export { runCommand } from './run.js';
148
+ export { statusCommand } from './status.js';
149
+ export { logsCommand } from './logs.js';
150
+ export { pauseCommand, stopCommand } from './halt.js';
126
151
  export { purgeCommand } from './purge.js';
127
152
  export { withCommand } from './with.js';
128
153
  export { gateCommand } from './gate.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * `vinta-ai-maestro logs <runId>` — what a run's job has said.
3
+ *
4
+ * The job's console, from `runs/<id>/job.log`: everything `run` used to print
5
+ * to the terminal it no longer holds — the preflight report, warnings, the
6
+ * start and end lines — and the daemon's own records for that process, one per
7
+ * line. Every attempt at the run appends to the same file, so a resumed run
8
+ * reads as one history.
9
+ *
10
+ * `--follow` tails it until the job exits, which is how a pause or a stop is
11
+ * watched to its end. It is a poll of the file's size, because a follow that
12
+ * depended on a filesystem watcher would behave differently on every platform
13
+ * this package runs on, and a quarter-second is fast enough for a human.
14
+ *
15
+ * The file is read and printed as the job wrote it, and nothing here adds to
16
+ * it. What is in it is what §11 allows on a stream — identifiers and the
17
+ * operator's own command lines — and it sits under `runs/`, so `purge` takes
18
+ * it with the run.
19
+ */
20
+ import { closeSync, existsSync, openSync, readSync, statSync } from 'node:fs';
21
+ import { resolve } from 'node:path';
22
+ import { parseArgs } from 'node:util';
23
+ import { isAlive, jobLogPathFor, readJob } from '../job/job.js';
24
+ import { FAILED, OK, USAGE } from './io.js';
25
+ export const LOGS_USAGE = `usage: vinta-ai-maestro logs <runId> [--repo <dir>] [--follow] [--lines <n>]
26
+
27
+ Prints the run's job log: what the run would have printed to a terminal,
28
+ and its daemon's records, one per line. Every attempt — the original and
29
+ each resume — is in the same file.
30
+
31
+ --repo <dir> The project. Defaults to the current directory.
32
+ -f, --follow Keep printing as the job writes, until it exits.
33
+ -n, --lines <n> Only the last n lines. Defaults to all of them.`;
34
+ export async function logsCommand(argv, io, deps = {}) {
35
+ let parsed;
36
+ try {
37
+ parsed = parseArgs({
38
+ args: [...argv],
39
+ options: {
40
+ repo: { type: 'string' },
41
+ follow: { type: 'boolean', short: 'f' },
42
+ lines: { type: 'string', short: 'n' },
43
+ },
44
+ allowPositionals: true,
45
+ });
46
+ }
47
+ catch {
48
+ io.err(LOGS_USAGE);
49
+ return USAGE;
50
+ }
51
+ const runId = parsed.positionals[0];
52
+ if (runId === undefined || parsed.positionals.length > 1) {
53
+ io.err(LOGS_USAGE);
54
+ return USAGE;
55
+ }
56
+ const rawLines = parsed.values.lines;
57
+ const lines = rawLines === undefined ? undefined : Number(rawLines);
58
+ if (lines !== undefined && (!Number.isInteger(lines) || lines < 0)) {
59
+ io.err('vinta-ai-maestro: --lines must be a whole number');
60
+ return USAGE;
61
+ }
62
+ const repoPath = resolve(parsed.values.repo ?? process.cwd());
63
+ const path = jobLogPathFor(repoPath, runId);
64
+ if (!existsSync(path)) {
65
+ io.err(`vinta-ai-maestro: run "${runId}" has no job log in ${repoPath}.`);
66
+ return FAILED;
67
+ }
68
+ let offset = 0;
69
+ const initial = readFrom(path, 0);
70
+ offset = initial.length;
71
+ let text = initial.toString('utf8');
72
+ if (lines !== undefined) {
73
+ const all = text.split('\n');
74
+ if (all.at(-1) === '')
75
+ all.pop();
76
+ text = all.slice(Math.max(0, all.length - lines)).join('\n');
77
+ }
78
+ // A partial last line waits for the rest of itself before it is printed.
79
+ let pending = '';
80
+ const emit = (chunk) => {
81
+ const parts = (pending + chunk).split('\n');
82
+ pending = parts.pop() ?? '';
83
+ for (const line of parts)
84
+ io.out(line);
85
+ };
86
+ emit(lines === undefined || text === '' ? text : `${text}\n`);
87
+ if (parsed.values.follow !== true) {
88
+ if (pending !== '')
89
+ io.out(pending);
90
+ return OK;
91
+ }
92
+ const alive = deps.alive ?? isAlive;
93
+ const sleep = deps.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
94
+ for (;;) {
95
+ const job = readJob(repoPath, runId);
96
+ const chunk = readFrom(path, offset);
97
+ offset += chunk.length;
98
+ if (chunk.length > 0)
99
+ emit(chunk.toString('utf8'));
100
+ // Read once more after the job is gone, so its last words are not lost to
101
+ // the race between its final write and its exit.
102
+ if (job === null || !alive(job.pid)) {
103
+ const last = readFrom(path, offset);
104
+ if (last.length > 0)
105
+ emit(last.toString('utf8'));
106
+ if (pending !== '')
107
+ io.out(pending);
108
+ return OK;
109
+ }
110
+ await sleep(250);
111
+ }
112
+ }
113
+ function readFrom(path, offset) {
114
+ let size;
115
+ try {
116
+ size = statSync(path).size;
117
+ }
118
+ catch {
119
+ return Buffer.alloc(0);
120
+ }
121
+ if (size <= offset)
122
+ return Buffer.alloc(0);
123
+ const fd = openSync(path, 'r');
124
+ try {
125
+ const buffer = Buffer.alloc(size - offset);
126
+ const read = readSync(fd, buffer, 0, buffer.length, offset);
127
+ return buffer.subarray(0, read);
128
+ }
129
+ finally {
130
+ closeSync(fd);
131
+ }
132
+ }
133
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/cli/logs.ts"],"names":[],"mappings":"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"}