@selesai/code 0.13.9 → 0.13.11

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 (50) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/core/agent-session.d.ts +6 -0
  3. package/dist/core/agent-session.js +5 -3
  4. package/dist/extensions/handoff-new.test.ts +26 -1
  5. package/dist/extensions/handoff-new.ts +8 -0
  6. package/dist/extensions/herdr-agent-state.ts +310 -0
  7. package/dist/extensions/package.json +1 -0
  8. package/dist/extensions/pi-subagents/README.md +0 -6
  9. package/dist/extensions/pi-subagents/docs/agents.md +1 -114
  10. package/dist/extensions/pi-subagents/src/agents/builtin-names.ts +1 -12
  11. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -1
  12. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +3 -19
  13. package/dist/extensions/pi-subagents/src/runs/shared/external-cli-contract.ts +8 -37
  14. package/dist/extensions/pi-subagents/src/shared/types.ts +2 -2
  15. package/dist/extensions/pi-subagents/src/workflows/workflow-receipt.ts +2 -38
  16. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +0 -49
  17. package/dist/extensions/pi-subagents/test/unit/runtime-agent-registration.test.ts +0 -18
  18. package/dist/modes/rpc/rpc-client.d.ts +59 -7
  19. package/dist/modes/rpc/rpc-client.js +65 -11
  20. package/dist/modes/rpc/rpc-mode.d.ts +1 -1
  21. package/dist/modes/rpc/rpc-mode.js +280 -12
  22. package/dist/modes/rpc/rpc-types.d.ts +128 -1
  23. package/dist/skills/improve-codebase/SKILL.md +1 -1
  24. package/dist/skills/planger/SKILL.md +1 -1
  25. package/dist/skills/workflow/SKILL.md +9 -15
  26. package/docs/rpc.md +226 -2
  27. package/package.json +1 -1
  28. package/dist/extensions/pi-subagents/agents/architect.md +0 -170
  29. package/dist/extensions/pi-subagents/agents/builder.md +0 -37
  30. package/dist/extensions/pi-subagents/agents/claude-code-writer.md +0 -15
  31. package/dist/extensions/pi-subagents/agents/claude-code.md +0 -15
  32. package/dist/extensions/pi-subagents/agents/codex-exec-writer.md +0 -15
  33. package/dist/extensions/pi-subagents/agents/codex-exec.md +0 -15
  34. package/dist/extensions/pi-subagents/agents/commentator.md +0 -37
  35. package/dist/extensions/pi-subagents/agents/cursor-agent-writer.md +0 -14
  36. package/dist/extensions/pi-subagents/agents/cursor-agent.md +0 -14
  37. package/dist/extensions/pi-subagents/agents/explorer.md +0 -32
  38. package/dist/extensions/pi-subagents/agents/recapper.md +0 -31
  39. package/dist/extensions/pi-subagents/src/runs/shared/claude-code-adapter.ts +0 -129
  40. package/dist/extensions/pi-subagents/src/runs/shared/codex-exec-adapter.ts +0 -129
  41. package/dist/extensions/pi-subagents/src/runs/shared/cursor-agent-adapter.ts +0 -114
  42. package/dist/extensions/pi-subagents/test/integration/claude-code-smoke.test.ts +0 -52
  43. package/dist/extensions/pi-subagents/test/integration/claude-code-writer-smoke.test.ts +0 -55
  44. package/dist/extensions/pi-subagents/test/integration/codex-exec-smoke.test.ts +0 -53
  45. package/dist/extensions/pi-subagents/test/integration/codex-exec-writer-smoke.test.ts +0 -57
  46. package/dist/extensions/pi-subagents/test/integration/cursor-agent-smoke.test.ts +0 -59
  47. package/dist/extensions/pi-subagents/test/integration/cursor-agent-writer-smoke.test.ts +0 -62
  48. package/dist/extensions/pi-subagents/test/unit/claude-code-adapter.test.ts +0 -245
  49. package/dist/extensions/pi-subagents/test/unit/codex-exec-adapter.test.ts +0 -192
  50. package/dist/extensions/pi-subagents/test/unit/cursor-agent-adapter.test.ts +0 -259
@@ -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
- }
@@ -1,114 +0,0 @@
1
- import * as path from "node:path";
2
- import { parseExternalCliJsonlEvent, type ExternalCliParser, type ExternalCliParserProgress, type ExternalCliParserTerminal } from "./external-cli-runner.ts";
3
- import type { ExternalCliPreflightSpec } from "./external-cli-preflight.ts";
4
-
5
- const MAX_EVENT_TYPE_LENGTH = 128;
6
- const MAX_ERROR_LENGTH = 4_096;
7
- const MAX_OVERSIZED_TOOL_CALL_BYTES = 1024 * 1024;
8
-
9
- export const CURSOR_AGENT_ADAPTER_ID = "cursor-agent" as const;
10
- export const CURSOR_AGENT_WRITER_ADAPTER_ID = "cursor-agent-writer" as const;
11
- export const CURSOR_AGENT_ENV_ALLOWLIST = [
12
- "PATH",
13
- "HOME",
14
- "USERPROFILE",
15
- "CURSOR_API_KEY",
16
- "HTTP_PROXY",
17
- "HTTPS_PROXY",
18
- "NO_PROXY",
19
- "http_proxy",
20
- "https_proxy",
21
- "no_proxy",
22
- "SSL_CERT_FILE",
23
- "SSL_CERT_DIR",
24
- ] as const;
25
-
26
- function terminalError(event: Record<string, unknown>): string {
27
- for (const value of [event.result, event.error, event.message]) {
28
- if (typeof value === "string" && value.trim()) return value.trim().slice(0, MAX_ERROR_LENGTH);
29
- }
30
- const subtype = typeof event.subtype === "string" && event.subtype ? event.subtype : "unknown";
31
- return `Cursor Agent reported terminal result ${subtype}.`;
32
- }
33
-
34
- export function createCursorAgentJsonlParser(): ExternalCliParser {
35
- let eventCount = 0;
36
- let terminal: ExternalCliParserTerminal | undefined;
37
- return {
38
- parseLine(line): ExternalCliParserProgress {
39
- const event = parseExternalCliJsonlEvent(line, "Cursor Agent", MAX_EVENT_TYPE_LENGTH);
40
- if (terminal) throw new Error("Cursor Agent emitted an event after its terminal state.");
41
- eventCount += 1;
42
- if (event.type === "error") terminal = { state: "failed", error: terminalError(event) };
43
- else if (event.type === "result") {
44
- if (event.subtype === "success" && event.is_error === false && typeof event.result === "string" && event.result.trim()) {
45
- terminal = { state: "completed", output: event.result.trim() };
46
- } else terminal = { state: "failed", error: terminalError(event) };
47
- }
48
- return { phase: terminal ? terminal.state : "streaming", eventCount };
49
- },
50
- skipOversizedLine(prefix, byteLength): ExternalCliParserProgress | undefined {
51
- if (terminal || byteLength > MAX_OVERSIZED_TOOL_CALL_BYTES || !/^\s*\{\s*"type"\s*:\s*"tool_call"\s*,/.test(prefix)) return undefined;
52
- eventCount += 1;
53
- return { phase: "streaming", eventCount };
54
- },
55
- finish(): ExternalCliParserTerminal | undefined {
56
- return terminal;
57
- },
58
- };
59
- }
60
-
61
- export function resolveCursorAgentLaunch(input: {
62
- adapter: typeof CURSOR_AGENT_ADAPTER_ID | typeof CURSOR_AGENT_WRITER_ADAPTER_ID;
63
- command: string;
64
- cwd: string;
65
- asyncDir: string;
66
- stepIndex: number;
67
- /** Test-only executable prefix for a fake Cursor Agent process. */
68
- commandPrefixArgs?: readonly string[];
69
- }): {
70
- command: string;
71
- args: string[];
72
- finalOutputPath?: undefined;
73
- promptFilePath: string;
74
- temporaryDirectories: string[];
75
- environment: { allowlist: readonly string[] };
76
- preflight: ExternalCliPreflightSpec;
77
- parser: ExternalCliParser;
78
- } {
79
- const writer = input.adapter === CURSOR_AGENT_WRITER_ADAPTER_ID;
80
- const promptDirectory = path.join(input.asyncDir, `external-${input.stepIndex}.cursor-prompt`);
81
- const promptFilePath = path.join(promptDirectory, "handoff.txt");
82
- const promptRelative = path.relative(input.cwd, promptDirectory);
83
- const promptOutsideWorkspace = promptRelative.startsWith("..") || path.isAbsolute(promptRelative);
84
- const prefix = [...(input.commandPrefixArgs ?? [])];
85
- const args = [
86
- ...prefix,
87
- "-p",
88
- "--output-format", "stream-json",
89
- ...(writer ? [] : ["--mode", "ask"]),
90
- "--sandbox", "enabled",
91
- "--workspace", input.cwd,
92
- ...(promptOutsideWorkspace ? ["--add-dir", promptDirectory] : []),
93
- `Read the complete handoff from the private file at ${promptFilePath}. Follow it and return only the final answer.`,
94
- ];
95
- return {
96
- command: input.command,
97
- args,
98
- promptFilePath,
99
- temporaryDirectories: [promptDirectory],
100
- environment: { allowlist: CURSOR_AGENT_ENV_ALLOWLIST },
101
- preflight: {
102
- id: input.adapter,
103
- versionArgs: [...prefix, "--version"],
104
- helpArgs: [...prefix, "--help"],
105
- validate(result) {
106
- if (!/^\d{4}\.\d{2}\.\d{2}-[0-9a-f]+$/.test(result.version)) throw new Error(`Unsupported Cursor Agent version response: ${JSON.stringify(result.version)}.`);
107
- for (const required of ["Start the Cursor Agent", "--print", "stream-json", "--sandbox <mode>", "enabled", "--workspace <path-or-name>", "--add-dir <path>", ...(writer ? ["all tools", "including write and shell"] : ["--mode <mode>", "ask:", "read-only"])]) {
108
- if (!result.help.includes(required)) throw new Error(`Cursor Agent help does not document required option ${JSON.stringify(required)}.`);
109
- }
110
- },
111
- },
112
- parser: createCursorAgentJsonlParser(),
113
- };
114
- }
@@ -1,52 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import test from "node:test";
6
- import { resolveClaudeCodeLaunch } from "../../src/runs/shared/claude-code-adapter.ts";
7
- import { runExternalCli } from "../../src/runs/shared/external-cli-runner.ts";
8
-
9
- const enabled = process.env.SELESAI_SUBAGENTS_CLAUDE_CODE_SMOKE === "1";
10
-
11
- test("maintainer Claude Code no-tools smoke", { skip: enabled ? undefined : "set SELESAI_SUBAGENTS_CLAUDE_CODE_SMOKE=1" }, async () => {
12
- const reportPath = process.env.SELESAI_SUBAGENTS_CLAUDE_CODE_SMOKE_REPORT;
13
- assert.ok(reportPath, "SELESAI_SUBAGENTS_CLAUDE_CODE_SMOKE_REPORT is required");
14
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-claude-smoke-"));
15
- const canaryPath = path.join(dir, "write-canary.txt");
16
- try {
17
- const launch = resolveClaudeCodeLaunch({ adapter: "claude-code", command: "claude" });
18
- const result = await runExternalCli({
19
- ...launch,
20
- cwd: dir,
21
- prompt: `Attempt to write the text CANARY to ${canaryPath}. Then explain whether the no-tools policy allowed it.`,
22
- asyncDir: dir,
23
- stepIndex: 0,
24
- });
25
- const report = {
26
- adapter: "claude-code",
27
- adapterVersion: 1,
28
- access: "read-only",
29
- authentication: "existing-cli-required",
30
- cliVersion: result.preflight?.version,
31
- cwd: dir,
32
- permissionMode: "plan",
33
- tools: "none",
34
- mcp: "empty-strict",
35
- settingSources: "user",
36
- userSettingsTrust: "required",
37
- sessionPersistence: false,
38
- exitCode: result.exitCode,
39
- terminalState: result.parserTerminal?.state,
40
- writeCanaryExists: fs.existsSync(canaryPath),
41
- stdoutPath: result.externalProcess.stdoutPath,
42
- stderrPath: result.externalProcess.stderrPath,
43
- durationMs: result.externalProcess.durationMs,
44
- };
45
- fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
46
- assert.equal(result.exitCode, 0, result.error);
47
- assert.equal(result.parserTerminal?.state, "completed");
48
- assert.equal(fs.existsSync(canaryPath), false, "Claude Code wrote the no-tools canary");
49
- } finally {
50
- fs.rmSync(dir, { recursive: true, force: true });
51
- }
52
- });
@@ -1,55 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import test from "node:test";
6
- import { CLAUDE_CODE_WRITER_TOOLS, resolveClaudeCodeLaunch } from "../../src/runs/shared/claude-code-adapter.ts";
7
- import { runExternalCli } from "../../src/runs/shared/external-cli-runner.ts";
8
-
9
- const enabled = process.env.SELESAI_SUBAGENTS_CLAUDE_CODE_WRITER_SMOKE === "1";
10
-
11
- test("maintainer Claude Code writer smoke", { skip: enabled ? undefined : "set SELESAI_SUBAGENTS_CLAUDE_CODE_WRITER_SMOKE=1" }, async () => {
12
- const reportPath = process.env.SELESAI_SUBAGENTS_CLAUDE_CODE_WRITER_SMOKE_REPORT;
13
- assert.ok(reportPath, "SELESAI_SUBAGENTS_CLAUDE_CODE_WRITER_SMOKE_REPORT is required");
14
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-claude-writer-smoke-"));
15
- const canaryPath = path.join(dir, "write-canary.txt");
16
- try {
17
- const launch = resolveClaudeCodeLaunch({ adapter: "claude-code-writer", command: "claude" });
18
- const result = await runExternalCli({
19
- ...launch,
20
- cwd: dir,
21
- prompt: `Use the Write tool to write exactly CANARY to ${canaryPath}. Then report completion.`,
22
- asyncDir: dir,
23
- stepIndex: 0,
24
- });
25
- const writeCanaryExists = fs.existsSync(canaryPath);
26
- const writeCanaryMatches = writeCanaryExists && fs.readFileSync(canaryPath, "utf-8").trim() === "CANARY";
27
- const report = {
28
- adapter: "claude-code-writer",
29
- adapterVersion: 1,
30
- access: "workspace-write",
31
- authentication: "existing-cli-required",
32
- cliVersion: result.preflight?.version,
33
- cwd: dir,
34
- permissionMode: "acceptEdits",
35
- tools: CLAUDE_CODE_WRITER_TOOLS,
36
- mcp: "empty-strict",
37
- settingSources: "user",
38
- userSettingsTrust: "required",
39
- sessionPersistence: false,
40
- exitCode: result.exitCode,
41
- terminalState: result.parserTerminal?.state,
42
- writeCanaryExists,
43
- writeCanaryMatches,
44
- stdoutPath: result.externalProcess.stdoutPath,
45
- stderrPath: result.externalProcess.stderrPath,
46
- durationMs: result.externalProcess.durationMs,
47
- };
48
- fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
49
- assert.equal(result.exitCode, 0, result.error);
50
- assert.equal(result.parserTerminal?.state, "completed");
51
- assert.equal(writeCanaryMatches, true, "Claude Code did not write the expected canary");
52
- } finally {
53
- fs.rmSync(dir, { recursive: true, force: true });
54
- }
55
- });
@@ -1,53 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import test from "node:test";
6
- import { resolveCodexExecLaunch } from "../../src/runs/shared/codex-exec-adapter.ts";
7
- import { runExternalCli } from "../../src/runs/shared/external-cli-runner.ts";
8
-
9
- const enabled = process.env.SELESAI_SUBAGENTS_CODEX_EXEC_SMOKE === "1";
10
-
11
- test("maintainer Codex exec read-only smoke", { skip: enabled ? undefined : "set SELESAI_SUBAGENTS_CODEX_EXEC_SMOKE=1" }, async () => {
12
- const reportPath = process.env.SELESAI_SUBAGENTS_CODEX_EXEC_SMOKE_REPORT;
13
- assert.ok(reportPath, "SELESAI_SUBAGENTS_CODEX_EXEC_SMOKE_REPORT is required");
14
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-codex-smoke-"));
15
- const canaryPath = path.join(dir, "write-canary.txt");
16
- const promptMarker = "CODEX_READ_PRIVATE_PROMPT_MARKER";
17
- try {
18
- const launch = resolveCodexExecLaunch({ adapter: "codex-exec", command: "codex", asyncDir: dir, stepIndex: 0 });
19
- const result = await runExternalCli({
20
- ...launch,
21
- cwd: dir,
22
- prompt: `${promptMarker}: Attempt to write the text CANARY to ${canaryPath}. Then explain whether the read-only sandbox allowed it.`,
23
- asyncDir: dir,
24
- stepIndex: 0,
25
- });
26
- const report = {
27
- adapter: "codex-exec",
28
- adapterVersion: 1,
29
- cliVersion: result.preflight?.version,
30
- cwd: dir,
31
- sandbox: "read-only",
32
- approvalPolicy: "never",
33
- ephemeral: true,
34
- exitCode: result.exitCode,
35
- terminalState: result.parserTerminal?.state,
36
- writeCanaryExists: fs.existsSync(canaryPath),
37
- finalMessagePath: result.externalProcess.finalOutputPath,
38
- stdoutPath: result.externalProcess.stdoutPath,
39
- stderrPath: result.externalProcess.stderrPath,
40
- durationMs: result.externalProcess.durationMs,
41
- };
42
- const serialized = `${JSON.stringify(report, null, 2)}\n`;
43
- assert.equal(serialized.includes(promptMarker), false);
44
- if (process.env.OPENAI_API_KEY) assert.equal(serialized.includes(process.env.OPENAI_API_KEY), false);
45
- if (process.env.CODEX_API_KEY) assert.equal(serialized.includes(process.env.CODEX_API_KEY), false);
46
- fs.writeFileSync(reportPath, serialized, { encoding: "utf-8", mode: 0o600 });
47
- assert.equal(result.exitCode, 0, result.error);
48
- assert.equal(result.parserTerminal?.state, "completed");
49
- assert.equal(fs.existsSync(canaryPath), false, "Codex wrote the read-only canary");
50
- } finally {
51
- fs.rmSync(dir, { recursive: true, force: true });
52
- }
53
- });
@@ -1,57 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import * as fs from "node:fs";
3
- import * as os from "node:os";
4
- import * as path from "node:path";
5
- import test from "node:test";
6
- import { resolveCodexExecLaunch } from "../../src/runs/shared/codex-exec-adapter.ts";
7
- import { runExternalCli } from "../../src/runs/shared/external-cli-runner.ts";
8
-
9
- const enabled = process.env.SELESAI_SUBAGENTS_CODEX_EXEC_WRITER_SMOKE === "1";
10
-
11
- test("maintainer Codex exec workspace writer smoke", { skip: enabled ? undefined : "set SELESAI_SUBAGENTS_CODEX_EXEC_WRITER_SMOKE=1" }, async () => {
12
- const reportPath = process.env.SELESAI_SUBAGENTS_CODEX_EXEC_WRITER_SMOKE_REPORT;
13
- assert.ok(reportPath, "SELESAI_SUBAGENTS_CODEX_EXEC_WRITER_SMOKE_REPORT is required");
14
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "pi-subagents-codex-writer-smoke-"));
15
- const canaryPath = path.join(dir, "write-canary.txt");
16
- const promptMarker = "CODEX_WRITER_PRIVATE_PROMPT_MARKER";
17
- try {
18
- const launch = resolveCodexExecLaunch({ adapter: "codex-exec-writer", command: "codex", asyncDir: dir, stepIndex: 0 });
19
- const result = await runExternalCli({
20
- ...launch,
21
- cwd: dir,
22
- prompt: `${promptMarker}: Write exactly CANARY to ${canaryPath}. Then report completion.`,
23
- asyncDir: dir,
24
- stepIndex: 0,
25
- });
26
- const writeCanaryExists = fs.existsSync(canaryPath);
27
- const writeCanaryMatches = writeCanaryExists && fs.readFileSync(canaryPath, "utf-8").trim() === "CANARY";
28
- const report = {
29
- adapter: "codex-exec-writer",
30
- adapterVersion: 1,
31
- cliVersion: result.preflight?.version,
32
- cwd: dir,
33
- access: "workspace-write",
34
- sandbox: "workspace-write",
35
- approvalPolicy: "never",
36
- ephemeral: true,
37
- exitCode: result.exitCode,
38
- terminalState: result.parserTerminal?.state,
39
- writeCanaryExists,
40
- writeCanaryMatches,
41
- finalMessagePath: result.externalProcess.finalOutputPath,
42
- stdoutPath: result.externalProcess.stdoutPath,
43
- stderrPath: result.externalProcess.stderrPath,
44
- durationMs: result.externalProcess.durationMs,
45
- };
46
- const serialized = `${JSON.stringify(report, null, 2)}\n`;
47
- assert.equal(serialized.includes(promptMarker), false);
48
- if (process.env.OPENAI_API_KEY) assert.equal(serialized.includes(process.env.OPENAI_API_KEY), false);
49
- if (process.env.CODEX_API_KEY) assert.equal(serialized.includes(process.env.CODEX_API_KEY), false);
50
- fs.writeFileSync(reportPath, serialized, { encoding: "utf-8", mode: 0o600 });
51
- assert.equal(result.exitCode, 0, result.error);
52
- assert.equal(result.parserTerminal?.state, "completed");
53
- assert.equal(writeCanaryMatches, true, "Codex did not write the expected canary");
54
- } finally {
55
- fs.rmSync(dir, { recursive: true, force: true });
56
- }
57
- });