@tekmidian/pai 0.38.0 → 0.39.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.
- package/dist/{auto-route-DM7GhJ8y.mjs → auto-route-BlWOWT4z.mjs} +2 -2
- package/dist/{auto-route-DM7GhJ8y.mjs.map → auto-route-BlWOWT4z.mjs.map} +1 -1
- package/dist/{providers-DUshcB-d.mjs → chain-CqlgL7uH.mjs} +734 -1410
- package/dist/chain-CqlgL7uH.mjs.map +1 -0
- package/dist/cli/index.mjs +4 -3
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/program.mjs +4 -3
- package/dist/{clusters-Do4tEGyc.mjs → clusters-wlK0w41E.mjs} +1 -1
- package/dist/{clusters-Do4tEGyc.mjs.map → clusters-wlK0w41E.mjs.map} +1 -1
- package/dist/daemon/index.mjs +7 -7
- package/dist/{daemon-CGg1VCbA.mjs → daemon-CTkN_Vn8.mjs} +25 -25
- package/dist/{daemon-CGg1VCbA.mjs.map → daemon-CTkN_Vn8.mjs.map} +1 -1
- package/dist/{daemon-DsGGiIJM.mjs → daemon-CmHHmKpg.mjs} +7 -7
- package/dist/daemon-mcp/index.mjs +106 -3
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/{detector-CMap-9vw.mjs → detector-Bwk_4Pk4.mjs} +1 -1
- package/dist/{detector-CMap-9vw.mjs.map → detector-Bwk_4Pk4.mjs.map} +1 -1
- package/dist/detector-DO730Zq0.mjs +5 -0
- package/dist/{factory-DD2T33C9.mjs → factory-A9x-T2Kg.mjs} +5 -5
- package/dist/{factory-DD2T33C9.mjs.map → factory-A9x-T2Kg.mjs.map} +1 -1
- package/dist/factory-BD-np0Vd.mjs +3 -0
- package/dist/fallback-DqYhiACU.mjs +1675 -0
- package/dist/fallback-DqYhiACU.mjs.map +1 -0
- package/dist/hooks/route-agents-to-worker.mjs +73 -8
- package/dist/hooks/route-agents-to-worker.mjs.map +2 -2
- package/dist/hooks/worker-proxy.mjs +71 -6
- package/dist/hooks/worker-proxy.mjs.map +2 -2
- package/dist/hooks/worker-status-line.mjs +137 -11
- package/dist/hooks/worker-status-line.mjs.map +4 -4
- package/dist/{indexer-backend-Cox9BCo-.mjs → indexer-backend-TG64CCQC.mjs} +1 -1
- package/dist/{indexer-backend-Cox9BCo-.mjs.map → indexer-backend-TG64CCQC.mjs.map} +1 -1
- package/dist/{latent-ideas-BC1oINZ-.mjs → latent-ideas-B7wq75Pt.mjs} +2 -2
- package/dist/{latent-ideas-BC1oINZ-.mjs.map → latent-ideas-B7wq75Pt.mjs.map} +1 -1
- package/dist/{link-boost-QFLrJwD6.mjs → link-boost-HkG7JWZR.mjs} +1 -1
- package/dist/{link-boost-QFLrJwD6.mjs.map → link-boost-HkG7JWZR.mjs.map} +1 -1
- package/dist/{neighborhood-D9MJ1c8f.mjs → neighborhood-lThN-MaQ.mjs} +1 -1
- package/dist/{neighborhood-D9MJ1c8f.mjs.map → neighborhood-lThN-MaQ.mjs.map} +1 -1
- package/dist/{note-context-d1wT_-GA.mjs → note-context-b6k0mAKi.mjs} +1 -1
- package/dist/{note-context-d1wT_-GA.mjs.map → note-context-b6k0mAKi.mjs.map} +1 -1
- package/dist/planner-DGV8VFib.mjs +243 -0
- package/dist/planner-DGV8VFib.mjs.map +1 -0
- package/dist/{postgres--BjPtLa0.mjs → postgres-mW1n7Vi1.mjs} +1 -1
- package/dist/{postgres--BjPtLa0.mjs.map → postgres-mW1n7Vi1.mjs.map} +1 -1
- package/dist/{program-Y0hAiVy8.mjs → program-B-QJXCRb.mjs} +95 -19
- package/dist/{program-Y0hAiVy8.mjs.map → program-B-QJXCRb.mjs.map} +1 -1
- package/dist/{query-feedback-BUJxgw5B.mjs → query-feedback-BV4CcxqS.mjs} +1 -1
- package/dist/{query-feedback-BUJxgw5B.mjs.map → query-feedback-BV4CcxqS.mjs.map} +1 -1
- package/dist/query-feedback-DSVyHtrG.mjs +3 -0
- package/dist/router-Bk77E7hj.mjs +3 -0
- package/dist/{router-DK_sLsUL.mjs → router-DcHKnEPa.mjs} +1 -1
- package/dist/{router-DK_sLsUL.mjs.map → router-DcHKnEPa.mjs.map} +1 -1
- package/dist/skills/Worker/SKILL.md +13 -0
- package/dist/{sources-Bi7--33T.mjs → sources-kLnQsNrW.mjs} +1 -1
- package/dist/{sources-Bi7--33T.mjs.map → sources-kLnQsNrW.mjs.map} +1 -1
- package/dist/{sqlite-DtaL1glm.mjs → sqlite-BenGr3UP.mjs} +1 -1
- package/dist/{sqlite-DtaL1glm.mjs.map → sqlite-BenGr3UP.mjs.map} +1 -1
- package/dist/{state-BY2L6-vX.mjs → state-CAeyOdfq.mjs} +1 -1
- package/dist/{state-BY2L6-vX.mjs.map → state-CAeyOdfq.mjs.map} +1 -1
- package/dist/{state-8Hm9E4tW.mjs → state-Ca9F_sZc.mjs} +1 -1
- package/dist/{themes-BN0a2duq.mjs → themes-BI4GMRP9.mjs} +1 -1
- package/dist/{themes-BN0a2duq.mjs.map → themes-BI4GMRP9.mjs.map} +1 -1
- package/dist/{tools-CGPqpU3A.mjs → tools-Bp7hj6OW.mjs} +1 -1
- package/dist/{tools-y2bJpKom.mjs → tools-DGcB3o_S.mjs} +13 -13
- package/dist/{tools-y2bJpKom.mjs.map → tools-DGcB3o_S.mjs.map} +1 -1
- package/dist/{trace-h23JCcFD.mjs → trace-bobARFEX.mjs} +1 -1
- package/dist/{trace-h23JCcFD.mjs.map → trace-bobARFEX.mjs.map} +1 -1
- package/dist/{vault-indexer-DgsPjMgs.mjs → vault-indexer-C3OfXTNF.mjs} +1 -1
- package/dist/{vault-indexer-DgsPjMgs.mjs.map → vault-indexer-C3OfXTNF.mjs.map} +1 -1
- package/dist/{work-queue-worker-gsKd2LJa.mjs → work-queue-worker-DW8lz-Oo.mjs} +3 -3
- package/dist/{work-queue-worker-Dva_v_pI.mjs → work-queue-worker-R7UGIag_.mjs} +3 -3
- package/dist/{work-queue-worker-Dva_v_pI.mjs.map → work-queue-worker-R7UGIag_.mjs.map} +1 -1
- package/dist/{zettelkasten-vo7psPdT.mjs → zettelkasten-m5QPtb-L.mjs} +3 -3
- package/dist/{zettelkasten-vo7psPdT.mjs.map → zettelkasten-m5QPtb-L.mjs.map} +1 -1
- package/docs/commands/README.md +5 -0
- package/docs/commands/worker.md +74 -0
- package/docs/worker.md +132 -1
- package/package.json +1 -1
- package/src/hooks/ts/pre-tool-use/route-agents-to-worker.ts +2 -2
- package/dist/detector-DtLExmHN.mjs +0 -5
- package/dist/factory-BXzqRYVZ.mjs +0 -3
- package/dist/providers-DUshcB-d.mjs.map +0 -1
- package/dist/query-feedback-DhyLOe5S.mjs +0 -3
- package/dist/router-1zi8jiNF.mjs +0 -3
- /package/dist/{main-resolver-IhZo4pI0.mjs → main-resolver-D6IImXvF.mjs} +0 -0
package/docs/worker.md
CHANGED
|
@@ -265,6 +265,49 @@ A quota failure before the first tool call is re-run on the next provider and
|
|
|
265
265
|
logged as `WORKER-REROUTE`. `pai worker providers enable <name>` clears a
|
|
266
266
|
cooldown by hand.
|
|
267
267
|
|
|
268
|
+
## When the Anthropic plan runs out
|
|
269
|
+
|
|
270
|
+
`pai worker fallback on [provider]` switches the whole machine: every NEW
|
|
271
|
+
Claude Code process — interactive sessions, task-bus sessions, the daemon's
|
|
272
|
+
headless summarizer — runs on that worker provider instead of the Anthropic
|
|
273
|
+
login, until `pai worker fallback off`. Use it when the plan budget is gone
|
|
274
|
+
but work must continue.
|
|
275
|
+
|
|
276
|
+
What `on` does:
|
|
277
|
+
|
|
278
|
+
- Writes the provider into the `env` block of `~/.claude/settings.json`:
|
|
279
|
+
`ANTHROPIC_BASE_URL`, `ANTHROPIC_AUTH_TOKEN`, the three
|
|
280
|
+
`ANTHROPIC_DEFAULT_*_MODEL` pins (fast model as haiku, default as sonnet
|
|
281
|
+
and opus), the provider's own env (`API_TIMEOUT_MS` …), plus
|
|
282
|
+
`ENABLE_TOOL_SEARCH=true` and
|
|
283
|
+
`CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1`.
|
|
284
|
+
- Pins the top-level `model` key to the provider's default model.
|
|
285
|
+
- Saves the replaced values under `workers.fallback.saved` in
|
|
286
|
+
`~/.config/pai/config.json` and writes a `FALLBACK-ACTIVE.md` note into the
|
|
287
|
+
workers log dir telling running sessions what to do.
|
|
288
|
+
|
|
289
|
+
Notes:
|
|
290
|
+
|
|
291
|
+
- **The token sits in settings.json.** `on` reads it from the provider's key
|
|
292
|
+
file at switch time; `off` removes it again. That is the price of switching
|
|
293
|
+
every process without touching each one's environment.
|
|
294
|
+
- `off` restores settings.json exactly — only the keys `on` touched change.
|
|
295
|
+
Both files are written atomically; `CLAUDE_SETTINGS_PATH` points `on`/`off`
|
|
296
|
+
at a copy for dry runs.
|
|
297
|
+
- **Running sessions are not switched.** A Claude Code process keeps the
|
|
298
|
+
provider it started with until restarted. `pai worker fallback status`
|
|
299
|
+
lists running sessions (AIBroker registry when populated, else `ps`) and
|
|
300
|
+
the note path; its one-line instruction for each of them: restart the
|
|
301
|
+
session in its project directory, keep the same AIBroker name.
|
|
302
|
+
- The Agent hook is unchanged: subagents keep routing to `pai worker run`
|
|
303
|
+
workers, which set their own per-provider env.
|
|
304
|
+
- `on` is idempotent (re-applying heals an interrupted switch), and switching
|
|
305
|
+
providers while on restores the first switch's savings before saving fresh.
|
|
306
|
+
- MCP: `worker_fallback` (action `on`/`off`/`status`, optional `provider`);
|
|
307
|
+
chat phrases "switch everything to glm", "fallback on", "back to
|
|
308
|
+
anthropic", "fallback off", "is fallback on" are mapped in the Worker
|
|
309
|
+
skill.
|
|
310
|
+
|
|
268
311
|
## What a worker is
|
|
269
312
|
|
|
270
313
|
- One `claude -p … --output-format stream-json --verbose` process per call,
|
|
@@ -335,6 +378,92 @@ Ctrl-D leaves. The auto-exit countdown never fires while the prompt holds
|
|
|
335
378
|
unsent text. Without a TTY (piped output) the pane keeps the plain scrolling
|
|
336
379
|
behaviour — no prompt row, no ticker, stdin still the operator channel.
|
|
337
380
|
|
|
381
|
+
### Sub-workers and handoffs
|
|
382
|
+
|
|
383
|
+
Any worker may start its own workers: the runner exports `PAI_WORKER_ID` in
|
|
384
|
+
every worker's environment, and a `pai worker run` launched from inside one
|
|
385
|
+
records `parent` in its status — so the forest is visible in `ps` (children
|
|
386
|
+
indented under their parent, `├`/`└` connectors), the status line (`↳` under
|
|
387
|
+
the parent) and each child gets its own follow pane. Handoffs travel **up
|
|
388
|
+
only**, from a child to its parent:
|
|
389
|
+
|
|
390
|
+
```
|
|
391
|
+
pai worker handoff '{"kind":"proposal","text":"run this on a cheap provider","data":{…}}'
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
(or the MCP tool `worker_handoff`; kinds `proposal`, `question`, `blocker` —
|
|
395
|
+
`result` is sent automatically when a child finishes). The handoff is appended
|
|
396
|
+
to `<logDir>/<parent>.inbox.jsonl` (durable, ordered) and, when the parent is
|
|
397
|
+
running, also delivered as an operator message `[handoff from <child id>]`. The
|
|
398
|
+
parent sees it in its pane (`◆ from <id> · kind: text`, magenta), `ps` and the
|
|
399
|
+
status line show `◆N` for an inbox with N handoffs, and `replay`/`follow`
|
|
400
|
+
merge them into the transcript by timestamp. There is no sideways channel:
|
|
401
|
+
siblings never see each other, everything goes up.
|
|
402
|
+
|
|
403
|
+
Two caps keep the tree bounded (`workers.tree`):
|
|
404
|
+
|
|
405
|
+
- `maxDepth` (default 2) — how deep sub-workers may nest; a launch one level
|
|
406
|
+
past the cap fails with a message that suggests a handoff instead,
|
|
407
|
+
- `maxChildren` (default 4) — how many children of one parent may run at the
|
|
408
|
+
same time (finished children do not count).
|
|
409
|
+
|
|
410
|
+
Chain stages and planner sub-tasks carry a parent too, but a parent without a
|
|
411
|
+
status file (a chain id) is not a worker and is never capped by depth.
|
|
412
|
+
|
|
413
|
+
### Worktrees and merge
|
|
414
|
+
|
|
415
|
+
A run whose class writes files (`implement`, `complex`, `plan`) in a git repo,
|
|
416
|
+
with a prompt that is not read-only, gets **its own git worktree** by default:
|
|
417
|
+
`<logDir>/worktrees/<id>` on branch `worker/<id>` from the current HEAD. The
|
|
418
|
+
worker commits its work on that branch (the no-commit rule applies to the main
|
|
419
|
+
branch only — the appended system prompt says so); when git refuses (no
|
|
420
|
+
commits yet, detached setup) the run degrades to in place with a note on
|
|
421
|
+
stderr and in the ledger.
|
|
422
|
+
|
|
423
|
+
```
|
|
424
|
+
pai worker merge <id> # git merge --no-ff worker/<id> + remove the worktree
|
|
425
|
+
pai worker discard <id> # remove worktree and branch, keep nothing
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
`ps` marks a worker with an unmerged branch `⎇<commits>` (yellow); the status
|
|
429
|
+
file records `branch`, `commits` and `worktreeDir`. Chains give a worktree to
|
|
430
|
+
the implement stage only; `--worktree` forces one on, `--no-worktree` opts
|
|
431
|
+
out.
|
|
432
|
+
|
|
433
|
+
### The planner class
|
|
434
|
+
|
|
435
|
+
`--class plan` runs a small orchestration, not one worker:
|
|
436
|
+
|
|
437
|
+
1. a planner worker reads the repository and writes
|
|
438
|
+
`<logDir>/plans/<planner id>.json` — sub-tasks (`title`, `brief`, `class`,
|
|
439
|
+
`files`, `acceptance`), 5–50 of them, fewer only when the goal names a
|
|
440
|
+
smaller count;
|
|
441
|
+
2. the runner validates the plan and spawns the sub-tasks as children of the
|
|
442
|
+
planner, at most `workers.tree.maxChildren` at a time;
|
|
443
|
+
3. each child's structured report arrives in the planner's inbox as a
|
|
444
|
+
`kind: "result"` handoff;
|
|
445
|
+
4. the run finishes with a summary report (`n/m sub-tasks ok`) and the
|
|
446
|
+
`pai worker merge` lines for any unmerged branches.
|
|
447
|
+
|
|
448
|
+
The planner's prompt carries the prompt rules that make plans executable:
|
|
449
|
+
domain-specific instructions only (real files, real commands), constraints
|
|
450
|
+
over step lists, explicit quantity ranges, no checkbox style.
|
|
451
|
+
|
|
452
|
+
### Clickr controls (desktop set)
|
|
453
|
+
|
|
454
|
+
The default `mcpSets` ship one set: `desktop = ["clickr"]`. A worker launched
|
|
455
|
+
`--mcp desktop` receives the clickr MCP server — screen control for GUI work
|
|
456
|
+
— and follows the same control handover as a session:
|
|
457
|
+
|
|
458
|
+
```
|
|
459
|
+
pai worker controls <id> you # hand control of the desktop to the worker
|
|
460
|
+
pai worker controls <id> me # take it back
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
`controls` runs the `clickr controls you|me` CLI and the worker's screenshot
|
|
464
|
+
and input tools honour it. Control starts with the operator: a worker cannot
|
|
465
|
+
drive the desktop until it is handed over.
|
|
466
|
+
|
|
338
467
|
### Context meter
|
|
339
468
|
|
|
340
469
|
Status files carry `contextTokens` (input + cache read + cache creation +
|
|
@@ -422,7 +551,9 @@ background instead. Decisions are ledgered (`DENIED-ANTHROPIC-AGENT`,
|
|
|
422
551
|
`cost_tier`/`tags`), `worker_classes` (list/set/unset), `worker_run` (start a
|
|
423
552
|
worker or chain from chat, returns the id immediately), `worker_toggle`,
|
|
424
553
|
`worker_ps`, `worker_replay`, `worker_say` (message a running worker),
|
|
425
|
-
`worker_resume` (continue a finished one)
|
|
554
|
+
`worker_resume` (continue a finished one), `worker_handoff` (from inside a
|
|
555
|
+
worker: send a proposal/question/blocker up to its parent) — the same library
|
|
556
|
+
the CLI calls.
|
|
426
557
|
`worker_providers add` accepts a raw `key`, parks it in
|
|
427
558
|
`~/.config/pai/keys/<name>` (mode 0600) and stores only the path.
|
|
428
559
|
|
package/package.json
CHANGED
|
@@ -112,10 +112,10 @@ async function main(): Promise<void> {
|
|
|
112
112
|
deny(
|
|
113
113
|
"Agent tool is disabled: subagents run on the configured worker provider, not Anthropic. " +
|
|
114
114
|
"Delegate with Bash instead, in the background:\n\n" +
|
|
115
|
-
`pai worker run --label "${label || "task"}" --
|
|
115
|
+
`pai worker run --label "${label || "task"}" --class research -p '<full, self-contained task spec>' ` +
|
|
116
116
|
"--allowedTools 'Read,Edit,Write,Bash,Grep,Glob' --output-format json\n\n" +
|
|
117
117
|
"- Run it with run_in_background: true and always with a timeout.\n" +
|
|
118
|
-
"- Use --
|
|
118
|
+
"- Use --class spotcheck (or implement) as the task demands.\n" +
|
|
119
119
|
"- Web research: add WebSearch,WebFetch to --allowedTools.\n" +
|
|
120
120
|
"- The answer is in the `result` field of the JSON it prints. Review the diff yourself.\n" +
|
|
121
121
|
"- pai worker ps lists running workers; pai worker follow <id> shows one live.\n" +
|