@tekmidian/pai 0.37.0 → 0.38.0

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.
@@ -7,13 +7,13 @@ import "../helpers-IjZkXBhj.mjs";
7
7
  import "../sync-BWbe8JTg.mjs";
8
8
  import "../embeddings-DOLZnT1X.mjs";
9
9
  import "../search-Rpk1cSBC.mjs";
10
- import { n as drainStdio, t as buildProgram } from "../program-CEIHn_Ma.mjs";
10
+ import { n as drainStdio, t as buildProgram } from "../program-Y0hAiVy8.mjs";
11
11
  import "../context-handover-cache-BpUojjsi.mjs";
12
12
  import "../indexer-D7MvSQPY.mjs";
13
13
  import "../ipc-client-BmypMNYk.mjs";
14
14
  import "../config-B64vFg14.mjs";
15
15
  import "../factory-DD2T33C9.mjs";
16
- import "../providers-sXcK5bDZ.mjs";
16
+ import "../providers-DUshcB-d.mjs";
17
17
  import "../main-resolver-DlaLOFBA.mjs";
18
18
  import { CommanderError } from "commander";
19
19
 
@@ -6,13 +6,13 @@ import "../helpers-IjZkXBhj.mjs";
6
6
  import "../sync-BWbe8JTg.mjs";
7
7
  import "../embeddings-DOLZnT1X.mjs";
8
8
  import "../search-Rpk1cSBC.mjs";
9
- import { t as buildProgram } from "../program-CEIHn_Ma.mjs";
9
+ import { t as buildProgram } from "../program-Y0hAiVy8.mjs";
10
10
  import "../context-handover-cache-BpUojjsi.mjs";
11
11
  import "../indexer-D7MvSQPY.mjs";
12
12
  import "../ipc-client-BmypMNYk.mjs";
13
13
  import "../config-B64vFg14.mjs";
14
14
  import "../factory-DD2T33C9.mjs";
15
- import "../providers-sXcK5bDZ.mjs";
15
+ import "../providers-DUshcB-d.mjs";
16
16
  import "../main-resolver-DlaLOFBA.mjs";
17
17
 
18
18
  export { buildProgram };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { t as PaiClient } from "../ipc-client-BmypMNYk.mjs";
3
3
  import { o as loadConfig } from "../config-B64vFg14.mjs";
4
- import { C as loadStatus, D as workersLogDir, E as ledgerPath, a as setRole, c as useProvider, d as replayOutput, i as setProviderEnabled, k as readWorkersSection, m as testProvider, n as describeProviders, o as setWorkersEnabled, p as runWorker, r as removeProvider, s as unsetRole, t as addProvider, u as psOutput, v as sayToWorker, w as ledgerSummary } from "../providers-sXcK5bDZ.mjs";
4
+ import { A as ledgerPath, D as ledgerSummary, E as loadStatus, P as readWorkersSection, _ as testProvider, a as setClass, c as unsetClass, f as psOutput, g as runWorker, h as runChain, i as removeProvider, j as workersLogDir, l as updateProvider, n as classTargetText, o as setProviderEnabled, p as replayOutput, r as describeProviders, s as setWorkersEnabled, t as addProvider, u as useProvider, x as sayToWorker } from "../providers-DUshcB-d.mjs";
5
5
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
7
 
@@ -4524,40 +4524,62 @@ const worker = {
4524
4524
  description: "Run, watch and route subagent workers on configured providers",
4525
4525
  content: `## Worker Skill
4526
4526
 
4527
- USE WHEN delegating subagent work, checking on running workers, or managing worker providers.
4527
+ USE WHEN delegating subagent work, checking on running workers, or managing worker providers/classes from chat.
4528
4528
 
4529
4529
  ### Delegating
4530
4530
 
4531
4531
  The Agent tool is denied when workers are on — delegate with Bash instead:
4532
4532
 
4533
4533
  \`\`\`
4534
- pai worker run --label "short task label" --role research \\
4534
+ pai worker run --label "short task label" --class research \\
4535
4535
  -p '<full, self-contained task spec>' \\
4536
4536
  --allowedTools 'Read,Edit,Write,Bash,Grep,Glob' --output-format json
4537
4537
  \`\`\`
4538
4538
 
4539
4539
  - Always run in background (run_in_background: true) and with a timeout.
4540
- - Roles: implement (default quality), research (web: add WebSearch,WebFetch), spotcheck (cheap/fast model).
4540
+ - Classes: draft (spec writing), plan, implement (default quality), review,
4541
+ research (web: add WebSearch,WebFetch), spotcheck (cheap/fast model), simple,
4542
+ complex, image. \`--role\` still works as an alias of \`--class\`.
4543
+ - Anything larger than a one-file change goes through a chain:
4544
+ \`pai worker run --chain draft,implement …\` — the draft stage turns the brief
4545
+ into a spec file, implement runs with it, \`--chain draft,implement,review\`
4546
+ adds a review pass. \`--class spotcheck\` for verification runs.
4541
4547
  - The answer is in the \`result\` field of the JSON it prints. Review the diff yourself.
4542
4548
  - \`--no-pane\` suppresses the iTerm follow pane; panes open automatically otherwise.
4543
- - Panes run under the \`pai-worker\` dynamic profile (default profile's font
4544
- family at \`workers.pane.fontSize\`, default 13); \`pai worker pane <id> --check\`
4545
- reports the pane plus that profile's path, existence and font.
4549
+ - \`--agent <name>\` runs a definition from ~/.claude/agents/<name>.md (the agent
4550
+ library runs on workers: body becomes the system prompt, tools the allowlist,
4551
+ model the class).
4546
4552
 
4547
4553
  ### Watching
4548
4554
 
4549
- - \`worker_ps\` — running + last finished workers.
4555
+ - \`worker_ps\` — running + last finished workers (chains show as trees).
4550
4556
  - \`worker_replay\` with id — the transcript of one worker.
4551
4557
  - CLI equivalents: \`pai worker ps\`, \`pai worker follow <id>\`.
4552
4558
 
4553
4559
  ### Routing
4554
4560
 
4555
4561
  - \`worker_status\` — on/off, active provider, providers, run tally.
4556
- - \`worker_providers\` — list/add/remove/use/enable/disable/test. Adding needs
4557
- name, base_url, model and a key (key file path, or a raw key which is
4558
- parked in ~/.config/pai/keys/<name>, mode 0600).
4559
- - \`worker_roles\` list/set/unset role provider[/fast].
4560
- - \`worker_toggle\` — off makes Agent subagents run on Anthropic again.`
4562
+ - \`worker_providers\` — list/add/update/remove/use/enable/disable/test. Adding
4563
+ needs name, base_url, model and a key (key file path, or a raw key which is
4564
+ parked in ~/.config/pai/keys/<name>, mode 0600). update changes cost_tier
4565
+ (1 cheapest 5 most expensive) and tags (code, vision, image-gen,
4566
+ long-context, fast, reasoning).
4567
+ - \`worker_classes\` — list/set/unset class → provider[/fast], or constraints
4568
+ only (max_cost_tier, require_tags) so auto-routing picks a qualifying provider.
4569
+ - \`worker_run\` — start a worker or chain from chat; returns the id immediately.
4570
+ - \`worker_toggle\` — off makes Agent subagents run on Anthropic again.
4571
+
4572
+ ### Preferences from chat (map the phrase, use the tool, never mention files)
4573
+
4574
+ - "use <provider> for image generation" / "route research to <provider>" →
4575
+ worker_classes set <class>=<provider>. One line back.
4576
+ - "prefer the flash model for simple tasks" / "cheap only for drafts" →
4577
+ worker_classes set simple|draft=<provider>/fast (or a max_cost_tier). One line.
4578
+ - "reviews should use a reasoning model" → worker_classes set review with
4579
+ require_tags ["reasoning"] (drop the pin if one exists). One line.
4580
+ - "what handles reviews" / "show the routing table" → worker_classes list
4581
+ (plus worker_status for providers). One or two lines.
4582
+ - Never tell the user to edit a config file — the tools do it.`
4561
4583
  };
4562
4584
 
4563
4585
  //#endregion
@@ -5738,20 +5760,22 @@ async function startShim() {
5738
5760
  }
5739
5761
  });
5740
5762
  server.tool("worker_providers", [
5741
- "Manage worker providers (list, add, remove, use, enable, disable, test).",
5763
+ "Manage worker providers (list, add, update, remove, use, enable, disable, test).",
5742
5764
  "",
5743
- "action=list (default) shows providers, roles and routing.",
5765
+ "action=list (default) shows providers (with cost tier and tags), classes and routing.",
5744
5766
  "action=add needs name, model — and either key_file or key, plus:",
5745
5767
  " base_url (anthropic protocol) or upstream_url (protocol=openai, runs",
5746
5768
  " through the local PAI proxy). A raw key is written to",
5747
5769
  " ~/.config/pai/keys/<name> (mode 0600); only the path lands in the config.",
5748
5770
  " engine=codex runs the Codex CLI instead of Claude Code.",
5771
+ "action=update changes cost_tier / tags of an existing provider.",
5749
5772
  "action=test runs a one-word pong probe through the provider",
5750
5773
  " (reports 'codex not installed' when that engine's CLI is missing)."
5751
5774
  ].join("\n"), {
5752
5775
  action: _enum([
5753
5776
  "list",
5754
5777
  "add",
5778
+ "update",
5755
5779
  "remove",
5756
5780
  "use",
5757
5781
  "enable",
@@ -5770,12 +5794,29 @@ async function startShim() {
5770
5794
  fast_model: string().optional().describe("Cheaper model for spotchecks (add, optional)."),
5771
5795
  env: record(string(), string()).optional().describe("Extra env for runs (add, optional)."),
5772
5796
  note: string().optional().describe("Human note shown in listings (add, optional)."),
5773
- quota_probe: string().optional().describe("URL whose JSON first number is the quota percent (add, optional).")
5797
+ quota_probe: string().optional().describe("URL whose JSON first number is the quota percent (add, optional)."),
5798
+ cost_tier: number().int().min(1).max(5).optional().describe("Cost tier 1 (cheapest) … 5 (most expensive). Default 3. (add, update)"),
5799
+ tags: array(_enum([
5800
+ "code",
5801
+ "vision",
5802
+ "image-gen",
5803
+ "long-context",
5804
+ "fast",
5805
+ "reasoning"
5806
+ ])).optional().describe("Capability tags (add, update).")
5774
5807
  }, async (args) => {
5775
5808
  try {
5776
5809
  const action = args.action ?? "list";
5777
5810
  if (action === "list") return workerText(describeProviders(readWorkersSection().workers).join("\n"));
5778
5811
  if (!args.name) return workerError(/* @__PURE__ */ new Error("name is required for this action"));
5812
+ if (action === "update") {
5813
+ if (args.cost_tier === void 0 && args.tags === void 0) return workerError(/* @__PURE__ */ new Error("update needs cost_tier and/or tags"));
5814
+ updateProvider(args.name, {
5815
+ ...args.cost_tier !== void 0 ? { costTier: args.cost_tier } : {},
5816
+ ...args.tags !== void 0 ? { tags: args.tags } : {}
5817
+ });
5818
+ return workerText([`provider ${args.name} updated`, ...describeProviders(readWorkersSection().workers)].join("\n"));
5819
+ }
5779
5820
  if (action === "add") {
5780
5821
  if (!args.model) return workerError(/* @__PURE__ */ new Error("add needs model"));
5781
5822
  if (args.protocol !== "openai" && !args.base_url) return workerError(/* @__PURE__ */ new Error("add needs base_url (only protocol=openai goes without it)"));
@@ -5792,7 +5833,9 @@ async function startShim() {
5792
5833
  ...args.upstream_url ? { upstreamUrl: args.upstream_url } : {},
5793
5834
  ...args.engine ? { engine: args.engine } : {},
5794
5835
  ...args.context_window ? { contextWindow: args.context_window } : {},
5795
- ...args.quota_probe ? { quotaProbe: args.quota_probe } : {}
5836
+ ...args.quota_probe ? { quotaProbe: args.quota_probe } : {},
5837
+ ...args.cost_tier !== void 0 ? { costTier: args.cost_tier } : {},
5838
+ ...args.tags?.length ? { tags: args.tags } : {}
5796
5839
  });
5797
5840
  return workerText([`provider ${args.name} added; active: ${workers.active ?? "(none)"}; workers ${workers.enabled ? "on" : "off"}`, ...describeProviders(workers)].join("\n"));
5798
5841
  }
@@ -5818,37 +5861,115 @@ async function startShim() {
5818
5861
  return workerError(e);
5819
5862
  }
5820
5863
  });
5821
- server.tool("worker_roles", [
5822
- "Manage worker roles (list, set, unset).",
5864
+ server.tool("worker_classes", [
5865
+ "Manage worker classes (list, set, unset).",
5823
5866
  "",
5824
- "A role maps a task class to a provider, optionally its fast model:",
5867
+ "A class maps a task class to a provider, optionally its fast model:",
5825
5868
  "implement=glm, research=glm, spotcheck=glm/fast.",
5826
- "Runs pick the provider via --role first, then the active provider."
5869
+ "set can instead give routing constraints only (max_cost_tier,",
5870
+ "require_tags) — auto-routing then picks a qualifying provider.",
5871
+ "Runs pick the provider via --class first, then the active provider."
5827
5872
  ].join("\n"), {
5828
5873
  action: _enum([
5829
5874
  "list",
5830
5875
  "set",
5831
5876
  "unset"
5832
5877
  ]).optional().describe("Default: list."),
5833
- role: string().optional().describe("Role name (set/unset)."),
5834
- target: string().optional().describe("Provider or provider/fast (set).")
5878
+ class: string().optional().describe("Class name (set/unset), e.g. implement, spotcheck."),
5879
+ target: string().optional().describe("Provider or provider/fast (set)."),
5880
+ max_cost_tier: number().int().min(1).max(5).optional().describe("Auto-routing considers only providers up to this cost tier (set)."),
5881
+ require_tags: array(_enum([
5882
+ "code",
5883
+ "vision",
5884
+ "image-gen",
5885
+ "long-context",
5886
+ "fast",
5887
+ "reasoning"
5888
+ ])).optional().describe("Auto-routing needs these tags (set).")
5835
5889
  }, async (args) => {
5836
5890
  try {
5837
5891
  const action = args.action ?? "list";
5838
5892
  if (action === "list") {
5839
5893
  const { workers } = readWorkersSection();
5840
- const entries = Object.entries(workers.roles);
5841
- const text = (t) => typeof t === "string" ? t : typeof t === "object" && t !== null ? `${t.provider ?? "?"}${t.mcp?.length ? ` +mcp(${t.mcp.join(",")})` : ""}` : String(t);
5842
- return workerText(entries.length ? entries.map(([r, t]) => `${r}: ${text(t)}`).join("\n") : "no roles set");
5894
+ const entries = Object.entries(workers.classes);
5895
+ return workerText(entries.length ? entries.map(([cls, t]) => `${cls}: ${classTargetText(t)}`).join("\n") : "no classes set");
5843
5896
  }
5844
- if (!args.role) return workerError(/* @__PURE__ */ new Error("role is required for this action"));
5897
+ if (!args.class) return workerError(/* @__PURE__ */ new Error("class is required for this action"));
5845
5898
  if (action === "set") {
5846
- if (!args.target) return workerError(/* @__PURE__ */ new Error("set needs target provider[/fast]"));
5847
- setRole(args.role, args.target);
5848
- return workerText(`role ${args.role} ${args.target}`);
5899
+ const hasConstraints = args.max_cost_tier !== void 0 || args.require_tags !== void 0;
5900
+ if (!args.target && !hasConstraints) return workerError(/* @__PURE__ */ new Error("set needs target provider[/fast] and/or max_cost_tier/require_tags"));
5901
+ const target = args.target !== void 0 && !hasConstraints ? args.target : {
5902
+ ...args.target ? { provider: args.target.split("/")[0] } : {},
5903
+ ...args.max_cost_tier !== void 0 ? { maxCostTier: args.max_cost_tier } : {},
5904
+ ...args.require_tags !== void 0 ? { requireTags: args.require_tags } : {}
5905
+ };
5906
+ setClass(args.class, target);
5907
+ return workerText(`class ${args.class} → ${classTargetText(target)}`);
5849
5908
  }
5850
- unsetRole(args.role);
5851
- return workerText(`role ${args.role} removed`);
5909
+ unsetClass(args.class);
5910
+ return workerText(`class ${args.class} removed`);
5911
+ } catch (e) {
5912
+ return workerError(e);
5913
+ }
5914
+ });
5915
+ server.tool("worker_run", [
5916
+ "Start a worker (or a chain) and return its id immediately.",
5917
+ "",
5918
+ "This is the chat-side face of `pai worker run`: prompt is required,",
5919
+ "chain (e.g. \"draft,implement\") runs spec-first stages, class picks the",
5920
+ "provider (default: active). Check on it with worker_ps / worker_replay,",
5921
+ "talk to it with worker_say."
5922
+ ].join("\n"), {
5923
+ prompt: string().min(1).describe("The task (the -p value)."),
5924
+ chain: string().optional().describe("Comma-separated stages, e.g. draft,implement or draft,implement,review."),
5925
+ class: string().optional().describe("Task class (draft, implement, review, research, spotcheck, simple, complex, image)."),
5926
+ label: string().optional().describe("Short task label shown in worker_ps."),
5927
+ cwd: string().optional().describe("Working directory (default: here)."),
5928
+ allowed_tools: string().optional().describe("Comma-separated tool allowlist passed to the worker."),
5929
+ mcp: string().optional().describe("MCP servers/sets the worker may use (comma-separated).")
5930
+ }, async (args) => {
5931
+ try {
5932
+ const claudeArgs = [
5933
+ "-p",
5934
+ args.prompt,
5935
+ ...args.allowed_tools ? ["--allowedTools", args.allowed_tools] : []
5936
+ ];
5937
+ let startedIdResolve;
5938
+ let startupFailure = null;
5939
+ const idArrived = new Promise((resolve) => {
5940
+ startedIdResolve = resolve;
5941
+ });
5942
+ const background = async () => {
5943
+ if (args.chain) return runChain({
5944
+ stages: args.chain.split(","),
5945
+ className: args.class,
5946
+ label: args.label,
5947
+ noPane: false,
5948
+ mcpFlag: args.mcp,
5949
+ brief: args.prompt,
5950
+ claudeArgs,
5951
+ ...args.cwd ? { cwd: args.cwd } : {},
5952
+ onChainStart: (id) => startedIdResolve(id),
5953
+ quiet: true
5954
+ });
5955
+ return runWorker({
5956
+ className: args.class,
5957
+ label: args.label,
5958
+ noPane: false,
5959
+ mcpFlag: args.mcp,
5960
+ claudeArgs,
5961
+ ...args.cwd ? { cwd: args.cwd } : {},
5962
+ onWorkerStart: (wid) => startedIdResolve(wid),
5963
+ quiet: true
5964
+ });
5965
+ };
5966
+ background().catch((e) => {
5967
+ startupFailure = e;
5968
+ startedIdResolve("");
5969
+ });
5970
+ const id = await Promise.race([idArrived, new Promise((_, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error("worker did not start within 15s")), 15e3))]);
5971
+ if (!id) return workerError(startupFailure ?? /* @__PURE__ */ new Error("worker failed to start"));
5972
+ return workerText(`${id} started${args.chain ? ` (chain: ${args.chain})` : ""} — check on it with worker_ps, worker_replay, worker_say`);
5852
5973
  } catch (e) {
5853
5974
  return workerError(e);
5854
5975
  }