@adecore/agents 0.0.1 → 0.17.0-beta.2
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/LICENSE +110 -0
- package/README.md +48 -0
- package/dist/async.d.ts +2 -0
- package/dist/async.js +31 -0
- package/dist/chat/attachment-store.d.ts +10 -0
- package/dist/chat/attachment-store.js +93 -0
- package/dist/chat/backend.d.ts +214 -0
- package/dist/chat/backend.js +1 -0
- package/dist/chat/background-work.d.ts +8 -0
- package/dist/chat/background-work.js +33 -0
- package/dist/chat/bookmark-store.d.ts +25 -0
- package/dist/chat/bookmark-store.js +141 -0
- package/dist/chat/chat-core.d.ts +311 -0
- package/dist/chat/chat-core.js +1025 -0
- package/dist/chat/chat-log.d.ts +33 -0
- package/dist/chat/chat-log.js +163 -0
- package/dist/chat/chat-process.d.ts +46 -0
- package/dist/chat/chat-process.js +190 -0
- package/dist/chat/chat-session.d.ts +209 -0
- package/dist/chat/chat-session.js +1511 -0
- package/dist/chat/chat-store.d.ts +37 -0
- package/dist/chat/chat-store.js +202 -0
- package/dist/chat/chat-title.d.ts +10 -0
- package/dist/chat/chat-title.js +74 -0
- package/dist/chat/claude-backend.d.ts +35 -0
- package/dist/chat/claude-backend.js +214 -0
- package/dist/chat/claude-protocol.d.ts +41 -0
- package/dist/chat/claude-protocol.js +642 -0
- package/dist/chat/claude-title.d.ts +11 -0
- package/dist/chat/claude-title.js +97 -0
- package/dist/chat/claude-transcript.d.ts +28 -0
- package/dist/chat/claude-transcript.js +211 -0
- package/dist/chat/codex-backend.d.ts +51 -0
- package/dist/chat/codex-backend.js +364 -0
- package/dist/chat/codex-protocol.d.ts +56 -0
- package/dist/chat/codex-protocol.js +767 -0
- package/dist/chat/codex-thread.d.ts +32 -0
- package/dist/chat/codex-thread.js +131 -0
- package/dist/chat/codex-transport.d.ts +42 -0
- package/dist/chat/codex-transport.js +188 -0
- package/dist/chat/composer-preferences.d.ts +15 -0
- package/dist/chat/composer-preferences.js +46 -0
- package/dist/chat/context-breakdown.d.ts +2 -0
- package/dist/chat/context-breakdown.js +74 -0
- package/dist/chat/delta-coalescer.d.ts +12 -0
- package/dist/chat/delta-coalescer.js +55 -0
- package/dist/chat/errors.d.ts +4 -0
- package/dist/chat/errors.js +4 -0
- package/dist/chat/fake-claude.d.ts +3 -0
- package/dist/chat/fake-claude.js +982 -0
- package/dist/chat/fake-cli.d.ts +25 -0
- package/dist/chat/fake-cli.js +157 -0
- package/dist/chat/fake-codex.d.ts +10 -0
- package/dist/chat/fake-codex.js +502 -0
- package/dist/chat/fixtures/claude-2.1.285-auto-fallback.json +24 -0
- package/dist/chat/fixtures/claude-2.1.285-native-children.json +6600 -0
- package/dist/chat/fixtures/claude-2.1.285-resume-direct-compact.json +202 -0
- package/dist/chat/fixtures/claude-2.1.285-resume-task-notification.json +57 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.jsonl +10 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.meta.json +1 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.jsonl +2 -0
- package/dist/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.meta.json +1 -0
- package/dist/chat/fixtures/codex-0.159.2-native-approval.json +119 -0
- package/dist/chat/fixtures/codex-0.159.2-native-file-approval.json +136 -0
- package/dist/chat/fixtures/codex-0.159.2-native-interacted.json +220 -0
- package/dist/chat/fixtures/codex-0.159.2-usage-windows.json +74 -0
- package/dist/chat/input.d.ts +19 -0
- package/dist/chat/input.js +71 -0
- package/dist/chat/limit-resume.d.ts +11 -0
- package/dist/chat/limit-resume.js +68 -0
- package/dist/chat/projector.d.ts +60 -0
- package/dist/chat/projector.js +860 -0
- package/dist/chat/request-summary.d.ts +3 -0
- package/dist/chat/request-summary.js +143 -0
- package/dist/chat/subagent-projection.d.ts +8 -0
- package/dist/chat/subagent-projection.js +40 -0
- package/dist/chat/subagent-reader.d.ts +66 -0
- package/dist/chat/subagent-reader.js +396 -0
- package/dist/chat/subagent-settlement.d.ts +6 -0
- package/dist/chat/subagent-settlement.js +103 -0
- package/dist/chat/thread.d.ts +30 -0
- package/dist/chat/thread.js +147 -0
- package/dist/chat/wake-chat.d.ts +20 -0
- package/dist/chat/wake-chat.js +27 -0
- package/dist/client-sinks.d.ts +9 -0
- package/dist/client-sinks.js +34 -0
- package/dist/coded-error.d.ts +5 -0
- package/dist/coded-error.js +17 -0
- package/dist/context/argv.d.ts +11 -0
- package/dist/context/argv.js +58 -0
- package/dist/context/refusal.d.ts +9 -0
- package/dist/context/refusal.js +38 -0
- package/dist/context/verb.d.ts +85 -0
- package/dist/context/verb.js +233 -0
- package/dist/error-text.d.ts +5 -0
- package/dist/error-text.js +24 -0
- package/dist/events.d.ts +12 -0
- package/dist/events.js +1 -0
- package/dist/fs.d.ts +9 -0
- package/dist/fs.js +107 -0
- package/dist/host/agent-host.d.ts +28 -0
- package/dist/host/agent-host.js +105 -0
- package/dist/host/environment.d.ts +7 -0
- package/dist/host/environment.js +22 -0
- package/dist/host/handlers.d.ts +30 -0
- package/dist/host/handlers.js +111 -0
- package/dist/host/memory-port.d.ts +2 -0
- package/dist/host/memory-port.js +25 -0
- package/dist/host/wiring.d.ts +40 -0
- package/dist/host/wiring.js +120 -0
- package/dist/lineage.d.ts +44 -0
- package/dist/lineage.js +134 -0
- package/dist/messages/deliver-message.d.ts +21 -0
- package/dist/messages/deliver-message.js +42 -0
- package/dist/messages/deliver-notice.d.ts +25 -0
- package/dist/messages/deliver-notice.js +75 -0
- package/dist/messages/notice-notes.d.ts +15 -0
- package/dist/messages/notice-notes.js +26 -0
- package/dist/messages/notice-store.d.ts +30 -0
- package/dist/messages/notice-store.js +120 -0
- package/dist/modes.d.ts +11 -0
- package/dist/modes.js +28 -0
- package/dist/outbox/manual-clock.d.ts +10 -0
- package/dist/outbox/manual-clock.js +25 -0
- package/dist/outbox/outbox-worker.d.ts +44 -0
- package/dist/outbox/outbox-worker.js +173 -0
- package/dist/outbox/outbox.d.ts +37 -0
- package/dist/outbox/outbox.js +76 -0
- package/dist/providers/accounts/accounts.d.ts +20 -0
- package/dist/providers/accounts/accounts.js +158 -0
- package/dist/providers/accounts/launch.d.ts +17 -0
- package/dist/providers/accounts/launch.js +30 -0
- package/dist/providers/accounts/service.d.ts +87 -0
- package/dist/providers/accounts/service.js +538 -0
- package/dist/providers/accounts/shadow-home.d.ts +8 -0
- package/dist/providers/accounts/shadow-home.js +70 -0
- package/dist/providers/accounts/status.d.ts +12 -0
- package/dist/providers/accounts/status.js +85 -0
- package/dist/providers/accounts/store.d.ts +14 -0
- package/dist/providers/accounts/store.js +112 -0
- package/dist/providers/accounts/test-accounts.d.ts +12 -0
- package/dist/providers/accounts/test-accounts.js +38 -0
- package/dist/providers/accounts/variables.d.ts +27 -0
- package/dist/providers/accounts/variables.js +94 -0
- package/dist/providers/catalog.d.ts +17 -0
- package/dist/providers/catalog.js +76 -0
- package/dist/providers/claude-models.json +114 -0
- package/dist/providers/claude-provider.d.ts +4 -0
- package/dist/providers/claude-provider.js +29 -0
- package/dist/providers/claude.d.ts +15 -0
- package/dist/providers/claude.js +105 -0
- package/dist/providers/codex-models.json +135 -0
- package/dist/providers/codex-provider.d.ts +4 -0
- package/dist/providers/codex-provider.js +27 -0
- package/dist/providers/codex-rules.d.ts +9 -0
- package/dist/providers/codex-rules.js +35 -0
- package/dist/providers/codex.d.ts +14 -0
- package/dist/providers/codex.js +52 -0
- package/dist/providers/detect.d.ts +6 -0
- package/dist/providers/detect.js +30 -0
- package/dist/providers/provider.d.ts +25 -0
- package/dist/providers/provider.js +1 -0
- package/dist/providers/registry.d.ts +28 -0
- package/dist/providers/registry.js +118 -0
- package/dist/record-directory.d.ts +27 -0
- package/dist/record-directory.js +114 -0
- package/dist/run-process.d.ts +13 -0
- package/dist/run-process.js +42 -0
- package/dist/serializer.d.ts +9 -0
- package/dist/serializer.js +34 -0
- package/dist/skills.d.ts +21 -0
- package/dist/skills.js +174 -0
- package/dist/tasks/background-limit.d.ts +16 -0
- package/dist/tasks/background-limit.js +55 -0
- package/dist/tasks/end-children.d.ts +34 -0
- package/dist/tasks/end-children.js +53 -0
- package/dist/tasks/give-task.d.ts +14 -0
- package/dist/tasks/give-task.js +33 -0
- package/dist/tasks/task-coordinator.d.ts +52 -0
- package/dist/tasks/task-coordinator.js +323 -0
- package/dist/tasks/task-store.d.ts +49 -0
- package/dist/tasks/task-store.js +197 -0
- package/dist/tasks/task-work.d.ts +47 -0
- package/dist/tasks/task-work.js +28 -0
- package/dist/tasks/waiting-child.d.ts +51 -0
- package/dist/tasks/waiting-child.js +143 -0
- package/dist/tasks/wake-parent.d.ts +19 -0
- package/dist/tasks/wake-parent.js +109 -0
- package/dist/tasks/wiring.d.ts +51 -0
- package/dist/tasks/wiring.js +151 -0
- package/dist/title-file.d.ts +2 -0
- package/dist/title-file.js +55 -0
- package/dist/usage/accounts.d.ts +15 -0
- package/dist/usage/accounts.js +64 -0
- package/dist/usage/aggregate.d.ts +12 -0
- package/dist/usage/aggregate.js +154 -0
- package/dist/usage/exchange.d.ts +16 -0
- package/dist/usage/exchange.js +84 -0
- package/dist/usage/index-file.d.ts +15 -0
- package/dist/usage/index-file.js +123 -0
- package/dist/usage/limits/monitor.d.ts +50 -0
- package/dist/usage/limits/monitor.js +179 -0
- package/dist/usage/limits/normalize.d.ts +21 -0
- package/dist/usage/limits/normalize.js +160 -0
- package/dist/usage/limits/probe.d.ts +9 -0
- package/dist/usage/limits/probe.js +113 -0
- package/dist/usage/prices-snapshot.json +1071 -0
- package/dist/usage/pricing.d.ts +34 -0
- package/dist/usage/pricing.js +186 -0
- package/dist/usage/projects.d.ts +17 -0
- package/dist/usage/projects.js +49 -0
- package/dist/usage/readers/claude.d.ts +3 -0
- package/dist/usage/readers/claude.js +59 -0
- package/dist/usage/readers/codex.d.ts +23 -0
- package/dist/usage/readers/codex.js +155 -0
- package/dist/usage/record.d.ts +15 -0
- package/dist/usage/record.js +39 -0
- package/dist/usage/roots.d.ts +12 -0
- package/dist/usage/roots.js +29 -0
- package/dist/usage/scanner.d.ts +30 -0
- package/dist/usage/scanner.js +234 -0
- package/dist/usage/usage-service.d.ts +35 -0
- package/dist/usage/usage-service.js +130 -0
- package/dist/watch-seam.d.ts +29 -0
- package/dist/watch-seam.js +78 -0
- package/dist/watch-test-helpers.d.ts +28 -0
- package/dist/watch-test-helpers.js +86 -0
- package/package.json +42 -3
- package/src/async.ts +40 -0
- package/src/chat/attachment-store.ts +108 -0
- package/src/chat/backend.ts +199 -0
- package/src/chat/background-work.ts +42 -0
- package/src/chat/bookmark-store.ts +166 -0
- package/src/chat/chat-core.ts +1228 -0
- package/src/chat/chat-log.ts +187 -0
- package/src/chat/chat-process.ts +237 -0
- package/src/chat/chat-session.ts +1723 -0
- package/src/chat/chat-store.ts +261 -0
- package/src/chat/chat-title.ts +90 -0
- package/src/chat/claude-backend.ts +235 -0
- package/src/chat/claude-protocol.ts +684 -0
- package/src/chat/claude-title.ts +106 -0
- package/src/chat/claude-transcript.ts +233 -0
- package/src/chat/codex-backend.ts +397 -0
- package/src/chat/codex-protocol.ts +872 -0
- package/src/chat/codex-thread.ts +162 -0
- package/src/chat/codex-transport.ts +224 -0
- package/src/chat/composer-preferences.ts +67 -0
- package/src/chat/context-breakdown.ts +80 -0
- package/src/chat/delta-coalescer.ts +62 -0
- package/src/chat/errors.ts +25 -0
- package/src/chat/fake-claude.ts +1006 -0
- package/src/chat/fake-cli.ts +199 -0
- package/src/chat/fake-codex.ts +534 -0
- package/src/chat/fixtures/claude-2.1.285-auto-fallback.json +24 -0
- package/src/chat/fixtures/claude-2.1.285-native-children.json +6600 -0
- package/src/chat/fixtures/claude-2.1.285-resume-direct-compact.json +202 -0
- package/src/chat/fixtures/claude-2.1.285-resume-task-notification.json +57 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.jsonl +10 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a4c2e8f10b3d5a7e9.meta.json +1 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.jsonl +2 -0
- package/src/chat/fixtures/claude-projects/-work-demo/5f1c2a9e-0b7d-4c1e-9a53-3e2f8d6b7a10/subagents/agent-a9b8c7d6e5f4a3b21.meta.json +1 -0
- package/src/chat/fixtures/codex-0.159.2-native-approval.json +119 -0
- package/src/chat/fixtures/codex-0.159.2-native-file-approval.json +136 -0
- package/src/chat/fixtures/codex-0.159.2-native-interacted.json +220 -0
- package/src/chat/fixtures/codex-0.159.2-usage-windows.json +74 -0
- package/src/chat/input.ts +89 -0
- package/src/chat/limit-resume.ts +74 -0
- package/src/chat/projector.ts +969 -0
- package/src/chat/request-summary.ts +168 -0
- package/src/chat/subagent-projection.ts +45 -0
- package/src/chat/subagent-reader.ts +469 -0
- package/src/chat/subagent-settlement.ts +111 -0
- package/src/chat/thread.ts +167 -0
- package/src/chat/wake-chat.ts +45 -0
- package/src/client-sinks.ts +39 -0
- package/src/coded-error.ts +18 -0
- package/src/context/argv.ts +61 -0
- package/src/context/refusal.ts +50 -0
- package/src/context/verb.ts +392 -0
- package/src/error-text.ts +28 -0
- package/src/events.ts +9 -0
- package/src/fs.ts +118 -0
- package/src/host/agent-host.ts +124 -0
- package/src/host/environment.ts +31 -0
- package/src/host/handlers.ts +142 -0
- package/src/host/memory-port.ts +27 -0
- package/src/host/wiring.ts +164 -0
- package/src/lineage.ts +159 -0
- package/src/messages/deliver-message.ts +61 -0
- package/src/messages/deliver-notice.ts +120 -0
- package/src/messages/notice-notes.ts +34 -0
- package/src/messages/notice-store.ts +142 -0
- package/src/modes.ts +38 -0
- package/src/outbox/manual-clock.ts +31 -0
- package/src/outbox/outbox-worker.ts +214 -0
- package/src/outbox/outbox.ts +106 -0
- package/src/providers/accounts/accounts.ts +192 -0
- package/src/providers/accounts/launch.ts +55 -0
- package/src/providers/accounts/service.ts +632 -0
- package/src/providers/accounts/shadow-home.ts +85 -0
- package/src/providers/accounts/status.ts +106 -0
- package/src/providers/accounts/store.ts +158 -0
- package/src/providers/accounts/test-accounts.ts +48 -0
- package/src/providers/accounts/variables.ts +132 -0
- package/src/providers/catalog.ts +90 -0
- package/src/providers/claude-models.json +114 -0
- package/src/providers/claude-provider.ts +32 -0
- package/src/providers/claude.ts +123 -0
- package/src/providers/codex-models.json +135 -0
- package/src/providers/codex-provider.ts +30 -0
- package/src/providers/codex-rules.ts +54 -0
- package/src/providers/codex.ts +69 -0
- package/src/providers/detect.ts +40 -0
- package/src/providers/provider.ts +46 -0
- package/src/providers/registry.ts +150 -0
- package/src/record-directory.ts +136 -0
- package/src/run-process.ts +61 -0
- package/src/serializer.ts +41 -0
- package/src/skills.ts +199 -0
- package/src/tasks/background-limit.ts +72 -0
- package/src/tasks/end-children.ts +100 -0
- package/src/tasks/give-task.ts +52 -0
- package/src/tasks/task-coordinator.ts +384 -0
- package/src/tasks/task-store.ts +236 -0
- package/src/tasks/task-work.ts +62 -0
- package/src/tasks/waiting-child.ts +203 -0
- package/src/tasks/wake-parent.ts +142 -0
- package/src/tasks/wiring.ts +231 -0
- package/src/title-file.ts +57 -0
- package/src/usage/accounts.ts +86 -0
- package/src/usage/aggregate.ts +220 -0
- package/src/usage/exchange.ts +93 -0
- package/src/usage/index-file.ts +178 -0
- package/src/usage/limits/monitor.ts +239 -0
- package/src/usage/limits/normalize.ts +188 -0
- package/src/usage/limits/probe.ts +120 -0
- package/src/usage/prices-snapshot.json +1071 -0
- package/src/usage/pricing.ts +236 -0
- package/src/usage/projects.ts +65 -0
- package/src/usage/readers/claude.ts +60 -0
- package/src/usage/readers/codex.ts +196 -0
- package/src/usage/record.ts +60 -0
- package/src/usage/roots.ts +39 -0
- package/src/usage/scanner.ts +265 -0
- package/src/usage/usage-service.ts +161 -0
- package/src/watch-seam.ts +111 -0
- package/src/watch-test-helpers.ts +102 -0
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { accessSync, constants } from 'node:fs';
|
|
2
|
+
import { attachmentImageMime } from '@adecore/agent-contracts';
|
|
3
|
+
import { codexServiceTier, codexThreadOptions } from '../providers/codex.js';
|
|
4
|
+
import { CodexProtocol } from './codex-protocol.js';
|
|
5
|
+
import { attachmentNote } from './input.js';
|
|
6
|
+
import { CodexTransport, DEFAULT_CODEX_CLIENT } from './codex-transport.js';
|
|
7
|
+
import { errorText } from '../error-text.js';
|
|
8
|
+
function textInput(text) {
|
|
9
|
+
return [{ type: 'text', text, text_elements: [] }];
|
|
10
|
+
}
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
/* The enabled skills of a `skills/list` answer; Codex reports one entry per folder it was asked about. */
|
|
15
|
+
export function parseSkillsList(result) {
|
|
16
|
+
const data = isRecord(result) && Array.isArray(result.data) ? result.data : [];
|
|
17
|
+
const skills = new Map();
|
|
18
|
+
for (const entry of data) {
|
|
19
|
+
for (const skill of isRecord(entry) && Array.isArray(entry.skills) ? entry.skills : []) {
|
|
20
|
+
if (!isRecord(skill) || typeof skill.name !== 'string' || skill.enabled === false || skills.has(skill.name)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
skills.set(skill.name, {
|
|
24
|
+
name: skill.name,
|
|
25
|
+
description: typeof skill.description === 'string' ? skill.description : '',
|
|
26
|
+
source: skill.pluginId ? 'plugin' : skill.scope === 'repo' ? 'project' : 'user'
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return [...skills.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
31
|
+
}
|
|
32
|
+
/*
|
|
33
|
+
* One `codex app-server` process on JSON-RPC over stdio. `start` handshakes (initialize, then
|
|
34
|
+
* thread/start or thread/resume) before the first turn goes out, so every later request knows the
|
|
35
|
+
* thread id. What the host tells the agent goes in as the thread's developer instructions, which
|
|
36
|
+
* Codex keeps with the thread. The runtime modes are an approval policy plus a sandbox.
|
|
37
|
+
*/
|
|
38
|
+
export class CodexBackend {
|
|
39
|
+
acknowledgesTurns = true;
|
|
40
|
+
promptId = null;
|
|
41
|
+
launch;
|
|
42
|
+
host;
|
|
43
|
+
protocol;
|
|
44
|
+
client;
|
|
45
|
+
transport = null;
|
|
46
|
+
threadId = '';
|
|
47
|
+
/*
|
|
48
|
+
* Codex 0.154 ignores developer instructions on `thread/resume` (measured), so a thread started
|
|
49
|
+
* before anything was linked would never hear of its links; the first prompt of a resumed process says so instead.
|
|
50
|
+
*/
|
|
51
|
+
contextPending = null;
|
|
52
|
+
imageInputSupported = null;
|
|
53
|
+
/* A turn asked for that Codex has not named yet; `turn/interrupt` needs that name. */
|
|
54
|
+
turnUnnamed = false;
|
|
55
|
+
/* A stop that came in while the turn was unnamed, sent once `turn/started` names it. */
|
|
56
|
+
interruptOwed = false;
|
|
57
|
+
constructor(launch, host, options = {}) {
|
|
58
|
+
this.launch = launch;
|
|
59
|
+
this.host = host;
|
|
60
|
+
this.client = options.client ?? DEFAULT_CODEX_CLIENT;
|
|
61
|
+
this.protocol = new CodexProtocol(launch.generation);
|
|
62
|
+
this.threadId = launch.resume ?? '';
|
|
63
|
+
}
|
|
64
|
+
get running() {
|
|
65
|
+
return this.transport !== null;
|
|
66
|
+
}
|
|
67
|
+
get pid() {
|
|
68
|
+
return this.transport?.pid ?? null;
|
|
69
|
+
}
|
|
70
|
+
async start() {
|
|
71
|
+
if (this.transport) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const transport = new CodexTransport({
|
|
75
|
+
command: this.launch.command,
|
|
76
|
+
cwd: this.launch.cwd,
|
|
77
|
+
env: this.launch.env,
|
|
78
|
+
...(this.launch.spawn ? { spawn: this.launch.spawn } : {}),
|
|
79
|
+
onError: (error) => this.fail(error),
|
|
80
|
+
onFrame: (frame) => this.handleFrame(transport, frame),
|
|
81
|
+
onExit: (exitCode, stderr) => this.handleExit(transport, exitCode, stderr)
|
|
82
|
+
});
|
|
83
|
+
this.transport = transport;
|
|
84
|
+
await transport.request('initialize', { clientInfo: this.client, capabilities: { experimentalApi: true, requestAttestation: false } });
|
|
85
|
+
transport.notify('initialized', {});
|
|
86
|
+
const tier = codexServiceTier(this.launch.selection);
|
|
87
|
+
const params = {
|
|
88
|
+
cwd: this.launch.cwd,
|
|
89
|
+
model: this.launch.selection.model,
|
|
90
|
+
...(tier === null ? {} : { serviceTier: tier }),
|
|
91
|
+
...codexThreadOptions(this.launch.runtimeMode)
|
|
92
|
+
};
|
|
93
|
+
const instructions = this.launch.instructions === null ? {} : { developerInstructions: this.launch.instructions };
|
|
94
|
+
let result;
|
|
95
|
+
if (this.launch.resume) {
|
|
96
|
+
try {
|
|
97
|
+
result = await transport.request('thread/resume', { threadId: this.launch.resume, excludeTurns: true, ...params });
|
|
98
|
+
this.contextPending = this.launch.resumeNote;
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
// The thread is gone from Codex's store; a fresh one keeps the chat usable.
|
|
102
|
+
this.emit({ type: 'note', level: 'warning', text: `Codex could not resume its thread (${errorText(error)}). Started a new one.` });
|
|
103
|
+
result = await transport.request('thread/start', { ...params, ...instructions });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
result = await transport.request('thread/start', { ...params, ...instructions });
|
|
108
|
+
}
|
|
109
|
+
this.imageInputSupported = await this.readImageSupport(transport);
|
|
110
|
+
for (const event of this.protocol.threadReady(result)) {
|
|
111
|
+
if (event.type === 'session' && event.agentSessionId) {
|
|
112
|
+
this.threadId = event.agentSessionId;
|
|
113
|
+
}
|
|
114
|
+
this.emit(event);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
sendTurn(input) {
|
|
118
|
+
const images = input.attachments.filter((attachment) => attachmentImageMime(attachment) !== null);
|
|
119
|
+
if (images.length > 0 && this.imageInputSupported === false) {
|
|
120
|
+
throw new Error(`${this.launch.modelName} does not support image input. Choose a model that accepts images.`);
|
|
121
|
+
}
|
|
122
|
+
for (const attachment of images) {
|
|
123
|
+
try {
|
|
124
|
+
accessSync(attachment.path, constants.R_OK);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
throw new Error(`Could not read attached image "${attachment.name}". Attach it again and retry.`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const parts = [];
|
|
131
|
+
if (this.contextPending !== null) {
|
|
132
|
+
parts.push(`${this.contextPending}\n\n`);
|
|
133
|
+
this.contextPending = null;
|
|
134
|
+
}
|
|
135
|
+
if (input.preamble !== null) {
|
|
136
|
+
parts.push(`${input.preamble}\n\n`);
|
|
137
|
+
}
|
|
138
|
+
parts.push(input.text);
|
|
139
|
+
const note = attachmentNote(input.attachments);
|
|
140
|
+
if (note !== '') {
|
|
141
|
+
parts.push(`\n\n${note}`);
|
|
142
|
+
}
|
|
143
|
+
const effort = this.launch.selection.options.effort;
|
|
144
|
+
const tier = codexServiceTier(this.launch.selection);
|
|
145
|
+
this.turnUnnamed = true;
|
|
146
|
+
this.promptId = input.promptId ?? null;
|
|
147
|
+
this.request('turn/start', {
|
|
148
|
+
threadId: this.threadId,
|
|
149
|
+
input: [...textInput(parts.join('')), ...images.map((attachment) => ({ type: 'localImage', path: attachment.path }))],
|
|
150
|
+
model: this.launch.selection.model,
|
|
151
|
+
...(typeof effort === 'string' ? { effort } : {}),
|
|
152
|
+
...(tier === null ? {} : { serviceTier: tier })
|
|
153
|
+
}, () => this.acceptPrompt(input.promptId));
|
|
154
|
+
}
|
|
155
|
+
acceptPrompt(promptId) {
|
|
156
|
+
if (promptId !== undefined && this.promptId === promptId) {
|
|
157
|
+
this.emit({ type: 'turn.accepted', promptId });
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
fail(error) {
|
|
161
|
+
this.protocol.forgetPending();
|
|
162
|
+
this.emit({ type: 'failed', message: errorText(error), processAlive: this.running });
|
|
163
|
+
}
|
|
164
|
+
respond(requestId, rpcId, result) {
|
|
165
|
+
try {
|
|
166
|
+
if (!this.transport) {
|
|
167
|
+
throw new Error('Codex is not running');
|
|
168
|
+
}
|
|
169
|
+
this.transport.respond(rpcId, result);
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
this.emit({ type: 'request.withdrawn', requestId });
|
|
174
|
+
this.fail(error);
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async readImageSupport(transport) {
|
|
179
|
+
let cursor = null;
|
|
180
|
+
const seen = new Set();
|
|
181
|
+
try {
|
|
182
|
+
do {
|
|
183
|
+
const result = await transport.request('model/list', { includeHidden: true, ...(cursor === null ? {} : { cursor }) });
|
|
184
|
+
if (!isRecord(result) || !Array.isArray(result.data)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const model = result.data.find((entry) => isRecord(entry) && entry.model === this.launch.selection.model);
|
|
188
|
+
if (isRecord(model)) {
|
|
189
|
+
return Array.isArray(model.inputModalities) ? model.inputModalities.includes('image') : null;
|
|
190
|
+
}
|
|
191
|
+
cursor = typeof result.nextCursor === 'string' ? result.nextCursor : null;
|
|
192
|
+
if (cursor !== null) {
|
|
193
|
+
if (seen.has(cursor)) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
seen.add(cursor);
|
|
197
|
+
}
|
|
198
|
+
} while (cursor !== null);
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// Older app-servers may not report modalities; their turn response remains authoritative.
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
compact() {
|
|
206
|
+
this.turnUnnamed = true;
|
|
207
|
+
this.request('thread/compact/start', { threadId: this.threadId });
|
|
208
|
+
}
|
|
209
|
+
/* Not a turn: a name Codex refuses is not something the person has to hear about. */
|
|
210
|
+
setTitle(title) {
|
|
211
|
+
if (!this.transport || this.threadId === '') {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
void this.transport.request('thread/name/set', { threadId: this.threadId, name: title }).catch(() => undefined);
|
|
215
|
+
}
|
|
216
|
+
/* Codex keeps its own skill index, so the running app-server is the authority on what it will run. */
|
|
217
|
+
async listSkills() {
|
|
218
|
+
const transport = this.transport;
|
|
219
|
+
if (!transport) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
return parseSkillsList(await transport.request('skills/list', { cwds: [this.launch.cwd] }));
|
|
223
|
+
}
|
|
224
|
+
listThreadItems(params) {
|
|
225
|
+
const transport = this.transport;
|
|
226
|
+
if (!transport) {
|
|
227
|
+
return Promise.reject(new Error('thread/items/list failed: Codex is not running'));
|
|
228
|
+
}
|
|
229
|
+
return transport.request('thread/items/list', params);
|
|
230
|
+
}
|
|
231
|
+
interrupt() {
|
|
232
|
+
const turnId = this.protocol.turnId;
|
|
233
|
+
if (!this.transport) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (!turnId) {
|
|
237
|
+
this.interruptOwed = this.turnUnnamed;
|
|
238
|
+
// A child request can open a host turn after the root's native turn already ended.
|
|
239
|
+
if (!this.turnUnnamed) {
|
|
240
|
+
this.emit({ type: 'turn.done', state: 'aborted', costUsd: 0 });
|
|
241
|
+
}
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
void this.transport.request('turn/interrupt', { threadId: this.threadId, turnId }).catch(() => {
|
|
245
|
+
// A turn that already ended has nothing to interrupt.
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
stopTask(taskId) {
|
|
249
|
+
if (!this.transport) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
void this.transport.request('thread/backgroundTerminals/terminate', { threadId: this.threadId, processId: taskId }).catch(() => {
|
|
253
|
+
// A process that already ended has nothing to terminate; its item says so on its own.
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
respondApproval(requestId, decision) {
|
|
257
|
+
if (!this.transport) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
const answer = this.protocol.approvalDecision(requestId, decision);
|
|
261
|
+
if (!answer) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
return this.respond(requestId, answer.rpcId, answer.result);
|
|
265
|
+
}
|
|
266
|
+
respondQuestion(requestId, answers) {
|
|
267
|
+
if (!this.transport) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
const answer = this.protocol.questionAnswer(requestId, answers);
|
|
271
|
+
if (!answer) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
if (answer.kind === 'respond') {
|
|
275
|
+
return this.respond(requestId, answer.rpcId, answer.result);
|
|
276
|
+
}
|
|
277
|
+
const turnId = this.protocol.turnId;
|
|
278
|
+
if (!turnId) {
|
|
279
|
+
// Codex refuses a steer without the turn it is meant for, and there is none left to join.
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
this.request('turn/steer', { threadId: this.threadId, expectedTurnId: turnId, input: textInput(answer.text) });
|
|
283
|
+
return true;
|
|
284
|
+
}
|
|
285
|
+
/* Codex asks its async question once and waits; forgetting it locally is all a dismissal is. */
|
|
286
|
+
dismissRequest(requestId) {
|
|
287
|
+
return this.protocol.dismissQuestion(requestId);
|
|
288
|
+
}
|
|
289
|
+
declineRequest(requestId, _message) {
|
|
290
|
+
const answer = this.protocol.declineRequest(requestId);
|
|
291
|
+
if (!this.transport || !answer) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
return this.respond(requestId, answer.rpcId, answer.result);
|
|
295
|
+
}
|
|
296
|
+
stop() {
|
|
297
|
+
this.transport?.stop();
|
|
298
|
+
}
|
|
299
|
+
dispose() {
|
|
300
|
+
const transport = this.transport;
|
|
301
|
+
this.transport = null;
|
|
302
|
+
return transport?.dispose() ?? Promise.resolve();
|
|
303
|
+
}
|
|
304
|
+
/* A request whose failure the person has to hear about, since it is the turn that cannot go on. */
|
|
305
|
+
request(method, params, accepted) {
|
|
306
|
+
const transport = this.transport;
|
|
307
|
+
if (!transport) {
|
|
308
|
+
this.emit({ type: 'failed', message: `${method} failed: Codex is not running` });
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
void transport
|
|
312
|
+
.request(method, params)
|
|
313
|
+
.then(() => {
|
|
314
|
+
if (this.transport === transport) {
|
|
315
|
+
accepted?.();
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
.catch((error) => {
|
|
319
|
+
if (this.transport === transport) {
|
|
320
|
+
this.emit({ type: 'failed', message: errorText(error) });
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
handleFrame(transport, frame) {
|
|
325
|
+
if (this.transport !== transport) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const events = this.protocol.handle(frame);
|
|
329
|
+
// A server request the protocol did not take still needs an answer, or Codex waits forever.
|
|
330
|
+
if (frame.id !== undefined && events.length === 0 && (typeof frame.id === 'number' || typeof frame.id === 'string')) {
|
|
331
|
+
transport.respondError(frame.id, -32601, `${this.client.title} does not handle ${String(frame.method)}`);
|
|
332
|
+
}
|
|
333
|
+
if (frame.method === 'turn/started' && this.protocol.turnId !== null) {
|
|
334
|
+
this.acceptPrompt(this.promptId ?? undefined);
|
|
335
|
+
}
|
|
336
|
+
for (const event of events) {
|
|
337
|
+
this.emit(event);
|
|
338
|
+
}
|
|
339
|
+
if (this.turnUnnamed && this.protocol.turnId !== null) {
|
|
340
|
+
this.turnUnnamed = false;
|
|
341
|
+
if (this.interruptOwed) {
|
|
342
|
+
this.interruptOwed = false;
|
|
343
|
+
this.interrupt();
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
handleExit(transport, exitCode, stderr) {
|
|
348
|
+
if (this.transport !== transport) {
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
this.transport = null;
|
|
352
|
+
this.protocol.forgetPending();
|
|
353
|
+
this.emit({ type: 'exit', exitCode, ...(stderr === null ? {} : { stderr }) });
|
|
354
|
+
}
|
|
355
|
+
emit(event) {
|
|
356
|
+
// A turn that ended or never started leaves no stop for the next one to take.
|
|
357
|
+
if (event.type === 'turn.done' || event.type === 'failed' || event.type === 'exit') {
|
|
358
|
+
this.turnUnnamed = false;
|
|
359
|
+
this.interruptOwed = false;
|
|
360
|
+
this.promptId = null;
|
|
361
|
+
}
|
|
362
|
+
this.host.onEvent(event);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ApprovalDecision, BackendEvent } from './backend.ts';
|
|
2
|
+
import type { CodexFrame } from './codex-transport.ts';
|
|
3
|
+
type CodexRpcId = number | string;
|
|
4
|
+
type CodexAnswer = {
|
|
5
|
+
kind: 'respond';
|
|
6
|
+
rpcId: CodexRpcId;
|
|
7
|
+
result: unknown;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'steer';
|
|
10
|
+
text: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function unwrapCommand(command: string): string;
|
|
13
|
+
export declare class CodexProtocol {
|
|
14
|
+
private readonly generation;
|
|
15
|
+
private readonly pending;
|
|
16
|
+
private readonly toolInputs;
|
|
17
|
+
private codexTurnId;
|
|
18
|
+
private readonly openTerminals;
|
|
19
|
+
private readonly backgroundTerminals;
|
|
20
|
+
private spentUntil;
|
|
21
|
+
private ownThreadId;
|
|
22
|
+
private readonly agentRows;
|
|
23
|
+
constructor(generation: number);
|
|
24
|
+
get turnId(): string | null;
|
|
25
|
+
threadReady(result: unknown): BackendEvent[];
|
|
26
|
+
handle(frame: CodexFrame): BackendEvent[];
|
|
27
|
+
approvalDecision(requestId: string, decision: ApprovalDecision): {
|
|
28
|
+
rpcId: CodexRpcId;
|
|
29
|
+
result: unknown;
|
|
30
|
+
} | null;
|
|
31
|
+
questionAnswer(requestId: string, answers: Record<string, string>): CodexAnswer | null;
|
|
32
|
+
dismissQuestion(requestId: string): boolean;
|
|
33
|
+
forgetPending(): void;
|
|
34
|
+
declineRequest(requestId: string): {
|
|
35
|
+
rpcId: CodexRpcId;
|
|
36
|
+
result: unknown;
|
|
37
|
+
} | null;
|
|
38
|
+
private owner;
|
|
39
|
+
private toolKey;
|
|
40
|
+
private rememberPatch;
|
|
41
|
+
private withdrawOwner;
|
|
42
|
+
private isChildRequest;
|
|
43
|
+
private requestDescription;
|
|
44
|
+
private requestId;
|
|
45
|
+
private handleServerRequest;
|
|
46
|
+
private elicitation;
|
|
47
|
+
private handleItem;
|
|
48
|
+
private handleTurnCompleted;
|
|
49
|
+
private limitOf;
|
|
50
|
+
private text;
|
|
51
|
+
private spawnedAgent;
|
|
52
|
+
private settleEndedAgents;
|
|
53
|
+
private subAgentActivity;
|
|
54
|
+
private tool;
|
|
55
|
+
}
|
|
56
|
+
export {};
|