@pleri/olam-cli 0.1.188 → 0.1.195

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 (156) hide show
  1. package/README.md +1 -1
  2. package/dist/ask/knowledge-pack.generated.d.ts.map +1 -1
  3. package/dist/ask/knowledge-pack.generated.js +37 -12
  4. package/dist/ask/knowledge-pack.generated.js.map +1 -1
  5. package/dist/commands/bootstrap.d.ts +4 -0
  6. package/dist/commands/bootstrap.d.ts.map +1 -1
  7. package/dist/commands/bootstrap.js +6 -9
  8. package/dist/commands/bootstrap.js.map +1 -1
  9. package/dist/commands/clean.js +1 -1
  10. package/dist/commands/clean.js.map +1 -1
  11. package/dist/commands/completion.d.ts.map +1 -1
  12. package/dist/commands/completion.js +1 -4
  13. package/dist/commands/completion.js.map +1 -1
  14. package/dist/commands/create.d.ts.map +1 -1
  15. package/dist/commands/create.js +6 -0
  16. package/dist/commands/create.js.map +1 -1
  17. package/dist/commands/crystallize.js +12 -14
  18. package/dist/commands/crystallize.js.map +1 -1
  19. package/dist/commands/destroy.d.ts +13 -1
  20. package/dist/commands/destroy.d.ts.map +1 -1
  21. package/dist/commands/destroy.js +52 -6
  22. package/dist/commands/destroy.js.map +1 -1
  23. package/dist/commands/dispatch.d.ts +9 -0
  24. package/dist/commands/dispatch.d.ts.map +1 -1
  25. package/dist/commands/dispatch.js +21 -2
  26. package/dist/commands/dispatch.js.map +1 -1
  27. package/dist/commands/doctor.d.ts +1 -1
  28. package/dist/commands/doctor.d.ts.map +1 -1
  29. package/dist/commands/doctor.js +29 -22
  30. package/dist/commands/doctor.js.map +1 -1
  31. package/dist/commands/enter.d.ts +3 -3
  32. package/dist/commands/enter.d.ts.map +1 -1
  33. package/dist/commands/enter.js +57 -44
  34. package/dist/commands/enter.js.map +1 -1
  35. package/dist/commands/flywheel/index.d.ts.map +1 -1
  36. package/dist/commands/flywheel/index.js +1 -1
  37. package/dist/commands/flywheel/index.js.map +1 -1
  38. package/dist/commands/host-cp.d.ts.map +1 -1
  39. package/dist/commands/host-cp.js +2 -1
  40. package/dist/commands/host-cp.js.map +1 -1
  41. package/dist/commands/implode.d.ts.map +1 -1
  42. package/dist/commands/implode.js +1 -1
  43. package/dist/commands/implode.js.map +1 -1
  44. package/dist/commands/init.d.ts +20 -0
  45. package/dist/commands/init.d.ts.map +1 -1
  46. package/dist/commands/init.js +102 -9
  47. package/dist/commands/init.js.map +1 -1
  48. package/dist/commands/kg-build.d.ts.map +1 -1
  49. package/dist/commands/kg-build.js +3 -0
  50. package/dist/commands/kg-build.js.map +1 -1
  51. package/dist/commands/kg-classify.d.ts +20 -0
  52. package/dist/commands/kg-classify.d.ts.map +1 -1
  53. package/dist/commands/kg-classify.js +59 -42
  54. package/dist/commands/kg-classify.js.map +1 -1
  55. package/dist/commands/kg-mirror.d.ts +40 -0
  56. package/dist/commands/kg-mirror.d.ts.map +1 -0
  57. package/dist/commands/kg-mirror.js +228 -0
  58. package/dist/commands/kg-mirror.js.map +1 -0
  59. package/dist/commands/mcp/index.js +1 -1
  60. package/dist/commands/mcp/index.js.map +1 -1
  61. package/dist/commands/memory/index.d.ts.map +1 -1
  62. package/dist/commands/memory/index.js +1 -1
  63. package/dist/commands/memory/index.js.map +1 -1
  64. package/dist/commands/resume.d.ts.map +1 -1
  65. package/dist/commands/resume.js +1 -1
  66. package/dist/commands/resume.js.map +1 -1
  67. package/dist/commands/services-tls.d.ts +120 -0
  68. package/dist/commands/services-tls.d.ts.map +1 -0
  69. package/dist/commands/services-tls.js +434 -0
  70. package/dist/commands/services-tls.js.map +1 -0
  71. package/dist/commands/services.d.ts.map +1 -1
  72. package/dist/commands/services.js +28 -1
  73. package/dist/commands/services.js.map +1 -1
  74. package/dist/commands/setup-linux-gate.d.ts.map +1 -1
  75. package/dist/commands/setup-linux-gate.js +1 -3
  76. package/dist/commands/setup-linux-gate.js.map +1 -1
  77. package/dist/commands/setup-metrics.d.ts.map +1 -1
  78. package/dist/commands/setup-metrics.js +1 -2
  79. package/dist/commands/setup-metrics.js.map +1 -1
  80. package/dist/commands/setup-phase-5a-skill-source.d.ts +17 -1
  81. package/dist/commands/setup-phase-5a-skill-source.d.ts.map +1 -1
  82. package/dist/commands/setup-phase-5a-skill-source.js +69 -6
  83. package/dist/commands/setup-phase-5a-skill-source.js.map +1 -1
  84. package/dist/commands/setup.d.ts +26 -1
  85. package/dist/commands/setup.d.ts.map +1 -1
  86. package/dist/commands/setup.js +189 -47
  87. package/dist/commands/setup.js.map +1 -1
  88. package/dist/commands/skills-onboard.d.ts.map +1 -1
  89. package/dist/commands/skills-onboard.js +4 -1
  90. package/dist/commands/skills-onboard.js.map +1 -1
  91. package/dist/commands/skills-source.d.ts.map +1 -1
  92. package/dist/commands/skills-source.js +20 -4
  93. package/dist/commands/skills-source.js.map +1 -1
  94. package/dist/commands/status.js +1 -1
  95. package/dist/commands/status.js.map +1 -1
  96. package/dist/commands/upgrade.d.ts.map +1 -1
  97. package/dist/commands/upgrade.js +1 -3
  98. package/dist/commands/upgrade.js.map +1 -1
  99. package/dist/commands/yolo.d.ts.map +1 -1
  100. package/dist/commands/yolo.js +1 -1
  101. package/dist/commands/yolo.js.map +1 -1
  102. package/dist/context.d.ts +4 -0
  103. package/dist/context.d.ts.map +1 -1
  104. package/dist/context.js +3 -2
  105. package/dist/context.js.map +1 -1
  106. package/dist/image-digests.json +8 -8
  107. package/dist/index.js +3846 -2232
  108. package/dist/index.js.map +1 -1
  109. package/dist/lib/auth-refresh-kubernetes.d.ts.map +1 -1
  110. package/dist/lib/auth-refresh-kubernetes.js +14 -5
  111. package/dist/lib/auth-refresh-kubernetes.js.map +1 -1
  112. package/dist/lib/bootstrap-kubernetes.d.ts +41 -0
  113. package/dist/lib/bootstrap-kubernetes.d.ts.map +1 -1
  114. package/dist/lib/bootstrap-kubernetes.js +289 -36
  115. package/dist/lib/bootstrap-kubernetes.js.map +1 -1
  116. package/dist/lib/cf-access-token.d.ts.map +1 -1
  117. package/dist/lib/cf-access-token.js +2 -3
  118. package/dist/lib/cf-access-token.js.map +1 -1
  119. package/dist/lib/help-groups.d.ts +36 -0
  120. package/dist/lib/help-groups.d.ts.map +1 -0
  121. package/dist/lib/help-groups.js +124 -0
  122. package/dist/lib/help-groups.js.map +1 -0
  123. package/dist/lib/k8s-bootstrap.d.ts +6 -0
  124. package/dist/lib/k8s-bootstrap.d.ts.map +1 -1
  125. package/dist/lib/k8s-bootstrap.js +15 -2
  126. package/dist/lib/k8s-bootstrap.js.map +1 -1
  127. package/dist/lib/k8s-secret-render.d.ts.map +1 -1
  128. package/dist/lib/k8s-secret-render.js +17 -10
  129. package/dist/lib/k8s-secret-render.js.map +1 -1
  130. package/dist/lib/memory-secret.d.ts +15 -2
  131. package/dist/lib/memory-secret.d.ts.map +1 -1
  132. package/dist/lib/memory-secret.js +25 -8
  133. package/dist/lib/memory-secret.js.map +1 -1
  134. package/dist/lib/upgrade-check.d.ts +60 -0
  135. package/dist/lib/upgrade-check.d.ts.map +1 -0
  136. package/dist/lib/upgrade-check.js +169 -0
  137. package/dist/lib/upgrade-check.js.map +1 -0
  138. package/dist/lib/upgrade-kubernetes.d.ts +17 -0
  139. package/dist/lib/upgrade-kubernetes.d.ts.map +1 -1
  140. package/dist/lib/upgrade-kubernetes.js +125 -1
  141. package/dist/lib/upgrade-kubernetes.js.map +1 -1
  142. package/dist/mcp-server.js +2651 -2850
  143. package/hermes-bundle/version.json +1 -1
  144. package/host-cp/k8s/manifests/30-configmap.yaml +8 -1
  145. package/host-cp/k8s/manifests/50-deployment.yaml +1 -1
  146. package/host-cp/k8s/manifests/60-service.yaml +12 -4
  147. package/host-cp/k8s/manifests/70-ingressroute.yaml +58 -0
  148. package/host-cp/k8s/manifests/auth-service/50-deployment.yaml +1 -1
  149. package/host-cp/k8s/manifests/kg-service/50-deployment.yaml +1 -1
  150. package/host-cp/k8s/manifests/mcp-auth-service/50-deployment.yaml +1 -1
  151. package/host-cp/k8s/manifests/memory-service/50-deployment.yaml +1 -1
  152. package/host-cp/src/plan-chat-secret.mjs +16 -1
  153. package/host-cp/src/plan-chat-service.mjs +493 -11
  154. package/host-cp/src/planning-sessions.mjs +252 -0
  155. package/host-cp/src/server.mjs +92 -2
  156. package/package.json +2 -1
@@ -175,6 +175,216 @@ export async function setCrystallizeStatus({ pool, sessionId, status, worldId =
175
175
  );
176
176
  }
177
177
 
178
+ /**
179
+ * Create a new multi-turn DISPATCH session (multi-turn-cloud-sandbox-dispatch
180
+ * Phase A2 — distinct from createPlanningSession which is for planning-flow
181
+ * crystallization sessions under world_id='_planning').
182
+ *
183
+ * Allocates a UUID session_id, INSERTs a planning_sessions row with
184
+ * session_type='dispatch' + caller-supplied world_id, applies operator-supplied
185
+ * budget_usd_cap / allow_unpriced_models defaults, returns the session_id.
186
+ *
187
+ * No seed chunk: dispatch sessions accumulate chunks from the agent runtime
188
+ * (via /v1/chunks); we don't pre-seed a system chunk because Electric shape
189
+ * subscribers for dispatch sessions can wait for the first real agent chunk.
190
+ *
191
+ * @param {object} opts
192
+ * @param {object} opts.pool
193
+ * @param {string} opts.actorId
194
+ * @param {string} opts.worldId — operator-supplied; identifies the dispatch
195
+ * target world (NOT the '_planning' sentinel used by createPlanningSession).
196
+ * @param {number | null} [opts.budgetUsdCap=null] — per-session budget cap;
197
+ * null = uncapped. When null AND `OLAM_SESSION_BUDGET_DEFAULT_USD` is set,
198
+ * the env-default applies at /v1/dispatch-turn check time (Phase D); here
199
+ * we record the row exactly as supplied.
200
+ * @param {boolean} [opts.allowUnpricedModels=false] — opt session into the
201
+ * pricingForModel-returns-null fallback (Plan A T11 mitigation; default
202
+ * refuses unknown models with 502).
203
+ * @returns {Promise<{ session_id: string }>}
204
+ */
205
+ export async function createDispatchSession({
206
+ pool,
207
+ actorId,
208
+ worldId,
209
+ budgetUsdCap = null,
210
+ allowUnpricedModels = false,
211
+ sessionId: providedSessionId = null,
212
+ }) {
213
+ if (!actorId || typeof actorId !== 'string') {
214
+ throw new Error('createDispatchSession: actorId required');
215
+ }
216
+ if (!worldId || typeof worldId !== 'string') {
217
+ throw new Error('createDispatchSession: worldId required');
218
+ }
219
+ // A6 (Decision 9 always-on threading): callers MAY supply session_id to
220
+ // upsert an existing planning_sessions row (e.g. /api/cloud-dispatch
221
+ // pre-creating the thread before forwarding to plan-DO). When omitted,
222
+ // we generate a UUID. ON CONFLICT DO NOTHING handles the race where
223
+ // the SPA called /v1/sessions/create concurrently AND server-side
224
+ // cloud-dispatch tried to pre-create the same row.
225
+ const sessionId = providedSessionId ?? randomUUID();
226
+ await pool.query(
227
+ `INSERT INTO planning_sessions
228
+ (session_id, actor_id, session_type, world_id, budget_usd_cap, allow_unpriced_models)
229
+ VALUES ($1, $2, 'dispatch', $3, $4, $5)
230
+ ON CONFLICT (session_id) DO NOTHING`,
231
+ [sessionId, actorId, worldId, budgetUsdCap, allowUnpricedModels],
232
+ );
233
+ return { session_id: sessionId };
234
+ }
235
+
236
+ /**
237
+ * Atomic test-and-set lock claim on a dispatch session
238
+ * (multi-turn-cloud-sandbox-dispatch Phase A3 — Decision 4 + T5 mitigation).
239
+ *
240
+ * Pattern: single-statement UPDATE ... WHERE in_flight_turn_id IS NULL RETURNING.
241
+ * Two concurrent attempts: first claim wins (RETURNING yields 1 row); second
242
+ * sees empty result + must return 409 to caller. Matches the established
243
+ * planning-sessions.mjs:169 setCrystallizeStatus atomic-write idiom.
244
+ *
245
+ * @param {object} opts
246
+ * @param {object} opts.pool
247
+ * @param {string} opts.sessionId
248
+ * @param {string} opts.turnId — operator-or-server-generated turn UUID
249
+ * @returns {Promise<boolean>} true if lock claimed, false if already held
250
+ */
251
+ export async function claimDispatchTurnLock({ pool, sessionId, turnId }) {
252
+ const result = await pool.query(
253
+ `UPDATE planning_sessions
254
+ SET in_flight_turn_id = $1,
255
+ in_flight_turn_started_at = NOW(),
256
+ last_turn_at = NOW()
257
+ WHERE session_id = $2
258
+ AND session_type = 'dispatch'
259
+ AND in_flight_turn_id IS NULL
260
+ RETURNING session_id`,
261
+ [turnId, sessionId],
262
+ );
263
+ return (result.rows?.length ?? 0) > 0;
264
+ }
265
+
266
+ /**
267
+ * Clear the in-flight turn lock after dispatch completes (success OR failure).
268
+ *
269
+ * @param {object} opts
270
+ * @param {object} opts.pool
271
+ * @param {string} opts.sessionId
272
+ */
273
+ export async function clearDispatchTurnLock({ pool, sessionId }) {
274
+ await pool.query(
275
+ `UPDATE planning_sessions
276
+ SET in_flight_turn_id = NULL,
277
+ in_flight_turn_started_at = NULL
278
+ WHERE session_id = $1
279
+ AND session_type = 'dispatch'`,
280
+ [sessionId],
281
+ );
282
+ }
283
+
284
+ /**
285
+ * Halt a dispatch session — operator-driven "block next turn" state (T13).
286
+ *
287
+ * Sets halted_at to NOW() AND clears in_flight_turn_id. Future /v1/dispatch-turn
288
+ * calls return 409 'session_halted' until reactivateDispatchSession clears
289
+ * halted_at. Does NOT stop an in-flight container — the running container
290
+ * completes its current turn naturally. UX is "Block next turn" not "Stop"
291
+ * (Plan A Phase C C6).
292
+ *
293
+ * Scoped by actor_id for ownership isolation.
294
+ *
295
+ * @param {object} opts
296
+ * @param {object} opts.pool
297
+ * @param {string} opts.sessionId
298
+ * @param {string} opts.actorId
299
+ * @returns {Promise<boolean>} true if a session row was updated; false if
300
+ * the session_id was not found / not owned by actorId.
301
+ */
302
+ export async function haltDispatchSession({ pool, sessionId, actorId }) {
303
+ const result = await pool.query(
304
+ `UPDATE planning_sessions
305
+ SET halted_at = NOW(),
306
+ in_flight_turn_id = NULL,
307
+ in_flight_turn_started_at = NULL
308
+ WHERE session_id = $1
309
+ AND session_type = 'dispatch'
310
+ AND actor_id = $2
311
+ RETURNING session_id`,
312
+ [sessionId, actorId],
313
+ );
314
+ return (result.rows?.length ?? 0) > 0;
315
+ }
316
+
317
+ /**
318
+ * Reactivate a halted dispatch session — clears halted_at so subsequent
319
+ * /v1/dispatch-turn calls can claim the lock again. Idempotent (clearing an
320
+ * already-null halted_at is a no-op).
321
+ *
322
+ * @param {object} opts
323
+ * @param {object} opts.pool
324
+ * @param {string} opts.sessionId
325
+ * @param {string} opts.actorId
326
+ * @returns {Promise<boolean>} true if a session row was updated; false if
327
+ * the session_id was not found / not owned by actorId.
328
+ */
329
+ export async function reactivateDispatchSession({ pool, sessionId, actorId }) {
330
+ const result = await pool.query(
331
+ `UPDATE planning_sessions
332
+ SET halted_at = NULL
333
+ WHERE session_id = $1
334
+ AND session_type = 'dispatch'
335
+ AND actor_id = $2
336
+ RETURNING session_id`,
337
+ [sessionId, actorId],
338
+ );
339
+ return (result.rows?.length ?? 0) > 0;
340
+ }
341
+
342
+ /**
343
+ * Read a dispatch session by session_id + scope to caller's actor_id
344
+ * (ownership check). Returns the session metadata needed for budget check
345
+ * + plan-DO forward, OR null when not found / not owned.
346
+ *
347
+ * @param {object} opts
348
+ * @param {object} opts.pool
349
+ * @param {string} opts.sessionId
350
+ * @param {string} opts.actorId
351
+ * @returns {Promise<null | {
352
+ * session_id: string,
353
+ * world_id: string | null,
354
+ * actor_id: string,
355
+ * total_usd: number,
356
+ * budget_usd_cap: number | null,
357
+ * allow_unpriced_models: boolean,
358
+ * halted_at: string | null,
359
+ * }>}
360
+ */
361
+ export async function getDispatchSession({ pool, sessionId, actorId }) {
362
+ const result = await pool.query(
363
+ `SELECT session_id, world_id, actor_id,
364
+ total_usd, budget_usd_cap, allow_unpriced_models,
365
+ halted_at
366
+ FROM planning_sessions
367
+ WHERE session_id = $1
368
+ AND session_type = 'dispatch'
369
+ AND actor_id = $2`,
370
+ [sessionId, actorId],
371
+ );
372
+ const row = result.rows?.[0];
373
+ if (!row) return null;
374
+ return {
375
+ session_id: row.session_id,
376
+ world_id: row.world_id ?? null,
377
+ actor_id: row.actor_id,
378
+ total_usd: Number(row.total_usd ?? 0),
379
+ budget_usd_cap:
380
+ row.budget_usd_cap === null || row.budget_usd_cap === undefined
381
+ ? null
382
+ : Number(row.budget_usd_cap),
383
+ allow_unpriced_models: Boolean(row.allow_unpriced_models),
384
+ halted_at: row.halted_at ?? null,
385
+ };
386
+ }
387
+
178
388
  /**
179
389
  * List planning sessions for a given actorId, ordered by created_at DESC.
180
390
  *
@@ -204,6 +414,48 @@ export async function listPlanningSessions({ pool, actorId, limit = 50 }) {
204
414
  return result.rows;
205
415
  }
206
416
 
417
+ /**
418
+ * List multi-turn DISPATCH sessions for a given actorId, ordered by
419
+ * last_turn_at DESC (most recently active first), excluding archived sessions.
420
+ *
421
+ * Distinct from listPlanningSessions: this returns only `session_type='dispatch'`
422
+ * rows + projects the multi-turn-specific columns (total_usd, in_flight_turn_id,
423
+ * halted_at, etc.) that the SPA's SessionsListView (Phase C C3) renders.
424
+ *
425
+ * @param {object} opts
426
+ * @param {object} opts.pool
427
+ * @param {string} opts.actorId
428
+ * @param {number} [opts.limit=50]
429
+ * @returns {Promise<Array<{
430
+ * session_id: string,
431
+ * world_id: string | null,
432
+ * total_usd: string,
433
+ * budget_usd_cap: string | null,
434
+ * in_flight_turn_id: string | null,
435
+ * halted_at: string | null,
436
+ * last_turn_at: string | null,
437
+ * created_at: string,
438
+ * summary: string | null,
439
+ * }>>}
440
+ */
441
+ export async function listDispatchSessions({ pool, actorId, limit = 50 }) {
442
+ const result = await pool.query(
443
+ `SELECT session_id, world_id,
444
+ total_usd, budget_usd_cap,
445
+ in_flight_turn_id, halted_at,
446
+ last_turn_at, created_at,
447
+ summary
448
+ FROM planning_sessions
449
+ WHERE actor_id = $1
450
+ AND session_type = 'dispatch'
451
+ AND archived_at IS NULL
452
+ ORDER BY last_turn_at DESC NULLS LAST, created_at DESC
453
+ LIMIT $2`,
454
+ [actorId, limit],
455
+ );
456
+ return result.rows;
457
+ }
458
+
207
459
  /**
208
460
  * Load lightweight metadata for an existing in-flight planning session.
209
461
  *
@@ -354,6 +354,50 @@ function readDogfoodRepoUrl() {
354
354
  return '';
355
355
  }
356
356
 
357
+ /**
358
+ * Resolve the cloud-kg-mirror classifier proxy URL the runner forwards
359
+ * into spawned CF Sandbox child worlds. When set, host-cp enriches
360
+ * cloud-dispatch bodies with `kgProxyUrl` + `kgProxyBearer`; the runner
361
+ * injects those into the sandbox env so the PreToolUse hook's
362
+ * cloud-sandbox flavor can POST /v1/classify against the
363
+ * kg-mirror-worker. Source order: OLAM_KG_PROXY_URL env, then
364
+ * ~/.olam/kg-proxy-url file. Absent → no injection (dispatched worlds
365
+ * see no proxy URL → hook falls through to grep via existing fail-open).
366
+ * Mirrors readAnthropicBaseUrl() — operators have ONE pattern.
367
+ *
368
+ * Phase 1 (cloud-kg-mirror): docs/plans/cloud-kg-mirror/README.md §6-7.
369
+ */
370
+ function readKgProxyUrl() {
371
+ const fromOlamEnv = process.env['OLAM_KG_PROXY_URL'];
372
+ if (fromOlamEnv && fromOlamEnv.length > 0) return fromOlamEnv.trim();
373
+ try {
374
+ const file = path.join(os.homedir(), '.olam', 'kg-proxy-url');
375
+ const content = fs.readFileSync(file, 'utf-8').trim();
376
+ if (content.length > 0) return content;
377
+ } catch {
378
+ // file absent — fall through
379
+ }
380
+ return '';
381
+ }
382
+
383
+ /**
384
+ * Bearer that pairs with readKgProxyUrl(). Stored in
385
+ * ~/.olam/kg-proxy-bearer (chmod 600 by operator). Mirrors the
386
+ * source-order convention; absent → no injection.
387
+ */
388
+ function readKgProxyBearer() {
389
+ const fromOlamEnv = process.env['OLAM_KG_PROXY_BEARER'];
390
+ if (fromOlamEnv && fromOlamEnv.length > 0) return fromOlamEnv.trim();
391
+ try {
392
+ const file = path.join(os.homedir(), '.olam', 'kg-proxy-bearer');
393
+ const content = fs.readFileSync(file, 'utf-8').trim();
394
+ if (content.length > 0) return content;
395
+ } catch {
396
+ // file absent — fall through
397
+ }
398
+ return '';
399
+ }
400
+
357
401
  /** @type {Record<string, number>} */
358
402
  let WORLDS = {};
359
403
 
@@ -2584,6 +2628,35 @@ const server = http.createServer(instrumentHandler('host-cp', async (req, res) =
2584
2628
  // Keep `messages` too — plan-DO may use it for multi-turn fidelity later.
2585
2629
  }
2586
2630
 
2631
+ // multi-turn-cloud-sandbox-dispatch Phase A6 (Decision 9 always-on
2632
+ // threading): pre-create the planning_sessions row server-side so
2633
+ // EVERY cloud dispatch surfaces as a SPA thread — including pre-PR
2634
+ // #1182-style one-shot calls that never invoked /v1/sessions/create
2635
+ // directly. Idempotent (ON CONFLICT DO NOTHING in createDispatchSession).
2636
+ // Fail-soft: thread-creation failure does NOT block the dispatch
2637
+ // (the operator's task still ships); we log + continue.
2638
+ try {
2639
+ if (parsed.world_id && parsed.session_id) {
2640
+ const planChatBearer = readPlanChatSecret();
2641
+ const planChatBase = process.env.PLAN_CHAT_SERVICE_URL || 'http://127.0.0.1:3200';
2642
+ await fetch(`${planChatBase}/v1/sessions/create`, {
2643
+ method: 'POST',
2644
+ headers: {
2645
+ 'content-type': 'application/json',
2646
+ authorization: `Bearer ${planChatBearer}`,
2647
+ },
2648
+ body: JSON.stringify({
2649
+ world_id: parsed.world_id,
2650
+ session_id: parsed.session_id,
2651
+ }),
2652
+ });
2653
+ }
2654
+ } catch (threadErr) {
2655
+ console.warn(
2656
+ `[cloud-dispatch] thread pre-create failed (continuing): ${threadErr?.message ?? threadErr}`,
2657
+ );
2658
+ }
2659
+
2587
2660
  // Gap 3: enrich the dispatch body with the operator's anthropicBaseUrl
2588
2661
  // so plan-DO can propagate it to spawned CF Sandbox child worlds.
2589
2662
  // Only injected when not already set by the SPA (SPA has no auth-worker
@@ -2596,9 +2669,18 @@ const server = http.createServer(instrumentHandler('host-cp', async (req, res) =
2596
2669
  // ~/.olam/dogfood-repo-url file. Absent → no injection (the dispatch
2597
2670
  // runs text-only, like before this change).
2598
2671
  const dogfoodRepoUrl = readDogfoodRepoUrl();
2672
+ // cloud-kg-mirror Phase 1: propagate the classifier Worker URL +
2673
+ // bearer so the runner can inject them into the sandbox env. When
2674
+ // either is missing the dispatched world's PreToolUse hook falls
2675
+ // through to grep via the existing fail-open path. Operator opts
2676
+ // in by writing ~/.olam/kg-proxy-url + ~/.olam/kg-proxy-bearer.
2677
+ const kgProxyUrl = readKgProxyUrl();
2678
+ const kgProxyBearer = readKgProxyBearer();
2599
2679
  let enrichedObj = null;
2600
2680
  if (anthropicBaseUrl && !parsed.anthropicBaseUrl) enrichedObj = { ...(enrichedObj ?? parsed), anthropicBaseUrl };
2601
2681
  if (dogfoodRepoUrl && !parsed.repoUrl) enrichedObj = { ...(enrichedObj ?? parsed), repoUrl: dogfoodRepoUrl };
2682
+ if (kgProxyUrl && !parsed.kgProxyUrl) enrichedObj = { ...(enrichedObj ?? parsed), kgProxyUrl };
2683
+ if (kgProxyBearer && !parsed.kgProxyBearer) enrichedObj = { ...(enrichedObj ?? parsed), kgProxyBearer };
2602
2684
  // Use `parsed` (not raw `body`) as the no-enrichment fallback so that the
2603
2685
  // messages→prompt normalisation above is always forwarded even when no
2604
2686
  // env-var enrichments are applied.
@@ -2667,10 +2749,18 @@ const server = http.createServer(instrumentHandler('host-cp', async (req, res) =
2667
2749
  // bearer registration without additional plan metadata.
2668
2750
  }
2669
2751
 
2670
- // Enrich with anthropicBaseUrl from the host config.
2752
+ // Enrich with anthropicBaseUrl + kgProxy from the host config.
2671
2753
  const anthropicBaseUrl = readAnthropicBaseUrl();
2754
+ const kgProxyUrl = readKgProxyUrl();
2755
+ const kgProxyBearer = readKgProxyBearer();
2672
2756
  const planId = parsed.planId ?? parsed.session_id ?? `plan-${Date.now()}`;
2673
- const requestBody = { ...parsed, planId, ...(anthropicBaseUrl ? { anthropicBaseUrl } : {}) };
2757
+ const requestBody = {
2758
+ ...parsed,
2759
+ planId,
2760
+ ...(anthropicBaseUrl ? { anthropicBaseUrl } : {}),
2761
+ ...(kgProxyUrl ? { kgProxyUrl } : {}),
2762
+ ...(kgProxyBearer ? { kgProxyBearer } : {}),
2763
+ };
2674
2764
 
2675
2765
  const basicAuth = Buffer.from(`operator:${showcasePw}`).toString('base64');
2676
2766
  // Phase H h2: attach CF Access service-token headers when configured.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pleri/olam-cli",
3
- "version": "0.1.188",
3
+ "version": "0.1.195",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "olam": "./bin/olam.cjs"
@@ -32,6 +32,7 @@
32
32
  "test": "vitest run --passWithNoTests",
33
33
  "test:ci": "vitest run --reporter=basic --passWithNoTests",
34
34
  "test:docker": "vitest run --config vitest.config.docker.ts",
35
+ "test:e2e:k3d-https": "node e2e/k3d-https-e2e.spec.mjs",
35
36
  "audit:publish-deps": "node scripts/audit-publish-deps.mjs",
36
37
  "audit:cli-bundle-k8s": "node scripts/audit-cli-bundle-k8s.mjs",
37
38
  "audit:cli-package-contents": "node scripts/audit-cli-package-contents.mjs"