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.
- package/LICENSE +201 -0
- package/NOTICE +6 -0
- package/README.md +220 -0
- package/bin/cli.js +34 -0
- package/install.sh +161 -0
- package/package.json +50 -0
- package/providers/_shared/wake-core.mjs +358 -0
- package/providers/claude/CLAUDE.md.snippet +31 -0
- package/providers/claude/install.sh +186 -0
- package/providers/claude/scripts/arm-session.sh +66 -0
- package/providers/claude/scripts/claude-armed.sh +101 -0
- package/providers/claude/scripts/heartbeat-watchdog.sh +209 -0
- package/providers/claude/scripts/inbox-selftest.sh +277 -0
- package/providers/claude/scripts/kijito-persona-lib.sh +94 -0
- package/providers/claude/scripts/kijito-qa-pass.sh +12 -0
- package/providers/claude/scripts/lifecycle-lib.sh +167 -0
- package/providers/claude/scripts/myctx.sh +46 -0
- package/providers/claude/scripts/self-clear.sh +134 -0
- package/providers/claude/scripts/session-autosend.sh +85 -0
- package/providers/claude/scripts/session-catchup-hint.sh +330 -0
- package/providers/claude/scripts/statusline-context.sh +67 -0
- package/providers/claude/skills/kijito-qa-memory/SKILL.md +110 -0
- package/providers/claude/skills/kijito-recall/SKILL.md +53 -0
- package/providers/claude/skills/kijito-start/SKILL.md +122 -0
- package/providers/claude/wiring/README.md +93 -0
- package/providers/claude/wiring/kijito-heartbeat@.service +21 -0
- package/providers/codex/README.md +75 -0
- package/providers/codex/codex-kijito-parity-plan.md +265 -0
- package/providers/codex/docs-codex-setup.md +64 -0
- package/providers/codex/install.mjs +184 -0
- package/providers/codex/n0-capability-probe-protocol.md +472 -0
- package/providers/codex/n0-harness/README.md +57 -0
- package/providers/codex/n0-harness/cli.mjs +58 -0
- package/providers/codex/n0-harness/evidence-manifest.mjs +77 -0
- package/providers/codex/n0-harness/fixture.mjs +279 -0
- package/providers/codex/n0-harness/lib.mjs +141 -0
- package/providers/codex/n0-harness/manifest.mjs +36 -0
- package/providers/codex/n0-harness/oracle.mjs +464 -0
- package/providers/codex/n0-harness/parser.mjs +200 -0
- package/providers/codex/n0-harness/prompt.mjs +43 -0
- package/providers/codex/n0-harness/snapshot.mjs +120 -0
- package/providers/codex/n0-harness/specimen.mjs +37 -0
- package/providers/codex/notify/kijito-notify-count.mjs +117 -0
- package/providers/codex/notify/kijito-notify-count.test.mjs +76 -0
- package/providers/codex/plans/gate4-battery-log.md +184 -0
- package/providers/codex/plans/gate4-battery-protocol.md +43 -0
- package/providers/codex/plans/hive-user-first-plan-QA.md +34 -0
- package/providers/codex/plans/hive-user-first-plan.md +247 -0
- package/providers/codex/release-manifest.json +53 -0
- package/providers/codex/same-chat-continuation-plan-gate.md +235 -0
- package/providers/codex/same-chat-continuation-plan.md +458 -0
- package/providers/codex/skills/kijito-qa-memory/SKILL.md +193 -0
- package/providers/codex/skills/kijito-qa-memory/agents/openai.yaml +4 -0
- package/providers/codex/skills/kijito-recall/SKILL.md +53 -0
- package/providers/codex/skills/kijito-start/SKILL.md +173 -0
- package/providers/codex/skills/kijito-start/agents/openai.yaml +4 -0
- package/providers/codex/test/n0-cli-refusal.test.mjs +31 -0
- package/providers/codex/test/n0-counterexample-matrix.test.mjs +118 -0
- package/providers/codex/test/n0-guard-census-core.mjs +1372 -0
- package/providers/codex/test/n0-guard-census.json +35479 -0
- package/providers/codex/test/n0-guard-census.mjs +31 -0
- package/providers/codex/test/n0-guard-census.test.mjs +374 -0
- package/providers/codex/test/n0-guard-counterexamples.mjs +1068 -0
- package/providers/codex/test/n0-guard-independent-projection.mjs +70 -0
- package/providers/codex/test/n0-guard-independent-projection.test.mjs +53 -0
- package/providers/codex/test/n0-guard-manifest-author.mjs +424 -0
- package/providers/codex/test/n0-guard-mutation-operators.test.mjs +244 -0
- package/providers/codex/test/n0-guard-mutation-runner.mjs +599 -0
- package/providers/codex/test/n0-guard-remainder-classes.test.mjs +314 -0
- package/providers/codex/test/n0-guard-sanitizer-pairs.test.mjs +68 -0
- package/providers/codex/test/n0-guard-shared-context-predicates.test.mjs +84 -0
- package/providers/codex/test/n0-harness.test.mjs +553 -0
- package/providers/codex/test/n0-notable-regressions.test.mjs +70 -0
- package/providers/codex/test/n0-subject-boundary.mjs +97 -0
- package/providers/codex/test/n0-subject-boundary.test.mjs +72 -0
- package/providers/codex/test/same-chat-plan-preflight.sh +167 -0
- package/providers/codex/tools/refresh-manifest.mjs +81 -0
- package/providers/codex/wake-helper/TRANSPORT-NOTES.md +25 -0
- package/providers/codex/wake-helper/integration.test.mjs +285 -0
- package/providers/codex/wake-helper/kijito-wake-helper.mjs +488 -0
- package/providers/codex/wake-helper/kijito-wake-helper.test.mjs +128 -0
- package/providers/codex/wake-helper/mock-daemon.mjs +64 -0
- package/providers/codex/wake-helper/status-probe.mjs +45 -0
- package/providers/codex/wake-helper/ws-uds.mjs +153 -0
- package/providers/monitor/.github/workflows/publish-npm.yml +38 -0
- package/providers/monitor/.github/workflows/publish-pypi.yml +30 -0
- package/providers/monitor/CHANGELOG.md +465 -0
- package/providers/monitor/IMPORT-PROVENANCE.md +68 -0
- package/providers/monitor/LICENSE +202 -0
- package/providers/monitor/NOTICE +7 -0
- package/providers/monitor/OPAQUE-OUTPUT-ENFORCEMENT.md +45 -0
- package/providers/monitor/README.md +571 -0
- package/providers/monitor/RELEASING.md +125 -0
- package/providers/monitor/arm-hive-monitor.sh +13 -0
- package/providers/monitor/bin/cli.js +43 -0
- package/providers/monitor/com.kijito.inbox-monitor.plist.template +59 -0
- package/providers/monitor/docs/DESIGN.md +984 -0
- package/providers/monitor/kijito-inbox-monitor@.service.template +95 -0
- package/providers/monitor/kijito_inbox_monitor.py +3666 -0
- package/providers/monitor/package.json +35 -0
- package/providers/monitor/pyproject.toml +42 -0
- package/providers/monitor/scripts/mutation-check.py +399 -0
- package/providers/monitor/scripts/prepublish-gate.sh +284 -0
- 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
|
+
}
|