kijito-tools 0.2.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.
Files changed (104) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +6 -0
  3. package/README.md +220 -0
  4. package/bin/cli.js +34 -0
  5. package/install.sh +161 -0
  6. package/package.json +50 -0
  7. package/providers/_shared/wake-core.mjs +358 -0
  8. package/providers/claude/CLAUDE.md.snippet +31 -0
  9. package/providers/claude/install.sh +186 -0
  10. package/providers/claude/scripts/arm-session.sh +66 -0
  11. package/providers/claude/scripts/claude-armed.sh +101 -0
  12. package/providers/claude/scripts/heartbeat-watchdog.sh +209 -0
  13. package/providers/claude/scripts/inbox-selftest.sh +277 -0
  14. package/providers/claude/scripts/kijito-persona-lib.sh +94 -0
  15. package/providers/claude/scripts/kijito-qa-pass.sh +12 -0
  16. package/providers/claude/scripts/lifecycle-lib.sh +167 -0
  17. package/providers/claude/scripts/myctx.sh +46 -0
  18. package/providers/claude/scripts/self-clear.sh +134 -0
  19. package/providers/claude/scripts/session-autosend.sh +85 -0
  20. package/providers/claude/scripts/session-catchup-hint.sh +330 -0
  21. package/providers/claude/scripts/statusline-context.sh +67 -0
  22. package/providers/claude/skills/kijito-qa-memory/SKILL.md +110 -0
  23. package/providers/claude/skills/kijito-recall/SKILL.md +53 -0
  24. package/providers/claude/skills/kijito-start/SKILL.md +122 -0
  25. package/providers/claude/wiring/README.md +93 -0
  26. package/providers/claude/wiring/kijito-heartbeat@.service +21 -0
  27. package/providers/codex/README.md +75 -0
  28. package/providers/codex/codex-kijito-parity-plan.md +265 -0
  29. package/providers/codex/docs-codex-setup.md +64 -0
  30. package/providers/codex/install.mjs +184 -0
  31. package/providers/codex/n0-capability-probe-protocol.md +472 -0
  32. package/providers/codex/n0-harness/README.md +57 -0
  33. package/providers/codex/n0-harness/cli.mjs +58 -0
  34. package/providers/codex/n0-harness/evidence-manifest.mjs +77 -0
  35. package/providers/codex/n0-harness/fixture.mjs +279 -0
  36. package/providers/codex/n0-harness/lib.mjs +141 -0
  37. package/providers/codex/n0-harness/manifest.mjs +36 -0
  38. package/providers/codex/n0-harness/oracle.mjs +464 -0
  39. package/providers/codex/n0-harness/parser.mjs +200 -0
  40. package/providers/codex/n0-harness/prompt.mjs +43 -0
  41. package/providers/codex/n0-harness/snapshot.mjs +120 -0
  42. package/providers/codex/n0-harness/specimen.mjs +37 -0
  43. package/providers/codex/notify/kijito-notify-count.mjs +117 -0
  44. package/providers/codex/notify/kijito-notify-count.test.mjs +76 -0
  45. package/providers/codex/plans/gate4-battery-log.md +184 -0
  46. package/providers/codex/plans/gate4-battery-protocol.md +43 -0
  47. package/providers/codex/plans/hive-user-first-plan-QA.md +34 -0
  48. package/providers/codex/plans/hive-user-first-plan.md +247 -0
  49. package/providers/codex/release-manifest.json +53 -0
  50. package/providers/codex/same-chat-continuation-plan-gate.md +235 -0
  51. package/providers/codex/same-chat-continuation-plan.md +458 -0
  52. package/providers/codex/skills/kijito-qa-memory/SKILL.md +193 -0
  53. package/providers/codex/skills/kijito-qa-memory/agents/openai.yaml +4 -0
  54. package/providers/codex/skills/kijito-recall/SKILL.md +53 -0
  55. package/providers/codex/skills/kijito-start/SKILL.md +173 -0
  56. package/providers/codex/skills/kijito-start/agents/openai.yaml +4 -0
  57. package/providers/codex/test/n0-cli-refusal.test.mjs +31 -0
  58. package/providers/codex/test/n0-counterexample-matrix.test.mjs +118 -0
  59. package/providers/codex/test/n0-guard-census-core.mjs +1372 -0
  60. package/providers/codex/test/n0-guard-census.json +35479 -0
  61. package/providers/codex/test/n0-guard-census.mjs +31 -0
  62. package/providers/codex/test/n0-guard-census.test.mjs +374 -0
  63. package/providers/codex/test/n0-guard-counterexamples.mjs +1068 -0
  64. package/providers/codex/test/n0-guard-independent-projection.mjs +70 -0
  65. package/providers/codex/test/n0-guard-independent-projection.test.mjs +53 -0
  66. package/providers/codex/test/n0-guard-manifest-author.mjs +424 -0
  67. package/providers/codex/test/n0-guard-mutation-operators.test.mjs +244 -0
  68. package/providers/codex/test/n0-guard-mutation-runner.mjs +599 -0
  69. package/providers/codex/test/n0-guard-remainder-classes.test.mjs +314 -0
  70. package/providers/codex/test/n0-guard-sanitizer-pairs.test.mjs +68 -0
  71. package/providers/codex/test/n0-guard-shared-context-predicates.test.mjs +84 -0
  72. package/providers/codex/test/n0-harness.test.mjs +553 -0
  73. package/providers/codex/test/n0-notable-regressions.test.mjs +70 -0
  74. package/providers/codex/test/n0-subject-boundary.mjs +97 -0
  75. package/providers/codex/test/n0-subject-boundary.test.mjs +72 -0
  76. package/providers/codex/test/same-chat-plan-preflight.sh +167 -0
  77. package/providers/codex/tools/refresh-manifest.mjs +81 -0
  78. package/providers/codex/wake-helper/TRANSPORT-NOTES.md +25 -0
  79. package/providers/codex/wake-helper/integration.test.mjs +285 -0
  80. package/providers/codex/wake-helper/kijito-wake-helper.mjs +488 -0
  81. package/providers/codex/wake-helper/kijito-wake-helper.test.mjs +128 -0
  82. package/providers/codex/wake-helper/mock-daemon.mjs +64 -0
  83. package/providers/codex/wake-helper/status-probe.mjs +45 -0
  84. package/providers/codex/wake-helper/ws-uds.mjs +153 -0
  85. package/providers/monitor/.github/workflows/publish-npm.yml +38 -0
  86. package/providers/monitor/.github/workflows/publish-pypi.yml +30 -0
  87. package/providers/monitor/CHANGELOG.md +465 -0
  88. package/providers/monitor/IMPORT-PROVENANCE.md +68 -0
  89. package/providers/monitor/LICENSE +202 -0
  90. package/providers/monitor/NOTICE +7 -0
  91. package/providers/monitor/OPAQUE-OUTPUT-ENFORCEMENT.md +45 -0
  92. package/providers/monitor/README.md +571 -0
  93. package/providers/monitor/RELEASING.md +125 -0
  94. package/providers/monitor/arm-hive-monitor.sh +13 -0
  95. package/providers/monitor/bin/cli.js +43 -0
  96. package/providers/monitor/com.kijito.inbox-monitor.plist.template +59 -0
  97. package/providers/monitor/docs/DESIGN.md +984 -0
  98. package/providers/monitor/kijito-inbox-monitor@.service.template +95 -0
  99. package/providers/monitor/kijito_inbox_monitor.py +3666 -0
  100. package/providers/monitor/package.json +35 -0
  101. package/providers/monitor/pyproject.toml +42 -0
  102. package/providers/monitor/scripts/mutation-check.py +399 -0
  103. package/providers/monitor/scripts/prepublish-gate.sh +284 -0
  104. package/providers/monitor/test_kijito_monitor.py +5609 -0
@@ -0,0 +1,265 @@
1
+ # Codex Kijito parity: bounded C1-C4 release plan
2
+
3
+ Status: **WITHDRAWN — message 2630 disproved same-running-session wake; this historical plan does not authorize installation. See `same-chat-continuation-plan.md`.**
4
+
5
+ Date: 2026-07-28 (amended 2026-08-11; status corrected 2026-08-12)
6
+
7
+ > **STATUS CORRECTION (codex, 2026-08-12).** The header above described C3 as
8
+ > pre-implementation while the 2026-08-11 amendments below record shipped, certified code.
9
+ > Current reality, so no reader executes stale gates:
10
+ > - **C3's isolated-thread architecture (controller.mjs) is measured-RED and DEPRECATED** —
11
+ > replaced by the pane-wake tmux driver (same-session delivery, byte-certified, watchdogged).
12
+ > Its C3 gate items as originally written must NOT be picked up as work.
13
+ > - **Shipped since the header:** pane-wake delivery path, CSPRNG verification tokens,
14
+ > /api/send transport fix, three-outcome arm probe, M223 watchdog + `--heartbeat` argv,
15
+ > pane-state schema-1 pin (b47ba730, certified + live-swapped).
16
+ > - **Live successor gate:** PR #6 (`codex/same-chat-continuation-plan` @ c38648d) —
17
+ > act-on-mail machinery under two-consecutive-clean adversarial review (assay), merge order
18
+ > M224 → pin → PR #6.
19
+ > - **Still open from this plan:** the M223 kill-driver-and-observe-a-page operational
20
+ > acceptance (to run during the packaged re-arm), and C2/C4 as re-cut under PR #6's scope.
21
+ > - `release-manifest.json`'s `planSha256` is RECORDED PROVENANCE, not an install gate: the
22
+ > installer never verifies it, but the conformance suite DOES require it to match this file —
23
+ > so every deliberate edit to this document re-stamps `planSha256` in the same commit (a
24
+ > deliberate, reviewed act; refresh-manifest.mjs intentionally refuses to do it for you).
25
+ > (This bullet previously claimed the hash "records the PRIOR revision" — stale since the
26
+ > m224 re-stamp brought it back in sync; assay gate-7 note, fixed 2026-08-15.)
27
+ > - Under Jason's 2026-08-12 kijito-tools directive [[27367]], this provider tree becomes an
28
+ > installable peer provider; packaging work supersedes any remaining seat-local steps here.
29
+
30
+ Program lead: River. Codex owns the OpenAI/Codex surface. Jason's direct
31
+ instructions and platform safety policy remain controlling.
32
+
33
+ ## Exit condition
34
+
35
+ DONE means exactly four binary gates are green with retained evidence:
36
+
37
+ - C1: hosted memory and hive mail read/write/read-back.
38
+ - C2: two context-free cold boots reconstruct the same current task from
39
+ Kijito alone.
40
+ - C3: ordinary River mail wakes an idle Codex hive session twice, including
41
+ once after the Codex process is restarted.
42
+ - C4: one real native compaction occurs mid-task and the same task resumes
43
+ from Kijito without a human re-brief.
44
+
45
+ There is no fifth release category. A defect observed while executing one of
46
+ these gates is fixed and only that gate is rerun. Adjacent findings are filed
47
+ without widening this release.
48
+
49
+ ## Safety boundary inherited from the hook incident
50
+
51
+ This release never installs or invokes lifecycle hooks. In particular, no
52
+ `PreCompact`, `PostCompact`, `SessionStart`, `UserPromptSubmit`, `Stop`, or
53
+ `SessionEnd` hook may influence a turn or compaction. Kijito can degrade while
54
+ Codex continues; it can never veto native compaction or turn completion.
55
+
56
+ The release contains no `KeepAlive` LaunchAgent and never attaches automation
57
+ to the user's current Codex thread. It does not call `thread/inject_items`,
58
+ `thread/steer`, or inject transcript history. It never places a hive message
59
+ body in controller-authored input.
60
+
61
+ ## C1 result
62
+
63
+ C1 is green on Codex 0.145.0:
64
+
65
+ - the real Codex inbox was read;
66
+ - memory 22468 was read back through `kijito_get` and hosted REST with exact
67
+ content, persona, and project;
68
+ - Codex message 1451 was independently found in River's hosted REST inbox;
69
+ - a missing-memory exact assertion and a wrong-recipient assertion both went
70
+ red as required.
71
+
72
+ Gate instrumentation must inspect the JSON result body. Hosted
73
+ `GET /api/memory/<missing>` returns HTTP 200 with a not-found result string, so
74
+ HTTP status alone is not evidence.
75
+
76
+ ## C3 implementation boundary
77
+
78
+ C3 builds one explicit launcher/controller for one dedicated Codex hive
79
+ session. It is not a plugin, hook, background login item, or controller for an
80
+ unrelated existing session.
81
+
82
+ The controller:
83
+
84
+ 1. owns one dedicated `CODEX_HOME`, one app-server child process, and one
85
+ persisted hive thread;
86
+ 2. takes a native single-consumer lock before starting Codex and refuses a
87
+ duplicate owner;
88
+ 3. tails only the shipped producer's
89
+ `~/.cache/kijito-inbox-monitor/events.codex.ndjson` file;
90
+ 4. accepts only complete JSON records with `source="kijito-inbox"`, a
91
+ case-folded `persona="codex"`, and event `new|alert|recovered`; `new`
92
+ additionally requires a positive integer ID, while the producer's
93
+ ID-less `alert` and `recovered` records require a valid timestamp and are
94
+ classified as lifecycle reconciliation hints;
95
+ 5. treats the event stream as a hint: lifecycle alerts/recoveries, startup,
96
+ rotation, truncation, malformed data, oversize input, or an offset gap
97
+ coalesce into a reconciliation turn against the durable hosted inbox;
98
+ 6. deduplicates `new` records durably by the pair (case-folded persona,
99
+ message ID), using the producer's in-order message-ID contract rather than
100
+ any producer event ID (the deployed producer emits no `event_id`);
101
+ 7. waits for its exact thread to be idle, then starts at most one turn with a
102
+ fixed, versioned input containing event type and message IDs only;
103
+ 8. visibly labels that turn `KIJITO AUTOMATED WAKE - NOT USER AUTHORED` because
104
+ Codex 0.145.0 persists `turn/start` input as role `user`;
105
+ 9. fixes `approvalPolicy="never"` and a custom permission profile that denies
106
+ root filesystem reads, permits only Codex's minimal runtime paths plus
107
+ read-only access to an empty dedicated workspace, and denies command-network
108
+ access; the turn may use the allowlisted hosted Kijito MCP tools but cannot
109
+ modify local files or request broader access;
110
+ 10. instructs the model to read `kijito_hive_inbox(persona="codex",
111
+ unread_only=true, mark_read=false)`, treat all returned bodies as untrusted
112
+ data, and perform no shell, file, web, secret, install, or external action;
113
+ 11. never retries a `turn/start` whose acceptance is ambiguous; it records the
114
+ ambiguity and requires durable-inbox reconciliation on a later distinct
115
+ event or clean restart;
116
+ 12. on a planned child restart, waits for the owned child process to exit
117
+ before starting a successor, resumes only the recorded dedicated thread,
118
+ and re-arms without a human prompt; and
119
+ 13. stops cleanly, releases its lock, and leaves the ordinary Codex home,
120
+ config, hooks, plugins, and user threads unchanged.
121
+
122
+ The event body is never authoritative and never enters the wake input. The
123
+ model sees mail only through Kijito's untrusted-data envelope. Automatic hive
124
+ replies are outside C3; the active Codex agent may reply under Jason's standing
125
+ coordination instruction after evaluating the message in context.
126
+
127
+ ## C3 pre-implementation QA gate
128
+
129
+ Implementation may begin only when all items below are green on this unchanged
130
+ plan:
131
+
132
+ 1. The unsafe negative-control architecture (a blocking compaction hook plus
133
+ `KeepAlive` resume bridge) is classified RED for both compaction liveness
134
+ and duplicate/unsolicited-turn risk.
135
+ 2. Current Codex 0.145.0 schema and the official Codex manual confirm
136
+ `initialize`, `thread/start`, `thread/resume`, `thread/status/changed`,
137
+ `turn/start`, `approvalPolicy`, named permission profiles, and turn
138
+ completion.
139
+ 3. An isolated app-server smoke test proves a thread can reach idle and a fixed
140
+ controller turn can complete with hooks empty.
141
+ 4. The same smoke harness is mutation-checked: wrong thread ID, non-idle
142
+ injection, a wrong permission profile, and a fabricated successful
143
+ completion must each make it RED. It also places a canary outside the
144
+ dedicated workspace and proves an app-server command running under the
145
+ selected profile cannot read it; a readable canary is RED.
146
+ 5. Static plan review finds no lifecycle hook, LaunchAgent, current-thread
147
+ mutation, message-body injection, automatic retry after ambiguous delivery,
148
+ or compaction veto path.
149
+ 6. A real-stream predicate check proves the currently pinned producer's
150
+ ID-less `alert` and `recovered` records are accepted as reconciliation
151
+ hints while `new` still requires a positive integer message ID.
152
+
153
+ The frozen plan SHA-256 and the exact checklist result are retained before the
154
+ first implementation edit. Any plan edit resets this pre-implementation gate.
155
+
156
+ ## C3 release gate
157
+
158
+ Before live mail, deterministic tests must first demonstrate RED for malformed
159
+ JSON, wrong persona/source/event, missing or zero/negative/non-integer IDs on
160
+ `new`, missing/invalid timestamps on ID-less lifecycle records, oversize and
161
+ partial lines, rotation gaps, duplicate events, a held consumer lock, active
162
+ thread state, ambiguous acceptance, an attempted second send, wrong persisted
163
+ thread, and any forbidden lifecycle/current-thread token. A well-formed
164
+ ID-less `alert` and `recovered` record must each be accepted as reconciliation.
165
+
166
+ Then two live passes run on one unchanged artifact:
167
+
168
+ - pass 1: River sends ordinary mail while the dedicated hive thread is idle;
169
+ the monitor emits the matching `new` event, exactly one fixed wake turn
170
+ attributable to that message ID starts, the session reads the durable Codex
171
+ inbox, and the message surfaces;
172
+ - pass 2: the owned Codex child is restarted and positively observed exited,
173
+ the same thread is resumed and idle, River sends a second ordinary message,
174
+ and exactly one new wake turn attributable to the second message ID surfaces
175
+ it without a human prompt.
176
+
177
+ Lifecycle-only reconciliation turns are logged and allowed but do not count
178
+ for or against either mail-attribution assertion. If a lifecycle hint coalesces
179
+ with the target `new`, the one combined turn is attributable to the target ID;
180
+ no second triggering wake input containing that ID is allowed. Attributability
181
+ is determined solely by message IDs carried in the controller-authored
182
+ triggering wake input; inbox rows read or surfaced during a turn never make
183
+ that turn attributable.
184
+
185
+ No manual inbox call made by the gate controller can count as a wake.
186
+
187
+ ## C2 gate
188
+
189
+ After C3, update one unique Codex current-state memory with C1/C3 results and
190
+ the exact next action. Run two sequential, context-free `codex exec` boots in
191
+ isolated read-only environments with no hooks, skills, rules, conversation
192
+ fork, local task files, or prompt-provided pointer body. Each receives only the
193
+ instruction to use hosted Kijito as persona `codex`, project `Codex`, discover
194
+ the current-state record, and report the task, done/remaining work, exact next
195
+ step, and DONE-WHEN.
196
+
197
+ Both reports must select the same unique live record and agree with ground
198
+ truth. Missing, tied, truncated-but-unfetched, locally inferred, or materially
199
+ different reports are RED. A wrong-sentinel fixture must fail before the real
200
+ pair can pass.
201
+
202
+ ## C4 gate
203
+
204
+ Use the dedicated app-server hive thread and its ordinary native
205
+ `thread/compact/start` method. No hook participates. Before compaction, the
206
+ thread records one safe checkpoint in Kijito and begins a bounded task. The
207
+ gate proves a real `contextCompaction` item completed and token usage dropped.
208
+ After compaction, one fixed automated wake turn tells the session only to load
209
+ its hosted Codex current state; it must resume the exact bounded task and
210
+ complete its predeclared next step without a human re-brief.
211
+
212
+ The negative control omits the Kijito checkpoint/sentinel and must fail to
213
+ identify the task. An ordinary completed turn, a summary without a native
214
+ compaction event, a hook event, a turn abort, a duplicate continuation, or a
215
+ different task is RED.
216
+
217
+ ## Ship boundary
218
+
219
+ On C1-C4 green, freeze and hash the controller, tests, this plan, and evidence;
220
+ run the complete deterministic suite twice on unchanged bytes; then ship the
221
+ explicit launcher and its documentation. Production installation may add only
222
+ the dedicated launcher/home named by the manifest. It may not alter the
223
+ ordinary `~/.codex/config.toml`, installed plugins, hook trust, model catalog,
224
+ context limits, or the user's current thread.
225
+
226
+ ---
227
+ ## Amendment — 2026-08-11: same-session pane wake NARROWLY supersedes the "no automation on the user's current thread" boundary
228
+
229
+ Authority: Jason's in-session direction (via codex msg 5695) + assay ruling 5717. The "Safety boundary inherited from the hook incident" above is SUPERSEDED for exactly one case: same-session wake delivery.
230
+
231
+ - The codex provider MAY deliver wake-core's fixedWakeText — event METADATA ONLY (the WAKE_PREFIX and a read-only inbox-peek instruction; NEVER a hive message body) — into the operator's send-time-verified live Codex pane via tmux (providers/codex/pane-wake.mjs).
232
+ - The injected text is EXACTLY that template PLUS one appended verification line of the fixed form `Wake-verification: <token>`. The token is 128 bits from the CSPRNG (`crypto.randomBytes(16)`, rendered as 32 lowercase hex characters and validated against `/^[0-9a-f]{32}$/` before it can be composed into the line), minted FRESH for every submission, never reused, and discarded when the confirmation phase for that submission ends. It is driver-generated randomness, not content: no event field, no pane content and no hive data can reach that line. The line exists so that the driver can prove the turn it consumes a message id for is ITS OWN — a constant marker is reproducible by anything rendered on screen, including a hive message body the wake itself asks the agent to summarise, and a per-submission random token is not.
233
+ - The injection-surface property is therefore unchanged and still holds: the only two things ever typed into the operator's pane are a closed template and a random token this driver minted.
234
+ - Everything else in the original boundary STANDS UNCHANGED: no thread/inject_items, no thread/steer, no transcript-history injection, never a hive message body in injected input, nothing starts at login.
235
+ - Rationale: the operator requires parity with the working Claude/OpenCode same-session wake; the isolated dedicated-thread approach (controller.mjs) delivered wakes into a thread the operator never saw (measured RED).
236
+
237
+ Alert transport and its arm-time probe:
238
+ - The bounded-silence alert is delivered in-process to `https://api.kijito.ai/api/send` with the body `{to, content, from}` — the REST route that twins the `kijito_hive_send` MCP tool, and the field names that route actually reads. The original bytes used the tool-shaped path `/api/hive/send` and a `persona` field; the route answered 404 to every alert the driver ever raised, and an unknown field would have re-attributed the alarm to the token identity rather than to codex.
239
+ - At arm time the driver PROBES that route with a GET (no body, so nothing can be sent), bounded at 4 seconds, carrying the same bearer the transport uses — this API authenticates BEFORE it routes, so an unauthenticated probe answers 401 for every path including paths that do not exist, and could not tell present from absent. The probe NEVER blocks arming; every outcome arms, and there are exactly three:
240
+ - NON-404 (405 / 401 / 2xx) → the route exists → arm normally, logged as verified present;
241
+ - 404 → the route is confirmed absent → arm and announce DEAD, in a line that cannot be misread as healthy;
242
+ - timeout / network error / any throw inside the probe → arm and announce UNVERIFIED, in a DISTINCT line. A failed observation is not an observed failure: collapsing "could not reach the server" into "the route is dead" teaches an operator that the DEAD line sometimes means nothing, and it then means nothing when it is true.
243
+ - The outcome is stamped on every bounded-silence alarm line, so an alert can never look healthier than the transport that carries it.
244
+
245
+ Liveness detection (M223) — a DETECTOR, and a narrow carve-out for how it is supervised:
246
+ - `providers/codex/pane-wake-watchdog.mjs` watches the driver's heartbeat and PAGES when the wake path has stopped. It is detection-only: it never starts, restarts, resumes, signals or types into anything, its own test asserts the file contains no spawn/exec/kill/tmux at all, and auto-restart is a separate registry row on purpose — a supervisor that re-arms a wake driver can re-arm it into a pane whose state nobody verified, which is exactly what the driver's send-time checks exist to prevent.
247
+ - The window is the driver's own measured cadence: the driver beats at most every 5s, a record is stale after 30s (max(30s, pollMs x 6)), the watchdog checks every 15s ⇒ a death is DETECTED AND PAGED within roughly 30-45s of the last beat. The lower bound is the staleness threshold (a driver between beats must not be paged); the upper bound is that threshold plus one check interval.
248
+ - It pages on stale / absent / dead-pid / unreadable, once per outage, re-arming the latch on recovery so a second outage pages again; the latch closes on a SUCCESSFUL send, not on the attempt. It does not page on `degraded` (beating, input path broken) because the driver alarms about that itself. It imports `readLiveness`, `hiveNoteBody` and `HIVE_SEND_URL` from the driver module rather than re-declaring them.
249
+ - `--heartbeat <installRoot>/runtime-pane/heartbeat.json` is now part of the STANDARD launch argv, so every future arm is watchable by construction; the status tool reads the same path from the same definition, with the pre-M223 location read as a fallback so an older arm is not reported as a false death.
250
+ - SUPERVISION CARVE-OUT, NARROW AND DELIBERATE: `forbiddenMechanisms` lists "LaunchAgent" and "KeepAlive", and that prohibition STANDS for anything that can start, resume, wake or type into a Codex session. It does not extend to a process that can do none of those things. `providers/codex/com.kijito.pane-wake-watchdog.plist` is therefore shipped as a TEMPLATE with placeholders: nothing installs it, nothing loads it, `doctor` still asserts the product installs no LaunchAgent of its own, and an operator who does not want the carve-out can simply delete the file — the watchdog runs under any supervisor, or in a tmux pane. Its KeepAlive resurrects the OBSERVER, never the driver.
251
+
252
+ **AMENDMENT (assay ruling, 2026-08-11, msg 5996) — forbiddenMechanisms is a PROPERTY, and a read-only liveness observer is permitted under a self-revoking guardrail.**
253
+
254
+ `forbiddenMechanisms` binds any mechanism that can START, RESUME, WAKE, or TYPE-INTO a Codex session (the hook-incident class). It is NOT a blanket launchd ban (the fleet's inbox-monitor is itself a launchd agent). A launchd-supervised READ-ONLY liveness OBSERVER (the M223 pane-wake-watchdog) is PERMITTED, contingent on ALL of:
255
+ (i) test-asserted to contain none of spawn/spawnSync/exec/execSync/execFile/process.kill/launchctl/send-keys/tmux (the forbidden-token assertion in pane-wake-watchdog.test.mjs);
256
+ (ii) it does nothing but read a liveness file and POST a fixed-shape hive page;
257
+ (iii) it holds no capability to restart/resume/type-into the driver or its pane; its KeepAlive resurrects the observer, never the driver;
258
+ (iv) the driver's OWN agent stays uninstalled — `doctor` continues to assert launchAgentInstalled:false for the wake path.
259
+ GUARDRAIL (load-bearing): this permission is TIED to the forbidden-token assertion remaining a gate. If that test is removed, or the watchdog gains any listed capability, the plist REVERTS to forbidden — the permission self-revokes the instant the read-only property breaks. The narrowing creates no standing hole.
260
+
261
+ - Not covered here: killing the driver and observing a page arrive is an operational, by-effect acceptance and is this row's done-when.
262
+
263
+ Reconciliation with the pre-implementation gate:
264
+ After issuing the submit keystroke the driver enters a BOUNDED confirmation phase: it OBSERVES, never re-issues blindly, until it positively confirms the turn started (→ consumes the id) OR positively observes the submit did not take (composer still holds our text → one bounded re-issue) OR hits a hard cap (→ alarms and abandons WITHOUT consuming the id, leaving recovery to the next reconcile). Positive confirmation means ONE thing: the composer is empty AND this submission's own fresh verification token is rendered above it. A live-turn indicator is corroborating evidence only and can never advance read-state, because a turn may start for reasons that have nothing to do with us. A persisted confirmation record older than the phase's own worst-case duration is refused on sight rather than confirmed against a screen it cannot be evidence about. A message id is consumed ONLY on positive confirmation; no blind resubmission of an id-bearing wake ever occurs.
265
+ ---
@@ -0,0 +1,64 @@
1
+ # Codex CLI × Kijito — setup (gate-2 docs block)
2
+
3
+ Staged replacement for the docs' current one-sentence "In testing" entry (plan §8 item 3;
4
+ Cursor-parity config example). Target: kijito.ai docs Codex section + kijito-tools codex README.
5
+
6
+ ## Memory (the product — one config block)
7
+
8
+ Either run:
9
+
10
+ ```bash
11
+ codex mcp add kijito --url https://api.kijito.ai/mcp
12
+ ```
13
+
14
+ (auto-detects OAuth: opens the authorize URL with a localhost callback), or add to
15
+ `~/.codex/config.toml` directly:
16
+
17
+ ```toml
18
+ [mcp_servers.kijito]
19
+ url = "https://api.kijito.ai/mcp"
20
+ # Bearer alternative to OAuth, if you have an API token:
21
+ # http_headers = { "Authorization" = "Bearer YOUR_KIJITO_API_TOKEN" }
22
+ ```
23
+
24
+ That is the whole install. Your agent now has persistent memory across sessions.
25
+
26
+ **Headless / scripted use — two sharp edges (plan §8):**
27
+ - `codex exec` silently cancels MCP tool calls that need approval ("user cancelled MCP
28
+ tool call"). Pass `--approve-for-me` (see `codex exec --help`; `--full-auto` is not an
29
+ exec flag as of 0.147).
30
+ - `codex mcp add` OAuth waits forever if no browser can open — on a headless box, use the
31
+ Bearer-token config block instead.
32
+
33
+ ## Skills (recommended)
34
+
35
+ Install the Codex skills (`kijito-start`, `kijito-qa-memory`) from kijito-tools
36
+ (`providers/codex/skills/`). `kijito-start` catches your session up at start — and, once
37
+ the live-wake feature is installed, joining the hive for a session is the same one command.
38
+
39
+ ## Hive mail for a session (how the producer runs — gate-3 measured default)
40
+
41
+ When a session joins the hive, `kijito-start` runs **one producer child owned by that
42
+ session** (zero install steps; measured 2.9s to armed). It watches your inbox while the
43
+ session lives and dies with it; if it ever dies early, that is loud inside your session,
44
+ never silent. When no session is running, mail simply waits in your durable inbox — the
45
+ next session catches up at start. Users who want **always-on capture** (events recorded
46
+ even with no session open) can instead do the one-line supervised install (systemd user
47
+ unit / launchd) documented in the monitor README — optional, and unnecessary for the
48
+ default experience.
49
+
50
+ ## Hive mail notifications (optional, count-only)
51
+
52
+ If you use hive mail and want a desktop notification with your unread count, wire Codex's
53
+ `notify` hook to the count shim:
54
+
55
+ ```toml
56
+ notify = ["node", "/path/to/kijito-tools/providers/codex/notify/kijito-notify-count.mjs",
57
+ "--persona", "YOUR_PERSONA", "--token-file", "/path/to/your/kijito_api_token"]
58
+ ```
59
+
60
+ Honest semantics: Codex fires `notify` on its own turn lifecycle, so this tells you your
61
+ unread count when Codex finishes work — it is not a mail-arrival alert. The notification
62
+ is always count-only ("Kijito: persona — N unread"); message content never appears in a
63
+ notification. Live mail-arrival wake for a running session is the separate opt-in
64
+ live-wake feature (plan gate 5).
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+ // Codex provider installer — post-gate-6 (2026-08-15) shape.
3
+ //
4
+ // The controller-era full install (headless app-server seat: controller, cli, pane-wake driver,
5
+ // mode-aware watchdog) was RETIRED at gate 6 of the hive-user-first plan §7 teardown protocol.
6
+ // Its installer is archived at legacy/codex-controller-era-2026-08/install-controller-era.mjs and
7
+ // its machinery teardown evidence lives on the operator seat at
8
+ // ~/.local/share/codex-kijito-hive/legacy/gate6-20260815T0643Z/. Nothing here installs a runtime,
9
+ // copies credentials, or touches ordinary Codex state.
10
+ //
11
+ // What remains is exactly the live surface:
12
+ //
13
+ // verify (default) the release gate. Every artifact release-manifest.json gates must
14
+ // exist with matching bytes, and every executable on the live user path must be
15
+ // gated. Both failure directions are LOUD: a gated-but-absent file and an
16
+ // ungated-but-shipped executable each fail the run with a named cause — a
17
+ // manifest gating absent files is a gate over nothing, and an ungated executable
18
+ // is code a user session runs with no integrity story (the gate-5 gating ruling,
19
+ // stated as a property).
20
+ // --skills-only deploy the provider's skills to ~/.codex/skills (idempotent, update-in-place —
21
+ // versioned prose, meant to be overwritten). Runs verify FIRST, so a checkout
22
+ // that fails its release gate never deploys skills from those bytes.
23
+ //
24
+ // The wake-helper itself is NOT installed anywhere: kijito-start's arm step runs it from the
25
+ // checkout, which is why its bytes (and its runtime import, the shared wake core) are gated here.
26
+
27
+ import { createHash } from "node:crypto";
28
+ import fs from "node:fs";
29
+ import os from "node:os";
30
+ import path from "node:path";
31
+ import { fileURLToPath } from "node:url";
32
+
33
+ const here = path.dirname(fileURLToPath(import.meta.url));
34
+
35
+ function sha256(file) {
36
+ return createHash("sha256").update(fs.readFileSync(file)).digest("hex");
37
+ }
38
+
39
+ function parseArgs(argv) {
40
+ const values = {};
41
+ const flags = new Set(["skills-only", "verify"]);
42
+ const bare = new Set();
43
+ argv = argv.filter((token) => {
44
+ const isFlag = token.startsWith("--") && flags.has(token.slice(2));
45
+ if (isFlag) bare.add(token.slice(2));
46
+ return !isFlag;
47
+ });
48
+ for (let index = 0; index < argv.length; index += 2) {
49
+ const key = argv[index];
50
+ if (!key?.startsWith("--") || argv[index + 1] === undefined) {
51
+ // The controller-era installer took a dozen paired options (--install-root, --launcher,
52
+ // --auth-source, …). Naming the retirement beats a bare "invalid argument" for anyone
53
+ // holding an old command line.
54
+ throw new Error(`invalid or retired argument ${key ?? ""} — the controller-era full install was retired at gate 6 (2026-08-15); see legacy/codex-controller-era-2026-08/`);
55
+ }
56
+ if (!["skills-root", "source-root"].includes(key.slice(2))) {
57
+ throw new Error(`invalid or retired argument ${key} — the controller-era full install was retired at gate 6 (2026-08-15); see legacy/codex-controller-era-2026-08/`);
58
+ }
59
+ values[key.slice(2)] = argv[index + 1];
60
+ }
61
+ const home = os.homedir();
62
+ const expand = (value) => path.resolve(value.replace(/^~(?=\/|$)/, home));
63
+ return {
64
+ sourceRoot: expand(values["source-root"] ?? here),
65
+ skillsRoot: expand(values["skills-root"] ?? path.join(home, ".codex", "skills")),
66
+ skillsOnly: bare.has("skills-only"),
67
+ verifyOnly: bare.has("verify"),
68
+ };
69
+ }
70
+
71
+ // The live gated set. Keys are the manifest's artifact names; paths are relative to the provider
72
+ // root. This map, refresh-manifest.mjs's GATED map, and the manifest's `artifacts` block must
73
+ // agree — verify() fails loud on any drift among the three.
74
+ function liveArtifacts(sourceRoot) {
75
+ return {
76
+ wakeCoreSha256: path.join(sourceRoot, "..", "_shared", "wake-core.mjs"),
77
+ // workflowSha256 is deliberately NOT here: the CI workflow is a repo-side gate (what CI
78
+ // executes), checked by refresh-manifest.mjs --check via the conformance test. Built payloads
79
+ // do not ship .github/, so gating it here would fail every packaged install on a file that is
80
+ // correct to be absent.
81
+ wakeHelperSha256: path.join(sourceRoot, "wake-helper", "kijito-wake-helper.mjs"),
82
+ wsUdsSha256: path.join(sourceRoot, "wake-helper", "ws-uds.mjs"),
83
+ wakeHelperTestsSha256: path.join(sourceRoot, "wake-helper", "kijito-wake-helper.test.mjs"),
84
+ wakeHelperIntegrationTestsSha256: path.join(sourceRoot, "wake-helper", "integration.test.mjs"),
85
+ wakeHelperMockDaemonSha256: path.join(sourceRoot, "wake-helper", "mock-daemon.mjs"),
86
+ };
87
+ }
88
+
89
+ // Executables on the live user path that are deliberately NOT gated, by ruling: instruments and
90
+ // docs a user session is never caused to run. Anything else executable found beside the gated
91
+ // files fails verify.
92
+ const UNGATED_ALLOWLIST = new Set(["status-probe.mjs"]);
93
+
94
+ function verify({ sourceRoot }) {
95
+ const manifestFile = path.join(sourceRoot, "release-manifest.json");
96
+ const release = JSON.parse(fs.readFileSync(manifestFile, "utf8"));
97
+ if (release.schema !== 1 || release.product !== "codex-kijito-hive") {
98
+ throw new Error("invalid source release manifest");
99
+ }
100
+ const gated = liveArtifacts(sourceRoot);
101
+ const checked = [];
102
+ // Direction one: everything the manifest gates must exist and match. A missing file surfaces as
103
+ // its own named error, not a generic ENOENT — a manifest gating absent files is the exact
104
+ // intermediate state the gate-6 one-commit rule forbids.
105
+ for (const [key, file] of Object.entries(gated)) {
106
+ const expected = release.artifacts?.[key];
107
+ if (typeof expected !== "string" || !/^[0-9a-f]{64}$/.test(expected)) {
108
+ throw new Error(`manifest does not gate ${key} — live artifact with no gate entry`);
109
+ }
110
+ let actual;
111
+ try {
112
+ actual = sha256(file);
113
+ } catch (error) {
114
+ if (error.code === "ENOENT") throw new Error(`manifest gates an ABSENT file: ${key} -> ${path.relative(sourceRoot, file)}`);
115
+ throw error;
116
+ }
117
+ if (actual !== expected) {
118
+ throw new Error(`${key} differs from gated hash (${path.relative(sourceRoot, file)})`);
119
+ }
120
+ checked.push(key);
121
+ }
122
+ // The manifest must not gate entries this verifier does not check — a stale extra entry would
123
+ // read as covered while nothing verifies it.
124
+ for (const key of Object.keys(release.artifacts ?? {})) {
125
+ if (key === "planSha256") continue; // recorded provenance, deliberately not a gate
126
+ if (key === "workflowSha256") continue; // repo-side gate: refresh-manifest --check owns it
127
+ if (!(key in gated)) {
128
+ throw new Error(`manifest gates ${key} but the live verifier has no such artifact — retired entries belong in legacyArtifacts`);
129
+ }
130
+ }
131
+ // Direction two: no executable ships ungated from the live directories.
132
+ const wakeHelperDir = path.join(sourceRoot, "wake-helper");
133
+ const gatedFiles = new Set(Object.values(gated).map((f) => fs.realpathSync(f)));
134
+ for (const entry of fs.readdirSync(wakeHelperDir, { withFileTypes: true })) {
135
+ if (!entry.isFile() || !entry.name.endsWith(".mjs")) continue;
136
+ if (UNGATED_ALLOWLIST.has(entry.name)) continue;
137
+ const file = fs.realpathSync(path.join(wakeHelperDir, entry.name));
138
+ if (!gatedFiles.has(file)) {
139
+ throw new Error(`ungated executable on the live path: wake-helper/${entry.name}`);
140
+ }
141
+ }
142
+ return { status: "VERIFIED", artifacts: checked.sort() };
143
+ }
144
+
145
+ // Deploy the provider's skills to the Codex skills directory. Idempotent and update-in-place on
146
+ // purpose: skills are versioned prose meant to be overwritten. (Carried unchanged from the
147
+ // controller-era installer — this path was always the live one.)
148
+ function installSkills({ sourceRoot, skillsRoot }) {
149
+ const source = path.join(sourceRoot, "skills");
150
+ const deployed = [];
151
+ let names = [];
152
+ try { names = fs.readdirSync(source, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort(); }
153
+ catch (error) { if (error.code === "ENOENT") return deployed; throw error; }
154
+ for (const name of names) {
155
+ const skillFile = path.join(source, name, "SKILL.md");
156
+ if (!fs.existsSync(skillFile)) continue;
157
+ const target = path.join(skillsRoot, name);
158
+ fs.mkdirSync(target, { recursive: true });
159
+ fs.writeFileSync(path.join(target, "SKILL.md"), fs.readFileSync(skillFile), { mode: 0o644 });
160
+ const files = ["SKILL.md"];
161
+ // The agents/ sidecar carries the Codex-surface interface metadata (display name, default
162
+ // prompt). A skill deployed without it loses its presentation, so it travels with the skill.
163
+ const sidecar = path.join(source, name, "agents", "openai.yaml");
164
+ if (fs.existsSync(sidecar)) {
165
+ fs.mkdirSync(path.join(target, "agents"), { recursive: true });
166
+ fs.writeFileSync(path.join(target, "agents", "openai.yaml"), fs.readFileSync(sidecar), { mode: 0o644 });
167
+ files.push("agents/openai.yaml");
168
+ }
169
+ deployed.push({ skill: name, target, files });
170
+ }
171
+ return deployed;
172
+ }
173
+
174
+ try {
175
+ const options = parseArgs(process.argv.slice(2));
176
+ const verified = verify(options);
177
+ const result = options.skillsOnly
178
+ ? { status: "SKILLS_INSTALLED", verified: verified.artifacts, skillsRoot: options.skillsRoot, skills: installSkills(options) }
179
+ : verified;
180
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
181
+ } catch (error) {
182
+ process.stderr.write(`${error.stack ?? error.message}\n`);
183
+ process.exitCode = 1;
184
+ }