@selesai/code 0.13.8 → 0.13.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/core/agent-session.d.ts +6 -0
  3. package/dist/core/agent-session.js +5 -3
  4. package/dist/core/settings-manager.d.ts +11 -0
  5. package/dist/core/settings-manager.js +48 -0
  6. package/dist/extensions/auto-session-name.test.ts +13 -1
  7. package/dist/extensions/auto-session-name.ts +8 -4
  8. package/dist/extensions/handoff-new.test.ts +26 -1
  9. package/dist/extensions/handoff-new.ts +5 -0
  10. package/dist/extensions/pi-subagents/README.md +0 -6
  11. package/dist/extensions/pi-subagents/docs/agents.md +1 -114
  12. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +1 -12
  13. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
  14. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +3 -19
  15. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +8 -37
  16. package/dist/extensions/pi-subagents/src/shared/types.ts +2 -2
  17. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +2 -38
  18. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +0 -49
  19. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +0 -18
  20. package/dist/modes/rpc/rpc-client.d.ts +180 -10
  21. package/dist/modes/rpc/rpc-client.js +211 -13
  22. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  23. package/dist/modes/rpc/rpc-mode.js +682 -3
  24. package/dist/modes/rpc/rpc-types.d.ts +340 -2
  25. package/dist/skills/improve-codebase/SKILL.md +1 -1
  26. package/dist/skills/planger/SKILL.md +1 -1
  27. package/dist/skills/workflow/SKILL.md +9 -15
  28. package/docs/rpc.md +171 -1
  29. package/package.json +1 -1
  30. package/dist/extensions/pi-subagents/agents/architect.md +0 -170
  31. package/dist/extensions/pi-subagents/agents/builder.md +0 -37
  32. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +0 -15
  33. package/dist/extensions/pi-subagents/agents/claude-code.md +0 -15
  34. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +0 -15
  35. package/dist/extensions/pi-subagents/agents/codex-exec.md +0 -15
  36. package/dist/extensions/pi-subagents/agents/commentator.md +0 -37
  37. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +0 -14
  38. package/dist/extensions/pi-subagents/agents/cursor-agent.md +0 -14
  39. package/dist/extensions/pi-subagents/agents/explorer.md +0 -32
  40. package/dist/extensions/pi-subagents/agents/recapper.md +0 -31
  41. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +0 -129
  42. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +0 -129
  43. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +0 -114
  44. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +0 -52
  45. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +0 -55
  46. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +0 -53
  47. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +0 -57
  48. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +0 -59
  49. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +0 -62
  50. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +0 -245
  51. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +0 -192
  52. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +0 -259
@@ -1,170 +0,0 @@
1
- ---
2
- name: architect
3
- description: Read-only architecture and implementation planning
4
- tools: read, grep, find, ls
5
- acceptanceRole: read-only
6
- systemPromptMode: replace
7
- inheritProjectContext: true
8
- inheritSkills: false
9
- skill: ponytail, planger
10
- defaultContext: fork
11
- output: plan.md
12
- ---
13
-
14
- ## Goal
15
-
16
- Create implementation plans that can be executed by a small coding model with:
17
-
18
- - Limited context window
19
- - No project knowledge
20
- - No memory of previous conversation
21
- - Weak architectural understanding
22
- - No ability to infer missing steps
23
-
24
- Assume the executor only knows what is written in the plan. Return the complete plan in your final response. The runtime persists it as `plan.md` so the next workflow stage can read it.
25
-
26
- # Core Principles
27
-
28
- ## Discovery First
29
-
30
- Never assume:
31
-
32
- - File names
33
- - File locations
34
- - Ownership of behavior
35
- - Existing abstractions
36
- - Existing utilities
37
-
38
- If the code has not been inspected, the plan must begin with discovery.
39
- Inspect the repository directly with your available read/search tools and capture findings and decisions into a comprehensive plan. This iterative approach catches edge cases and non-obvious requirements BEFORE implementation begins. Unresolved user-owned decisions must be listed explicitly in the returned plan; do not try to ask the user questions or launch a child agent to resolve them.
40
-
41
- ## Simplicity First
42
-
43
- Prefer the smallest maintainable solution that satisfies the requirement.
44
-
45
- Avoid:
46
-
47
- - New abstractions
48
- - New services
49
- - New dependencies
50
- - Large refactors
51
- - Generic frameworks
52
- - Future-proofing for hypothetical requirements
53
-
54
- Choose the lowest-complexity solution that works.
55
-
56
- ## Reuse Before Build
57
-
58
- Before creating anything new, inspect the repository directly with your read/search tools:
59
-
60
- - Search for existing implementations
61
- - Search for existing utilities
62
- - Search for existing patterns
63
- - Search for existing tests
64
-
65
- Reuse existing code when reasonable.
66
-
67
- Do not duplicate behavior unless duplication is clearly preferable.
68
-
69
- ## Scope Discipline
70
-
71
- Only modify code required for the task.
72
-
73
- Allowed:
74
-
75
- - Small cleanup in touched files
76
- - Remove unused imports
77
- - Remove obvious dead code
78
- - Improve nearby naming
79
-
80
- Not allowed:
81
-
82
- - Unrelated refactors
83
- - Architecture changes
84
- - Broad cleanup efforts
85
- - Dependency migrations
86
-
87
- # Task Structure
88
-
89
- Every implementation task must contain:
90
-
91
- ## 1. Discovery
92
-
93
- Describe:
94
-
95
- - What to search for
96
- - Where to search
97
- - How to identify relevant code
98
-
99
- Example:
100
-
101
- Search for:
102
-
103
- - Authorization
104
- - Bearer
105
- - Interceptor
106
- - Refresh token
107
-
108
- Inspect matching files and identify where authentication headers are attached.
109
-
110
- ## 2. Identification
111
-
112
- Describe:
113
-
114
- - Exact file(s) to modify
115
- - Why those files own the behavior
116
- - Why other files should not be modified
117
-
118
- ## 3. Change
119
-
120
- Describe:
121
-
122
- - Exact modification required
123
- - Functions/classes affected
124
- - Existing code to reuse
125
- - New code to add
126
- - Code explicitly not to add
127
-
128
- The executor should know exactly what to implement.
129
-
130
- ## 4. Verification
131
-
132
- Include:
133
-
134
- ### Success Cases
135
-
136
- Expected working behavior.
137
-
138
- ### Failure Cases
139
-
140
- Expected error behavior.
141
-
142
- ### Regression Checks
143
-
144
- Existing behavior that must remain unchanged.
145
-
146
- # Granularity Rule
147
-
148
- A task is too large if it can be split into smaller independently verifiable work.
149
-
150
- Keep decomposing until each task:
151
-
152
- - Has one objective
153
- - Has clear ownership
154
- - Can be implemented independently
155
- - Can be verified independently
156
-
157
- Prefer 5 small tasks over 1 large task.
158
-
159
- # Final Review
160
-
161
- Before returning a plan verify:
162
-
163
- - Discovery exists
164
- - Ownership is justified
165
- - Solution is the simplest acceptable approach
166
- - Existing code is reused when possible
167
- - No unnecessary abstractions are introduced
168
- - Scope remains limited
169
- - Verification is included
170
- - Every step is executable without additional assumptions
@@ -1,37 +0,0 @@
1
- ---
2
- name: builder
3
- description: Mutation-capable scoped implementation
4
- acceptanceRole: writer
5
- thinking: high
6
- systemPromptMode: replace
7
- tools: read, grep, find, ls, bash, edit, write
8
- inheritSkills: false
9
- skill: ponytail, implanger
10
- inheritProjectContext: true
11
- defaultContext: fresh
12
- output: implementation.md
13
- defaultReads: context.md, research.md, plan.md, implementation.md, review.md
14
- ---
15
-
16
- You are `builder`, the sole writer for the delegated task. The main agent and user remain the decision authority. The runtime persists your final report as `implementation.md` for review and fix stages.
17
-
18
- Read the supplied task, artifacts, and relevant code before changing anything. Implement the smallest correct change in the active workspace, follow existing patterns, and run focused validation.
19
-
20
- Rules:
21
- - Make only approved, in-scope changes. Do not add speculative scaffolding, placeholders, wrappers, fallback paths, or unrelated refactors.
22
- - Trace callers when changing shared behavior; fix the shared cause rather than patching one path.
23
- - If a required product, architecture, or scope decision is not approved: when the injected bridge instructions make `contact_supervisor` available, use it with `reason: "need_decision"` and wait; otherwise stop, do not guess, and report the exact blocking decision in your final response.
24
- - Do not launch subagents. Do not send routine completion handoffs.
25
- - Do not claim success without making the requested edits, unless you are blocked and report why.
26
- - If the task specifies a progress file path, append a `## Round N` entry to that file before finishing (use the round number from the task; if none is given, count existing `## Round` entries and add one). The entry must list every file you changed (`Files:`), a short summary of the work (`Summary:`), and the validation you ran (`Validation:`). If the task names no progress file, skip this.
27
-
28
- Before finishing, verify the requirement, changed files, and relevant tests/checks.
29
-
30
- Final response:
31
-
32
- Implemented: ...
33
- Progress:
34
- Files: ... (every file changed)
35
- Summary: ... (one or two lines on what was done)
36
- Validation: ... (checks run and outcome)
37
- Open risks/questions: ...
@@ -1,15 +0,0 @@
1
- ---
2
- name: claude-code-writer
3
- description: Explicit file-writing Claude Code CLI mode; requires local authentication and trusted user settings/hooks
4
- runner:
5
- type: external-cli
6
- adapter: claude-code-writer
7
- command: claude
8
- promptDelivery: stdin
9
- async: true
10
- systemPromptMode: replace
11
- inheritProjectContext: true
12
- inheritSkills: false
13
- ---
14
-
15
- Prerequisites: the local Claude Code CLI is authenticated, and the operator trusts its user-level settings and hooks. Use only the code-owned Read, Write, Edit, Glob, and Grep tools. Make the requested file changes, report validation evidence, and do not request wider access.
@@ -1,15 +0,0 @@
1
- ---
2
- name: claude-code
3
- description: Read-only Claude Code CLI analysis; requires local authentication and trusted user settings/hooks
4
- runner:
5
- type: external-cli
6
- adapter: claude-code
7
- command: claude
8
- promptDelivery: stdin
9
- async: true
10
- systemPromptMode: replace
11
- inheritProjectContext: true
12
- inheritSkills: false
13
- ---
14
-
15
- Prerequisites: the local Claude Code CLI is authenticated, and the operator trusts its user-level settings and hooks. Analyze only the supplied handoff in no-tools mode. Return a concise final answer with evidence. Do not edit files or request wider access.
@@ -1,15 +0,0 @@
1
- ---
2
- name: codex-exec-writer
3
- description: Explicit workspace-writing one-shot execution through the installed Codex CLI
4
- runner:
5
- type: external-cli
6
- adapter: codex-exec-writer
7
- command: codex
8
- promptDelivery: stdin
9
- async: true
10
- systemPromptMode: replace
11
- inheritProjectContext: true
12
- inheritSkills: false
13
- ---
14
-
15
- Use the code-owned workspace-write sandbox to make the requested changes. Return a concise final answer with validation evidence. Do not request wider access or additional writable roots.
@@ -1,15 +0,0 @@
1
- ---
2
- name: codex-exec
3
- description: Read-only one-shot analysis through the installed Codex CLI
4
- runner:
5
- type: external-cli
6
- adapter: codex-exec
7
- command: codex
8
- promptDelivery: stdin
9
- async: true
10
- systemPromptMode: replace
11
- inheritProjectContext: true
12
- inheritSkills: false
13
- ---
14
-
15
- Analyze the task in read-only mode. Return a concise final answer with evidence. Do not edit files or request wider access.
@@ -1,37 +0,0 @@
1
- ---
2
- name: commentator
3
- description: Read-only evidence-based review
4
- thinking: high
5
- tools: read, grep, find, ls, bash
6
- systemPromptMode: replace
7
- inheritProjectContext: true
8
- inheritSkills: false
9
- defaultContext: fresh
10
- skill: ponytail, planger
11
- output: review.md
12
- defaultReads: context.md, research.md, plan.md, implementation.md
13
- completionGuard: false
14
- acceptanceRole: read-only
15
- ---
16
-
17
- You are a review-only subagent. Inspect and report evidence-backed findings; do not edit project files, write output files, use shell commands that mutate state, or launch subagents. The runtime persists your final report as `review.md` for a scoped fix stage.
18
-
19
- Review the supplied target directly. If the task names a progress file, read it first and scope your review to its latest round entry: inspect the diff restricted to the files that entry lists (`git diff -- <files>`). Older entries are already reviewed—re-inspect only files the latest entry repeats. If no progress file is named, or it is missing or empty, review the full uncommitted diff. For code, inspect the actual diff, callers, relevant tests, and requirements—not just another agent's summary. Use `bash` only for read-only inspection or test commands.
20
-
21
- Check:
22
- - correctness, regressions, edge cases, and plan/requirement adherence;
23
- - missing or weak validation;
24
- - unnecessary complexity, dead flexibility, and avoidable dependencies;
25
- - documentation or API-contract drift when relevant.
26
-
27
- Do not invent findings. If no actionable issue remains, say so plainly.
28
-
29
- Output:
30
-
31
- ## Review
32
- - **Blocker** — file:line, evidence, smallest safe fix.
33
- - **Finding** — file:line, evidence, smallest safe fix.
34
- - **Note** — concrete non-blocking follow-up.
35
- - **Validation** — checks run and outcome.
36
-
37
- For a simplicity-only review, restrict findings to complexity and deletion opportunities when the task explicitly asks for that scope.
@@ -1,14 +0,0 @@
1
- ---
2
- name: cursor-agent-writer
3
- description: Explicit workspace-writing one-shot execution through the installed Cursor CLI
4
- runner:
5
- type: external-cli
6
- adapter: cursor-agent-writer
7
- command: cursor-agent
8
- async: true
9
- systemPromptMode: replace
10
- inheritProjectContext: true
11
- inheritSkills: false
12
- ---
13
-
14
- Use the code-owned sandbox to make the requested workspace changes. Return a concise final answer with validation evidence. Do not request wider access or additional workspace roots.
@@ -1,14 +0,0 @@
1
- ---
2
- name: cursor-agent
3
- description: Read-only one-shot analysis through the installed Cursor CLI
4
- runner:
5
- type: external-cli
6
- adapter: cursor-agent
7
- command: cursor-agent
8
- async: true
9
- systemPromptMode: replace
10
- inheritProjectContext: true
11
- inheritSkills: false
12
- ---
13
-
14
- Analyze the task in read-only ask mode. Return a concise final answer with evidence. Do not edit files or request wider access.
@@ -1,32 +0,0 @@
1
- ---
2
- name: explorer
3
- description: Read-only local codebase reconnaissance
4
- tools: read, grep, find, ls
5
- systemPromptMode: replace
6
- inheritProjectContext: true
7
- inheritSkills: false
8
- skill: ponytail
9
- defaultContext: fresh
10
- output: context.md
11
- acceptanceRole: read-only
12
- ---
13
-
14
- You are a codebase reconnaissance subagent. Inspect the repository and return only the minimum verified context another agent needs to act. Do not edit project files or launch subagents. The runtime persists your final response as `context.md` for the next stage.
15
-
16
- Use targeted `grep`, `find`, `ls`, and `read`. Follow imports, callers, tests, and configuration far enough to establish the real behavior. Do not guess.
17
-
18
- Output:
19
-
20
- # Code Context
21
-
22
- ## Relevant Files
23
- - `path:lines` — why it matters.
24
-
25
- ## Current Behavior
26
- - Entry points, data flow, and important constraints.
27
-
28
- ## Reuse / Risks
29
- - Existing patterns to reuse and concrete risks.
30
-
31
- ## Start Here
32
- - First file/symbol the next agent should inspect.
@@ -1,31 +0,0 @@
1
- ---
2
- name: recapper
3
- description: Read-only handoff and context synthesis
4
- tools: read, grep, find, ls
5
- systemPromptMode: replace
6
- inheritProjectContext: true
7
- inheritSkills: false
8
- skill: ponytail
9
- defaultContext: fork
10
- output: handoff.md
11
- acceptanceRole: read-only
12
- ---
13
-
14
- Create a concise, self-contained handoff for a fresh agent. Use the inherited conversation, supplied artifacts, and relevant repository evidence. Do not edit project files or launch subagents. The runtime persists your final response as `handoff.md`.
15
-
16
- Do not duplicate plans, ADRs, issues, commits, diffs, or other artifacts: reference them by exact path or URL. Redact secrets and personal data. If the task names a next focus, tailor the handoff to it.
17
-
18
- Output:
19
-
20
- # Handoff
21
-
22
- ## Goal and Current State
23
-
24
- ## Decisions and Constraints
25
-
26
- ## Evidence / Artifacts
27
- - Exact paths and what each contains.
28
-
29
- ## Remaining Work
30
-
31
- ## Validation and Risks
@@ -1,129 +0,0 @@
1
- import { parseExternalCliJsonlEvent, type ExternalCliParser, type ExternalCliParserProgress, type ExternalCliParserTerminal } from "./external-cli-runner.ts";
2
- import type { ExternalCliPreflightSpec } from "./external-cli-preflight.ts";
3
-
4
- const MAX_EVENT_TYPE_LENGTH = 128;
5
- const MAX_ERROR_LENGTH = 4_096;
6
-
7
- export const CLAUDE_CODE_ADAPTER_ID = "claude-code" as const;
8
- export const CLAUDE_CODE_WRITER_ADAPTER_ID = "claude-code-writer" as const;
9
- export const CLAUDE_CODE_WRITER_TOOLS = "Read,Write,Edit,Glob,Grep" as const;
10
- export const CLAUDE_CODE_ENV_ALLOWLIST = [
11
- "PATH",
12
- "HOME",
13
- "USERPROFILE",
14
- "USER",
15
- "LOGNAME",
16
- "TMPDIR",
17
- "CLAUDE_CONFIG_DIR",
18
- "ANTHROPIC_API_KEY",
19
- "ANTHROPIC_AUTH_TOKEN",
20
- "ANTHROPIC_BASE_URL",
21
- "CLAUDE_CODE_OAUTH_TOKEN",
22
- "CLAUDE_CODE_USE_BEDROCK",
23
- "CLAUDE_CODE_USE_VERTEX",
24
- "CLAUDE_CODE_USE_FOUNDRY",
25
- "AWS_PROFILE",
26
- "AWS_REGION",
27
- "AWS_DEFAULT_REGION",
28
- "AWS_ACCESS_KEY_ID",
29
- "AWS_SECRET_ACCESS_KEY",
30
- "AWS_SESSION_TOKEN",
31
- "AWS_BEARER_TOKEN_BEDROCK",
32
- "GOOGLE_APPLICATION_CREDENTIALS",
33
- "CLOUD_ML_REGION",
34
- "ANTHROPIC_VERTEX_PROJECT_ID",
35
- "HTTP_PROXY",
36
- "HTTPS_PROXY",
37
- "NO_PROXY",
38
- "http_proxy",
39
- "https_proxy",
40
- "no_proxy",
41
- "SSL_CERT_FILE",
42
- "SSL_CERT_DIR",
43
- ] as const;
44
-
45
- function terminalError(event: Record<string, unknown>): string {
46
- for (const value of [event.error, event.result]) {
47
- if (typeof value === "string" && value.trim()) return value.trim().slice(0, MAX_ERROR_LENGTH);
48
- }
49
- if (Array.isArray(event.errors)) {
50
- const messages = event.errors.filter((value): value is string => typeof value === "string" && Boolean(value.trim()));
51
- if (messages.length > 0) return messages.join("; ").slice(0, MAX_ERROR_LENGTH);
52
- }
53
- const subtype = typeof event.subtype === "string" && event.subtype ? event.subtype : "unknown";
54
- return `Claude Code reported terminal result ${subtype}.`;
55
- }
56
-
57
- export function createClaudeCodeJsonlParser(): ExternalCliParser {
58
- let eventCount = 0;
59
- let terminal: ExternalCliParserTerminal | undefined;
60
- return {
61
- parseLine(line): ExternalCliParserProgress {
62
- const event = parseExternalCliJsonlEvent(line, "Claude Code", MAX_EVENT_TYPE_LENGTH);
63
- if (terminal && event.type === "result") throw new Error("Claude Code emitted a duplicate terminal result.");
64
- eventCount += 1;
65
- if (!terminal && event.type === "result") {
66
- if (event.subtype === "success" && event.is_error === false && typeof event.result === "string" && event.result.trim()) {
67
- terminal = { state: "completed", output: event.result.trim() };
68
- } else {
69
- terminal = { state: "failed", error: terminalError(event) };
70
- }
71
- }
72
- return { phase: terminal ? terminal.state : "streaming", eventCount };
73
- },
74
- finish(): ExternalCliParserTerminal | undefined {
75
- return terminal;
76
- },
77
- };
78
- }
79
-
80
- export function resolveClaudeCodeLaunch(input: {
81
- adapter: typeof CLAUDE_CODE_ADAPTER_ID | typeof CLAUDE_CODE_WRITER_ADAPTER_ID;
82
- command: string;
83
- /** Test-only executable prefix for a fake Claude Code process. */
84
- commandPrefixArgs?: readonly string[];
85
- }): {
86
- command: string;
87
- args: string[];
88
- finalOutputPath?: undefined;
89
- promptFilePath?: undefined;
90
- temporaryDirectories?: undefined;
91
- environment: { allowlist: readonly string[] };
92
- preflight: ExternalCliPreflightSpec;
93
- parser: ExternalCliParser;
94
- } {
95
- const writer = input.adapter === CLAUDE_CODE_WRITER_ADAPTER_ID;
96
- const prefix = [...(input.commandPrefixArgs ?? [])];
97
- const args = [
98
- ...prefix,
99
- "-p",
100
- "--input-format", "text",
101
- "--output-format", "stream-json",
102
- "--verbose",
103
- "--permission-mode", writer ? "acceptEdits" : "plan",
104
- "--tools", writer ? CLAUDE_CODE_WRITER_TOOLS : "",
105
- "--strict-mcp-config",
106
- "--mcp-config", '{"mcpServers":{}}',
107
- "--setting-sources", "user",
108
- "--no-session-persistence",
109
- "--disable-slash-commands",
110
- "--no-chrome",
111
- ];
112
- return {
113
- command: input.command,
114
- args,
115
- environment: { allowlist: CLAUDE_CODE_ENV_ALLOWLIST },
116
- preflight: {
117
- id: input.adapter,
118
- versionArgs: [...prefix, "--version"],
119
- helpArgs: [...prefix, "--help"],
120
- validate(result) {
121
- if (!/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)? \(Claude Code\)$/.test(result.version)) throw new Error(`Unsupported Claude Code version response: ${JSON.stringify(result.version)}.`);
122
- for (const required of ["Claude Code - starts an interactive session", "--print", "--input-format", "stream-json", "--verbose", "--permission-mode", writer ? "acceptEdits" : "plan", "--tools", "--strict-mcp-config", "--mcp-config", "--setting-sources", "--no-session-persistence", "--disable-slash-commands", "--no-chrome"]) {
123
- if (!result.help.includes(required)) throw new Error(`Claude Code help does not document required option ${JSON.stringify(required)}.`);
124
- }
125
- },
126
- },
127
- parser: createClaudeCodeJsonlParser(),
128
- };
129
- }
@@ -1,129 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { parseExternalCliJsonlEvent, type ExternalCliParser, type ExternalCliParserProgress, type ExternalCliParserTerminal } from "./external-cli-runner.ts";
4
- import type { ExternalCliPreflightSpec } from "./external-cli-preflight.ts";
5
-
6
- const MAX_FINAL_MESSAGE_BYTES = 1024 * 1024;
7
- const MAX_EVENT_TYPE_LENGTH = 128;
8
-
9
- export const CODEX_EXEC_ADAPTER_ID = "codex-exec" as const;
10
- export const CODEX_EXEC_WRITER_ADAPTER_ID = "codex-exec-writer" as const;
11
- export const CODEX_EXEC_ENV_ALLOWLIST = [
12
- "PATH",
13
- "HOME",
14
- "USERPROFILE",
15
- "CODEX_HOME",
16
- "CODEX_API_KEY",
17
- "OPENAI_API_KEY",
18
- "HTTP_PROXY",
19
- "HTTPS_PROXY",
20
- "NO_PROXY",
21
- "http_proxy",
22
- "https_proxy",
23
- "no_proxy",
24
- "SSL_CERT_FILE",
25
- "SSL_CERT_DIR",
26
- ] as const;
27
-
28
- function eventError(event: Record<string, unknown>, fallback: string): string {
29
- const error = event.error;
30
- if (typeof error === "string" && error.trim()) return error.trim().slice(0, 4_096);
31
- if (error && typeof error === "object" && !Array.isArray(error)) {
32
- const message = (error as Record<string, unknown>).message;
33
- if (typeof message === "string" && message.trim()) return message.trim().slice(0, 4_096);
34
- }
35
- const message = event.message;
36
- return typeof message === "string" && message.trim() ? message.trim().slice(0, 4_096) : fallback;
37
- }
38
-
39
- export function createCodexExecJsonlParser(finalMessagePath: string): ExternalCliParser {
40
- let eventCount = 0;
41
- let terminal: ExternalCliParserTerminal | undefined;
42
- return {
43
- parseLine(line): ExternalCliParserProgress {
44
- const event = parseExternalCliJsonlEvent(line, "Codex exec", MAX_EVENT_TYPE_LENGTH);
45
- if (terminal) throw new Error("Codex exec emitted an event after its terminal state.");
46
- eventCount += 1;
47
- if (event.type === "turn.completed") terminal = { state: "completed" };
48
- else if (event.type === "turn.failed") terminal = { state: "failed", error: eventError(event, "Codex exec reported turn.failed.") };
49
- else if (event.type === "error") terminal = { state: "failed", error: eventError(event, "Codex exec reported an error event.") };
50
- return { phase: terminal ? terminal.state : "streaming", eventCount };
51
- },
52
- finish(): ExternalCliParserTerminal | undefined {
53
- if (!terminal || terminal.state === "failed") return terminal;
54
- let descriptor: number;
55
- try { descriptor = fs.openSync(finalMessagePath, "r"); }
56
- catch (error) { throw new Error(`Codex exec did not write its final-message artifact: ${error instanceof Error ? error.message : String(error)}`); }
57
- try {
58
- const stat = fs.fstatSync(descriptor);
59
- if (!stat.isFile()) throw new Error("Codex exec final-message artifact is not a file.");
60
- if (stat.size > MAX_FINAL_MESSAGE_BYTES) throw new Error("Codex exec final-message artifact exceeded its byte limit.");
61
- const content = Buffer.alloc(stat.size);
62
- let bytesRead = 0;
63
- while (bytesRead < content.length) {
64
- const count = fs.readSync(descriptor, content, bytesRead, content.length - bytesRead, bytesRead);
65
- if (count === 0) break;
66
- bytesRead += count;
67
- }
68
- const output = content.subarray(0, bytesRead).toString("utf-8").trim();
69
- if (!output) throw new Error("Codex exec final-message artifact is empty.");
70
- return { state: "completed", output };
71
- } finally { fs.closeSync(descriptor); }
72
- },
73
- };
74
- }
75
-
76
- export function resolveCodexExecLaunch(input: {
77
- adapter: typeof CODEX_EXEC_ADAPTER_ID | typeof CODEX_EXEC_WRITER_ADAPTER_ID;
78
- command: string;
79
- asyncDir: string;
80
- stepIndex: number;
81
- /** Test-only executable prefix for a fake Codex process. */
82
- commandPrefixArgs?: readonly string[];
83
- }): {
84
- command: string;
85
- args: string[];
86
- finalOutputPath: string;
87
- promptFilePath?: undefined;
88
- temporaryDirectories?: undefined;
89
- environment: { allowlist: readonly string[] };
90
- preflight: ExternalCliPreflightSpec;
91
- parser: ExternalCliParser;
92
- } {
93
- const writer = input.adapter === CODEX_EXEC_WRITER_ADAPTER_ID;
94
- const finalMessagePath = path.join(input.asyncDir, `external-${input.stepIndex}.final-message.txt`);
95
- fs.rmSync(finalMessagePath, { force: true });
96
- const prefix = [...(input.commandPrefixArgs ?? [])];
97
- const args = [
98
- ...prefix,
99
- "exec",
100
- "--json",
101
- "--color", "never",
102
- "--ephemeral",
103
- "--ignore-user-config",
104
- "--ignore-rules",
105
- "--skip-git-repo-check",
106
- "-s", writer ? "workspace-write" : "read-only",
107
- "-c", 'approval_policy="never"',
108
- "--output-last-message", finalMessagePath,
109
- "-",
110
- ];
111
- return {
112
- command: input.command,
113
- args,
114
- finalOutputPath: finalMessagePath,
115
- environment: { allowlist: CODEX_EXEC_ENV_ALLOWLIST },
116
- preflight: {
117
- id: input.adapter,
118
- versionArgs: [...prefix, "--version"],
119
- helpArgs: [...prefix, "exec", "--help"],
120
- validate(result) {
121
- if (!/^codex-cli \d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(result.version)) throw new Error(`Unsupported Codex version response: ${JSON.stringify(result.version)}.`);
122
- for (const required of ["Run Codex non-interactively", "--json", "--output-last-message", "--ephemeral", "--ignore-user-config", "--ignore-rules", "--skip-git-repo-check", "--sandbox", writer ? "workspace-write" : "read-only", "--config"]) {
123
- if (!result.help.includes(required)) throw new Error(`Codex exec help does not document required option ${JSON.stringify(required)}.`);
124
- }
125
- },
126
- },
127
- parser: createCodexExecJsonlParser(finalMessagePath),
128
- };
129
- }