pikiloom 0.4.26 → 0.4.28

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/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DtjuI8NU.js} +1 -1
  2. package/dashboard/dist/assets/ConnectionModal-BIx6a5o1.js +1 -0
  3. package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-BINh_7NB.js} +1 -1
  4. package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-CHpd147b.js} +1 -1
  5. package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C4DbGxbG.js} +1 -1
  6. package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-D_PUi-5O.js} +1 -1
  7. package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-BahDEMqf.js} +1 -1
  8. package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-Cp5VXb_2.js} +1 -1
  9. package/dashboard/dist/assets/SessionPanel-DZVNXptx.js +1 -0
  10. package/dashboard/dist/assets/SystemTab-CLznS31f.js +1 -0
  11. package/dashboard/dist/assets/index-CLGovkkw.js +3 -0
  12. package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
  13. package/dashboard/dist/assets/index-DcYOI6rL.js +23 -0
  14. package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C8DCTJ44.js} +1 -1
  15. package/dashboard/dist/index.html +2 -2
  16. package/dist/agent/acp-client.js +0 -59
  17. package/dist/agent/artifacts.js +1 -38
  18. package/dist/agent/auto-update.js +2 -78
  19. package/dist/agent/await-resume.js +0 -20
  20. package/dist/agent/cli/auth.js +3 -51
  21. package/dist/agent/cli/catalog.js +0 -3
  22. package/dist/agent/cli/detector.js +1 -19
  23. package/dist/agent/cli/index.js +0 -3
  24. package/dist/agent/cli/registry.js +0 -25
  25. package/dist/agent/driver.js +0 -14
  26. package/dist/agent/drivers/claude-tui.js +2 -769
  27. package/dist/agent/drivers/claude.js +12 -631
  28. package/dist/agent/drivers/codex.js +11 -162
  29. package/dist/agent/drivers/gemini.js +6 -114
  30. package/dist/agent/drivers/hermes.js +0 -128
  31. package/dist/agent/goal.js +0 -45
  32. package/dist/agent/handover.js +1 -46
  33. package/dist/agent/images.js +7 -133
  34. package/dist/agent/index.js +1 -29
  35. package/dist/agent/mcp/bridge.js +1 -197
  36. package/dist/agent/mcp/extensions.js +5 -84
  37. package/dist/agent/mcp/oauth.js +2 -69
  38. package/dist/agent/mcp/registry.js +2 -33
  39. package/dist/agent/mcp/session-server.js +2 -45
  40. package/dist/agent/mcp/tools/ask-user.js +0 -6
  41. package/dist/agent/mcp/tools/await-resume.js +0 -15
  42. package/dist/agent/mcp/tools/goal.js +0 -25
  43. package/dist/agent/mcp/tools/types.js +0 -5
  44. package/dist/agent/mcp/tools/workspace.js +0 -23
  45. package/dist/agent/npm.js +0 -14
  46. package/dist/agent/session.js +86 -196
  47. package/dist/agent/skill-installer.js +3 -70
  48. package/dist/agent/skills.js +0 -43
  49. package/dist/agent/stream.js +5 -148
  50. package/dist/agent/types.js +0 -12
  51. package/dist/agent/utils.js +2 -120
  52. package/dist/bot/bot.js +3 -468
  53. package/dist/bot/command-ui.js +0 -45
  54. package/dist/bot/commands.js +0 -51
  55. package/dist/bot/headless-bot.js +0 -21
  56. package/dist/bot/host.js +1 -5
  57. package/dist/bot/human-loop.js +0 -8
  58. package/dist/bot/menu.js +0 -3
  59. package/dist/bot/orchestration.js +0 -3
  60. package/dist/bot/render-shared.js +0 -63
  61. package/dist/bot/session-hub.js +14 -92
  62. package/dist/bot/session-status.js +1 -5
  63. package/dist/bot/streaming.js +0 -53
  64. package/dist/browser-profile.js +0 -51
  65. package/dist/browser-supervisor.js +0 -75
  66. package/dist/catalog/cli-tools.js +0 -53
  67. package/dist/catalog/index.js +0 -18
  68. package/dist/catalog/local-models.js +0 -18
  69. package/dist/catalog/mcp-servers.js +0 -35
  70. package/dist/catalog/skill-repos.js +0 -32
  71. package/dist/channels/base.js +0 -19
  72. package/dist/channels/dingtalk/bot.js +0 -3
  73. package/dist/channels/dingtalk/channel.js +0 -33
  74. package/dist/channels/discord/bot.js +0 -7
  75. package/dist/channels/discord/channel.js +0 -22
  76. package/dist/channels/feishu/bot.js +0 -71
  77. package/dist/channels/feishu/channel.js +0 -78
  78. package/dist/channels/feishu/markdown.js +0 -5
  79. package/dist/channels/feishu/render.js +0 -45
  80. package/dist/channels/health.js +0 -46
  81. package/dist/channels/slack/bot.js +0 -7
  82. package/dist/channels/slack/channel.js +1 -27
  83. package/dist/channels/states.js +0 -3
  84. package/dist/channels/telegram/bot.js +0 -60
  85. package/dist/channels/telegram/channel.js +2 -99
  86. package/dist/channels/telegram/directory.js +0 -3
  87. package/dist/channels/telegram/live-preview.js +0 -8
  88. package/dist/channels/telegram/render.js +0 -30
  89. package/dist/channels/wecom/bot.js +0 -3
  90. package/dist/channels/wecom/channel.js +0 -58
  91. package/dist/channels/weixin/api.js +0 -9
  92. package/dist/channels/weixin/bot.js +0 -88
  93. package/dist/channels/weixin/channel.js +0 -6
  94. package/dist/cli/autostart.js +1 -44
  95. package/dist/cli/channel-supervisor.js +1 -59
  96. package/dist/cli/channels.js +0 -6
  97. package/dist/cli/main.js +1 -145
  98. package/dist/cli/onboarding.js +0 -6
  99. package/dist/cli/run.js +0 -11
  100. package/dist/cli/setup-wizard.js +0 -3
  101. package/dist/core/config/runtime-config.js +0 -38
  102. package/dist/core/config/user-config.js +0 -63
  103. package/dist/core/config/validation.js +0 -9
  104. package/dist/core/constants.js +0 -268
  105. package/dist/core/git.js +0 -31
  106. package/dist/core/legacy-compat.js +1 -28
  107. package/dist/core/logging.js +0 -3
  108. package/dist/core/platform.js +0 -28
  109. package/dist/core/process-control.js +0 -8
  110. package/dist/core/secrets/index.js +0 -17
  111. package/dist/core/secrets/inline-seal.js +0 -7
  112. package/dist/core/secrets/ref.js +0 -19
  113. package/dist/core/secrets/resolver.js +0 -8
  114. package/dist/core/secrets/store.js +1 -12
  115. package/dist/core/utils.js +1 -37
  116. package/dist/core/version.js +0 -3
  117. package/dist/dashboard/platform.js +1 -35
  118. package/dist/dashboard/routes/agents.js +2 -71
  119. package/dist/dashboard/routes/cli.js +0 -27
  120. package/dist/dashboard/routes/config.js +0 -54
  121. package/dist/dashboard/routes/extensions.js +0 -132
  122. package/dist/dashboard/routes/local-models.js +0 -68
  123. package/dist/dashboard/routes/models.js +0 -46
  124. package/dist/dashboard/routes/sessions.js +0 -100
  125. package/dist/dashboard/runtime.js +1 -58
  126. package/dist/dashboard/server.js +0 -49
  127. package/dist/dashboard/session-control.js +2 -69
  128. package/dist/model/catalog.js +1 -17
  129. package/dist/model/index.js +0 -15
  130. package/dist/model/injector.js +1 -181
  131. package/dist/model/provider-models.js +1 -48
  132. package/dist/model/responses-bridge.js +3 -45
  133. package/dist/model/store.js +0 -17
  134. package/dist/model/types.js +0 -12
  135. package/dist/model/validation.js +1 -18
  136. package/dist/pikichannel/adapter-pikiloom.js +0 -31
  137. package/dist/pikichannel/code.js +0 -12
  138. package/dist/pikichannel/codec.js +0 -14
  139. package/dist/pikichannel/host.js +4 -33
  140. package/dist/pikichannel/protocol.js +0 -52
  141. package/dist/pikichannel/rendezvous-broker.js +5 -26
  142. package/dist/pikichannel/rendezvous-host.js +3 -16
  143. package/dist/pikichannel/server.js +6 -55
  144. package/dist/pikichannel/transport.js +0 -25
  145. package/dist/pikichannel/transports/webrtc-host.js +0 -18
  146. package/dist/pikichannel/transports/webrtc-shared.js +4 -34
  147. package/dist/pikichannel/transports/websocket-host.js +1 -14
  148. package/dist/pikichannel/turn.js +2 -69
  149. package/dist/pikichannel/web/sdk.js +14 -71
  150. package/package.json +1 -1
  151. package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
  152. package/dashboard/dist/assets/SessionPanel-_0dLWvEb.js +0 -1
  153. package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
  154. package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
  155. package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
  156. package/dashboard/dist/assets/index-CyCspgtc.css +0 -1
@@ -1,19 +1,11 @@
1
- /**
2
- * Codex CLI driver: HTTP server management, streaming, human-in-the-loop.
3
- */
4
1
  import fs from 'node:fs';
5
2
  import path from 'node:path';
6
3
  import { execSync, spawn } from 'node:child_process';
7
4
  import { registerDriver } from '../driver.js';
8
5
  import { terminateProcessTree } from '../../core/process-control.js';
9
- import { mimeForExt,
10
- // shared helpers
11
- agentLog, agentWarn, buildStreamPreviewMeta, pushRecentActivity, normalizeActivityLine, firstNonEmptyLine, shortValue, numberOrNull, normalizeStreamPreviewPlan, IMAGE_EXTS, listPikiloomSessions, findPikiloomSession, mergeManagedAndNativeSessions, managedRecordToSessionInfo, stripInjectedPrompts, sanitizeSessionUserPreviewText, computeContext, readTailLines, applyTurnWindow, roundPercent, toIsoFromEpochSeconds, labelFromWindowMinutes, usageWindowFromRateLimit, parseJsonTail, emptyUsage, attachAgentImage, codexHome, Q, } from '../index.js';
6
+ import { mimeForExt, agentLog, agentWarn, buildStreamPreviewMeta, pushRecentActivity, normalizeActivityLine, firstNonEmptyLine, shortValue, numberOrNull, normalizeStreamPreviewPlan, IMAGE_EXTS, listPikiloomSessions, findPikiloomSession, mergeManagedAndNativeSessions, managedRecordToSessionInfo, stripInjectedPrompts, sanitizeSessionUserPreviewText, computeContext, readTailLines, applyTurnWindow, roundPercent, toIsoFromEpochSeconds, labelFromWindowMinutes, usageWindowFromRateLimit, parseJsonTail, emptyUsage, attachAgentImage, codexHome, Q, } from '../index.js';
12
7
  import { CODEX_APPSERVER_SPAWN_TIMEOUT_MS as _CODEX_APPSERVER_SPAWN_TIMEOUT_MS, CODEX_STREAM_HARD_KILL_GRACE_MS, SESSION_RUNNING_THRESHOLD_MS, } from '../../core/constants.js';
13
8
  import { getHome } from '../../core/platform.js';
14
- // ---------------------------------------------------------------------------
15
- // App-server JSON-RPC client
16
- // ---------------------------------------------------------------------------
17
9
  const CODEX_APPSERVER_SPAWN_TIMEOUT_MS = _CODEX_APPSERVER_SPAWN_TIMEOUT_MS;
18
10
  export class CodexAppServer {
19
11
  proc = null;
@@ -42,9 +34,6 @@ export class CodexAppServer {
42
34
  return new Promise((resolve) => {
43
35
  const timer = setTimeout(() => { this.kill(); resolve(false); }, CODEX_APPSERVER_SPAWN_TIMEOUT_MS);
44
36
  const args = ['app-server'];
45
- // Always enable codex's native /goal feature so pikiloom can route through
46
- // codex's own `thread/goal/*` RPC + continuation engine. User-supplied -c
47
- // overrides win.
48
37
  const overrides = this.configOverrides.some(entry => /^features\.goals\s*=/.test(entry))
49
38
  ? this.configOverrides
50
39
  : [...this.configOverrides, 'features.goals=true'];
@@ -109,15 +98,11 @@ export class CodexAppServer {
109
98
  proc.on('close', () => {
110
99
  this.ready = false;
111
100
  this.proc = null;
112
- // Resolve any pending RPC calls so callers don't hang forever
113
101
  for (const [id, cb] of this.pending) {
114
102
  cb({ error: { message: 'process exited before responding' } });
115
103
  }
116
104
  this.pending.clear();
117
105
  });
118
- // Declare experimentalApi so `thread/goal/*` is reachable. Codex 0.130+
119
- // gates these RPCs behind that capability — without it, every goal call
120
- // returns "requires experimentalApi capability".
121
106
  this.call('initialize', {
122
107
  clientInfo: { name: 'pikiloom', version: '0.2.0' },
123
108
  capabilities: { experimentalApi: true },
@@ -213,7 +198,6 @@ export class CodexAppServer {
213
198
  catch { }
214
199
  }
215
200
  }
216
- /** Singleton app-server for shared operations (sessions, models, usage). */
217
201
  let _sharedServer = null;
218
202
  function getSharedServer() {
219
203
  if (!_sharedServer)
@@ -248,7 +232,6 @@ function unwrapGoal(raw) {
248
232
  updatedAt: typeof g.updatedAt === 'number' ? g.updatedAt : 0,
249
233
  };
250
234
  }
251
- /** Set / replace the active goal on a codex thread. Codex auto-starts a continuation turn if it is idle. */
252
235
  export async function setCodexGoal(opts) {
253
236
  const srv = await ensureSharedServerForGoal();
254
237
  if (!srv)
@@ -291,9 +274,6 @@ export async function pauseCodexGoal(threadId) {
291
274
  export async function resumeCodexGoal(threadId) {
292
275
  return setCodexGoal({ threadId, status: 'active' });
293
276
  }
294
- // ---------------------------------------------------------------------------
295
- // Effort mapping
296
- // ---------------------------------------------------------------------------
297
277
  const EFFORT_MAP = {
298
278
  low: 'low', medium: 'medium', high: 'high', min: 'minimal', max: 'xhigh',
299
279
  };
@@ -482,19 +462,9 @@ function formatCodexPlanSummary(plan) {
482
462
  lines.push(`[${step.status}] ${step.step}`);
483
463
  return lines.join('\n').trim();
484
464
  }
485
- /**
486
- * Resolve the on-disk path Codex writes generated images to. Format:
487
- * `$CODEX_HOME/generated_images/<sessionId>/<call_id>.png`
488
- *
489
- * The developer-message Codex injects when its built-in `image_gen` tool fires
490
- * documents this convention (`Generated images are saved to … as …/<id>.png`).
491
- * We honour `$CODEX_HOME`; the SKILL.md prescribes `.png` as the only output
492
- * format for the built-in tool.
493
- */
494
465
  function codexImagePathFor(sessionId, callId) {
495
466
  return path.join(codexHome(), 'generated_images', sessionId, `${callId}.png`);
496
467
  }
497
- /** Build an image MessageBlock from a Codex `image_generation_call` payload. */
498
468
  function buildCodexImageBlock(sessionId, payload, phase) {
499
469
  const callId = typeof payload?.id === 'string' ? payload.id
500
470
  : typeof payload?.call_id === 'string' ? payload.call_id
@@ -503,22 +473,8 @@ function buildCodexImageBlock(sessionId, payload, phase) {
503
473
  return null;
504
474
  const filePath = codexImagePathFor(sessionId, callId);
505
475
  const caption = typeof payload?.revised_prompt === 'string' ? payload.revised_prompt : undefined;
506
- return attachAgentImage({ imagePath: filePath, caption, phase });
507
- }
508
- /**
509
- * Idempotently push the image MessageBlock for a Codex `image_gen` call to the
510
- * stream state. Returns true if a block was emitted on this invocation.
511
- *
512
- * Codex emits image_generation_call across several inconsistent paths depending
513
- * on the app-server build: `item/started`, `item/completed`, and
514
- * `rawResponseItem/completed` may all fire — or some may be skipped (we've seen
515
- * runs where only `image_generation_end` lands and the response item is frozen
516
- * at status="generating", so no completion notification ever arrives). This
517
- * helper lets every code path call into one place; the pendingImageGen map is
518
- * the source of truth for "not yet emitted." On success we drop the pending
519
- * entry and decrement the in-flight counter; on miss (file not yet on disk) we
520
- * leave the entry so a later event — or the turn-end drain — can retry.
521
- */
476
+ return attachAgentImage({ imagePath: filePath, caption, captionKind: 'prompt', phase });
477
+ }
522
478
  function tryEmitCodexImageBlock(s, callId, revisedPrompt) {
523
479
  if (!callId || !s.sessionId)
524
480
  return false;
@@ -711,9 +667,6 @@ function buildCodexPreviewText(s) {
711
667
  return `${commentary}\n\n${finalText}`;
712
668
  return commentary || finalText;
713
669
  }
714
- // ---------------------------------------------------------------------------
715
- // Token usage
716
- // ---------------------------------------------------------------------------
717
670
  function buildCodexCumulativeUsage(raw) {
718
671
  if (!raw || typeof raw !== 'object')
719
672
  return null;
@@ -769,19 +722,12 @@ function applyCodexTokenUsage(s, rawUsage, prev) {
769
722
  if (lastCached == null)
770
723
  s.cachedInputTokens = prev ? Math.max(0, total.cached - prev.cached) : total.cached;
771
724
  }
772
- // NOTE: do NOT set s.contextUsedTokens from cumulative totals —
773
- // those counters span the full thread, not the current turn. Use the per-turn
774
- // `last` usage only. `cached_input_tokens` is already a subset of
775
- // `input_tokens`, so adding it again inflates the context percentage.
776
725
  if (!s.byokContextWindow) {
777
726
  const contextWindow = numberOrNull(info.modelContextWindow, info.model_context_window, rawUsage.modelContextWindow, rawUsage.model_context_window);
778
727
  if (contextWindow != null && contextWindow > 0)
779
728
  s.contextWindow = contextWindow;
780
729
  }
781
730
  }
782
- // ---------------------------------------------------------------------------
783
- // Turn input
784
- // ---------------------------------------------------------------------------
785
731
  export function buildCodexTurnInput(prompt, attachments) {
786
732
  const input = [];
787
733
  for (const filePath of attachments) {
@@ -796,8 +742,6 @@ export function buildCodexTurnInput(prompt, attachments) {
796
742
  return input;
797
743
  }
798
744
  function createCodexStreamState(opts) {
799
- // BYOK: lock in the provider-cached context window so codex's own (often
800
- // wrong, model-dependent) `model_context_window` reports get ignored later.
801
745
  const byokWindow = opts.byokContextWindow && opts.byokContextWindow > 0
802
746
  ? opts.byokContextWindow
803
747
  : null;
@@ -839,14 +783,10 @@ function codexErrorResult(error, start, sessionId, model, thinkingEffort) {
839
783
  codexCumulative: null, stopReason: null, incomplete: true, activity: null,
840
784
  };
841
785
  }
842
- // ---------------------------------------------------------------------------
843
- // Stream notification handler (extracted from doCodexStream)
844
- // ---------------------------------------------------------------------------
845
786
  function handleCodexNotification(method, params, s, opts, deadline, emit, hardTimer, settleTurnDone, publishTurnControl) {
846
787
  if (Date.now() > deadline)
847
788
  return;
848
789
  if (params.threadId !== s.sessionId) {
849
- // Only turn/started and model/rerouted are checked below; all others already filter on threadId.
850
790
  if (method !== 'turn/started' && method !== 'model/rerouted')
851
791
  return;
852
792
  if (params.threadId !== s.sessionId)
@@ -926,10 +866,6 @@ function handleItemStarted(item, s, emit) {
926
866
  emit();
927
867
  }
928
868
  }
929
- // Codex's built-in `image_gen` tool surfaces as a distinct item type. Track
930
- // the in-flight count so renderers can show "Generating image…" while the
931
- // bytes are being written. Item id naming differs across Codex versions
932
- // (`imageGenerationCall` / `image_generation_call`); accept either form.
933
869
  if (item.id && (item.type === 'imageGenerationCall' || item.type === 'image_generation_call')) {
934
870
  if (!s.pendingImageGen.has(item.id))
935
871
  s.generatingImages++;
@@ -938,11 +874,6 @@ function handleItemStarted(item, s, emit) {
938
874
  : typeof item.revised_prompt === 'string' ? item.revised_prompt : undefined,
939
875
  });
940
876
  pushRecentActivity(s.recentNarrative, 'Generating image...');
941
- // Some codex builds never fire a "completed" event for image_generation_call
942
- // (rollout shows the item frozen at status="generating"). The PNG is on
943
- // disk by the time item/started lands, so try an opportunistic emit here;
944
- // tryEmit is a no-op when the file isn't ready yet — handleItemCompleted /
945
- // rawResponseItem/completed / the turn-end drain will pick it up later.
946
877
  tryEmitCodexImageBlock(s, item.id);
947
878
  emit();
948
879
  }
@@ -1005,17 +936,10 @@ function handleRawResponseItemCompleted(item, s, emit) {
1005
936
  return;
1006
937
  }
1007
938
  }
1008
- // image_generation_call: Codex's built-in image_gen has just finished writing
1009
- // the file at $CODEX_HOME/generated_images/<sessionId>/<id>.png. Read it into
1010
- // an image MessageBlock so the bot's final-reply path can dispatch it to IM
1011
- // channels and the dashboard renders it inline.
1012
939
  if (item?.type === 'image_generation_call' || item?.type === 'imageGenerationCall') {
1013
940
  const callId = typeof item.id === 'string' ? item.id
1014
941
  : typeof item.call_id === 'string' ? item.call_id : '';
1015
942
  if (callId) {
1016
- // Merge revised_prompt from this event with anything we stashed earlier —
1017
- // different Codex builds attach it on different events. Idempotent helper
1018
- // handles the dedupe against item/started + handleItemCompleted paths.
1019
943
  const revisedPrompt = typeof item.revised_prompt === 'string' ? item.revised_prompt
1020
944
  : typeof item.revisedPrompt === 'string' ? item.revisedPrompt
1021
945
  : undefined;
@@ -1036,12 +960,6 @@ function handleCompletedAgentMessage(item, s, emit) {
1036
960
  const text = item.text?.trim();
1037
961
  if (text) {
1038
962
  s.msgs.push(text);
1039
- // When Codex emits the final-answer body without intervening deltas
1040
- // (short replies, certain provider configs), `s.text` is empty and the
1041
- // preview would stay blank until doCodexStream's turn-end backfill.
1042
- // Append the completed body now so the live stream catches up. The
1043
- // delta-seen set tells us whether we'd be duplicating content already
1044
- // accumulated via item/agentMessage/delta.
1045
963
  const alreadyStreamed = item.id && s.deltaSeenForItem.has(item.id);
1046
964
  if (!alreadyStreamed) {
1047
965
  s.text = s.text.trim() ? `${s.text.trim()}\n\n${text}` : text;
@@ -1098,9 +1016,6 @@ function handleTurnPlanUpdated(params, s, emit) {
1098
1016
  };
1099
1017
  emit();
1100
1018
  }
1101
- // ---------------------------------------------------------------------------
1102
- // Stream request handler (extracted from doCodexStream)
1103
- // ---------------------------------------------------------------------------
1104
1019
  async function handleCodexRequest(method, params, requestId, s, opts, emit) {
1105
1020
  const interaction = toAgentInteraction(method, params, requestId);
1106
1021
  if (!interaction)
@@ -1119,9 +1034,6 @@ async function handleCodexRequest(method, params, requestId, s, opts, emit) {
1119
1034
  }
1120
1035
  return defaultAgentInteractionResponse(interaction);
1121
1036
  }
1122
- // ---------------------------------------------------------------------------
1123
- // Stream via app-server
1124
- // ---------------------------------------------------------------------------
1125
1037
  export async function doCodexStream(opts) {
1126
1038
  const start = Date.now();
1127
1039
  const srv = new CodexAppServer();
@@ -1140,9 +1052,6 @@ export async function doCodexStream(opts) {
1140
1052
  config.push(opts.codexExtraArgs[++i]);
1141
1053
  }
1142
1054
  }
1143
- // Enable codex's native `/goal` feature so `thread/goal/*` RPCs work and
1144
- // the model gets the native `create_goal` / `update_goal` / `get_goal`
1145
- // tools + continuation engine. User-provided -c overrides win.
1146
1055
  if (!config.some(entry => /^features\.goals\s*=/.test(entry))) {
1147
1056
  config.push('features.goals=true');
1148
1057
  }
@@ -1189,7 +1098,6 @@ export async function doCodexStream(opts) {
1189
1098
  agentWarn(`[codex-rpc] onCodexTurnReady error: ${error?.message || error}`);
1190
1099
  }
1191
1100
  };
1192
- // thread/start or thread/resume
1193
1101
  let threadResp;
1194
1102
  const threadParams = {
1195
1103
  cwd: opts.workdir,
@@ -1223,7 +1131,6 @@ export async function doCodexStream(opts) {
1223
1131
  }
1224
1132
  }
1225
1133
  agentLog(`[codex-rpc] thread ready: id=${s.sessionId} model=${s.model}`);
1226
- // turn/start
1227
1134
  const input = buildCodexTurnInput(opts.prompt, opts.attachments || []);
1228
1135
  const deadline = start + opts.timeout * 1000;
1229
1136
  const turnDone = new Promise((resolve) => {
@@ -1264,9 +1171,6 @@ export async function doCodexStream(opts) {
1264
1171
  s.turnError = s.turnError || 'Interrupted by user.';
1265
1172
  agentWarn(`[codex-rpc] abort requested thread=${s.sessionId || '?'} turn=${s.turnId || '?'}`);
1266
1173
  if (s.turnId && s.sessionId) {
1267
- // Send turn/interrupt and wait for Codex to acknowledge before settling.
1268
- // Don't kill the process here — let the finally block handle it after
1269
- // Codex has had time to persist the interrupted session state.
1270
1174
  srv.call('turn/interrupt', { threadId: s.sessionId, turnId: s.turnId }, 5_000)
1271
1175
  .finally(() => settleTurnDone?.());
1272
1176
  }
@@ -1278,7 +1182,6 @@ export async function doCodexStream(opts) {
1278
1182
  if (opts.abortSignal?.aborted)
1279
1183
  abortStream();
1280
1184
  opts.abortSignal?.addEventListener('abort', abortStream, { once: true });
1281
- // Log equivalent CLI command for reproducibility
1282
1185
  const cliParts = ['codex'];
1283
1186
  if (opts.codexModel)
1284
1187
  cliParts.push('--model', opts.codexModel);
@@ -1317,11 +1220,6 @@ export async function doCodexStream(opts) {
1317
1220
  s.text = s.msgs.join('\n\n');
1318
1221
  if (!s.thinking.trim() && s.thinkParts.length)
1319
1222
  s.thinking = s.thinkParts.join('\n\n');
1320
- // Drain any image_gen calls that started but never received a completion
1321
- // event. We've observed runs where the response_item stays at
1322
- // status="generating" and no `rawResponseItem/completed` fires — the PNG
1323
- // is on disk, we just never got told to emit it. Try once at turn end;
1324
- // tryEmit is a no-op for already-emitted entries.
1325
1223
  for (const callId of [...s.pendingImageGen.keys()]) {
1326
1224
  tryEmitCodexImageBlock(s, callId);
1327
1225
  }
@@ -1354,10 +1252,6 @@ export async function doCodexStream(opts) {
1354
1252
  srv.kill();
1355
1253
  }
1356
1254
  }
1357
- // ---------------------------------------------------------------------------
1358
- // Sessions
1359
- // ---------------------------------------------------------------------------
1360
- /** Load title index from ~/.codex/session_index.jsonl (deduped, last entry wins). */
1361
1255
  function loadCodexSessionIndex() {
1362
1256
  const home = getHome();
1363
1257
  if (!home)
@@ -1376,13 +1270,12 @@ function loadCodexSessionIndex() {
1376
1270
  if (entry.id)
1377
1271
  map.set(entry.id, { threadName: entry.thread_name || '', updatedAt: entry.updated_at || '' });
1378
1272
  }
1379
- catch { /* skip */ }
1273
+ catch { }
1380
1274
  }
1381
1275
  }
1382
- catch { /* skip */ }
1276
+ catch { }
1383
1277
  return map;
1384
1278
  }
1385
- /** Scan ~/.codex/sessions/ rollout files to find sessions matching the given workdir. */
1386
1279
  function extractCodexTailQA(filePath) {
1387
1280
  const lines = readTailLines(filePath, 128 * 1024);
1388
1281
  let lastQuestion = null;
@@ -1410,7 +1303,7 @@ function extractCodexTailQA(filePath) {
1410
1303
  }
1411
1304
  }
1412
1305
  }
1413
- catch { /* skip */ }
1306
+ catch { }
1414
1307
  }
1415
1308
  return { lastQuestion, lastAnswer, lastMessageText };
1416
1309
  }
@@ -1439,12 +1332,6 @@ function readCodexSessionHead(filePath) {
1439
1332
  return null;
1440
1333
  }
1441
1334
  }
1442
- // Per-file cache of the head meta + tail Q&A. getNativeCodexSessions walks the
1443
- // whole y/m/d rollout tree and reads each file's 8KB head (to filter by cwd) on
1444
- // every list request AND per workspace×agent in the overview fan-out. Keyed by
1445
- // (mtime,size): unchanged rollouts — including other workspaces' files passed
1446
- // while filtering — are never re-read. `running` depends on Date.now() so it is
1447
- // recomputed per call, not cached.
1448
1335
  const nativeCodexContentCache = new Map();
1449
1336
  function getNativeCodexSessions(workdir, limit) {
1450
1337
  const home = getHome();
@@ -1455,9 +1342,6 @@ function getNativeCodexSessions(workdir, limit) {
1455
1342
  return [];
1456
1343
  const resolvedWorkdir = path.resolve(workdir);
1457
1344
  const titleIndex = loadCodexSessionIndex();
1458
- // Collect rollout files across the year/month/day tree, newest-first, then read
1459
- // bodies only as far as needed: `limit` applies to a recency-sorted merge
1460
- // downstream, so older rollouts can't surface in a top-`limit` view.
1461
1345
  const files = [];
1462
1346
  const walkDir = (dir) => {
1463
1347
  let entries;
@@ -1478,7 +1362,7 @@ function getNativeCodexSessions(workdir, limit) {
1478
1362
  try {
1479
1363
  files.push({ filePath: fullPath, stat: fs.statSync(fullPath) });
1480
1364
  }
1481
- catch { /* skip */ }
1365
+ catch { }
1482
1366
  }
1483
1367
  };
1484
1368
  walkDir(sessionsDir);
@@ -1488,8 +1372,6 @@ function getNativeCodexSessions(workdir, limit) {
1488
1372
  for (const { filePath, stat } of files) {
1489
1373
  let cached = nativeCodexContentCache.get(filePath);
1490
1374
  if (!cached || cached.mtimeMs !== stat.mtimeMs || cached.size !== stat.size) {
1491
- // First line can be very large (base_instructions), so read a head chunk
1492
- // and extract session_meta via regex instead of a full JSON parse.
1493
1375
  const meta = readCodexSessionHead(filePath);
1494
1376
  const matches = !!meta && !meta.isSubagent && path.resolve(meta.cwd) === resolvedWorkdir;
1495
1377
  cached = { mtimeMs: stat.mtimeMs, size: stat.size, meta, tailQA: matches ? extractCodexTailQA(filePath) : null };
@@ -1613,9 +1495,6 @@ function getCodexSessionTailFromRollout(opts) {
1613
1495
  }
1614
1496
  function getCodexSessions(workdir, limit) {
1615
1497
  const resolvedWorkdir = path.resolve(workdir);
1616
- // Merge pikiloom-tracked sessions with native Codex sessions
1617
- // Canonical record→SessionInfo mapper (single source of truth) — see claude.ts.
1618
- // Hand-rolling dropped thinkingEffort/workflowEnabled/profileId from the merge.
1619
1498
  const pikiloomSessions = listPikiloomSessions(resolvedWorkdir, 'codex').map(managedRecordToSessionInfo);
1620
1499
  const nativeSessions = getNativeCodexSessions(resolvedWorkdir, limit);
1621
1500
  const merged = mergeManagedAndNativeSessions(pikiloomSessions, nativeSessions);
@@ -1663,16 +1542,12 @@ async function getCodexSessionTail(opts) {
1663
1542
  return { ok: true, messages, error: null };
1664
1543
  return getCodexSessionTailFromRollout(opts);
1665
1544
  }
1666
- // ---------------------------------------------------------------------------
1667
- // Session messages (full content)
1668
- // ---------------------------------------------------------------------------
1669
1545
  async function getCodexSessionMessages(opts) {
1670
1546
  if (opts.rich) {
1671
1547
  const rolloutResult = getCodexSessionMessagesFromRollout(opts);
1672
1548
  if (rolloutResult.ok)
1673
1549
  return rolloutResult;
1674
1550
  }
1675
- // Try RPC first
1676
1551
  const srv = getSharedServer();
1677
1552
  if (await srv.ensureRunning()) {
1678
1553
  try {
@@ -1690,7 +1565,6 @@ async function getCodexSessionMessages(opts) {
1690
1565
  if (c.type === 'text' && c.text)
1691
1566
  parts.push(c.text);
1692
1567
  else if (c.type === 'localImage' && c.path) {
1693
- // Read the image file if it still exists
1694
1568
  try {
1695
1569
  if (fs.existsSync(c.path) && fs.statSync(c.path).size <= 4 * 1024 * 1024) {
1696
1570
  const ext = path.extname(c.path).toLowerCase();
@@ -1698,7 +1572,7 @@ async function getCodexSessionMessages(opts) {
1698
1572
  blocks.push({ type: 'image', content: `data:${mimeForExt(ext)};base64,${data}` });
1699
1573
  }
1700
1574
  }
1701
- catch { /* skip unreadable images */ }
1575
+ catch { }
1702
1576
  }
1703
1577
  }
1704
1578
  if (parts.length || blocks.length) {
@@ -1730,9 +1604,8 @@ async function getCodexSessionMessages(opts) {
1730
1604
  }
1731
1605
  }
1732
1606
  }
1733
- catch { /* fall through to rollout */ }
1607
+ catch { }
1734
1608
  }
1735
- // Fallback: read full rollout file
1736
1609
  return getCodexSessionMessagesFromRollout(opts);
1737
1610
  }
1738
1611
  function getCodexSessionMessagesFromRollout(opts) {
@@ -1867,9 +1740,6 @@ function getCodexSessionMessagesFromRollout(opts) {
1867
1740
  sawAssistantResponseItems = true;
1868
1741
  continue;
1869
1742
  }
1870
- // image_generation_call: Codex's built-in `image_gen` tool — surface the
1871
- // file on disk as an image block so historical sessions render images
1872
- // (not just text). Path: $CODEX_HOME/generated_images/<sessionId>/<id>.png
1873
1743
  if (payload.type === 'image_generation_call' || payload.type === 'imageGenerationCall') {
1874
1744
  const block = buildCodexImageBlock(opts.sessionId, payload);
1875
1745
  if (block) {
@@ -1900,10 +1770,7 @@ function getCodexSessionMessagesFromRollout(opts) {
1900
1770
  return { ok: false, messages: [], totalTurns: 0, error: e?.message || 'Failed to read session history' };
1901
1771
  }
1902
1772
  }
1903
- // ---------------------------------------------------------------------------
1904
- // Models (with TTL cache + stale fallback)
1905
- // ---------------------------------------------------------------------------
1906
- const MODEL_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
1773
+ const MODEL_CACHE_TTL_MS = 5 * 60 * 1000;
1907
1774
  let modelCache = null;
1908
1775
  function pushModel(models, seen, id, alias) {
1909
1776
  const cleanId = id.trim();
@@ -1912,7 +1779,6 @@ function pushModel(models, seen, id, alias) {
1912
1779
  seen.add(cleanId);
1913
1780
  models.push({ id: cleanId, alias: alias?.trim() || null });
1914
1781
  }
1915
- /** Merge currentModel into a cached result so the selected model always appears first. */
1916
1782
  function withCurrentModel(cached, currentModel) {
1917
1783
  if (!currentModel?.trim())
1918
1784
  return cached;
@@ -1922,11 +1788,9 @@ function withCurrentModel(cached, currentModel) {
1922
1788
  return { ...cached, models: [{ id: cm, alias: null }, ...cached.models] };
1923
1789
  }
1924
1790
  async function discoverCodexModels(opts) {
1925
- // Return cached result if still fresh
1926
1791
  if (modelCache && Date.now() - modelCache.fetchedAt < MODEL_CACHE_TTL_MS) {
1927
1792
  return withCurrentModel(modelCache.result, opts.currentModel);
1928
1793
  }
1929
- // Try fetching fresh
1930
1794
  const srv = getSharedServer();
1931
1795
  if (!(await srv.ensureRunning())) {
1932
1796
  if (modelCache)
@@ -1954,9 +1818,6 @@ async function discoverCodexModels(opts) {
1954
1818
  modelCache = { result, fetchedAt: Date.now() };
1955
1819
  return result;
1956
1820
  }
1957
- // ---------------------------------------------------------------------------
1958
- // Usage
1959
- // ---------------------------------------------------------------------------
1960
1821
  function getCodexStateDbPath(home) {
1961
1822
  const root = path.join(home, '.codex');
1962
1823
  if (!fs.existsSync(root))
@@ -1994,9 +1855,6 @@ function getCodexUsageFromStateDb(home) {
1994
1855
  return null;
1995
1856
  try {
1996
1857
  const query = "SELECT ts || '|' || message FROM logs WHERE message LIKE '%codex.rate_limits%' ORDER BY ts DESC LIMIT 1;";
1997
- // stdio: 'pipe' keeps sqlite3 stderr ("no such table", "unable to open") out
1998
- // of pikiloom's own stderr — this probe is best-effort and the catch below
1999
- // already swallows failures.
2000
1858
  const out = execSync(`sqlite3 -noheader ${Q(dbPath)} ${Q(query)}`, { encoding: 'utf-8', timeout: 3000, stdio: ['ignore', 'pipe', 'pipe'] }).trim();
2001
1859
  if (!out)
2002
1860
  return null;
@@ -2103,9 +1961,6 @@ export async function getCodexUsageLive() {
2103
1961
  windows, error: windows.length > 0 ? null : 'No rate limit windows.',
2104
1962
  };
2105
1963
  }
2106
- // ---------------------------------------------------------------------------
2107
- // Driver
2108
- // ---------------------------------------------------------------------------
2109
1964
  class CodexDriver {
2110
1965
  id = 'codex';
2111
1966
  cmd = 'codex';
@@ -2136,12 +1991,6 @@ class CodexDriver {
2136
1991
  }
2137
1992
  shutdown() { shutdownCodexServer(); }
2138
1993
  }
2139
- /**
2140
- * Locate and remove the codex rollout file backing a session. Codex stores
2141
- * sessions under `~/.codex/sessions/<year>/<month>/<day>/rollout-<...>.jsonl`,
2142
- * keyed by `meta.sessionId` inside the file rather than the filename — so we
2143
- * walk the tree and match on the parsed head metadata, scoped to `workdir`.
2144
- */
2145
1994
  async function deleteNativeCodexSession(workdir, sessionId) {
2146
1995
  const home = getHome();
2147
1996
  if (!home || !sessionId)
@@ -2178,7 +2027,7 @@ async function deleteNativeCodexSession(workdir, sessionId) {
2178
2027
  removed.push(full);
2179
2028
  return true;
2180
2029
  }
2181
- catch { /* skip */ }
2030
+ catch { }
2182
2031
  }
2183
2032
  return false;
2184
2033
  };