skimpyclaw 0.3.14 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -37
- package/dist/__tests__/adapter-types.test.d.ts +4 -0
- package/dist/__tests__/adapter-types.test.js +63 -0
- package/dist/__tests__/anthropic-adapter.test.d.ts +4 -0
- package/dist/__tests__/anthropic-adapter.test.js +264 -0
- package/dist/__tests__/api.test.js +0 -1
- package/dist/__tests__/cli.integration.test.js +2 -4
- package/dist/__tests__/cli.test.js +0 -1
- package/dist/__tests__/code-agents-notifications.test.js +137 -0
- package/dist/__tests__/code-agents-parser.test.js +19 -1
- package/dist/__tests__/code-agents-preflight.test.js +3 -28
- package/dist/__tests__/code-agents-utils.test.js +34 -9
- package/dist/__tests__/code-agents-worktrees.test.js +116 -0
- package/dist/__tests__/codex-adapter.test.js +184 -0
- package/dist/__tests__/codex-auth.test.js +66 -0
- package/dist/__tests__/codex-provider-gating.test.js +35 -0
- package/dist/__tests__/codex-unified-loop.test.js +111 -0
- package/dist/__tests__/config-security.test.js +127 -0
- package/dist/__tests__/config.test.js +23 -0
- package/dist/__tests__/context-manager.test.js +243 -164
- package/dist/__tests__/cron-run.test.js +250 -0
- package/dist/__tests__/cron.test.js +12 -38
- package/dist/__tests__/digests.test.js +67 -0
- package/dist/__tests__/discord-attachments.test.js +211 -0
- package/dist/__tests__/discord-docs.test.d.ts +1 -0
- package/dist/__tests__/discord-docs.test.js +27 -0
- package/dist/__tests__/discord-thread-agents.test.d.ts +1 -0
- package/dist/__tests__/discord-thread-agents.test.js +115 -0
- package/dist/__tests__/discord-thread-context.test.d.ts +1 -0
- package/dist/__tests__/discord-thread-context.test.js +42 -0
- package/dist/__tests__/doctor.formatters.test.js +4 -4
- package/dist/__tests__/doctor.index.test.js +1 -1
- package/dist/__tests__/doctor.runner.test.js +3 -15
- package/dist/__tests__/env-sanitizer.test.d.ts +1 -0
- package/dist/__tests__/env-sanitizer.test.js +45 -0
- package/dist/__tests__/exec-approval.test.js +61 -0
- package/dist/__tests__/fetch-tool.test.d.ts +1 -0
- package/dist/__tests__/fetch-tool.test.js +85 -0
- package/dist/__tests__/gateway-status-auth.test.d.ts +1 -0
- package/dist/__tests__/gateway-status-auth.test.js +72 -0
- package/dist/__tests__/heartbeat.test.js +3 -3
- package/dist/__tests__/interactive-sessions.test.d.ts +1 -0
- package/dist/__tests__/interactive-sessions.test.js +96 -0
- package/dist/__tests__/langfuse.test.js +6 -18
- package/dist/__tests__/model-selection.test.js +3 -4
- package/dist/__tests__/providers-init.test.js +2 -8
- package/dist/__tests__/providers-routing.test.js +1 -1
- package/dist/__tests__/providers-utils.test.js +13 -3
- package/dist/__tests__/sessions.test.js +14 -10
- package/dist/__tests__/setup.test.js +12 -29
- package/dist/__tests__/skills.test.js +10 -7
- package/dist/__tests__/stream-formatter.test.d.ts +1 -0
- package/dist/__tests__/stream-formatter.test.js +114 -0
- package/dist/__tests__/token-efficiency.test.js +131 -15
- package/dist/__tests__/tool-loop.test.d.ts +4 -0
- package/dist/__tests__/tool-loop.test.js +505 -0
- package/dist/__tests__/tools.test.js +101 -276
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +14 -0
- package/dist/__tests__/voice.test.js +21 -0
- package/dist/agent.js +35 -4
- package/dist/api.js +113 -37
- package/dist/channels/discord/attachments.d.ts +50 -0
- package/dist/channels/discord/attachments.js +137 -0
- package/dist/channels/discord/delegation.d.ts +5 -0
- package/dist/channels/discord/delegation.js +136 -0
- package/dist/channels/discord/handlers.js +694 -7
- package/dist/channels/discord/index.d.ts +16 -1
- package/dist/channels/discord/index.js +64 -1
- package/dist/channels/discord/thread-agents.d.ts +54 -0
- package/dist/channels/discord/thread-agents.js +323 -0
- package/dist/channels/discord/threads.d.ts +58 -0
- package/dist/channels/discord/threads.js +192 -0
- package/dist/channels/discord/types.js +4 -2
- package/dist/channels/discord/utils.d.ts +16 -0
- package/dist/channels/discord/utils.js +86 -6
- package/dist/channels/telegram/index.d.ts +1 -1
- package/dist/channels/telegram/types.js +1 -1
- package/dist/channels/telegram/utils.js +9 -3
- package/dist/channels.d.ts +1 -1
- package/dist/cli.js +20 -400
- package/dist/code-agents/executor.d.ts +1 -1
- package/dist/code-agents/executor.js +101 -45
- package/dist/code-agents/index.d.ts +2 -7
- package/dist/code-agents/index.js +111 -80
- package/dist/code-agents/interactive-resume.d.ts +6 -0
- package/dist/code-agents/interactive-resume.js +98 -0
- package/dist/code-agents/interactive-sessions.d.ts +20 -0
- package/dist/code-agents/interactive-sessions.js +132 -0
- package/dist/code-agents/parser.js +5 -1
- package/dist/code-agents/registry.d.ts +7 -1
- package/dist/code-agents/registry.js +11 -23
- package/dist/code-agents/stream-formatter.d.ts +8 -0
- package/dist/code-agents/stream-formatter.js +92 -0
- package/dist/code-agents/types.d.ts +16 -24
- package/dist/code-agents/utils.d.ts +35 -11
- package/dist/code-agents/utils.js +349 -95
- package/dist/code-agents/worktrees.d.ts +37 -0
- package/dist/code-agents/worktrees.js +116 -0
- package/dist/config.d.ts +2 -4
- package/dist/config.js +123 -23
- package/dist/cron.d.ts +1 -6
- package/dist/cron.js +175 -82
- package/dist/dashboard/assets/index-B345aOO-.js +65 -0
- package/dist/dashboard/assets/index-ZWK4dalJ.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/digests.d.ts +1 -0
- package/dist/digests.js +132 -42
- package/dist/doctor/checks.d.ts +0 -3
- package/dist/doctor/checks.js +1 -108
- package/dist/doctor/runner.js +1 -4
- package/dist/env-sanitizer.d.ts +2 -0
- package/dist/env-sanitizer.js +61 -0
- package/dist/exec-approval.d.ts +11 -1
- package/dist/exec-approval.js +17 -4
- package/dist/gateway.d.ts +3 -1
- package/dist/gateway.js +17 -7
- package/dist/heartbeat.js +1 -6
- package/dist/langfuse.js +3 -29
- package/dist/model-selection.js +3 -1
- package/dist/providers/adapter.d.ts +118 -0
- package/dist/providers/adapter.js +6 -0
- package/dist/providers/adapters/anthropic-adapter.d.ts +22 -0
- package/dist/providers/adapters/anthropic-adapter.js +204 -0
- package/dist/providers/adapters/codex-adapter.d.ts +26 -0
- package/dist/providers/adapters/codex-adapter.js +203 -0
- package/dist/providers/anthropic.d.ts +1 -0
- package/dist/providers/anthropic.js +10 -272
- package/dist/providers/codex.d.ts +21 -0
- package/dist/providers/codex.js +149 -330
- package/dist/providers/content.d.ts +1 -1
- package/dist/providers/content.js +2 -2
- package/dist/providers/context-manager.d.ts +18 -6
- package/dist/providers/context-manager.js +199 -223
- package/dist/providers/index.d.ts +9 -1
- package/dist/providers/index.js +73 -64
- package/dist/providers/loop-utils.d.ts +20 -0
- package/dist/providers/loop-utils.js +30 -0
- package/dist/providers/tool-loop.d.ts +12 -0
- package/dist/providers/tool-loop.js +251 -0
- package/dist/providers/utils.d.ts +19 -3
- package/dist/providers/utils.js +100 -29
- package/dist/secure-store.d.ts +8 -0
- package/dist/secure-store.js +80 -0
- package/dist/service.js +3 -28
- package/dist/sessions.d.ts +3 -0
- package/dist/sessions.js +147 -18
- package/dist/setup-templates.js +13 -25
- package/dist/setup.d.ts +10 -6
- package/dist/setup.js +84 -292
- package/dist/skills.js +3 -11
- package/dist/tools/agent-delegation.d.ts +19 -0
- package/dist/tools/agent-delegation.js +49 -0
- package/dist/tools/bash-tool.js +89 -34
- package/dist/tools/definitions.d.ts +199 -302
- package/dist/tools/definitions.js +70 -123
- package/dist/tools/execute-context.d.ts +13 -4
- package/dist/tools/fetch-tool.js +109 -13
- package/dist/tools/file-tools.js +7 -1
- package/dist/tools.d.ts +7 -7
- package/dist/tools.js +133 -151
- package/dist/types.d.ts +37 -30
- package/dist/utils.js +4 -6
- package/dist/voice.d.ts +1 -1
- package/dist/voice.js +17 -4
- package/package.json +33 -23
- package/templates/TOOLS.md +0 -27
- package/dist/__tests__/audit.test.js +0 -122
- package/dist/__tests__/code-agents-orchestrator.test.js +0 -216
- package/dist/__tests__/code-agents-sandbox.test.js +0 -163
- package/dist/__tests__/orchestrator.test.js +0 -425
- package/dist/__tests__/sandbox-bridge.test.js +0 -116
- package/dist/__tests__/sandbox-manager.test.js +0 -144
- package/dist/__tests__/sandbox-mount-security.test.js +0 -139
- package/dist/__tests__/sandbox-runtime.test.js +0 -176
- package/dist/__tests__/subagent.test.js +0 -240
- package/dist/__tests__/telegram.test.js +0 -42
- package/dist/code-agents/orchestrator.d.ts +0 -29
- package/dist/code-agents/orchestrator.js +0 -694
- package/dist/code-agents/worktree.d.ts +0 -40
- package/dist/code-agents/worktree.js +0 -215
- package/dist/dashboard/assets/index-BoTHPby4.js +0 -65
- package/dist/dashboard/assets/index-D4mufvBg.css +0 -1
- package/dist/dashboard.d.ts +0 -8
- package/dist/dashboard.js +0 -4071
- package/dist/discord.d.ts +0 -8
- package/dist/discord.js +0 -792
- package/dist/mcp-context-a8c.d.ts +0 -13
- package/dist/mcp-context-a8c.js +0 -34
- package/dist/orchestrator.d.ts +0 -15
- package/dist/orchestrator.js +0 -676
- package/dist/providers/openai.d.ts +0 -10
- package/dist/providers/openai.js +0 -355
- package/dist/sandbox/bridge.d.ts +0 -5
- package/dist/sandbox/bridge.js +0 -63
- package/dist/sandbox/index.d.ts +0 -5
- package/dist/sandbox/index.js +0 -4
- package/dist/sandbox/manager.d.ts +0 -7
- package/dist/sandbox/manager.js +0 -100
- package/dist/sandbox/mount-security.d.ts +0 -12
- package/dist/sandbox/mount-security.js +0 -122
- package/dist/sandbox/runtime.d.ts +0 -39
- package/dist/sandbox/runtime.js +0 -192
- package/dist/sandbox-utils.d.ts +0 -6
- package/dist/sandbox-utils.js +0 -36
- package/dist/subagent.d.ts +0 -19
- package/dist/subagent.js +0 -407
- package/dist/telegram.d.ts +0 -2
- package/dist/telegram.js +0 -11
- package/dist/tools/browser-tool.d.ts +0 -3
- package/dist/tools/browser-tool.js +0 -266
- package/sandbox/Dockerfile +0 -40
- /package/dist/__tests__/{audit.test.d.ts → code-agents-notifications.test.d.ts} +0 -0
- /package/dist/__tests__/{code-agents-orchestrator.test.d.ts → code-agents-worktrees.test.d.ts} +0 -0
- /package/dist/__tests__/{code-agents-sandbox.test.d.ts → codex-adapter.test.d.ts} +0 -0
- /package/dist/__tests__/{orchestrator.test.d.ts → codex-auth.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-bridge.test.d.ts → codex-provider-gating.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-manager.test.d.ts → codex-unified-loop.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-mount-security.test.d.ts → config-security.test.d.ts} +0 -0
- /package/dist/__tests__/{sandbox-runtime.test.d.ts → cron-run.test.d.ts} +0 -0
- /package/dist/__tests__/{subagent.test.d.ts → digests.test.d.ts} +0 -0
- /package/dist/__tests__/{telegram.test.d.ts → discord-attachments.test.d.ts} +0 -0
package/dist/providers/codex.js
CHANGED
|
@@ -2,17 +2,10 @@
|
|
|
2
2
|
import { readFileSync, existsSync } from 'fs';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
|
-
import {
|
|
6
|
-
import { toErrorMessage } from '../utils.js';
|
|
7
|
-
import { compactCodexMessages } from './context-manager.js';
|
|
8
|
-
import { toCodexContent, toCodexToolDefinitions } from './content.js';
|
|
9
|
-
import { toNumericUsageDetails, toCostDetails } from './observability.js';
|
|
10
|
-
import { executeTool } from '../tools.js';
|
|
11
|
-
import { ToolCallGuard } from './tool-guard.js';
|
|
12
|
-
import { startTrace, addEvent } from '../audit.js';
|
|
13
|
-
import { startObservation } from '@langfuse/tracing';
|
|
5
|
+
import { toCostDetails } from './observability.js';
|
|
14
6
|
import { buildUsageRecord, recordUsage } from '../usage.js';
|
|
15
|
-
const
|
|
7
|
+
const DEFAULT_CODEX_HOME = join(homedir(), '.codex');
|
|
8
|
+
const DEFAULT_CODEX_AUTH_PATH = join(DEFAULT_CODEX_HOME, 'auth.json');
|
|
16
9
|
const DEFAULT_CODEX_BASE_URL = 'https://chatgpt.com/backend-api';
|
|
17
10
|
const DEFAULT_CODEX_FETCH_TIMEOUT_MS = 120_000;
|
|
18
11
|
let codexAuthPath = DEFAULT_CODEX_AUTH_PATH;
|
|
@@ -33,12 +26,34 @@ export function resetCodexProviderState() {
|
|
|
33
26
|
codexAuth = null;
|
|
34
27
|
}
|
|
35
28
|
export function setCodexAuthPath(path) {
|
|
36
|
-
codexAuthPath = path;
|
|
29
|
+
codexAuthPath = resolveCodexAuthPath(path);
|
|
37
30
|
}
|
|
38
31
|
export function setCodexBaseUrl(url) {
|
|
39
32
|
codexBaseUrl = url;
|
|
40
33
|
}
|
|
34
|
+
export function resolveCodexAuthPath(authPath) {
|
|
35
|
+
if (!authPath)
|
|
36
|
+
return DEFAULT_CODEX_AUTH_PATH;
|
|
37
|
+
if (authPath === '~')
|
|
38
|
+
return homedir();
|
|
39
|
+
if (authPath.startsWith('~/'))
|
|
40
|
+
return join(homedir(), authPath.slice(2));
|
|
41
|
+
return authPath;
|
|
42
|
+
}
|
|
43
|
+
function decodeJwtPayload(token) {
|
|
44
|
+
const payloadPart = token.split('.')[1];
|
|
45
|
+
if (!payloadPart)
|
|
46
|
+
return null;
|
|
47
|
+
try {
|
|
48
|
+
const normalized = payloadPart.replace(/-/g, '+').replace(/_/g, '/');
|
|
49
|
+
return JSON.parse(Buffer.from(normalized, 'base64').toString());
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
41
55
|
export function loadCodexAuth(authPath = codexAuthPath) {
|
|
56
|
+
authPath = resolveCodexAuthPath(authPath);
|
|
42
57
|
if (!existsSync(authPath)) {
|
|
43
58
|
console.log(`[codex] No auth file at ${authPath}`);
|
|
44
59
|
return null;
|
|
@@ -46,28 +61,32 @@ export function loadCodexAuth(authPath = codexAuthPath) {
|
|
|
46
61
|
try {
|
|
47
62
|
const raw = JSON.parse(readFileSync(authPath, 'utf-8'));
|
|
48
63
|
const token = raw?.tokens?.access_token;
|
|
49
|
-
if (
|
|
64
|
+
if (typeof token !== 'string' || token.length === 0) {
|
|
50
65
|
console.log('[codex] No access_token in auth file');
|
|
51
66
|
return null;
|
|
52
67
|
}
|
|
53
68
|
// Decode JWT to check expiry and extract account ID
|
|
54
|
-
const payload =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
const payload = decodeJwtPayload(token);
|
|
70
|
+
if (payload?.exp) {
|
|
71
|
+
const exp = payload.exp * 1000;
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
if (now > exp) {
|
|
74
|
+
const expiredAgo = Math.round((now - exp) / 60000);
|
|
75
|
+
console.warn(`[codex] Token expired ${expiredAgo} min ago. Run 'codex' to re-auth.`);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
61
78
|
}
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
// Current Codex CLI auth files include tokens.account_id; older files only
|
|
80
|
+
// expose it in the JWT claims.
|
|
81
|
+
const authClaims = payload?.['https://api.openai.com/auth'];
|
|
82
|
+
const accountId = raw?.tokens?.account_id || authClaims?.chatgpt_account_id;
|
|
83
|
+
if (typeof accountId !== 'string' || accountId.length === 0) {
|
|
84
|
+
console.error('[codex] No account ID in auth file');
|
|
67
85
|
return null;
|
|
68
86
|
}
|
|
69
|
-
const expiresIn = Math.round((exp - now) / 60000);
|
|
70
|
-
|
|
87
|
+
const expiresIn = payload?.exp ? Math.round((payload.exp * 1000 - Date.now()) / 60000) : null;
|
|
88
|
+
const expiryDetail = expiresIn === null ? 'expiry unknown' : `expires in ${expiresIn} min`;
|
|
89
|
+
console.log(`[codex] Token valid (${expiryDetail}, account: ${accountId.slice(0, 8)}...)`);
|
|
71
90
|
return { accessToken: token, accountId };
|
|
72
91
|
}
|
|
73
92
|
catch (error) {
|
|
@@ -77,7 +96,7 @@ export function loadCodexAuth(authPath = codexAuthPath) {
|
|
|
77
96
|
}
|
|
78
97
|
export function initCodexAuth(path, baseUrl) {
|
|
79
98
|
if (path)
|
|
80
|
-
codexAuthPath = path;
|
|
99
|
+
codexAuthPath = resolveCodexAuthPath(path);
|
|
81
100
|
if (baseUrl)
|
|
82
101
|
codexBaseUrl = baseUrl;
|
|
83
102
|
codexAuth = loadCodexAuth();
|
|
@@ -90,7 +109,7 @@ export function isCodexAvailable() {
|
|
|
90
109
|
return codexAuth !== null;
|
|
91
110
|
}
|
|
92
111
|
const LANGFUSE_APP_NAME = 'skimpyclaw';
|
|
93
|
-
function recordCodexUsage(params) {
|
|
112
|
+
export function recordCodexUsage(params) {
|
|
94
113
|
const usage = params.usage;
|
|
95
114
|
const inputTokens = typeof usage?.input_tokens === 'number' ? usage.input_tokens : 0;
|
|
96
115
|
const outputTokens = typeof usage?.output_tokens === 'number' ? usage.output_tokens : 0;
|
|
@@ -115,12 +134,13 @@ async function startGenerationObservation(name, attributes) {
|
|
|
115
134
|
if (!isLangfuseEnabled())
|
|
116
135
|
return null;
|
|
117
136
|
attributes.metadata = { app: LANGFUSE_APP_NAME, ...attributes.metadata };
|
|
137
|
+
const { startObservation } = await import('@langfuse/tracing');
|
|
118
138
|
return startObservation(name, attributes, { asType: 'generation' });
|
|
119
139
|
}
|
|
120
140
|
/**
|
|
121
141
|
* Make a single Codex API call. Returns raw SSE text.
|
|
122
142
|
*/
|
|
123
|
-
async function codexFetch(body, timeoutMs = DEFAULT_CODEX_FETCH_TIMEOUT_MS) {
|
|
143
|
+
export async function codexFetch(body, timeoutMs = DEFAULT_CODEX_FETCH_TIMEOUT_MS) {
|
|
124
144
|
if (!codexAuth) {
|
|
125
145
|
throw new Error('Codex auth not initialized. Run "codex" CLI to authenticate.');
|
|
126
146
|
}
|
|
@@ -160,9 +180,14 @@ async function codexFetch(body, timeoutMs = DEFAULT_CODEX_FETCH_TIMEOUT_MS) {
|
|
|
160
180
|
* Parse an SSE response from the Codex backend.
|
|
161
181
|
* Extracts function calls from the completed response object.
|
|
162
182
|
*/
|
|
163
|
-
function parseCodexSSE(text) {
|
|
183
|
+
export function parseCodexSSE(text) {
|
|
164
184
|
let outputText = '';
|
|
165
185
|
let completedResponse = null;
|
|
186
|
+
// Track streaming function calls and output items
|
|
187
|
+
const streamingFunctionCalls = new Map();
|
|
188
|
+
const streamingOutputTexts = new Map();
|
|
189
|
+
let currentOutputIndex = -1;
|
|
190
|
+
let currentFcIndex = -1;
|
|
166
191
|
for (const line of text.split('\n')) {
|
|
167
192
|
if (!line.startsWith('data: '))
|
|
168
193
|
continue;
|
|
@@ -172,7 +197,38 @@ function parseCodexSSE(text) {
|
|
|
172
197
|
try {
|
|
173
198
|
const event = JSON.parse(data);
|
|
174
199
|
if (event.type === 'response.output_text.delta') {
|
|
175
|
-
|
|
200
|
+
// Text delta — accumulate by output_index or globally
|
|
201
|
+
const idx = event.output_index ?? currentOutputIndex;
|
|
202
|
+
if (idx >= 0) {
|
|
203
|
+
streamingOutputTexts.set(idx, (streamingOutputTexts.get(idx) || '') + (event.delta || ''));
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
outputText += event.delta || '';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
else if (event.type === 'response.output_item.added') {
|
|
210
|
+
// New output item — track its type
|
|
211
|
+
const item = event.item;
|
|
212
|
+
const idx = event.output_index ?? -1;
|
|
213
|
+
if (item?.type === 'function_call') {
|
|
214
|
+
currentFcIndex = idx;
|
|
215
|
+
streamingFunctionCalls.set(idx, {
|
|
216
|
+
callId: item.call_id || item.id || `fc-${idx}`,
|
|
217
|
+
name: item.name || '',
|
|
218
|
+
arguments: '',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
else if (item?.type === 'output_text' || item?.type === 'message') {
|
|
222
|
+
currentOutputIndex = idx;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
else if (event.type === 'response.function_call_arguments.delta') {
|
|
226
|
+
// Function call argument delta
|
|
227
|
+
const idx = event.output_index ?? currentFcIndex;
|
|
228
|
+
const fc = streamingFunctionCalls.get(idx);
|
|
229
|
+
if (fc) {
|
|
230
|
+
fc.arguments += event.delta || '';
|
|
231
|
+
}
|
|
176
232
|
}
|
|
177
233
|
else if (event.type === 'response.completed' && event.response) {
|
|
178
234
|
completedResponse = event.response;
|
|
@@ -182,9 +238,9 @@ function parseCodexSSE(text) {
|
|
|
182
238
|
}
|
|
183
239
|
catch { /* skip non-JSON lines */ }
|
|
184
240
|
}
|
|
185
|
-
//
|
|
241
|
+
// Build function calls — prefer completed response, fall back to streaming
|
|
186
242
|
const functionCalls = [];
|
|
187
|
-
if (completedResponse?.output) {
|
|
243
|
+
if (completedResponse?.output?.length) {
|
|
188
244
|
for (const item of completedResponse.output) {
|
|
189
245
|
if (item.type === 'function_call') {
|
|
190
246
|
functionCalls.push({
|
|
@@ -194,10 +250,43 @@ function parseCodexSSE(text) {
|
|
|
194
250
|
});
|
|
195
251
|
}
|
|
196
252
|
else if (item.type === 'output_text' && item.text) {
|
|
197
|
-
// Capture output_text items that may not appear at top-level
|
|
198
253
|
if (!outputText)
|
|
199
254
|
outputText = item.text;
|
|
200
255
|
}
|
|
256
|
+
else if (item.type === 'message' && item.content) {
|
|
257
|
+
for (const c of item.content) {
|
|
258
|
+
if (c.type === 'output_text' && c.text) {
|
|
259
|
+
outputText += c.text;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
// Completed response had empty output — use streaming data
|
|
267
|
+
// Also patch the rawResponse so appendAssistantResponse includes these items
|
|
268
|
+
const patchedOutput = [];
|
|
269
|
+
for (const [, fc] of streamingFunctionCalls) {
|
|
270
|
+
if (fc.name) {
|
|
271
|
+
functionCalls.push(fc);
|
|
272
|
+
patchedOutput.push({
|
|
273
|
+
type: 'function_call',
|
|
274
|
+
call_id: fc.callId,
|
|
275
|
+
name: fc.name,
|
|
276
|
+
arguments: fc.arguments,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (const [, txt] of streamingOutputTexts) {
|
|
281
|
+
if (txt) {
|
|
282
|
+
if (!outputText)
|
|
283
|
+
outputText = txt;
|
|
284
|
+
patchedOutput.push({ type: 'output_text', text: txt });
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (completedResponse && patchedOutput.length > 0) {
|
|
288
|
+
completedResponse.output = patchedOutput;
|
|
289
|
+
console.log(`[codex] Patched empty response.output with ${patchedOutput.length} streaming items`);
|
|
201
290
|
}
|
|
202
291
|
}
|
|
203
292
|
if (completedResponse?.usage) {
|
|
@@ -206,305 +295,35 @@ function parseCodexSSE(text) {
|
|
|
206
295
|
else {
|
|
207
296
|
console.log('[codex] No usage data in response');
|
|
208
297
|
}
|
|
298
|
+
// Fallback: Codex may put text in response.text instead of output_text or output[]
|
|
299
|
+
if (!outputText && completedResponse?.text) {
|
|
300
|
+
const textContent = typeof completedResponse.text === 'string'
|
|
301
|
+
? completedResponse.text
|
|
302
|
+
: typeof completedResponse.text?.content === 'string'
|
|
303
|
+
? completedResponse.text.content
|
|
304
|
+
: '';
|
|
305
|
+
if (textContent)
|
|
306
|
+
outputText = textContent;
|
|
307
|
+
}
|
|
308
|
+
// Debug: log when output is empty despite having tokens
|
|
309
|
+
if (!outputText && completedResponse) {
|
|
310
|
+
const outputTypes = (completedResponse.output || []).map((item) => `${item.type}${item.content ? `[${(item.content || []).map((c) => c.type).join(',')}]` : ''}`);
|
|
311
|
+
console.warn(`[codex] Empty outputText! status: ${completedResponse.status}, output: ${JSON.stringify(completedResponse.output)?.slice(0, 1000)}, text: ${JSON.stringify(completedResponse.text)?.slice(0, 200)}, reasoning: ${JSON.stringify(completedResponse.reasoning)?.slice(0, 200)}`);
|
|
312
|
+
}
|
|
209
313
|
return { outputText, functionCalls, response: completedResponse };
|
|
210
314
|
}
|
|
315
|
+
/** @deprecated Use adapter.chat() via the provider registry instead. */
|
|
211
316
|
export async function chatCodex(params) {
|
|
212
|
-
const {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
let instructions = 'You are a helpful assistant.';
|
|
216
|
-
const input = [];
|
|
217
|
-
for (const m of messages) {
|
|
218
|
-
if (m.role === 'system') {
|
|
219
|
-
// Convert content to text
|
|
220
|
-
const { contentToText } = await import('./utils.js');
|
|
221
|
-
instructions = contentToText(m.content);
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
const contentType = m.role === 'assistant' ? 'output_text' : 'input_text';
|
|
225
|
-
const content = toCodexContent(m.content, contentType);
|
|
226
|
-
input.push({
|
|
227
|
-
type: 'message',
|
|
228
|
-
role: m.role,
|
|
229
|
-
content,
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
const body = {
|
|
234
|
-
model: modelId,
|
|
235
|
-
instructions,
|
|
236
|
-
input,
|
|
237
|
-
store: false,
|
|
238
|
-
stream: true,
|
|
239
|
-
reasoning: { effort: 'medium', summary: 'auto' },
|
|
240
|
-
include: ['reasoning.encrypted_content'],
|
|
241
|
-
};
|
|
242
|
-
const genObs = await startGenerationObservation(`codex:${modelId}`, {
|
|
243
|
-
input: { instructions, input },
|
|
244
|
-
model: modelId,
|
|
245
|
-
modelParameters: { stream: true, reasoning: body.reasoning },
|
|
246
|
-
metadata: { provider: 'codex' },
|
|
247
|
-
});
|
|
248
|
-
try {
|
|
249
|
-
const sseText = await codexFetch(body);
|
|
250
|
-
const parsed = parseCodexSSE(sseText);
|
|
251
|
-
recordCodexUsage({ model: modelId, usage: parsed.response?.usage, trigger: 'api' });
|
|
252
|
-
genObs?.update({
|
|
253
|
-
output: { text: parsed.outputText },
|
|
254
|
-
usageDetails: toNumericUsageDetails(parsed.response?.usage),
|
|
255
|
-
costDetails: toCostDetails(modelId, parsed.response?.usage),
|
|
256
|
-
});
|
|
257
|
-
genObs?.end();
|
|
258
|
-
return parsed.outputText || '[No response from Codex]';
|
|
259
|
-
}
|
|
260
|
-
catch (err) {
|
|
261
|
-
const errorMessage = toErrorMessage(err);
|
|
262
|
-
genObs?.update({ level: 'ERROR', statusMessage: errorMessage, output: { error: errorMessage } });
|
|
263
|
-
genObs?.end();
|
|
264
|
-
throw err;
|
|
265
|
-
}
|
|
317
|
+
const { CodexAdapter } = await import('./adapters/codex-adapter.js');
|
|
318
|
+
const adapter = new CodexAdapter();
|
|
319
|
+
return adapter.chat(params.messages, params.options, params.config);
|
|
266
320
|
}
|
|
267
321
|
export async function chatWithToolsCodex(params) {
|
|
268
|
-
const {
|
|
269
|
-
const
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if (m.role === 'system') {
|
|
276
|
-
const { contentToText } = await import('./utils.js');
|
|
277
|
-
instructions = contentToText(m.content);
|
|
278
|
-
}
|
|
279
|
-
else {
|
|
280
|
-
const contentType = m.role === 'assistant' ? 'output_text' : 'input_text';
|
|
281
|
-
const content = toCodexContent(m.content, contentType);
|
|
282
|
-
input.push({
|
|
283
|
-
type: 'message',
|
|
284
|
-
role: m.role,
|
|
285
|
-
content,
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
// Get tool definitions
|
|
290
|
-
const { getToolDefinitions } = await import('../tools.js');
|
|
291
|
-
const includeSpawn = !!(toolContext?.fullConfig && (toolContext?.chatId || toolContext?.isCronJob));
|
|
292
|
-
const toolDefs = await getToolDefinitions(toolConfig, {
|
|
293
|
-
includeAgentTools: includeSpawn,
|
|
294
|
-
includeMcp: false,
|
|
295
|
-
projects: toolContext?.fullConfig?.projects
|
|
296
|
-
});
|
|
297
|
-
const tools = toolDefs ? toCodexToolDefinitions(toolDefs) : undefined;
|
|
298
|
-
const toolLog = [];
|
|
299
|
-
const auditTraceId = toolContext?.auditTraceId || startTrace((toolContext?.trigger || 'api'));
|
|
300
|
-
// Guard: spin detection, no-progress detection, token budget
|
|
301
|
-
const guard = new ToolCallGuard(toolConfig.maxTurnTokens);
|
|
302
|
-
for (let i = 0; i < maxIterations; i++) {
|
|
303
|
-
// Check abort signal before each iteration
|
|
304
|
-
if (toolContext?.abortSignal?.aborted) {
|
|
305
|
-
return {
|
|
306
|
-
response: `[Cancelled after ${toolLog.length} tool calls]`,
|
|
307
|
-
toolCalls: toolLog,
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
// Compact old tool results if context is growing large
|
|
311
|
-
const compactionResult = await compactCodexMessages(input, toolConfig.contextManagement, i + 1, config);
|
|
312
|
-
const inputForApi = compactionResult.messages;
|
|
313
|
-
if (compactionResult.compacted) {
|
|
314
|
-
const method = compactionResult.method === 'llm' ? 'LLM summary' : 'truncation';
|
|
315
|
-
const detail = `~${Math.round((compactionResult.tokensBefore || 0) / 1000)}k → ~${Math.round((compactionResult.tokensAfter || 0) / 1000)}k tokens`;
|
|
316
|
-
toolLog.push(`[context compacted via ${method}: ${detail}]`);
|
|
317
|
-
}
|
|
318
|
-
const body = {
|
|
319
|
-
model: modelId,
|
|
320
|
-
instructions,
|
|
321
|
-
input: inputForApi,
|
|
322
|
-
store: false,
|
|
323
|
-
stream: true,
|
|
324
|
-
reasoning: { effort: 'medium', summary: 'auto' },
|
|
325
|
-
include: ['reasoning.encrypted_content'],
|
|
326
|
-
};
|
|
327
|
-
if (tools)
|
|
328
|
-
body.tools = tools;
|
|
329
|
-
console.log(`[codex] Iteration ${i + 1}/${maxIterations} (model: ${modelId})`);
|
|
330
|
-
const genObs = await startGenerationObservation(`codex:${modelId}`, {
|
|
331
|
-
input: { instructions, input },
|
|
332
|
-
model: modelId,
|
|
333
|
-
modelParameters: { stream: true, reasoning: body.reasoning },
|
|
334
|
-
metadata: { provider: 'codex', iteration: i + 1 },
|
|
335
|
-
});
|
|
336
|
-
let parsed;
|
|
337
|
-
try {
|
|
338
|
-
const sseText = await codexFetch(body);
|
|
339
|
-
parsed = parseCodexSSE(sseText);
|
|
340
|
-
recordCodexUsage({
|
|
341
|
-
model: modelId,
|
|
342
|
-
usage: parsed.response?.usage,
|
|
343
|
-
trigger: toolContext?.trigger || 'api',
|
|
344
|
-
agentId: toolContext?.agentId,
|
|
345
|
-
});
|
|
346
|
-
genObs?.update({
|
|
347
|
-
output: { text: parsed.outputText },
|
|
348
|
-
usageDetails: toNumericUsageDetails(parsed.response?.usage),
|
|
349
|
-
costDetails: toCostDetails(modelId, parsed.response?.usage),
|
|
350
|
-
});
|
|
351
|
-
genObs?.end();
|
|
352
|
-
// Guard: track token usage (stats only, no enforcement)
|
|
353
|
-
guard.recordTokens(parsed.response?.usage?.input_tokens ?? 0, parsed.response?.usage?.output_tokens ?? 0);
|
|
354
|
-
}
|
|
355
|
-
catch (err) {
|
|
356
|
-
const errorMessage = toErrorMessage(err);
|
|
357
|
-
genObs?.update({ level: 'ERROR', statusMessage: errorMessage, output: { error: errorMessage } });
|
|
358
|
-
genObs?.end();
|
|
359
|
-
throw err;
|
|
360
|
-
}
|
|
361
|
-
// No function calls — we're done
|
|
362
|
-
if (parsed.functionCalls.length === 0) {
|
|
363
|
-
// Debug: log what Codex actually returned when outputText is empty
|
|
364
|
-
if (!parsed.outputText) {
|
|
365
|
-
console.log(`[codex] Empty outputText. Response output items:`, JSON.stringify(parsed.response?.output?.map((i) => ({ type: i.type, text: i.text?.slice(0, 200) })), null, 2));
|
|
366
|
-
}
|
|
367
|
-
let finalText = parsed.outputText;
|
|
368
|
-
if (!finalText && toolLog.length > 0) {
|
|
369
|
-
try {
|
|
370
|
-
// Some Codex runs finish tool execution but omit final text. Ask once more
|
|
371
|
-
// (without tools) for a user-facing answer from the gathered context.
|
|
372
|
-
const finalizeInput = [...input];
|
|
373
|
-
if (parsed.response?.output) {
|
|
374
|
-
for (const item of parsed.response.output) {
|
|
375
|
-
finalizeInput.push(item);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
finalizeInput.push({
|
|
379
|
-
type: 'message',
|
|
380
|
-
role: 'user',
|
|
381
|
-
content: [
|
|
382
|
-
{
|
|
383
|
-
type: 'input_text',
|
|
384
|
-
text: 'Provide the final answer to the user using the tool results above. Do not call tools. Be concise.',
|
|
385
|
-
},
|
|
386
|
-
],
|
|
387
|
-
});
|
|
388
|
-
const finalizeBody = {
|
|
389
|
-
model: modelId,
|
|
390
|
-
instructions,
|
|
391
|
-
input: finalizeInput,
|
|
392
|
-
store: false,
|
|
393
|
-
stream: true,
|
|
394
|
-
reasoning: { effort: 'medium', summary: 'auto' },
|
|
395
|
-
include: ['reasoning.encrypted_content'],
|
|
396
|
-
};
|
|
397
|
-
console.log('[codex] Finalizing tool run with a text-only follow-up');
|
|
398
|
-
const finalizeSse = await codexFetch(finalizeBody);
|
|
399
|
-
const finalized = parseCodexSSE(finalizeSse);
|
|
400
|
-
finalText = finalized.outputText?.trim() || '';
|
|
401
|
-
}
|
|
402
|
-
catch (err) {
|
|
403
|
-
const msg = toErrorMessage(err);
|
|
404
|
-
console.warn(`[codex] Finalization pass failed: ${msg}`);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
if (!finalText && toolLog.length > 0) {
|
|
408
|
-
finalText = `[Completed ${toolLog.length} tool calls, but no final text response was generated.]`;
|
|
409
|
-
}
|
|
410
|
-
const usage = parsed.response?.usage;
|
|
411
|
-
return {
|
|
412
|
-
response: finalText || '[No response from Codex]',
|
|
413
|
-
toolCalls: toolLog,
|
|
414
|
-
usage: {
|
|
415
|
-
prompt_tokens: usage?.input_tokens ?? 0,
|
|
416
|
-
completion_tokens: usage?.output_tokens ?? 0,
|
|
417
|
-
total_tokens: (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0),
|
|
418
|
-
},
|
|
419
|
-
cost: toCostDetails(modelId, usage),
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
// Add the assistant's output items to input for next turn
|
|
423
|
-
if (parsed.response?.output) {
|
|
424
|
-
for (const item of parsed.response.output) {
|
|
425
|
-
input.push(item);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
// Execute each function call and add results to input
|
|
429
|
-
for (const fc of parsed.functionCalls) {
|
|
430
|
-
const argsStr = fc.arguments || '{}';
|
|
431
|
-
let args;
|
|
432
|
-
try {
|
|
433
|
-
args = JSON.parse(argsStr);
|
|
434
|
-
}
|
|
435
|
-
catch {
|
|
436
|
-
args = {};
|
|
437
|
-
}
|
|
438
|
-
const inputStr = argsStr.slice(0, 200);
|
|
439
|
-
console.log(`[codex:tools] -> ${fc.name}(${inputStr})`);
|
|
440
|
-
// Guard: spin detection
|
|
441
|
-
const guardResult = guard.recordCall(fc.name, args);
|
|
442
|
-
if (guardResult.warning)
|
|
443
|
-
console.warn(`[codex:tools:guard] ${guardResult.warning}`);
|
|
444
|
-
if (guardResult.blocked) {
|
|
445
|
-
input.push({
|
|
446
|
-
type: 'function_call_output',
|
|
447
|
-
call_id: fc.callId,
|
|
448
|
-
output: guardResult.warning || 'Blocked: repeated identical call',
|
|
449
|
-
});
|
|
450
|
-
toolLog.push(`${fc.name} [BLOCKED: spin detected]`);
|
|
451
|
-
continue;
|
|
452
|
-
}
|
|
453
|
-
const { isLangfuseEnabled } = await import('../langfuse.js');
|
|
454
|
-
const toolObs = isLangfuseEnabled()
|
|
455
|
-
? startObservation(`tool:${fc.name}`, { input: args, metadata: { app: LANGFUSE_APP_NAME, tool: fc.name } }, { asType: 'tool' })
|
|
456
|
-
: null;
|
|
457
|
-
const toolStart = Date.now();
|
|
458
|
-
try {
|
|
459
|
-
const result = await executeTool(fc.name, args, toolConfig, toolContext) || '';
|
|
460
|
-
const truncatedResult = truncateToolResult(result);
|
|
461
|
-
const resultPreview = result.slice(0, 200) + (result.length > 200 ? '...' : '');
|
|
462
|
-
console.log(`[codex:tools] <- ${resultPreview}`);
|
|
463
|
-
toolLog.push(`${fc.name}(${inputStr}) → ${resultPreview}`);
|
|
464
|
-
toolObs?.update({ output: result });
|
|
465
|
-
toolObs?.end();
|
|
466
|
-
if (toolContext?.auditTraceId) {
|
|
467
|
-
addEvent(toolContext.auditTraceId, {
|
|
468
|
-
type: 'tool_use',
|
|
469
|
-
summary: `${fc.name}(${inputStr})`,
|
|
470
|
-
durationMs: Date.now() - toolStart,
|
|
471
|
-
});
|
|
472
|
-
}
|
|
473
|
-
input.push({
|
|
474
|
-
type: 'function_call_output',
|
|
475
|
-
call_id: fc.callId,
|
|
476
|
-
output: truncatedResult,
|
|
477
|
-
});
|
|
478
|
-
// Guard: no-progress detection
|
|
479
|
-
const progressResult = guard.recordResult(result);
|
|
480
|
-
if (progressResult.nudge) {
|
|
481
|
-
console.warn(`[codex:tools:guard] ${progressResult.nudge}`);
|
|
482
|
-
input[input.length - 1].output += `\n\n[System: ${progressResult.nudge}]`;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
catch (err) {
|
|
486
|
-
const errorMessage = toErrorMessage(err);
|
|
487
|
-
toolObs?.update({ level: 'ERROR', statusMessage: errorMessage, output: { error: errorMessage } });
|
|
488
|
-
toolObs?.end();
|
|
489
|
-
if (toolContext?.auditTraceId) {
|
|
490
|
-
addEvent(toolContext.auditTraceId, {
|
|
491
|
-
type: 'tool_error',
|
|
492
|
-
summary: `${fc.name} error: ${errorMessage.slice(0, 150)}`,
|
|
493
|
-
durationMs: Date.now() - toolStart,
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
|
-
throw err;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
console.warn(`[codex:tools] Max iterations (${maxIterations}) reached`);
|
|
501
|
-
return { response: '[Tool use loop reached maximum iterations]', toolCalls: toolLog };
|
|
502
|
-
}
|
|
503
|
-
/** Build UsageDetails from Codex usage response */
|
|
504
|
-
function buildCodexUsageDetails(usage) {
|
|
505
|
-
return {
|
|
506
|
-
prompt_tokens: usage?.input_tokens ?? 0,
|
|
507
|
-
completion_tokens: usage?.output_tokens ?? 0,
|
|
508
|
-
total_tokens: (usage?.input_tokens ?? 0) + (usage?.output_tokens ?? 0),
|
|
509
|
-
};
|
|
322
|
+
const { runToolLoop } = await import('./tool-loop.js');
|
|
323
|
+
const { CodexAdapter } = await import('./adapters/codex-adapter.js');
|
|
324
|
+
const adapter = new CodexAdapter();
|
|
325
|
+
const unifiedToolConfig = params.toolConfig.maxIterations
|
|
326
|
+
? params.toolConfig
|
|
327
|
+
: { ...params.toolConfig, maxIterations: 100 };
|
|
328
|
+
return runToolLoop(adapter, params.messages, params.options, params.config, unifiedToolConfig, params.toolContext);
|
|
510
329
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ContentBlock } from '../types.js';
|
|
2
2
|
/**
|
|
3
3
|
* Convert content array to OpenAI vision-compatible format.
|
|
4
|
-
* Preserves images as data URIs for multimodal
|
|
4
|
+
* Preserves images as data URIs for multimodal model formats.
|
|
5
5
|
*/
|
|
6
6
|
export declare function toOpenAIContent(content: string | ContentBlock[]): string | Array<{
|
|
7
7
|
type: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Content Format Conversions for Different Providers
|
|
2
2
|
/**
|
|
3
3
|
* Convert content array to OpenAI vision-compatible format.
|
|
4
|
-
* Preserves images as data URIs for multimodal
|
|
4
|
+
* Preserves images as data URIs for multimodal model formats.
|
|
5
5
|
*/
|
|
6
6
|
export function toOpenAIContent(content) {
|
|
7
7
|
if (typeof content === 'string')
|
|
@@ -50,6 +50,6 @@ export function toCodexToolDefinitions(tools) {
|
|
|
50
50
|
type: 'function',
|
|
51
51
|
name: t.name,
|
|
52
52
|
description: t.description,
|
|
53
|
-
parameters: t.input_schema,
|
|
53
|
+
parameters: t.input_schema && t.input_schema.type ? t.input_schema : { type: 'object', properties: {} },
|
|
54
54
|
}));
|
|
55
55
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ContextManagementConfig } from './types.js';
|
|
2
2
|
import type { Config } from '../types.js';
|
|
3
|
+
import type { MessageFormatHelper } from './adapter.js';
|
|
3
4
|
export type { ContextManagementConfig };
|
|
4
5
|
/** Result of a compaction attempt, including metadata about what happened. */
|
|
5
6
|
export interface CompactionResult<T> {
|
|
@@ -17,6 +18,20 @@ export interface CompactionResult<T> {
|
|
|
17
18
|
}
|
|
18
19
|
/** Rough token estimate: 1 token ≈ 4 chars of JSON. */
|
|
19
20
|
export declare function estimateTokens(data: any[]): number;
|
|
21
|
+
/**
|
|
22
|
+
* Generic compaction function for any message format.
|
|
23
|
+
* Delegates format-specific concerns (truncation, serialization, summary building)
|
|
24
|
+
* to the provided MessageFormatHelper.
|
|
25
|
+
*
|
|
26
|
+
* Does NOT mutate the input array — returns a new array.
|
|
27
|
+
*/
|
|
28
|
+
export declare function compactMessages<T>(items: T[], helper: MessageFormatHelper<T>, config?: ContextManagementConfig, iteration?: number, fullConfig?: Config): Promise<CompactionResult<T>>;
|
|
29
|
+
/** Anthropic message format helper. */
|
|
30
|
+
export declare const anthropicFormatHelper: MessageFormatHelper<any>;
|
|
31
|
+
/** OpenAI message format helper. */
|
|
32
|
+
export declare const openaiFormatHelper: MessageFormatHelper<any>;
|
|
33
|
+
/** Codex message format helper. */
|
|
34
|
+
export declare const codexFormatHelper: MessageFormatHelper<any>;
|
|
20
35
|
/**
|
|
21
36
|
* Serialize Anthropic-format messages into a human-readable conversation transcript
|
|
22
37
|
* suitable for LLM summarization.
|
|
@@ -32,20 +47,17 @@ declare function serializeOpenAIMessages(messages: any[]): string;
|
|
|
32
47
|
declare function serializeCodexMessages(items: any[]): string;
|
|
33
48
|
/**
|
|
34
49
|
* Compact Anthropic-format apiMessages when over threshold.
|
|
35
|
-
*
|
|
36
|
-
* Does NOT mutate the input array — returns a new array.
|
|
50
|
+
* @deprecated Use compactMessages() with anthropicFormatHelper instead.
|
|
37
51
|
*/
|
|
38
52
|
export declare function compactAnthropicMessages(messages: any[], config?: ContextManagementConfig, iteration?: number, fullConfig?: Config): Promise<CompactionResult<any>>;
|
|
39
53
|
/**
|
|
40
54
|
* Compact OpenAI-format apiMessages when over threshold.
|
|
41
|
-
*
|
|
42
|
-
* Does NOT mutate the input array — returns a new array.
|
|
55
|
+
* @deprecated Use compactMessages() with openaiFormatHelper instead.
|
|
43
56
|
*/
|
|
44
57
|
export declare function compactOpenAIMessages(messages: any[], config?: ContextManagementConfig, iteration?: number, fullConfig?: Config): Promise<CompactionResult<any>>;
|
|
45
58
|
/**
|
|
46
59
|
* Compact Codex-format input items when over threshold.
|
|
47
|
-
*
|
|
48
|
-
* Does NOT mutate the input array — returns a new array.
|
|
60
|
+
* @deprecated Use compactMessages() with codexFormatHelper instead.
|
|
49
61
|
*/
|
|
50
62
|
export declare function compactCodexMessages(input: any[], config?: ContextManagementConfig, iteration?: number, fullConfig?: Config): Promise<CompactionResult<any>>;
|
|
51
63
|
export { serializeAnthropicMessages, serializeOpenAIMessages, serializeCodexMessages };
|