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.
- package/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DtjuI8NU.js} +1 -1
- package/dashboard/dist/assets/ConnectionModal-BIx6a5o1.js +1 -0
- package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-BINh_7NB.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-CHpd147b.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C4DbGxbG.js} +1 -1
- package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-D_PUi-5O.js} +1 -1
- package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-BahDEMqf.js} +1 -1
- package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-Cp5VXb_2.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-DZVNXptx.js +1 -0
- package/dashboard/dist/assets/SystemTab-CLznS31f.js +1 -0
- package/dashboard/dist/assets/index-CLGovkkw.js +3 -0
- package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
- package/dashboard/dist/assets/index-DcYOI6rL.js +23 -0
- package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C8DCTJ44.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/acp-client.js +0 -59
- package/dist/agent/artifacts.js +1 -38
- package/dist/agent/auto-update.js +2 -78
- package/dist/agent/await-resume.js +0 -20
- package/dist/agent/cli/auth.js +3 -51
- package/dist/agent/cli/catalog.js +0 -3
- package/dist/agent/cli/detector.js +1 -19
- package/dist/agent/cli/index.js +0 -3
- package/dist/agent/cli/registry.js +0 -25
- package/dist/agent/driver.js +0 -14
- package/dist/agent/drivers/claude-tui.js +2 -769
- package/dist/agent/drivers/claude.js +12 -631
- package/dist/agent/drivers/codex.js +11 -162
- package/dist/agent/drivers/gemini.js +6 -114
- package/dist/agent/drivers/hermes.js +0 -128
- package/dist/agent/goal.js +0 -45
- package/dist/agent/handover.js +1 -46
- package/dist/agent/images.js +7 -133
- package/dist/agent/index.js +1 -29
- package/dist/agent/mcp/bridge.js +1 -197
- package/dist/agent/mcp/extensions.js +5 -84
- package/dist/agent/mcp/oauth.js +2 -69
- package/dist/agent/mcp/registry.js +2 -33
- package/dist/agent/mcp/session-server.js +2 -45
- package/dist/agent/mcp/tools/ask-user.js +0 -6
- package/dist/agent/mcp/tools/await-resume.js +0 -15
- package/dist/agent/mcp/tools/goal.js +0 -25
- package/dist/agent/mcp/tools/types.js +0 -5
- package/dist/agent/mcp/tools/workspace.js +0 -23
- package/dist/agent/npm.js +0 -14
- package/dist/agent/session.js +86 -196
- package/dist/agent/skill-installer.js +3 -70
- package/dist/agent/skills.js +0 -43
- package/dist/agent/stream.js +5 -148
- package/dist/agent/types.js +0 -12
- package/dist/agent/utils.js +2 -120
- package/dist/bot/bot.js +3 -468
- package/dist/bot/command-ui.js +0 -45
- package/dist/bot/commands.js +0 -51
- package/dist/bot/headless-bot.js +0 -21
- package/dist/bot/host.js +1 -5
- package/dist/bot/human-loop.js +0 -8
- package/dist/bot/menu.js +0 -3
- package/dist/bot/orchestration.js +0 -3
- package/dist/bot/render-shared.js +0 -63
- package/dist/bot/session-hub.js +14 -92
- package/dist/bot/session-status.js +1 -5
- package/dist/bot/streaming.js +0 -53
- package/dist/browser-profile.js +0 -51
- package/dist/browser-supervisor.js +0 -75
- package/dist/catalog/cli-tools.js +0 -53
- package/dist/catalog/index.js +0 -18
- package/dist/catalog/local-models.js +0 -18
- package/dist/catalog/mcp-servers.js +0 -35
- package/dist/catalog/skill-repos.js +0 -32
- package/dist/channels/base.js +0 -19
- package/dist/channels/dingtalk/bot.js +0 -3
- package/dist/channels/dingtalk/channel.js +0 -33
- package/dist/channels/discord/bot.js +0 -7
- package/dist/channels/discord/channel.js +0 -22
- package/dist/channels/feishu/bot.js +0 -71
- package/dist/channels/feishu/channel.js +0 -78
- package/dist/channels/feishu/markdown.js +0 -5
- package/dist/channels/feishu/render.js +0 -45
- package/dist/channels/health.js +0 -46
- package/dist/channels/slack/bot.js +0 -7
- package/dist/channels/slack/channel.js +1 -27
- package/dist/channels/states.js +0 -3
- package/dist/channels/telegram/bot.js +0 -60
- package/dist/channels/telegram/channel.js +2 -99
- package/dist/channels/telegram/directory.js +0 -3
- package/dist/channels/telegram/live-preview.js +0 -8
- package/dist/channels/telegram/render.js +0 -30
- package/dist/channels/wecom/bot.js +0 -3
- package/dist/channels/wecom/channel.js +0 -58
- package/dist/channels/weixin/api.js +0 -9
- package/dist/channels/weixin/bot.js +0 -88
- package/dist/channels/weixin/channel.js +0 -6
- package/dist/cli/autostart.js +1 -44
- package/dist/cli/channel-supervisor.js +1 -59
- package/dist/cli/channels.js +0 -6
- package/dist/cli/main.js +1 -145
- package/dist/cli/onboarding.js +0 -6
- package/dist/cli/run.js +0 -11
- package/dist/cli/setup-wizard.js +0 -3
- package/dist/core/config/runtime-config.js +0 -38
- package/dist/core/config/user-config.js +0 -63
- package/dist/core/config/validation.js +0 -9
- package/dist/core/constants.js +0 -268
- package/dist/core/git.js +0 -31
- package/dist/core/legacy-compat.js +1 -28
- package/dist/core/logging.js +0 -3
- package/dist/core/platform.js +0 -28
- package/dist/core/process-control.js +0 -8
- package/dist/core/secrets/index.js +0 -17
- package/dist/core/secrets/inline-seal.js +0 -7
- package/dist/core/secrets/ref.js +0 -19
- package/dist/core/secrets/resolver.js +0 -8
- package/dist/core/secrets/store.js +1 -12
- package/dist/core/utils.js +1 -37
- package/dist/core/version.js +0 -3
- package/dist/dashboard/platform.js +1 -35
- package/dist/dashboard/routes/agents.js +2 -71
- package/dist/dashboard/routes/cli.js +0 -27
- package/dist/dashboard/routes/config.js +0 -54
- package/dist/dashboard/routes/extensions.js +0 -132
- package/dist/dashboard/routes/local-models.js +0 -68
- package/dist/dashboard/routes/models.js +0 -46
- package/dist/dashboard/routes/sessions.js +0 -100
- package/dist/dashboard/runtime.js +1 -58
- package/dist/dashboard/server.js +0 -49
- package/dist/dashboard/session-control.js +2 -69
- package/dist/model/catalog.js +1 -17
- package/dist/model/index.js +0 -15
- package/dist/model/injector.js +1 -181
- package/dist/model/provider-models.js +1 -48
- package/dist/model/responses-bridge.js +3 -45
- package/dist/model/store.js +0 -17
- package/dist/model/types.js +0 -12
- package/dist/model/validation.js +1 -18
- package/dist/pikichannel/adapter-pikiloom.js +0 -31
- package/dist/pikichannel/code.js +0 -12
- package/dist/pikichannel/codec.js +0 -14
- package/dist/pikichannel/host.js +4 -33
- package/dist/pikichannel/protocol.js +0 -52
- package/dist/pikichannel/rendezvous-broker.js +5 -26
- package/dist/pikichannel/rendezvous-host.js +3 -16
- package/dist/pikichannel/server.js +6 -55
- package/dist/pikichannel/transport.js +0 -25
- package/dist/pikichannel/transports/webrtc-host.js +0 -18
- package/dist/pikichannel/transports/webrtc-shared.js +4 -34
- package/dist/pikichannel/transports/websocket-host.js +1 -14
- package/dist/pikichannel/turn.js +2 -69
- package/dist/pikichannel/web/sdk.js +14 -71
- package/package.json +1 -1
- package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
- package/dashboard/dist/assets/SessionPanel-_0dLWvEb.js +0 -1
- package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
- package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
- package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
- 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 {
|
|
1273
|
+
catch { }
|
|
1380
1274
|
}
|
|
1381
1275
|
}
|
|
1382
|
-
catch {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
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 {
|
|
2030
|
+
catch { }
|
|
2182
2031
|
}
|
|
2183
2032
|
return false;
|
|
2184
2033
|
};
|