@tekmidian/pai 0.49.0 → 0.51.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.
- package/dist/{aibroker-client-D5NT0S1N.mjs → aibroker-client-Bu72yPKj.mjs} +1 -1
- package/dist/{aibroker-client-Bn9SnGMa.mjs → aibroker-client-CDIXmq-e.mjs} +1 -1
- package/dist/{aibroker-client-Bn9SnGMa.mjs.map → aibroker-client-CDIXmq-e.mjs.map} +1 -1
- package/dist/{chain-CNSa0A3G.mjs → chain-BBbVbWVv.mjs} +4 -4
- package/dist/{chain-CNSa0A3G.mjs.map → chain-BBbVbWVv.mjs.map} +1 -1
- package/dist/cli/index.mjs +14 -14
- package/dist/cli/program.mjs +14 -14
- package/dist/config-Bn9jpblO.mjs +653 -0
- package/dist/config-Bn9jpblO.mjs.map +1 -0
- package/dist/{config-DxMZjpvY.mjs → config-DYXARgk8.mjs} +180 -151
- package/dist/config-DYXARgk8.mjs.map +1 -0
- package/dist/config-RjeVGJ7K.mjs +3 -0
- package/dist/{context-handover-cache-B9NKSjwR.mjs → context-handover-cache-yOuK9RIk.mjs} +5 -5
- package/dist/{context-handover-cache-B9NKSjwR.mjs.map → context-handover-cache-yOuK9RIk.mjs.map} +1 -1
- package/dist/daemon/index.mjs +13 -12
- package/dist/daemon/index.mjs.map +1 -1
- package/dist/{daemon-DZ4Q0jrs.mjs → daemon-CBTQYtHF.mjs} +49 -30
- package/dist/daemon-CBTQYtHF.mjs.map +1 -0
- package/dist/daemon-Vf7pHnjR.mjs +25 -0
- package/dist/daemon-mcp/index.mjs +186 -23
- package/dist/daemon-mcp/index.mjs.map +1 -1
- package/dist/factory-Ba8j9K7q.mjs +3 -0
- package/dist/{factory-DNYAqjH0.mjs → factory-Bt7TWQje.mjs} +3 -3
- package/dist/{factory-DNYAqjH0.mjs.map → factory-Bt7TWQje.mjs.map} +1 -1
- package/dist/{fallback-IeR2FrFn.mjs → fallback-CIGspCSi.mjs} +260 -10
- package/dist/fallback-CIGspCSi.mjs.map +1 -0
- package/dist/hooks/capture-all-events.mjs +80 -8
- package/dist/hooks/capture-all-events.mjs.map +4 -4
- package/dist/hooks/context-compression-hook.mjs +54 -39
- package/dist/hooks/context-compression-hook.mjs.map +4 -4
- package/dist/hooks/post-compact-inject.mjs +54 -39
- package/dist/hooks/post-compact-inject.mjs.map +4 -4
- package/dist/hooks/route-agents-to-worker.mjs +204 -109
- package/dist/hooks/route-agents-to-worker.mjs.map +4 -4
- package/dist/hooks/whisper-rules.mjs +7613 -9
- package/dist/hooks/whisper-rules.mjs.map +4 -4
- package/dist/hooks/worker-proxy.mjs +204 -109
- package/dist/hooks/worker-proxy.mjs.map +4 -4
- package/dist/hooks/worker-status-line.mjs +211 -116
- package/dist/hooks/worker-status-line.mjs.map +4 -4
- package/dist/hooks/worker-supervision.mjs +208 -113
- package/dist/hooks/worker-supervision.mjs.map +4 -4
- package/dist/{kg-entity-BSjM5xZh.mjs → kg-entity-BSW7ra8i.mjs} +1 -1
- package/dist/{kg-entity-BSjM5xZh.mjs.map → kg-entity-BSW7ra8i.mjs.map} +1 -1
- package/dist/main-resolver-BTastpp4.mjs +7 -0
- package/dist/{main-resolver-DvYu9jbl.mjs → main-resolver-BmZPCmgj.mjs} +3 -3
- package/dist/{main-resolver-DvYu9jbl.mjs.map → main-resolver-BmZPCmgj.mjs.map} +1 -1
- package/dist/{planner-Cn8HmT7l.mjs → planner-BiH9msqq.mjs} +6 -6
- package/dist/{planner-Cn8HmT7l.mjs.map → planner-BiH9msqq.mjs.map} +1 -1
- package/dist/{program-D9-wfmkZ.mjs → program-srUeFUfW.mjs} +5806 -5615
- package/dist/program-srUeFUfW.mjs.map +1 -0
- package/dist/query-feedback-Bm6XvCmg.mjs +3 -0
- package/dist/{query-feedback-CYyOGi3y.mjs → query-feedback-CTs_jx6i.mjs} +1 -1
- package/dist/{query-feedback-CYyOGi3y.mjs.map → query-feedback-CTs_jx6i.mjs.map} +1 -1
- package/dist/{run-Cls0YGgV.mjs → run-TChOnMO_.mjs} +1388 -1183
- package/dist/run-TChOnMO_.mjs.map +1 -0
- package/dist/{server-Ch99xt5K.mjs → server-R9UkCn3v.mjs} +42 -3
- package/dist/server-R9UkCn3v.mjs.map +1 -0
- package/dist/session-keepalive-BuG3wvik.mjs +579 -0
- package/dist/session-keepalive-BuG3wvik.mjs.map +1 -0
- package/dist/skills/VaultContext/SKILL.md +1 -1
- package/dist/{state-DnYGRmS7.mjs → state-Ca0ltLgI.mjs} +1 -1
- package/dist/{state-PJrgfDLW.mjs → state-DwrsOoMi.mjs} +1 -1
- package/dist/{state-PJrgfDLW.mjs.map → state-DwrsOoMi.mjs.map} +1 -1
- package/dist/{tools-B77WHeVZ.mjs → tools-BEqiJt_j.mjs} +13 -13
- package/dist/{tools-B77WHeVZ.mjs.map → tools-BEqiJt_j.mjs.map} +1 -1
- package/dist/{tools-NnR_n-Ja.mjs → tools-BwsOTmjh.mjs} +2 -2
- package/dist/{wakeup-UrhiW-KU.mjs → wakeup-Yk5lQ0jb.mjs} +1 -1
- package/dist/{wakeup-UrhiW-KU.mjs.map → wakeup-Yk5lQ0jb.mjs.map} +1 -1
- package/dist/{work-queue-worker-BA5IDBwQ.mjs → work-queue-worker-B9zVWImP.mjs} +3 -3
- package/dist/{work-queue-worker-BA5IDBwQ.mjs.map → work-queue-worker-B9zVWImP.mjs.map} +1 -1
- package/dist/work-queue-worker-D-DnQ7oh.mjs +14 -0
- package/dist/{zettelkasten-BBjA7GcP.mjs → zettelkasten-DezbXghl.mjs} +2 -2
- package/dist/{zettelkasten-BBjA7GcP.mjs.map → zettelkasten-DezbXghl.mjs.map} +1 -1
- package/docs/cache-keepalive.md +138 -0
- package/docs/commands/README.md +7 -0
- package/docs/commands/audit.md +1 -1
- package/docs/commands/config.md +84 -0
- package/docs/commands/daemon.md +6 -0
- package/docs/commands/worker.md +37 -2
- package/docs/config.md +171 -0
- package/docs/model-catalogue.md +502 -0
- package/docs/provider-independence-details.md +1 -1
- package/docs/task-bus.md +1 -1
- package/docs/worker.md +33 -5
- package/docs/workers-config.md +105 -12
- package/package.json +1 -1
- package/src/hooks/ts/capture-all-events.test.ts +71 -0
- package/src/hooks/ts/capture-all-events.ts +10 -3
- package/src/hooks/ts/lib/context-fill.test.ts +124 -46
- package/src/hooks/ts/lib/context-fill.ts +57 -14
- package/src/hooks/ts/user-prompt/whisper-rules.test.ts +56 -1
- package/src/hooks/ts/user-prompt/whisper-rules.ts +24 -0
- package/statusline-command.sh +103 -16
- package/dist/config-DCu8THKi.mjs +0 -228
- package/dist/config-DCu8THKi.mjs.map +0 -1
- package/dist/config-DeqFqa1N.mjs +0 -3
- package/dist/config-DxMZjpvY.mjs.map +0 -1
- package/dist/daemon-CYpVy_Dw.mjs +0 -24
- package/dist/daemon-DZ4Q0jrs.mjs.map +0 -1
- package/dist/factory-47Qf_df7.mjs +0 -3
- package/dist/fallback-IeR2FrFn.mjs.map +0 -1
- package/dist/indexer-C5Le7XE7.mjs +0 -1
- package/dist/main-resolver-CnfnWa8C.mjs +0 -7
- package/dist/program-D9-wfmkZ.mjs.map +0 -1
- package/dist/query-feedback-C7rSfiXU.mjs +0 -3
- package/dist/run-Cls0YGgV.mjs.map +0 -1
- package/dist/server-Ch99xt5K.mjs.map +0 -1
- package/dist/work-queue-worker-DLM2wlqM.mjs +0 -14
package/docs/commands/config.md
CHANGED
|
@@ -15,6 +15,11 @@ pai config <subcommand> [options]
|
|
|
15
15
|
| Command | Description |
|
|
16
16
|
|---------|-------------|
|
|
17
17
|
| [`pai config path`](#pai-config-path) | Print the PAI_HOME namespace dir and each resolved per-user file |
|
|
18
|
+
| [`pai config yaml`](#pai-config-yaml) | Convert config.json → config.yaml (and voices.json → voices.yaml), with a short |
|
|
19
|
+
| [`pai config list`](#pai-config-list) | Print the main PAI config as YAML (secrets masked). Defaults to what the file explicitly sets. |
|
|
20
|
+
| [`pai config get <path>`](#pai-config-get-path) | Print one config value (dotted path, e.g. search.recencyBoostDays), masked if it looks like a secret. An object/array subtree prints as YAML; pass --json for JSON. |
|
|
21
|
+
| [`pai config set <path> <value>`](#pai-config-set-path-value) | Set one config value (dotted path). Value parsing: true/false, null, numbers, |
|
|
22
|
+
| [`pai config unset <path>`](#pai-config-unset-path) | Remove one config value (dotted path), reverting it to the built-in default |
|
|
18
23
|
| [`pai config migrate`](#pai-config-migrate) | Move config.json, workers.yaml, whisper-rules.md, advisor-mode.json, session-state/, |
|
|
19
24
|
|
|
20
25
|
### pai config path
|
|
@@ -22,6 +27,85 @@ pai config <subcommand> [options]
|
|
|
22
27
|
Print the PAI_HOME namespace dir and each resolved per-user file
|
|
23
28
|
|
|
24
29
|
|
|
30
|
+
### pai config yaml
|
|
31
|
+
|
|
32
|
+
Convert config.json → config.yaml (and voices.json → voices.yaml), with a short
|
|
33
|
+
|
|
34
|
+
# comment above every top-level section and any "_comment"/"_...Note" JSON-
|
|
35
|
+
workaround key re-attached as a real comment. Verifies the generated YAML
|
|
36
|
+
re-parses to the exact same data before writing anything; the JSON is then
|
|
37
|
+
renamed to <name>.migrated-<YYYY-MM-DD> (never deleted). Every writer keeps
|
|
38
|
+
working unmodified afterwards — see docs/config.md.
|
|
39
|
+
|
|
40
|
+
**Options**
|
|
41
|
+
|
|
42
|
+
| Option | Description | Default |
|
|
43
|
+
|--------|-------------|---------|
|
|
44
|
+
| `--dry-run` | Print the plan without writing anything | |
|
|
45
|
+
| `--force` | Regenerate config.yaml (or voices.yaml) even if it already exists | |
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### pai config list
|
|
49
|
+
|
|
50
|
+
Print the main PAI config as YAML (secrets masked). Defaults to what the file explicitly sets.
|
|
51
|
+
|
|
52
|
+
**Options**
|
|
53
|
+
|
|
54
|
+
| Option | Description | Default |
|
|
55
|
+
|--------|-------------|---------|
|
|
56
|
+
| `--all` | Include every default value, not just what the file sets | |
|
|
57
|
+
| `--json` | Print JSON instead of YAML | |
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
### pai config get <path>
|
|
61
|
+
|
|
62
|
+
Print one config value (dotted path, e.g. search.recencyBoostDays), masked if it looks like a secret. An object/array subtree prints as YAML; pass --json for JSON.
|
|
63
|
+
|
|
64
|
+
**Arguments**
|
|
65
|
+
|
|
66
|
+
| Argument | Kind |
|
|
67
|
+
|----------|------|
|
|
68
|
+
| `<path>` | required |
|
|
69
|
+
|
|
70
|
+
**Options**
|
|
71
|
+
|
|
72
|
+
| Option | Description | Default |
|
|
73
|
+
|--------|-------------|---------|
|
|
74
|
+
| `--json` | Print an object/array value as JSON instead of YAML | |
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
### pai config set <path> <value>
|
|
78
|
+
|
|
79
|
+
Set one config value (dotted path). Value parsing: true/false, null, numbers,
|
|
80
|
+
|
|
81
|
+
[...] / {...} as JSON, else a string. Creates config.yaml (from config.json,
|
|
82
|
+
if any) on first use, then writes comment-preserving.
|
|
83
|
+
|
|
84
|
+
**Arguments**
|
|
85
|
+
|
|
86
|
+
| Argument | Kind |
|
|
87
|
+
|----------|------|
|
|
88
|
+
| `<path>` | required |
|
|
89
|
+
| `<value>` | required |
|
|
90
|
+
|
|
91
|
+
**Options**
|
|
92
|
+
|
|
93
|
+
| Option | Description | Default |
|
|
94
|
+
|--------|-------------|---------|
|
|
95
|
+
| `--force` | Set an unknown top-level key, or a value of a different type than the default | |
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
### pai config unset <path>
|
|
99
|
+
|
|
100
|
+
Remove one config value (dotted path), reverting it to the built-in default
|
|
101
|
+
|
|
102
|
+
**Arguments**
|
|
103
|
+
|
|
104
|
+
| Argument | Kind |
|
|
105
|
+
|----------|------|
|
|
106
|
+
| `<path>` | required |
|
|
107
|
+
|
|
108
|
+
|
|
25
109
|
### pai config migrate
|
|
26
110
|
|
|
27
111
|
Move config.json, workers.yaml, whisper-rules.md, advisor-mode.json, session-state/,
|
package/docs/commands/daemon.md
CHANGED
|
@@ -20,6 +20,7 @@ pai daemon <subcommand> [options]
|
|
|
20
20
|
| [`pai daemon restart`](#pai-daemon-restart) | Send SIGTERM to the running daemon (launchd will restart it) |
|
|
21
21
|
| [`pai daemon install`](#pai-daemon-install) | Install daemon as a launchd service and update ~/.claude.json to use the shim |
|
|
22
22
|
| [`pai daemon uninstall`](#pai-daemon-uninstall) | Remove the launchd service and revert to direct MCP |
|
|
23
|
+
| [`pai daemon keepalive`](#pai-daemon-keepalive) | Show interactive-session cache keepalive config, per-session beat counters, and recent ledger lines |
|
|
23
24
|
| [`pai daemon logs`](#pai-daemon-logs) | Tail the daemon log (/tmp/pai-daemon.log) |
|
|
24
25
|
|
|
25
26
|
### pai daemon serve
|
|
@@ -58,6 +59,11 @@ Install daemon as a launchd service and update ~/.claude.json to use the shim
|
|
|
58
59
|
Remove the launchd service and revert to direct MCP
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
### pai daemon keepalive
|
|
63
|
+
|
|
64
|
+
Show interactive-session cache keepalive config, per-session beat counters, and recent ledger lines
|
|
65
|
+
|
|
66
|
+
|
|
61
67
|
### pai daemon logs
|
|
62
68
|
|
|
63
69
|
Tail the daemon log (/tmp/pai-daemon.log)
|
package/docs/commands/worker.md
CHANGED
|
@@ -44,6 +44,7 @@ pai worker <subcommand> [options]
|
|
|
44
44
|
| [`pai worker providers`](#pai-worker-providers) | Providers: list (default), add, remove, use, enable, disable, test |
|
|
45
45
|
| [`pai worker classes`](#pai-worker-classes) | Classes: which provider serves draft / implement / review / … |
|
|
46
46
|
| [`pai worker model [what] [model]`](#pai-worker-model-what-model) | Model ids per provider: no args lists them, |
|
|
47
|
+
| [`pai worker capability [name] [providers]`](#pai-worker-capability-name-providers) | Which provider(s) serve a capability (e.g. image), independent of any one |
|
|
47
48
|
| [`pai worker config`](#pai-worker-config) | workers.yaml itself: path, init, migrate, check, inline-keys |
|
|
48
49
|
|
|
49
50
|
### pai worker run [args...]
|
|
@@ -75,6 +76,11 @@ Grant MCP tools by naming mcp__server__tool in --allowedTools (the server loads
|
|
|
75
76
|
| `--provider <name>` | Provider to run on (default: active, else routing order) | |
|
|
76
77
|
| `--class <name>` | Use the provider of this class (draft, implement, review, research, spotcheck, simple, complex, image) | |
|
|
77
78
|
| `--role <name>` | Alias of --class (roles were renamed to classes) | |
|
|
79
|
+
| `--capability <name>` | Run on whichever configured provider serves this capability (see `pai worker capability`), not the active provider or --class's own one — e.g. --capability image runs an `engine: image` provider directly, POSTing a prompt and writing the PNG instead of spawning claude | |
|
|
80
|
+
| `--for <name>` | Alias of --capability | |
|
|
81
|
+
| `--out <file>` | Where an image-capability run writes its PNG (default: <logDir>/<worker-id>.png) | |
|
|
82
|
+
| `--size <WxH>` | Image size for an image-capability run, e.g. 1024x1024 (default: 1024x1024) | |
|
|
83
|
+
| `--timeout-ms <n>` | Timeout for an image-capability run's HTTP request (default: 120000) | |
|
|
78
84
|
| `--chain <stages>` | Comma-separated stage classes, e.g. draft,implement or draft,implement,review | |
|
|
79
85
|
| `--agent <name>` | Run the agent definition ~/.claude/agents/<name>.md on a worker | |
|
|
80
86
|
| `--model <model>` | Override the model for this run. Headless (-p) workers default to the --class model; an interactive launch (no -p) with no --model uses the harness default model from settings.json. | |
|
|
@@ -391,6 +397,8 @@ Example: pai worker providers add glm --url https://…/anthropic \
|
|
|
391
397
|
--key-file writes only a path to a 0600 file holding it — use one or the other.
|
|
392
398
|
OpenAI-protocol: --protocol openai --upstream-url https://…/v1 (runs via the PAI proxy).
|
|
393
399
|
Codex (ChatGPT plan): --engine codex — runs through the Codex CLI.
|
|
400
|
+
Image generation: --engine image — `pai worker run --capability image` then POSTs
|
|
401
|
+
straight to {url}/images/generations instead of spawning claude.
|
|
394
402
|
|
|
395
403
|
**Arguments**
|
|
396
404
|
|
|
@@ -412,7 +420,7 @@ Codex (ChatGPT plan): --engine codex — runs through the Codex CLI.
|
|
|
412
420
|
| `--note <text>` | Human note shown in `providers list` | |
|
|
413
421
|
| `--protocol <proto>` | anthropic (default) or openai — openai runs through the local PAI proxy | |
|
|
414
422
|
| `--upstream-url <url>` | Chat Completions base URL (required for --protocol openai) | |
|
|
415
|
-
| `--engine <engine>` | claude (default) or
|
|
423
|
+
| `--engine <engine>` | claude (default), codex, or image — codex runs `codex exec --json`, image POSTs {url}/images/generations | |
|
|
416
424
|
| `--context-window <tokens>` | Context window for the meter (default 200000; init event overrides) | |
|
|
417
425
|
| `--quota-probe <url>` | URL whose JSON first number is the quota percent (0-100) | |
|
|
418
426
|
| `--cost-tier <1-5>` | Cost tier 1 (cheapest) … 5 (most expensive; default 3) | |
|
|
@@ -543,7 +551,9 @@ Model ids per provider: no args lists them,
|
|
|
543
551
|
|
|
544
552
|
`model <model-id>` sets the active provider's default model (back-compat),
|
|
545
553
|
`model <capability>` shows one capability, `model <capability> <model-id>` sets it.
|
|
546
|
-
|
|
554
|
+
Capability names are open (default, fast, image, … — any ^[a-z][a-z0-9-]*$ name);
|
|
555
|
+
`pai worker capability` picks which provider serves one across the whole config.
|
|
556
|
+
--provider targets another provider.
|
|
547
557
|
|
|
548
558
|
**Arguments**
|
|
549
559
|
|
|
@@ -559,6 +569,31 @@ Capabilities: default, fast, image. --provider targets another provider.
|
|
|
559
569
|
| `--provider <name>` | Provider to read or change (default: the active one) | |
|
|
560
570
|
|
|
561
571
|
|
|
572
|
+
### pai worker capability [name] [providers]
|
|
573
|
+
|
|
574
|
+
Which provider(s) serve a capability (e.g. image), independent of any one
|
|
575
|
+
|
|
576
|
+
provider's own model table: no args lists every preference and what it
|
|
577
|
+
resolves to; `capability <name> <provider>[,<provider>…]` sets the
|
|
578
|
+
preference list (first usable one wins); `capability <name> --unset`
|
|
579
|
+
removes it. An `engine: image` provider preferred for "image" runs
|
|
580
|
+
`pai worker run --capability image` directly against its images API.
|
|
581
|
+
|
|
582
|
+
**Arguments**
|
|
583
|
+
|
|
584
|
+
| Argument | Kind |
|
|
585
|
+
|----------|------|
|
|
586
|
+
| `[name]` | optional |
|
|
587
|
+
| `[providers]` | optional |
|
|
588
|
+
|
|
589
|
+
**Options**
|
|
590
|
+
|
|
591
|
+
| Option | Description | Default |
|
|
592
|
+
|--------|-------------|---------|
|
|
593
|
+
| `--unset` | Remove the preference for <name> | |
|
|
594
|
+
| `--json` | Machine-readable output | |
|
|
595
|
+
|
|
596
|
+
|
|
562
597
|
### pai worker config
|
|
563
598
|
|
|
564
599
|
workers.yaml itself: path, init, migrate, check, inline-keys
|
package/docs/config.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# config.yaml
|
|
2
|
+
|
|
3
|
+
The main PAI config — everything `src/daemon/config.ts`'s `loadConfig()`
|
|
4
|
+
returns (search defaults, storage backend, notifications, task bus,
|
|
5
|
+
identity, the non-provider `workers` settings) plus whatever else a writer
|
|
6
|
+
has stashed in the file. Before 2026-09-20 this was JSON only, undocumented
|
|
7
|
+
in place, with no CLI or MCP surface to read or change a single value
|
|
8
|
+
without opening the file. `pai config yaml` converts it to YAML with
|
|
9
|
+
comments; `pai config list/get/set/unset` and the matching `config_*` MCP
|
|
10
|
+
tools work whether the file is YAML or JSON.
|
|
11
|
+
|
|
12
|
+
## File map
|
|
13
|
+
|
|
14
|
+
| File | Format | Read/written by |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `config.yaml` | YAML, comment-preserving | preferred whenever it exists — `readMainConfigRaw`/`writeMainConfigRaw` (`src/daemon/config.ts`) |
|
|
17
|
+
| `config.json` | JSON | fallback when no `config.yaml` exists; unchanged pre-2026-09-20 behaviour |
|
|
18
|
+
| `workers.yaml` | YAML, comment-preserving | `providers`/`classes`/`mcp_sets`/`active` only — see [docs/workers-config.md](workers-config.md) |
|
|
19
|
+
| `voices.yaml` | YAML, comment-preserving | preferred whenever it exists — `loadVoicesConfig`/`migrateVoicesToYaml` (`src/config/voices-config.ts`) |
|
|
20
|
+
| `voices.json` | JSON | fallback; currently an orphan (nothing in `src/` reads it) |
|
|
21
|
+
|
|
22
|
+
All five live under the PAI_HOME namespace dir (`~/.claude/pai` by default,
|
|
23
|
+
`PAI_HOME=<dir>` to move it; `pai config path` prints the resolved paths).
|
|
24
|
+
|
|
25
|
+
### Known external readers
|
|
26
|
+
|
|
27
|
+
`~/.claude/statusline-command.sh` (outside the repo) reads `workers.providers` from the config file using `jq` and therefore sees only JSON. Since `providers` moved into `workers.yaml` on 2026-09-19, that lookup now falls back to `anthropic` (a pre-existing default), which is fine — the file is for display and already treats missing providers gracefully.
|
|
28
|
+
|
|
29
|
+
**State files stay JSON** — they are written many times a second by the
|
|
30
|
+
daemon or hooks, never hand-edited, and gain nothing from comments:
|
|
31
|
+
`advisor-mode.json`, `scheduler-state.json`, `work-queue.json`,
|
|
32
|
+
`session-routing.json`, `summary-cooldowns.json`, `kg-backfill-state.json`,
|
|
33
|
+
`registry-scan.json`, `session-scan-cache.json`. Only the two human-facing
|
|
34
|
+
config files above became YAML.
|
|
35
|
+
|
|
36
|
+
## Migrating
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
pai config yaml --dry-run # print the plan, write nothing
|
|
40
|
+
pai config yaml # convert config.json → config.yaml (and
|
|
41
|
+
# voices.json → voices.yaml), each JSON file
|
|
42
|
+
# renamed to <name>.json.migrated-<YYYY-MM-DD>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
What changes on disk:
|
|
46
|
+
- A `#` comment is added above every top-level section, drawn from
|
|
47
|
+
`PaiDaemonConfig`'s doc comments (`MAIN_CONFIG_SECTION_COMMENTS` in
|
|
48
|
+
`src/config/main-config.ts`).
|
|
49
|
+
- Any `_comment` / `_...Note` JSON-comment-workaround key is folded into a
|
|
50
|
+
real `#` comment above the key it was documenting, then dropped from the
|
|
51
|
+
data.
|
|
52
|
+
- The generated YAML is re-parsed and checked byte-for-byte equal (as JSON)
|
|
53
|
+
to the original before anything is written — a mismatch aborts with
|
|
54
|
+
nothing touched.
|
|
55
|
+
- The JSON file is renamed to `config.json.migrated-<YYYY-MM-DD>` (never
|
|
56
|
+
deleted) only after that check passes.
|
|
57
|
+
- Idempotent: re-running `pai config yaml` once `config.yaml` exists is a
|
|
58
|
+
no-op unless you pass `--force` (which regenerates it from the current
|
|
59
|
+
JSON, if any).
|
|
60
|
+
|
|
61
|
+
Every existing writer (identity, notifications, obsidian vault path,
|
|
62
|
+
setup wizard, memory search settings, the workers section) keeps working
|
|
63
|
+
unmodified — they all go through `readMainConfigRaw`/`writeMainConfigRaw`,
|
|
64
|
+
which pick YAML over JSON automatically.
|
|
65
|
+
|
|
66
|
+
## Example `config.yaml`
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# Unix Domain Socket path for daemon IPC.
|
|
70
|
+
socketPath: /tmp/pai.sock
|
|
71
|
+
|
|
72
|
+
# How often the daemon re-indexes changed files, in seconds.
|
|
73
|
+
indexIntervalSecs: 300
|
|
74
|
+
|
|
75
|
+
# How often the daemon runs the embedding pass, in seconds.
|
|
76
|
+
embedIntervalSecs: 600
|
|
77
|
+
|
|
78
|
+
# Storage backend: "sqlite" (default) or "postgres".
|
|
79
|
+
storageBackend: sqlite
|
|
80
|
+
|
|
81
|
+
# PostgreSQL connection settings, used when storageBackend is "postgres".
|
|
82
|
+
postgres:
|
|
83
|
+
connectionString: postgresql://pai:pai@localhost:5432/pai_<username>
|
|
84
|
+
maxConnections: 5
|
|
85
|
+
connectionTimeoutMs: 5000
|
|
86
|
+
|
|
87
|
+
# Embedding model name, used for semantic/hybrid search.
|
|
88
|
+
embeddingModel: Snowflake/snowflake-arctic-embed-m-v1.5
|
|
89
|
+
|
|
90
|
+
# Daemon log level: debug, info, warn, or error.
|
|
91
|
+
logLevel: info
|
|
92
|
+
|
|
93
|
+
# Search defaults, applied when an MCP tool or CLI call doesn't specify one.
|
|
94
|
+
search:
|
|
95
|
+
mode: keyword
|
|
96
|
+
rerank: true
|
|
97
|
+
recencyBoostDays: 90
|
|
98
|
+
defaultLimit: 10
|
|
99
|
+
snippetLength: 200
|
|
100
|
+
|
|
101
|
+
# Who "me" is — addresses that count as the user's own. Empty by default
|
|
102
|
+
# and never guessed: nothing is self-addressed until this is set.
|
|
103
|
+
identity:
|
|
104
|
+
selfEmails:
|
|
105
|
+
- you@example.com
|
|
106
|
+
deliverTo: you@example.com
|
|
107
|
+
sendingAccount: you@example.com
|
|
108
|
+
|
|
109
|
+
# Non-provider worker settings (pane, routing, tree, cache keepalive,
|
|
110
|
+
# fallback). Providers/classes/mcp_sets live in workers.yaml, not here.
|
|
111
|
+
workers:
|
|
112
|
+
enabled: true
|
|
113
|
+
cacheKeepaliveSecs: 15
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`postgres.connectionString` and anything under a `key`/`token`/`secret`/
|
|
117
|
+
`password` field is a secret: `pai config list`/`get` and `config_list`/
|
|
118
|
+
`config_get` always print it masked (`****<last4>`), and the file itself is
|
|
119
|
+
written mode `0600`.
|
|
120
|
+
|
|
121
|
+
## CLI
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
pai config path # resolved paths for every PAI_HOME file
|
|
125
|
+
pai config yaml [--dry-run] [--force] # convert config.json/voices.json → YAML
|
|
126
|
+
pai config list [--all] [--json] # file-set values as YAML (--all: defaults merged in too)
|
|
127
|
+
pai config get search.recencyBoostDays # one value, defaults-merged, masked if secret
|
|
128
|
+
pai config set search.recencyBoostDays 45 # writes config.yaml, comment-preserving
|
|
129
|
+
pai config set some.newThing 1 --force # allow an unknown top-level key / type mismatch
|
|
130
|
+
pai config unset search.recencyBoostDays # revert to the built-in default
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`pai config set` value parsing: `true`/`false`, `null`, bare numbers,
|
|
134
|
+
`[...]`/`{...}` parsed as JSON, everything else a plain string. `set`
|
|
135
|
+
refuses an unknown top-level key, or a value whose type disagrees with the
|
|
136
|
+
built-in default in `DEFAULTS`, unless `--force`.
|
|
137
|
+
|
|
138
|
+
Example session:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
$ pai config set search.recencyBoostDays 45
|
|
142
|
+
Set search.recencyBoostDays = 45
|
|
143
|
+
|
|
144
|
+
$ pai config get search.recencyBoostDays
|
|
145
|
+
45
|
|
146
|
+
|
|
147
|
+
$ pai config list
|
|
148
|
+
search:
|
|
149
|
+
mode: keyword
|
|
150
|
+
rerank: true
|
|
151
|
+
recencyBoostDays: 45
|
|
152
|
+
defaultLimit: 10
|
|
153
|
+
snippetLength: 200
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## MCP tools
|
|
157
|
+
|
|
158
|
+
On the `pai` MCP server (`src/daemon-mcp/index.ts`), backed by the same
|
|
159
|
+
`src/config/main-config-ops.ts` functions the CLI calls — masking and
|
|
160
|
+
validation cannot drift between the two surfaces.
|
|
161
|
+
|
|
162
|
+
- **`config_list`** — `{ "all": false, "json": false }` → the file-set config
|
|
163
|
+
as YAML (or JSON), secrets masked.
|
|
164
|
+
- **`config_get`** — `{ "path": "search.recencyBoostDays" }` → the
|
|
165
|
+
defaults-merged value, masked if the path looks like a secret.
|
|
166
|
+
- **`config_set`** — `{ "path": "search.recencyBoostDays", "value": "45" }`
|
|
167
|
+
→ **writes config.yaml** (creating it from config.json first if neither
|
|
168
|
+
exists yet), comment-preserving. Add `"force": true` to bypass the
|
|
169
|
+
unknown-key/type checks.
|
|
170
|
+
- **`config_unset`** — `{ "path": "search.recencyBoostDays" }` → **writes
|
|
171
|
+
the file**, removing the key so it reverts to the built-in default.
|