@pleri/olam-cli 0.1.147 → 0.1.150

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 (137) hide show
  1. package/dist/agent-stream/agent-sdk-to-chunks.js +276 -0
  2. package/dist/agent-stream/agent-stream-launch.js +348 -0
  3. package/dist/agent-stream/chunks-subscriber-transport.js +262 -0
  4. package/dist/agent-stream/codex-runner.js +188 -0
  5. package/dist/agent-stream/driver-runner.js +347 -0
  6. package/dist/agent-stream/operator-subscription.js +179 -0
  7. package/dist/commands/auth.d.ts.map +1 -1
  8. package/dist/commands/auth.js +26 -1
  9. package/dist/commands/auth.js.map +1 -1
  10. package/dist/commands/create.d.ts.map +1 -1
  11. package/dist/commands/create.js +39 -0
  12. package/dist/commands/create.js.map +1 -1
  13. package/dist/commands/doctor.d.ts +54 -3
  14. package/dist/commands/doctor.d.ts.map +1 -1
  15. package/dist/commands/doctor.js +348 -6
  16. package/dist/commands/doctor.js.map +1 -1
  17. package/dist/commands/init.d.ts +46 -0
  18. package/dist/commands/init.d.ts.map +1 -1
  19. package/dist/commands/init.js +90 -0
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/kg-build.d.ts +23 -0
  22. package/dist/commands/kg-build.d.ts.map +1 -1
  23. package/dist/commands/kg-build.js +104 -2
  24. package/dist/commands/kg-build.js.map +1 -1
  25. package/dist/commands/restart.d.ts +18 -0
  26. package/dist/commands/restart.d.ts.map +1 -0
  27. package/dist/commands/restart.js +113 -0
  28. package/dist/commands/restart.js.map +1 -0
  29. package/dist/commands/services.d.ts +41 -3
  30. package/dist/commands/services.d.ts.map +1 -1
  31. package/dist/commands/services.js +221 -13
  32. package/dist/commands/services.js.map +1 -1
  33. package/dist/commands/setup-linux-gate.d.ts +26 -0
  34. package/dist/commands/setup-linux-gate.d.ts.map +1 -0
  35. package/dist/commands/setup-linux-gate.js +42 -0
  36. package/dist/commands/setup-linux-gate.js.map +1 -0
  37. package/dist/commands/setup-metrics.d.ts +26 -0
  38. package/dist/commands/setup-metrics.d.ts.map +1 -0
  39. package/dist/commands/setup-metrics.js +57 -0
  40. package/dist/commands/setup-metrics.js.map +1 -0
  41. package/dist/commands/setup-phase-5a-skill-source.d.ts +68 -0
  42. package/dist/commands/setup-phase-5a-skill-source.d.ts.map +1 -0
  43. package/dist/commands/setup-phase-5a-skill-source.js +196 -0
  44. package/dist/commands/setup-phase-5a-skill-source.js.map +1 -0
  45. package/dist/commands/setup-phase-5b-project-sweep.d.ts +38 -0
  46. package/dist/commands/setup-phase-5b-project-sweep.d.ts.map +1 -0
  47. package/dist/commands/setup-phase-5b-project-sweep.js +176 -0
  48. package/dist/commands/setup-phase-5b-project-sweep.js.map +1 -0
  49. package/dist/commands/setup.d.ts +19 -0
  50. package/dist/commands/setup.d.ts.map +1 -1
  51. package/dist/commands/setup.js +22 -0
  52. package/dist/commands/setup.js.map +1 -1
  53. package/dist/commands/skills-10x.d.ts +23 -0
  54. package/dist/commands/skills-10x.d.ts.map +1 -0
  55. package/dist/commands/skills-10x.js +308 -0
  56. package/dist/commands/skills-10x.js.map +1 -0
  57. package/dist/commands/substrate-audit-log.d.ts +2 -0
  58. package/dist/commands/substrate-audit-log.d.ts.map +1 -1
  59. package/dist/commands/substrate-audit-log.js +13 -0
  60. package/dist/commands/substrate-audit-log.js.map +1 -1
  61. package/dist/image-digests.json +7 -7
  62. package/dist/index.js +18102 -15234
  63. package/dist/index.js.map +1 -1
  64. package/dist/lib/auth-refresh-kubernetes.d.ts +62 -0
  65. package/dist/lib/auth-refresh-kubernetes.d.ts.map +1 -0
  66. package/dist/lib/auth-refresh-kubernetes.js +127 -0
  67. package/dist/lib/auth-refresh-kubernetes.js.map +1 -0
  68. package/dist/lib/build-if-stale.d.ts +33 -0
  69. package/dist/lib/build-if-stale.d.ts.map +1 -0
  70. package/dist/lib/build-if-stale.js +156 -0
  71. package/dist/lib/build-if-stale.js.map +1 -0
  72. package/dist/lib/bundle-freshness.d.ts +57 -0
  73. package/dist/lib/bundle-freshness.d.ts.map +1 -0
  74. package/dist/lib/bundle-freshness.js +223 -0
  75. package/dist/lib/bundle-freshness.js.map +1 -0
  76. package/dist/lib/bundle-source.d.ts +52 -0
  77. package/dist/lib/bundle-source.d.ts.map +1 -0
  78. package/dist/lib/bundle-source.js +83 -0
  79. package/dist/lib/bundle-source.js.map +1 -0
  80. package/dist/lib/kubectl-wrap.d.ts +6 -0
  81. package/dist/lib/kubectl-wrap.d.ts.map +1 -1
  82. package/dist/lib/kubectl-wrap.js +6 -1
  83. package/dist/lib/kubectl-wrap.js.map +1 -1
  84. package/dist/lib/manifest-refresh.d.ts +42 -1
  85. package/dist/lib/manifest-refresh.d.ts.map +1 -1
  86. package/dist/lib/manifest-refresh.js +83 -7
  87. package/dist/lib/manifest-refresh.js.map +1 -1
  88. package/dist/lib/peripheral-registry.d.ts +36 -0
  89. package/dist/lib/peripheral-registry.d.ts.map +1 -0
  90. package/dist/lib/peripheral-registry.js +55 -0
  91. package/dist/lib/peripheral-registry.js.map +1 -0
  92. package/dist/lib/port-forward.d.ts +67 -0
  93. package/dist/lib/port-forward.d.ts.map +1 -1
  94. package/dist/lib/port-forward.js +153 -0
  95. package/dist/lib/port-forward.js.map +1 -1
  96. package/dist/lib/upgrade-kubernetes.d.ts +52 -12
  97. package/dist/lib/upgrade-kubernetes.d.ts.map +1 -1
  98. package/dist/lib/upgrade-kubernetes.js +390 -22
  99. package/dist/lib/upgrade-kubernetes.js.map +1 -1
  100. package/dist/mcp-server.js +84 -58
  101. package/host-cp/compose.yaml +6 -0
  102. package/host-cp/k8s/manifests/30-configmap.yaml +6 -0
  103. package/host-cp/k8s/manifests/50-deployment.yaml +46 -9
  104. package/host-cp/k8s/manifests/auth-service/10-serviceaccount.yaml +8 -0
  105. package/host-cp/k8s/manifests/auth-service/20-rbac.yaml +34 -0
  106. package/host-cp/k8s/manifests/auth-service/30-configmap.yaml +24 -0
  107. package/host-cp/k8s/manifests/auth-service/45-pvc.yaml +25 -0
  108. package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +117 -0
  109. package/host-cp/k8s/manifests/auth-service/60-service.yaml +21 -0
  110. package/host-cp/k8s/manifests/kg-service/10-serviceaccount.yaml +8 -0
  111. package/host-cp/k8s/manifests/kg-service/20-rbac.yaml +34 -0
  112. package/host-cp/k8s/manifests/kg-service/30-configmap.yaml +18 -0
  113. package/host-cp/k8s/manifests/kg-service/45-pvc.yaml +25 -0
  114. package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +108 -0
  115. package/host-cp/k8s/manifests/kg-service/60-service.yaml +21 -0
  116. package/host-cp/k8s/manifests/mcp-auth-service/10-serviceaccount.yaml +8 -0
  117. package/host-cp/k8s/manifests/mcp-auth-service/20-rbac.yaml +34 -0
  118. package/host-cp/k8s/manifests/mcp-auth-service/30-configmap.yaml +18 -0
  119. package/host-cp/k8s/manifests/mcp-auth-service/45-pvc.yaml +25 -0
  120. package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +117 -0
  121. package/host-cp/k8s/manifests/mcp-auth-service/60-service.yaml +21 -0
  122. package/host-cp/k8s/manifests/memory-service/10-serviceaccount.yaml +8 -0
  123. package/host-cp/k8s/manifests/memory-service/20-rbac.yaml +34 -0
  124. package/host-cp/k8s/manifests/memory-service/30-configmap.yaml +20 -0
  125. package/host-cp/k8s/manifests/memory-service/45-pvc.yaml +25 -0
  126. package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +121 -0
  127. package/host-cp/k8s/manifests/memory-service/60-service.yaml +21 -0
  128. package/host-cp/k8s/templates/auth-service-secret-template.yaml +28 -0
  129. package/host-cp/k8s/templates/kg-service-secret-template.yaml +28 -0
  130. package/host-cp/k8s/templates/mcp-auth-service-secret-template.yaml +28 -0
  131. package/host-cp/k8s/templates/memory-service-secret-template.yaml +29 -0
  132. package/host-cp/src/agent-runtime-trigger.mjs +7 -5
  133. package/host-cp/src/plan-chat-secret.mjs +13 -2
  134. package/host-cp/src/plan-chat-service.mjs +116 -15
  135. package/host-cp/src/server.mjs +23 -11
  136. package/host-cp/src/upgrade-spawner.mjs +10 -5
  137. package/package.json +4 -2
@@ -0,0 +1,262 @@
1
+ /**
2
+ * chunks-subscriber-transport.ts — Phase B B1 transport module.
3
+ *
4
+ * Long-poll loop against host-cp's /v1/shape that delivers chunks
5
+ * to a subscriber via `onChunk` callback. Owns: bearer auth header,
6
+ * reconnect-on-error with exponential backoff (capped 2s), AbortSignal
7
+ * cancellation, and Electric SQL shape protocol parsing (handle / offset
8
+ * round-tripping for incremental delivery).
9
+ *
10
+ * Boundary discipline (per design doc `transport` + `subscriber`):
11
+ * - Transport: auth + reconnect + delivery. NO filtering / decision /
12
+ * dispatch — that's the subscriber's job (`operator-subscription.ts`
13
+ * pure helpers + per-persona runners).
14
+ * - One transport instance per subscriber process; ≤5 long-polls per
15
+ * session is acceptable scale.
16
+ *
17
+ * AbortSignal wiring (per A1.4 §transport defense-in-depth):
18
+ * - Both the long-poll wait AND the reconnect-backoff sleep honor
19
+ * the injected AbortSignal.
20
+ * - Per-child SIGTERM handler aborts → transport releases within
21
+ * milliseconds (not at the 2s backoff cap).
22
+ * - The same signal feeds into the SDK's query() AbortController
23
+ * in B5 driver-hot-loop so one abort releases both.
24
+ *
25
+ * Source: docs/design/olam-plan-chat-agent-runtime.md `transport` section.
26
+ */
27
+ /**
28
+ * Sleep with optional abort. Resolves on timer OR rejects on abort.
29
+ */
30
+ async function abortableSleep(ms, signal) {
31
+ if (signal?.aborted)
32
+ throw new DOMException('aborted', 'AbortError');
33
+ return new Promise((resolve, reject) => {
34
+ const t = setTimeout(() => {
35
+ signal?.removeEventListener('abort', onAbort);
36
+ resolve();
37
+ }, ms);
38
+ const onAbort = () => {
39
+ clearTimeout(t);
40
+ reject(new DOMException('aborted', 'AbortError'));
41
+ };
42
+ signal?.addEventListener('abort', onAbort, { once: true });
43
+ });
44
+ }
45
+ function isAbortError(err) {
46
+ return (err instanceof Error &&
47
+ (err.name === 'AbortError' || err.code === 'ABORT_ERR'));
48
+ }
49
+ /**
50
+ * Parse Electric SQL shape response messages into chunk rows.
51
+ * Shape protocol: array of `{ headers, value, ... }` messages; we only
52
+ * care about `value` payloads with `operation: 'insert'` (or no operation
53
+ * field on the initial snapshot).
54
+ */
55
+ export function parseShapeMessages(body) {
56
+ if (!Array.isArray(body))
57
+ return [];
58
+ const out = [];
59
+ for (const msg of body) {
60
+ if (!msg || typeof msg !== 'object')
61
+ continue;
62
+ const headers = msg.headers;
63
+ const op = headers?.operation;
64
+ // Initial snapshot has no operation; live inserts have operation='insert'.
65
+ // Delete + update aren't meaningful for append-only chunks but we skip them defensively.
66
+ if (op && op !== 'insert')
67
+ continue;
68
+ const value = msg.value;
69
+ if (!value || typeof value !== 'object')
70
+ continue;
71
+ // Electric SQL returns integer columns as STRINGS in the JSON wire format
72
+ // ('0' not 0). Coerce here so downstream `filterOperatorPickups` cursor
73
+ // comparisons (`c.seq <= lastConsumedSeq`) work numerically. Without this
74
+ // coercion EVERY row was rejected by the `typeof === 'number'` guard and
75
+ // the driver hung silently in iterationLoop waiting for onChunk to fire.
76
+ const v = value;
77
+ const rawSeq = v.seq;
78
+ const seq = typeof rawSeq === 'number'
79
+ ? rawSeq
80
+ : typeof rawSeq === 'string'
81
+ ? Number.parseInt(rawSeq, 10)
82
+ : NaN;
83
+ if (typeof v.world_id === 'string' &&
84
+ typeof v.session_id === 'string' &&
85
+ typeof v.message_id === 'string' &&
86
+ Number.isFinite(seq) &&
87
+ typeof v.actor_id === 'string' &&
88
+ typeof v.actor_type === 'string' &&
89
+ typeof v.role === 'string' &&
90
+ typeof v.chunk === 'string' &&
91
+ typeof v.created_at === 'string') {
92
+ out.push({
93
+ world_id: v.world_id,
94
+ session_id: v.session_id,
95
+ message_id: v.message_id,
96
+ seq,
97
+ actor_id: v.actor_id,
98
+ actor_type: v.actor_type,
99
+ role: v.role,
100
+ chunk: v.chunk,
101
+ created_at: v.created_at,
102
+ });
103
+ }
104
+ }
105
+ return out;
106
+ }
107
+ /**
108
+ * Extract Electric shape cursor from response headers.
109
+ * `electric-handle` + `electric-offset` per the protocol;
110
+ * absent on errors or non-shape responses.
111
+ */
112
+ export function extractCursor(headers) {
113
+ return {
114
+ handle: headers.get('electric-handle'),
115
+ offset: headers.get('electric-offset'),
116
+ };
117
+ }
118
+ /**
119
+ * Build the /v1/shape URL with cursor + live=true on subsequent polls.
120
+ */
121
+ export function buildShapeUrl(baseUrl, worldId, sessionId, cursor) {
122
+ const url = new URL('/v1/shape', baseUrl);
123
+ // Electric SQL requires the `table` query param; plan-chat-service strips
124
+ // world_id/session_id/where (per SCOPED_QUERY_PARAMS) but forwards everything
125
+ // else unchanged, so `table=chunks` is the client's responsibility. Without
126
+ // it, Electric upstream returns 400 `{"errors":{"table":["can't be blank"]}}`.
127
+ url.searchParams.set('table', 'chunks');
128
+ url.searchParams.set('world_id', worldId);
129
+ url.searchParams.set('session_id', sessionId);
130
+ if (cursor.handle) {
131
+ url.searchParams.set('handle', cursor.handle);
132
+ url.searchParams.set('offset', cursor.offset ?? '-1');
133
+ url.searchParams.set('live', 'true');
134
+ }
135
+ else {
136
+ url.searchParams.set('offset', '-1');
137
+ }
138
+ return url.toString();
139
+ }
140
+ /**
141
+ * Exponential backoff schedule: 100ms → 200ms → 400ms → 800ms → 1600ms → 2000ms (cap).
142
+ * Returns the delay for the given attempt index (0-based).
143
+ */
144
+ export function backoffDelay(attempt) {
145
+ const base = 100 * Math.pow(2, attempt);
146
+ return Math.min(base, 2000);
147
+ }
148
+ /**
149
+ * subscribeToChunks — start a long-poll subscription. Returns a handle
150
+ * with `stop()` (abortable) and `done` (resolves when loop exits).
151
+ *
152
+ * Failure semantics:
153
+ * - Network/5xx errors: exponential backoff up to 2s cap; retry forever
154
+ * until abort signal fires.
155
+ * - 401: re-authenticate is OUT-OF-SCOPE for v1; transport surfaces a
156
+ * terminal error via `done` rejection. (B9 JWT refresh handles this
157
+ * in the post-migration world.)
158
+ * - 4xx (other): terminal; reject `done`.
159
+ * - Abort signal: clean shutdown; `done` resolves (not rejects).
160
+ */
161
+ export function subscribeToChunks(opts) {
162
+ const { baseUrl, bearer, worldId, sessionId, onChunk, abortSignal: externalSignal, fetchImpl = fetch, sleepImpl = abortableSleep, } = opts;
163
+ // Internal controller composes with the external signal so stop() works
164
+ // even when no external abortSignal is provided.
165
+ const controller = new AbortController();
166
+ if (externalSignal) {
167
+ if (externalSignal.aborted)
168
+ controller.abort();
169
+ else
170
+ externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
171
+ }
172
+ const signal = controller.signal;
173
+ const cursor = { handle: null, offset: null };
174
+ let attempt = 0;
175
+ const loop = async () => {
176
+ while (!signal.aborted) {
177
+ try {
178
+ const url = buildShapeUrl(baseUrl, worldId, sessionId, cursor);
179
+ const res = await fetchImpl(url, {
180
+ method: 'GET',
181
+ headers: {
182
+ authorization: `Bearer ${bearer}`,
183
+ accept: 'application/json',
184
+ },
185
+ signal,
186
+ });
187
+ if (res.status === 401) {
188
+ // Terminal: shared-secret rejected or JWT scope-mismatch (B9 future).
189
+ throw new Error(`subscribeToChunks: 401 unauthorized at ${url}`);
190
+ }
191
+ if (res.status >= 400 && res.status < 500) {
192
+ throw new Error(`subscribeToChunks: ${res.status} ${res.statusText} at ${url}`);
193
+ }
194
+ if (!res.ok) {
195
+ // 5xx → backoff retry below
196
+ throw new Error(`subscribeToChunks: ${res.status} at ${url}`);
197
+ }
198
+ // Advance cursor BEFORE deliver so a slow onChunk callback doesn't re-fetch the same shape.
199
+ const newCursor = extractCursor(res.headers);
200
+ if (newCursor.handle)
201
+ cursor.handle = newCursor.handle;
202
+ if (newCursor.offset)
203
+ cursor.offset = newCursor.offset;
204
+ const body = await res.json().catch(() => null);
205
+ const chunks = parseShapeMessages(body);
206
+ for (const chunk of chunks) {
207
+ if (signal.aborted)
208
+ return;
209
+ await onChunk(chunk);
210
+ }
211
+ // Successful round-trip: reset backoff. In production, the next
212
+ // iteration's fetch blocks on Electric's `live=true` long-poll
213
+ // until new data arrives. If the upstream returns immediately
214
+ // with empty data (misconfigured shape, network proxy that
215
+ // doesn't honor long-poll, or mock fetch in tests), yield briefly
216
+ // so we don't CPU-burn. 100ms matches the smallest backoff step;
217
+ // imperceptible to operators since real long-polling already
218
+ // dominates wait time.
219
+ attempt = 0;
220
+ if (chunks.length === 0) {
221
+ try {
222
+ await sleepImpl(100, signal);
223
+ }
224
+ catch {
225
+ return;
226
+ }
227
+ }
228
+ }
229
+ catch (err) {
230
+ if (isAbortError(err) || signal.aborted)
231
+ return;
232
+ // 4xx terminal: propagate (will reject `done`).
233
+ if (err instanceof Error && /^subscribeToChunks: 4\d\d/.test(err.message)) {
234
+ throw err;
235
+ }
236
+ // 5xx / network → backoff + retry.
237
+ const delay = backoffDelay(attempt++);
238
+ try {
239
+ await sleepImpl(delay, signal);
240
+ }
241
+ catch {
242
+ // Abort during backoff sleep is a clean exit.
243
+ return;
244
+ }
245
+ }
246
+ }
247
+ };
248
+ const done = loop();
249
+ return {
250
+ stop: async () => {
251
+ controller.abort();
252
+ try {
253
+ await done;
254
+ }
255
+ catch {
256
+ // stop() never throws; the original `done` promise still carries the error.
257
+ }
258
+ },
259
+ done,
260
+ };
261
+ }
262
+ //# sourceMappingURL=chunks-subscriber-transport.js.map
@@ -0,0 +1,188 @@
1
+ /**
2
+ * codex-runner.ts — Phase B B2 (minimum-demo cut) codex agent.
3
+ *
4
+ * Subscribes to chunks; runs a two-stage state machine
5
+ * (candidate → approved) gated on a successful tool_result;
6
+ * emits an APPROVE chunk via host-cp POST /v1/chunks.
7
+ *
8
+ * Demo-cut simplifications (full contract deferred to B2-full):
9
+ * - NO SDK reasoning. Codex emits a structural APPROVE chunk
10
+ * using `codexApprovalDraft` (the pre-built reasoning
11
+ * boilerplate from C6). Full codex would invoke Claude Agent
12
+ * SDK with a codex-specific system prompt.
13
+ * - NO post-approval retraction monitor (OQ13 deferred). For
14
+ * demo G1, codex emits APPROVE on confirmed PR-draft; the
15
+ * follow-up BLOCK-on-retraction is post-demo.
16
+ * - NO byte-stable-chunk-on-retry adapter cache. We rely on
17
+ * the substrate PK (message_id+seq) idempotency at the server.
18
+ *
19
+ * Two-stage state machine (per Phase A `codex` section):
20
+ *
21
+ * candidate ← agent chunk matches detectCodexTrigger regex
22
+ * │ tool_result with is_error:false + PR-URL/push-ref output
23
+ * ▼
24
+ * approved → emits APPROVE chunk
25
+ *
26
+ * tool_result with is_error:true OR irrelevant → reverts to candidate
27
+ *
28
+ * Source: docs/design/olam-plan-chat-agent-runtime.md `codex` section,
29
+ * minimum-demo cut (retraction protocol + SDK reasoning deferred).
30
+ */
31
+ import { randomUUID } from 'node:crypto';
32
+ import { makeHostCpChunkPoster, } from './agent-sdk-to-chunks.js';
33
+ import { subscribeToChunks } from './chunks-subscriber-transport.js';
34
+ import { codexApprovalDraft, detectCodexTrigger, } from './operator-subscription.js';
35
+ /**
36
+ * Detect a tool_result chunk that confirms a PR-draft trigger.
37
+ * Looks for:
38
+ * - role='tool', kind='tool-result' (per C1's adapter mapping)
39
+ * - is_error:false (parsed from chunk content if JSON)
40
+ * - output containing a PR URL pattern OR successful-push reference
41
+ */
42
+ export function isPrDraftConfirmation(chunk) {
43
+ if (chunk.role !== 'tool')
44
+ return false;
45
+ // Parse the chunk content; tool-result blocks may be JSON-wrapped per C1.
46
+ let parsed;
47
+ try {
48
+ parsed = JSON.parse(chunk.chunk);
49
+ }
50
+ catch {
51
+ // Plain text — look for PR URL pattern.
52
+ return /https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/.test(chunk.chunk);
53
+ }
54
+ if (parsed === null || typeof parsed !== 'object')
55
+ return false;
56
+ const p = parsed;
57
+ if (p.is_error === true)
58
+ return false;
59
+ const output = typeof p.output === 'string' ? p.output : JSON.stringify(p.output ?? '');
60
+ return /https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/.test(output);
61
+ }
62
+ export function runCodex(opts) {
63
+ const { hostCpUrl, bearer, worldId, sessionId, abortSignal: externalSignal, subscribeImpl = subscribeToChunks, postChunkImpl = (url, b) => makeHostCpChunkPoster({ sidecarUrl: url, bearer: b }), } = opts;
64
+ const controller = new AbortController();
65
+ if (externalSignal) {
66
+ if (externalSignal.aborted)
67
+ controller.abort();
68
+ else
69
+ externalSignal.addEventListener('abort', () => controller.abort(), { once: true });
70
+ }
71
+ const signal = controller.signal;
72
+ const postChunk = postChunkImpl(hostCpUrl, bearer);
73
+ let state = { kind: 'idle' };
74
+ const emitApprove = async (triggerChunk, confirmationChunk) => {
75
+ const draft = codexApprovalDraft({
76
+ triggerChunk,
77
+ approved: true,
78
+ reason: `Confirmed by successful tool_result in message ${confirmationChunk.message_id}`,
79
+ });
80
+ const chunkRow = {
81
+ world_id: worldId,
82
+ session_id: sessionId,
83
+ message_id: `codex-${randomUUID()}`,
84
+ seq: 0,
85
+ role: 'assistant',
86
+ kind: 'text',
87
+ chunk: draft.content,
88
+ created_at: new Date().toISOString(),
89
+ };
90
+ try {
91
+ await postChunk(chunkRow);
92
+ }
93
+ catch (err) {
94
+ if (signal.aborted)
95
+ return;
96
+ // eslint-disable-next-line no-console
97
+ console.error('[codex] APPROVE POST failed:', err);
98
+ }
99
+ };
100
+ const handleChunk = async (chunk) => {
101
+ if (signal.aborted)
102
+ return;
103
+ // Skip our own chunks to avoid feedback loops.
104
+ if (chunk.actor_type === 'codex')
105
+ return;
106
+ if (state.kind === 'idle') {
107
+ // Look for a candidate trigger from the driver agent.
108
+ const trig = detectCodexTrigger(chunk);
109
+ if (trig.matched) {
110
+ state = {
111
+ kind: 'candidate',
112
+ triggerChunk: chunk,
113
+ matchedPattern: trig.matchedPattern ?? 'unknown',
114
+ };
115
+ }
116
+ return;
117
+ }
118
+ // state.kind === 'candidate' — waiting for a tool_result.
119
+ if (isPrDraftConfirmation(chunk)) {
120
+ const triggerChunk = state.triggerChunk;
121
+ state = { kind: 'idle' };
122
+ await emitApprove(triggerChunk, chunk);
123
+ }
124
+ else if (chunk.role === 'tool') {
125
+ // tool_result with is_error:true OR irrelevant output → revert to candidate.
126
+ // We stay in candidate state in case a follow-up tool_result confirms;
127
+ // alternative is to revert to idle and re-arm on a fresh trigger.
128
+ // For demo cut: stay in candidate to give the agent a chance to retry.
129
+ }
130
+ // Non-tool chunks while in candidate: ignored (we wait for the confirmation).
131
+ };
132
+ const transport = subscribeImpl({
133
+ baseUrl: hostCpUrl,
134
+ bearer,
135
+ worldId,
136
+ sessionId,
137
+ abortSignal: signal,
138
+ onChunk: handleChunk,
139
+ });
140
+ return {
141
+ stop: async () => {
142
+ controller.abort();
143
+ try {
144
+ await transport.done;
145
+ }
146
+ catch {
147
+ // swallow
148
+ }
149
+ },
150
+ done: transport.done,
151
+ };
152
+ }
153
+ /**
154
+ * CLI entry point. Reads env, installs SIGTERM handler, runs the codex loop.
155
+ */
156
+ export async function main() {
157
+ const hostCpUrl = process.env.HOST_CP_URL;
158
+ const bearer = process.env.HOST_CP_BEARER;
159
+ const worldId = process.env.WORLD_ID;
160
+ const sessionId = process.env.SESSION_ID;
161
+ if (!hostCpUrl || !bearer || !worldId || !sessionId) {
162
+ // eslint-disable-next-line no-console
163
+ console.error('[codex] missing required env: HOST_CP_URL, HOST_CP_BEARER, WORLD_ID, SESSION_ID');
164
+ process.exit(1);
165
+ }
166
+ const controller = new AbortController();
167
+ process.on('SIGTERM', () => controller.abort());
168
+ process.on('SIGINT', () => controller.abort());
169
+ const handle = runCodex({
170
+ hostCpUrl,
171
+ bearer,
172
+ worldId,
173
+ sessionId,
174
+ abortSignal: controller.signal,
175
+ });
176
+ await handle.done;
177
+ process.exit(0);
178
+ }
179
+ if (typeof process !== 'undefined' &&
180
+ process.argv[1] &&
181
+ (process.argv[1].endsWith('codex-runner.js') || process.argv[1].endsWith('codex-runner.ts'))) {
182
+ main().catch((err) => {
183
+ // eslint-disable-next-line no-console
184
+ console.error('[codex] fatal:', err);
185
+ process.exit(1);
186
+ });
187
+ }
188
+ //# sourceMappingURL=codex-runner.js.map