humanish 0.0.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +77 -0
- package/CONTRIBUTING.md +39 -0
- package/LICENSE +21 -0
- package/README.md +435 -8
- package/SECURITY.md +34 -0
- package/dist/actor-contract.d.ts +107 -0
- package/dist/actor-contract.js +286 -0
- package/dist/actor-contract.js.map +1 -0
- package/dist/actor-registry.d.ts +73 -0
- package/dist/actor-registry.js +93 -0
- package/dist/actor-registry.js.map +1 -0
- package/dist/adapter-extension.d.ts +54 -0
- package/dist/adapter-extension.js +214 -0
- package/dist/adapter-extension.js.map +1 -0
- package/dist/argv.d.ts +1 -0
- package/dist/argv.js +8 -0
- package/dist/argv.js.map +1 -0
- package/dist/artifact-reference.d.ts +22 -0
- package/dist/artifact-reference.js +47 -0
- package/dist/artifact-reference.js.map +1 -0
- package/dist/browser-evidence-hygiene.d.ts +7 -0
- package/dist/browser-evidence-hygiene.js +40 -0
- package/dist/browser-evidence-hygiene.js.map +1 -0
- package/dist/claude-agent-sdk.d.ts +94 -0
- package/dist/claude-agent-sdk.js +356 -0
- package/dist/claude-agent-sdk.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-app-server-ui.d.ts +39 -0
- package/dist/codex-app-server-ui.js +327 -0
- package/dist/codex-app-server-ui.js.map +1 -0
- package/dist/codex-app-server.d.ts +141 -0
- package/dist/codex-app-server.js +747 -0
- package/dist/codex-app-server.js.map +1 -0
- package/dist/command-failure.d.ts +29 -0
- package/dist/command-failure.js +54 -0
- package/dist/command-failure.js.map +1 -0
- package/dist/computer-use-actor.d.ts +52 -0
- package/dist/computer-use-actor.js +48 -0
- package/dist/computer-use-actor.js.map +1 -0
- package/dist/computer-use.d.ts +232 -0
- package/dist/computer-use.js +615 -0
- package/dist/computer-use.js.map +1 -0
- package/dist/concurrency.d.ts +1 -0
- package/dist/concurrency.js +27 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/concurrent-shared-world-lab.d.ts +115 -0
- package/dist/concurrent-shared-world-lab.js +1092 -0
- package/dist/concurrent-shared-world-lab.js.map +1 -0
- package/dist/core/git-state.d.ts +31 -0
- package/dist/core/git-state.js +142 -0
- package/dist/core/git-state.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/run-primitives.d.ts +66 -0
- package/dist/core/run-primitives.js +120 -0
- package/dist/core/run-primitives.js.map +1 -0
- package/dist/cua-actor-lab.d.ts +620 -0
- package/dist/cua-actor-lab.js +2834 -0
- package/dist/cua-actor-lab.js.map +1 -0
- package/dist/device-presets.d.ts +67 -0
- package/dist/device-presets.js +50 -0
- package/dist/device-presets.js.map +1 -0
- package/dist/e2b-desktop-executor.d.ts +101 -0
- package/dist/e2b-desktop-executor.js +309 -0
- package/dist/e2b-desktop-executor.js.map +1 -0
- package/dist/e2b-desktop-launch.d.ts +144 -0
- package/dist/e2b-desktop-launch.js +59 -0
- package/dist/e2b-desktop-launch.js.map +1 -0
- package/dist/e2b-detached.d.ts +53 -0
- package/dist/e2b-detached.js +130 -0
- package/dist/e2b-detached.js.map +1 -0
- package/dist/e2b-terminal-lab.d.ts +345 -0
- package/dist/e2b-terminal-lab.js +1467 -0
- package/dist/e2b-terminal-lab.js.map +1 -0
- package/dist/env-file.d.ts +14 -0
- package/dist/env-file.js +108 -0
- package/dist/env-file.js.map +1 -0
- package/dist/feedback.d.ts +51 -0
- package/dist/feedback.js +313 -0
- package/dist/feedback.js.map +1 -0
- package/dist/image-evidence.d.ts +2 -0
- package/dist/image-evidence.js +33 -0
- package/dist/image-evidence.js.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/init-templates.d.ts +12 -0
- package/dist/init-templates.js +369 -0
- package/dist/init-templates.js.map +1 -0
- package/dist/init.d.ts +26 -0
- package/dist/init.js +343 -0
- package/dist/init.js.map +1 -0
- package/dist/lab-app-runner.d.ts +78 -0
- package/dist/lab-app-runner.js +403 -0
- package/dist/lab-app-runner.js.map +1 -0
- package/dist/lab-config.d.ts +578 -0
- package/dist/lab-config.js +1751 -0
- package/dist/lab-config.js.map +1 -0
- package/dist/lab-engine.d.ts +77 -0
- package/dist/lab-engine.js +216 -0
- package/dist/lab-engine.js.map +1 -0
- package/dist/lab-preflight.d.ts +67 -0
- package/dist/lab-preflight.js +385 -0
- package/dist/lab-preflight.js.map +1 -0
- package/dist/labs.d.ts +53 -0
- package/dist/labs.js +176 -0
- package/dist/labs.js.map +1 -0
- package/dist/observer-assets.d.ts +2 -0
- package/dist/observer-assets.js +2630 -0
- package/dist/observer-assets.js.map +1 -0
- package/dist/observer-data.d.ts +63 -0
- package/dist/observer-data.js +145 -0
- package/dist/observer-data.js.map +1 -0
- package/dist/observer-static.d.ts +39 -0
- package/dist/observer-static.js +192 -0
- package/dist/observer-static.js.map +1 -0
- package/dist/observer.d.ts +46 -0
- package/dist/observer.js +353 -0
- package/dist/observer.js.map +1 -0
- package/dist/openai-responses-cu.d.ts +113 -0
- package/dist/openai-responses-cu.js +531 -0
- package/dist/openai-responses-cu.js.map +1 -0
- package/dist/oss-lab.d.ts +52 -0
- package/dist/oss-lab.js +299 -0
- package/dist/oss-lab.js.map +1 -0
- package/dist/oss-meta-lab-scoring.d.ts +15 -0
- package/dist/oss-meta-lab-scoring.js +145 -0
- package/dist/oss-meta-lab-scoring.js.map +1 -0
- package/dist/oss-meta-lab.d.ts +279 -0
- package/dist/oss-meta-lab.js +4889 -0
- package/dist/oss-meta-lab.js.map +1 -0
- package/dist/oss-remote-telemetry.d.ts +77 -0
- package/dist/oss-remote-telemetry.js +403 -0
- package/dist/oss-remote-telemetry.js.map +1 -0
- package/dist/persona.d.ts +35 -0
- package/dist/persona.js +133 -0
- package/dist/persona.js.map +1 -0
- package/dist/pi-agent-core.d.ts +77 -0
- package/dist/pi-agent-core.js +203 -0
- package/dist/pi-agent-core.js.map +1 -0
- package/dist/program.d.ts +54 -0
- package/dist/program.js +2347 -0
- package/dist/program.js.map +1 -0
- package/dist/redaction.d.ts +90 -0
- package/dist/redaction.js +292 -0
- package/dist/redaction.js.map +1 -0
- package/dist/run.d.ts +784 -0
- package/dist/run.js +4555 -0
- package/dist/run.js.map +1 -0
- package/dist/scripted-browser-actor.d.ts +294 -0
- package/dist/scripted-browser-actor.js +1286 -0
- package/dist/scripted-browser-actor.js.map +1 -0
- package/dist/scripted-browser-lab.d.ts +107 -0
- package/dist/scripted-browser-lab.js +738 -0
- package/dist/scripted-browser-lab.js.map +1 -0
- package/dist/shared-world-lab.d.ts +208 -0
- package/dist/shared-world-lab.js +1048 -0
- package/dist/shared-world-lab.js.map +1 -0
- package/dist/source-archive.d.ts +82 -0
- package/dist/source-archive.js +365 -0
- package/dist/source-archive.js.map +1 -0
- package/dist/stop-conditions.d.ts +26 -0
- package/dist/stop-conditions.js +66 -0
- package/dist/stop-conditions.js.map +1 -0
- package/dist/terminal-agent-actor.d.ts +36 -0
- package/dist/terminal-agent-actor.js +23 -0
- package/dist/terminal-agent-actor.js.map +1 -0
- package/docs/architecture/actor-contract.md +431 -0
- package/docs/architecture/github-feedback-loop.md +189 -0
- package/docs/architecture/local-codex-tui-actor.md +213 -0
- package/docs/architecture/observer.md +118 -0
- package/docs/architecture/oss-lab-poc.md +242 -0
- package/docs/architecture/project-layout.md +166 -0
- package/docs/architecture/state-driven-executor.md +203 -0
- package/docs/architecture/terminal-product-lane.md +148 -0
- package/docs/contracts/adapter-fixtures.md +80 -0
- package/docs/contracts/core.md +71 -0
- package/docs/contracts/feedback.md +145 -0
- package/docs/contracts/policy.md +305 -0
- package/docs/contracts/run-bundle.md +358 -0
- package/docs/contracts/schemas.md +984 -0
- package/docs/goals/current.md +409 -0
- package/docs/principles/invariants-and-defaults.md +135 -0
- package/docs/principles/self-driving-harness.md +129 -0
- package/docs/product/open-source-install-experience.md +229 -0
- package/docs/ramp/README.md +200 -0
- package/docs/release/open-source-readiness.md +208 -0
- package/docs/release/public-readiness-standard.md +205 -0
- package/docs/roadmap/world-class-open-source-v0.md +311 -0
- package/package.json +102 -8
- package/skills/humanish/SKILL.md +244 -0
- package/skills/humanish/agents/openai.yaml +7 -0
- package/index.js +0 -1
|
@@ -0,0 +1,747 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import readline from "node:readline";
|
|
5
|
+
import { digestText, publicPathForTrace, redactText, tailText } from "./redaction.js";
|
|
6
|
+
export const CODEX_APP_SERVER_TRACE_SCHEMA = "humanish.codex-app-server-trace.v1";
|
|
7
|
+
const authLikeKey = /(api[_-]?key|access[_-]?token|auth[_-]?url|authorization|bearer|credential|password|secret|token)$/i;
|
|
8
|
+
const pathLikeKey = /^(cwd|path|writableRoots|workspaceRoot)$/i;
|
|
9
|
+
export async function runCodexAppServerSession(options) {
|
|
10
|
+
const startedAt = new Date();
|
|
11
|
+
const startedMs = Date.now();
|
|
12
|
+
const relativeDir = "codex-app-server";
|
|
13
|
+
const eventsPath = path.join(relativeDir, "events.ndjson");
|
|
14
|
+
const tracePath = path.join(relativeDir, "summary.json");
|
|
15
|
+
const transcriptPath = path.join(relativeDir, "transcript.txt");
|
|
16
|
+
const absoluteEventsPath = path.join(options.runRoot, eventsPath);
|
|
17
|
+
const absoluteTracePath = path.join(options.runRoot, tracePath);
|
|
18
|
+
const absoluteTranscriptPath = path.join(options.runRoot, transcriptPath);
|
|
19
|
+
const commandParts = resolveAppServerCommand(options.actorCommand);
|
|
20
|
+
const childEnv = resolveCodexAppServerEnv(process.env);
|
|
21
|
+
const apiKey = appServerApiKeyForLogin(childEnv);
|
|
22
|
+
const child = spawn(commandParts.command, commandParts.args, {
|
|
23
|
+
cwd: options.cwd,
|
|
24
|
+
env: childEnv,
|
|
25
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
26
|
+
});
|
|
27
|
+
const recorder = new CodexTraceRecorder({
|
|
28
|
+
commandName: commandParts.name,
|
|
29
|
+
cwd: options.cwd,
|
|
30
|
+
experimentalApi: options.experimentalApi === true,
|
|
31
|
+
promptDigest: digestText(options.prompt),
|
|
32
|
+
startedAt: startedAt.toISOString()
|
|
33
|
+
});
|
|
34
|
+
const envelopes = [];
|
|
35
|
+
let transcript = "";
|
|
36
|
+
let nextId = 1;
|
|
37
|
+
let threadId;
|
|
38
|
+
let turnId;
|
|
39
|
+
let completed = false;
|
|
40
|
+
let timedOut = false;
|
|
41
|
+
let processError;
|
|
42
|
+
let stdinError;
|
|
43
|
+
let exitCode;
|
|
44
|
+
let signal;
|
|
45
|
+
let completionStatus;
|
|
46
|
+
let completionReason = "Codex app-server turn did not complete.";
|
|
47
|
+
const pending = new Map();
|
|
48
|
+
await mkdir(path.dirname(absoluteEventsPath), { recursive: true });
|
|
49
|
+
const appendEnvelope = (direction, message) => {
|
|
50
|
+
const redacted = redactCodexEnvelope(message, options.cwd);
|
|
51
|
+
recorder.observeEnvelope(direction, redacted);
|
|
52
|
+
envelopes.push(JSON.stringify({
|
|
53
|
+
at: new Date().toISOString(),
|
|
54
|
+
direction,
|
|
55
|
+
message: redacted
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
const send = (message) => {
|
|
59
|
+
appendEnvelope("client", message);
|
|
60
|
+
if (child.stdin.destroyed || stdinError || processError) {
|
|
61
|
+
throw stdinError ?? processError ?? new Error("Codex app-server stdin is closed.");
|
|
62
|
+
}
|
|
63
|
+
child.stdin.write(`${JSON.stringify(message)}\n`, (error) => {
|
|
64
|
+
if (error && !stdinError) {
|
|
65
|
+
stdinError = error;
|
|
66
|
+
recorder.addWarning("stdin", redactText(error.message).slice(0, 1_000));
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
const request = (method, params) => {
|
|
71
|
+
const id = nextId;
|
|
72
|
+
nextId += 1;
|
|
73
|
+
const message = params === undefined
|
|
74
|
+
? { method, id }
|
|
75
|
+
: { method, id, params };
|
|
76
|
+
const promise = new Promise((resolve, reject) => {
|
|
77
|
+
pending.set(id, { resolve, reject });
|
|
78
|
+
});
|
|
79
|
+
send(message);
|
|
80
|
+
return promise;
|
|
81
|
+
};
|
|
82
|
+
const respond = (id, result) => {
|
|
83
|
+
send({ id, result });
|
|
84
|
+
};
|
|
85
|
+
const rl = readline.createInterface({ input: child.stdout });
|
|
86
|
+
const closed = new Promise((resolve) => {
|
|
87
|
+
child.once("error", (error) => {
|
|
88
|
+
processError = error;
|
|
89
|
+
recorder.addError("process", redactText(error.message).slice(0, 1_000));
|
|
90
|
+
});
|
|
91
|
+
child.once("close", (code, childSignal) => {
|
|
92
|
+
exitCode = code === null ? undefined : code;
|
|
93
|
+
signal = childSignal === null ? undefined : childSignal;
|
|
94
|
+
resolve();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
child.stdin.on("error", (error) => {
|
|
98
|
+
stdinError = error;
|
|
99
|
+
recorder.addWarning("stdin", redactText(error.message).slice(0, 1_000));
|
|
100
|
+
});
|
|
101
|
+
const timeout = setTimeout(() => {
|
|
102
|
+
timedOut = true;
|
|
103
|
+
child.kill("SIGTERM");
|
|
104
|
+
setTimeout(() => child.kill("SIGKILL"), 2_000).unref();
|
|
105
|
+
}, options.timeoutMs);
|
|
106
|
+
timeout.unref();
|
|
107
|
+
child.stderr.on("data", (chunk) => {
|
|
108
|
+
const text = redactText(chunk.toString("utf8"));
|
|
109
|
+
if (text.trim()) {
|
|
110
|
+
recorder.addWarning("stderr", text.trim().slice(0, 1_000));
|
|
111
|
+
transcript = limitTranscript(`${transcript}${text}`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
rl.on("line", (line) => {
|
|
115
|
+
let parsed;
|
|
116
|
+
try {
|
|
117
|
+
parsed = JSON.parse(line);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
recorder.addWarning("parse", "Received non-JSON app-server output line.");
|
|
121
|
+
transcript = limitTranscript(`${transcript}${redactText(line)}\n`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
appendEnvelope("server", parsed);
|
|
125
|
+
recorder.observeServerMessage(parsed);
|
|
126
|
+
const id = parsed.id;
|
|
127
|
+
if (typeof id === "number" || typeof id === "string") {
|
|
128
|
+
if (typeof parsed.method === "string") {
|
|
129
|
+
const result = defaultServerRequestResponse(parsed);
|
|
130
|
+
recorder.recordApproval(parsed, result);
|
|
131
|
+
respond(id, result);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
const pendingRequest = pending.get(id);
|
|
135
|
+
if (pendingRequest) {
|
|
136
|
+
pending.delete(id);
|
|
137
|
+
if (isRecord(parsed.error)) {
|
|
138
|
+
pendingRequest.reject(new Error(formatJsonRpcError(parsed.error)));
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
pendingRequest.resolve(isRecord(parsed.result) ? parsed.result : {});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (parsed.method === "thread/started") {
|
|
146
|
+
threadId = readNestedString(parsed, ["params", "thread", "id"]) ?? threadId;
|
|
147
|
+
recorder.threadId = threadId;
|
|
148
|
+
recorder.sessionId = readNestedString(parsed, ["params", "thread", "sessionId"]) ?? recorder.sessionId;
|
|
149
|
+
recorder.model = readNestedString(parsed, ["params", "thread", "model"]) ?? recorder.model;
|
|
150
|
+
recorder.codexCliVersion = readNestedString(parsed, ["params", "thread", "cliVersion"]) ?? recorder.codexCliVersion;
|
|
151
|
+
}
|
|
152
|
+
if (parsed.method === "turn/started") {
|
|
153
|
+
turnId = readNestedString(parsed, ["params", "turn", "id"]) ?? turnId;
|
|
154
|
+
recorder.turnId = turnId;
|
|
155
|
+
}
|
|
156
|
+
if (parsed.method === "turn/completed") {
|
|
157
|
+
completionStatus = readNestedString(parsed, ["params", "turn", "status"]);
|
|
158
|
+
completionReason = completionStatus ? `turn completed with status ${completionStatus}` : "turn completed";
|
|
159
|
+
completed = true;
|
|
160
|
+
child.kill("SIGTERM");
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
const finish = async (status, reason) => {
|
|
164
|
+
clearTimeout(timeout);
|
|
165
|
+
for (const pendingRequest of pending.values()) {
|
|
166
|
+
pendingRequest.reject(new Error(reason));
|
|
167
|
+
}
|
|
168
|
+
pending.clear();
|
|
169
|
+
const completedAt = new Date().toISOString();
|
|
170
|
+
const durationMs = Date.now() - startedMs;
|
|
171
|
+
const trace = recorder.buildTrace({
|
|
172
|
+
completedAt,
|
|
173
|
+
durationMs,
|
|
174
|
+
reason,
|
|
175
|
+
status
|
|
176
|
+
});
|
|
177
|
+
const transcriptText = recorder.renderTranscript();
|
|
178
|
+
await writeFile(absoluteEventsPath, `${envelopes.join("\n")}${envelopes.length > 0 ? "\n" : ""}`, "utf8");
|
|
179
|
+
await writeFile(absoluteTracePath, `${JSON.stringify(trace, null, 2)}\n`, "utf8");
|
|
180
|
+
await writeFile(absoluteTranscriptPath, transcriptText.length > 0 ? transcriptText : "No Codex app-server transcript output captured.\n", "utf8");
|
|
181
|
+
return {
|
|
182
|
+
status,
|
|
183
|
+
reason,
|
|
184
|
+
durationMs,
|
|
185
|
+
...(exitCode === undefined ? {} : { exitCode }),
|
|
186
|
+
...(signal === undefined ? {} : { signal }),
|
|
187
|
+
...(trace.threadId === undefined ? {} : { threadId: trace.threadId }),
|
|
188
|
+
...(trace.turnId === undefined ? {} : { turnId: trace.turnId }),
|
|
189
|
+
...(trace.sessionId === undefined ? {} : { sessionId: trace.sessionId }),
|
|
190
|
+
...(trace.model === undefined ? {} : { model: trace.model }),
|
|
191
|
+
...(trace.server.codexCliVersion === undefined ? {} : { codexCliVersion: trace.server.codexCliVersion }),
|
|
192
|
+
experimentalApi: trace.client.experimentalApi,
|
|
193
|
+
counts: trace.counts,
|
|
194
|
+
tail: tailText(transcriptText, 6_000),
|
|
195
|
+
trace,
|
|
196
|
+
transcriptPath,
|
|
197
|
+
tracePath,
|
|
198
|
+
eventsPath
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
const waitForResponse = async (promise, method) => Promise.race([
|
|
202
|
+
promise,
|
|
203
|
+
closed.then(() => {
|
|
204
|
+
const commandName = `${commandParts.command} ${commandParts.args.join(" ")}`.trim();
|
|
205
|
+
const detail = processError?.message
|
|
206
|
+
?? stdinError?.message
|
|
207
|
+
?? (exitCode === undefined ? "without an exit code" : `with code ${exitCode}`);
|
|
208
|
+
throw new Error(`Codex app-server command '${commandName}' exited during ${method} ${detail}.`);
|
|
209
|
+
})
|
|
210
|
+
]);
|
|
211
|
+
try {
|
|
212
|
+
const initialize = request("initialize", {
|
|
213
|
+
clientInfo: {
|
|
214
|
+
name: "humanish_cli",
|
|
215
|
+
title: "Humanish CLI",
|
|
216
|
+
version: "0.1.0"
|
|
217
|
+
},
|
|
218
|
+
capabilities: {
|
|
219
|
+
experimentalApi: options.experimentalApi === true
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
send({ method: "initialized", params: {} });
|
|
223
|
+
await waitForResponse(initialize, "initialize");
|
|
224
|
+
if (apiKey) {
|
|
225
|
+
await waitForResponse(request("account/login/start", {
|
|
226
|
+
type: "apiKey",
|
|
227
|
+
apiKey
|
|
228
|
+
}), "account/login/start");
|
|
229
|
+
}
|
|
230
|
+
const threadResponse = await waitForResponse(request("thread/start", {
|
|
231
|
+
cwd: options.cwd,
|
|
232
|
+
approvalPolicy: normalizeApprovalPolicy(options.approvalPolicy),
|
|
233
|
+
sandbox: normalizeSandbox(options.sandbox),
|
|
234
|
+
serviceName: options.serviceName ?? "humanish",
|
|
235
|
+
...(options.model === undefined ? {} : { model: options.model })
|
|
236
|
+
}), "thread/start");
|
|
237
|
+
threadId = readNestedString(threadResponse, ["thread", "id"]) ?? threadId;
|
|
238
|
+
recorder.threadId = threadId;
|
|
239
|
+
recorder.sessionId = readNestedString(threadResponse, ["thread", "sessionId"]) ?? recorder.sessionId;
|
|
240
|
+
recorder.model = readNestedString(threadResponse, ["thread", "model"]) ?? options.model ?? recorder.model;
|
|
241
|
+
recorder.codexCliVersion = readNestedString(threadResponse, ["thread", "cliVersion"]) ?? recorder.codexCliVersion;
|
|
242
|
+
if (!threadId) {
|
|
243
|
+
throw new Error("thread/start did not return a thread id");
|
|
244
|
+
}
|
|
245
|
+
const turnResponse = await waitForResponse(request("turn/start", {
|
|
246
|
+
threadId,
|
|
247
|
+
cwd: options.cwd,
|
|
248
|
+
approvalPolicy: normalizeApprovalPolicy(options.approvalPolicy),
|
|
249
|
+
sandboxPolicy: normalizeTurnSandbox(options.sandbox, options.cwd),
|
|
250
|
+
input: [
|
|
251
|
+
{ type: "text", text: options.prompt, text_elements: [] }
|
|
252
|
+
]
|
|
253
|
+
}), "turn/start");
|
|
254
|
+
turnId = readNestedString(turnResponse, ["turn", "id"]) ?? turnId;
|
|
255
|
+
recorder.turnId = turnId;
|
|
256
|
+
while (!completed && !timedOut) {
|
|
257
|
+
await Promise.race([
|
|
258
|
+
closed,
|
|
259
|
+
new Promise((resolve) => setTimeout(resolve, 100))
|
|
260
|
+
]);
|
|
261
|
+
if (exitCode !== undefined || signal !== undefined) {
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (timedOut) {
|
|
266
|
+
await closed;
|
|
267
|
+
return finish("timed_out", `Codex app-server turn exceeded ${options.timeoutMs}ms timeout.`);
|
|
268
|
+
}
|
|
269
|
+
if (completed) {
|
|
270
|
+
await closed;
|
|
271
|
+
const status = completionStatus === "completed"
|
|
272
|
+
? "passed"
|
|
273
|
+
: completionStatus === "failed"
|
|
274
|
+
? "failed"
|
|
275
|
+
: "blocked";
|
|
276
|
+
return finish(status, completionReason);
|
|
277
|
+
}
|
|
278
|
+
await closed;
|
|
279
|
+
return finish(exitCode === 0 ? "passed" : "blocked", `Codex app-server process exited before turn completion${exitCode === undefined ? "" : ` with code ${exitCode}`}.`);
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
child.kill("SIGTERM");
|
|
283
|
+
await closed;
|
|
284
|
+
return finish("blocked", error instanceof Error ? error.message : String(error));
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function resolveAppServerCommand(overrideCommand) {
|
|
288
|
+
const commandParts = overrideCommand && overrideCommand.length > 0
|
|
289
|
+
? overrideCommand
|
|
290
|
+
: ["codex", "app-server", "--listen", "stdio://"];
|
|
291
|
+
const [command, ...args] = commandParts;
|
|
292
|
+
return {
|
|
293
|
+
command: command ?? "codex",
|
|
294
|
+
args,
|
|
295
|
+
name: path.basename(command ?? "codex")
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
function resolveCodexAppServerEnv(env) {
|
|
299
|
+
const privateApiKey = env.HUMANISH_PRIVATE_CODEX_API_KEY?.trim();
|
|
300
|
+
const privateAccessToken = env.HUMANISH_PRIVATE_CODEX_ACCESS_TOKEN?.trim();
|
|
301
|
+
return {
|
|
302
|
+
...env,
|
|
303
|
+
TERM: env.TERM ?? "xterm-256color",
|
|
304
|
+
...(privateApiKey && !env.CODEX_API_KEY ? { CODEX_API_KEY: privateApiKey } : {}),
|
|
305
|
+
...(privateApiKey && !env.OPENAI_API_KEY ? { OPENAI_API_KEY: privateApiKey } : {}),
|
|
306
|
+
...(privateAccessToken && !env.CODEX_ACCESS_TOKEN ? { CODEX_ACCESS_TOKEN: privateAccessToken } : {})
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
function appServerApiKeyForLogin(env) {
|
|
310
|
+
return env.HUMANISH_PRIVATE_CODEX_API_KEY?.trim()
|
|
311
|
+
|| env.CODEX_API_KEY?.trim()
|
|
312
|
+
|| env.OPENAI_API_KEY?.trim()
|
|
313
|
+
|| undefined;
|
|
314
|
+
}
|
|
315
|
+
function defaultServerRequestResponse(message) {
|
|
316
|
+
switch (message.method) {
|
|
317
|
+
case "item/commandExecution/requestApproval":
|
|
318
|
+
return { decision: "decline" };
|
|
319
|
+
case "item/fileChange/requestApproval":
|
|
320
|
+
return { decision: "decline" };
|
|
321
|
+
case "applyPatchApproval":
|
|
322
|
+
case "execCommandApproval":
|
|
323
|
+
return { decision: "denied" };
|
|
324
|
+
case "item/tool/requestUserInput":
|
|
325
|
+
return { answers: {} };
|
|
326
|
+
case "mcpServer/elicitation/request":
|
|
327
|
+
return { action: "decline" };
|
|
328
|
+
default:
|
|
329
|
+
return {};
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function normalizeApprovalPolicy(value) {
|
|
333
|
+
if (value === "on-failure")
|
|
334
|
+
return "on-failure";
|
|
335
|
+
if (value === "on-request")
|
|
336
|
+
return "on-request";
|
|
337
|
+
if (value === "untrusted")
|
|
338
|
+
return "untrusted";
|
|
339
|
+
return "never";
|
|
340
|
+
}
|
|
341
|
+
function normalizeSandbox(value) {
|
|
342
|
+
if (value === "danger-full-access")
|
|
343
|
+
return "danger-full-access";
|
|
344
|
+
if (value === "workspace-write")
|
|
345
|
+
return "workspace-write";
|
|
346
|
+
return "read-only";
|
|
347
|
+
}
|
|
348
|
+
function normalizeTurnSandbox(value, cwd) {
|
|
349
|
+
const mode = normalizeSandbox(value);
|
|
350
|
+
if (mode === "workspace-write") {
|
|
351
|
+
return {
|
|
352
|
+
type: "workspaceWrite",
|
|
353
|
+
writableRoots: [cwd],
|
|
354
|
+
networkAccess: false,
|
|
355
|
+
excludeTmpdirEnvVar: false,
|
|
356
|
+
excludeSlashTmp: false
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
if (mode === "danger-full-access") {
|
|
360
|
+
return { type: "dangerFullAccess" };
|
|
361
|
+
}
|
|
362
|
+
return { type: "readOnly", networkAccess: false };
|
|
363
|
+
}
|
|
364
|
+
class CodexTraceRecorder {
|
|
365
|
+
commandOutputs = new Map();
|
|
366
|
+
fileOutputs = new Map();
|
|
367
|
+
messageDeltas = new Map();
|
|
368
|
+
reasoningDeltas = new Map();
|
|
369
|
+
rootCwd;
|
|
370
|
+
trace;
|
|
371
|
+
codexCliVersion;
|
|
372
|
+
model;
|
|
373
|
+
sessionId;
|
|
374
|
+
threadId;
|
|
375
|
+
turnId;
|
|
376
|
+
constructor(args) {
|
|
377
|
+
this.rootCwd = args.cwd;
|
|
378
|
+
this.trace = {
|
|
379
|
+
schema: CODEX_APP_SERVER_TRACE_SCHEMA,
|
|
380
|
+
provider: "codex-app-server",
|
|
381
|
+
protocolVersion: "v2",
|
|
382
|
+
redaction: {
|
|
383
|
+
status: "passed",
|
|
384
|
+
notes: "Trace envelopes and text were redacted before persistence. App-server schemas are version-specific and are not embedded in this run artifact."
|
|
385
|
+
},
|
|
386
|
+
client: {
|
|
387
|
+
name: "humanish_cli",
|
|
388
|
+
title: "Humanish CLI",
|
|
389
|
+
experimentalApi: args.experimentalApi
|
|
390
|
+
},
|
|
391
|
+
server: {
|
|
392
|
+
commandName: args.commandName,
|
|
393
|
+
transport: "stdio"
|
|
394
|
+
},
|
|
395
|
+
cwd: publicPathForTrace(args.cwd, args.cwd),
|
|
396
|
+
promptDigest: args.promptDigest,
|
|
397
|
+
startedAt: args.startedAt,
|
|
398
|
+
counts: {
|
|
399
|
+
approvals: 0,
|
|
400
|
+
commandOutputs: 0,
|
|
401
|
+
envelopes: 0,
|
|
402
|
+
errors: 0,
|
|
403
|
+
fileChanges: 0,
|
|
404
|
+
itemCompletions: 0,
|
|
405
|
+
itemStarts: 0,
|
|
406
|
+
messages: 0,
|
|
407
|
+
reasoning: 0,
|
|
408
|
+
requests: 0,
|
|
409
|
+
responses: 0,
|
|
410
|
+
tools: 0,
|
|
411
|
+
warnings: 0
|
|
412
|
+
},
|
|
413
|
+
methods: {},
|
|
414
|
+
items: [],
|
|
415
|
+
messages: [],
|
|
416
|
+
reasoning: [],
|
|
417
|
+
plans: [],
|
|
418
|
+
commands: [],
|
|
419
|
+
fileChanges: [],
|
|
420
|
+
tools: [],
|
|
421
|
+
approvals: [],
|
|
422
|
+
warnings: [],
|
|
423
|
+
errors: []
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
observeEnvelope(direction, message) {
|
|
427
|
+
this.trace.counts.envelopes += 1;
|
|
428
|
+
if (direction === "client") {
|
|
429
|
+
this.trace.counts.requests += isRecord(message) && "id" in message ? 1 : 0;
|
|
430
|
+
}
|
|
431
|
+
else if (isRecord(message) && "id" in message && !("method" in message)) {
|
|
432
|
+
this.trace.counts.responses += 1;
|
|
433
|
+
}
|
|
434
|
+
const method = isRecord(message) && typeof message.method === "string" ? message.method : direction === "server" ? "response" : "request";
|
|
435
|
+
this.trace.methods[method] = (this.trace.methods[method] ?? 0) + 1;
|
|
436
|
+
}
|
|
437
|
+
observeServerMessage(message) {
|
|
438
|
+
const method = typeof message.method === "string" ? message.method : "";
|
|
439
|
+
if (method === "error") {
|
|
440
|
+
this.addError(method, readNestedString(message, ["params", "message"]) ?? "App-server emitted an error notification.");
|
|
441
|
+
}
|
|
442
|
+
if (method === "warning" || method === "guardianWarning" || method === "configWarning") {
|
|
443
|
+
this.addWarning(method, readNestedString(message, ["params", "message"]) ?? `App-server emitted ${method}.`);
|
|
444
|
+
}
|
|
445
|
+
if (method === "thread/tokenUsage/updated") {
|
|
446
|
+
if (isRecord(message.params)) {
|
|
447
|
+
this.trace.tokenUsage = message.params;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
if (method === "item/started" || method === "item/completed") {
|
|
451
|
+
this.recordItem(message, method === "item/started" ? "started" : "completed");
|
|
452
|
+
}
|
|
453
|
+
if (method === "item/agentMessage/delta") {
|
|
454
|
+
this.appendText(this.messageDeltas, message);
|
|
455
|
+
this.trace.counts.messages += 1;
|
|
456
|
+
}
|
|
457
|
+
if (method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
|
|
458
|
+
this.appendText(this.reasoningDeltas, message);
|
|
459
|
+
this.trace.counts.reasoning += 1;
|
|
460
|
+
}
|
|
461
|
+
if (method === "item/commandExecution/outputDelta" || method === "command/exec/outputDelta" || method === "process/outputDelta") {
|
|
462
|
+
this.appendText(this.commandOutputs, message);
|
|
463
|
+
this.trace.counts.commandOutputs += 1;
|
|
464
|
+
}
|
|
465
|
+
if (method === "item/fileChange/outputDelta") {
|
|
466
|
+
this.appendText(this.fileOutputs, message);
|
|
467
|
+
}
|
|
468
|
+
if (method === "turn/plan/updated") {
|
|
469
|
+
this.recordPlan(message);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
recordApproval(message, response) {
|
|
473
|
+
this.trace.counts.approvals += 1;
|
|
474
|
+
const itemId = readNestedString(message, ["params", "itemId"]);
|
|
475
|
+
this.trace.approvals.push({
|
|
476
|
+
id: message.id,
|
|
477
|
+
method: typeof message.method === "string" ? message.method : "unknown",
|
|
478
|
+
...(itemId === undefined ? {} : { itemId }),
|
|
479
|
+
decision: typeof response.decision === "string" && response.decision === "denied" ? "denied" : typeof response.decision === "string" ? "decline" : "empty",
|
|
480
|
+
reason: "Humanish records app-server approval requests and declines by default unless a future explicit policy says otherwise."
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
addWarning(method, message) {
|
|
484
|
+
this.trace.counts.warnings += 1;
|
|
485
|
+
this.trace.warnings.push({ method, message: redactText(message) });
|
|
486
|
+
}
|
|
487
|
+
addError(method, message) {
|
|
488
|
+
this.trace.counts.errors += 1;
|
|
489
|
+
this.trace.errors.push({ method, message: redactText(message) });
|
|
490
|
+
}
|
|
491
|
+
buildTrace(args) {
|
|
492
|
+
const messages = Array.from(this.messageDeltas.entries())
|
|
493
|
+
.filter(([, text]) => text.trim() !== "")
|
|
494
|
+
.map(([itemId, text]) => ({ itemId, text: redactText(text) }));
|
|
495
|
+
const reasoning = Array.from(this.reasoningDeltas.entries())
|
|
496
|
+
.filter(([, text]) => text.trim() !== "")
|
|
497
|
+
.map(([itemId, text]) => ({ itemId, text: redactText(text) }));
|
|
498
|
+
const commands = this.trace.commands.map((command) => ({
|
|
499
|
+
...command,
|
|
500
|
+
outputTail: tailText(redactText(this.commandOutputs.get(command.itemId) ?? command.outputTail ?? ""), 2_000)
|
|
501
|
+
}));
|
|
502
|
+
const fileChanges = this.trace.fileChanges.map((fileChange) => ({
|
|
503
|
+
...fileChange,
|
|
504
|
+
outputTail: tailText(redactText(this.fileOutputs.get(fileChange.itemId) ?? fileChange.outputTail ?? ""), 2_000)
|
|
505
|
+
}));
|
|
506
|
+
return {
|
|
507
|
+
...this.trace,
|
|
508
|
+
server: {
|
|
509
|
+
...this.trace.server,
|
|
510
|
+
...(this.codexCliVersion === undefined ? {} : { codexCliVersion: this.codexCliVersion })
|
|
511
|
+
},
|
|
512
|
+
...(this.threadId === undefined ? {} : { threadId: this.threadId }),
|
|
513
|
+
...(this.turnId === undefined ? {} : { turnId: this.turnId }),
|
|
514
|
+
...(this.sessionId === undefined ? {} : { sessionId: this.sessionId }),
|
|
515
|
+
...(this.model === undefined ? {} : { model: this.model }),
|
|
516
|
+
completedAt: args.completedAt,
|
|
517
|
+
durationMs: args.durationMs,
|
|
518
|
+
status: args.status,
|
|
519
|
+
reason: redactText(args.reason),
|
|
520
|
+
messages,
|
|
521
|
+
reasoning,
|
|
522
|
+
commands,
|
|
523
|
+
fileChanges
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
renderTranscript() {
|
|
527
|
+
const trace = this.buildTrace({
|
|
528
|
+
completedAt: new Date().toISOString(),
|
|
529
|
+
durationMs: 0,
|
|
530
|
+
reason: "transcript render",
|
|
531
|
+
status: "blocked"
|
|
532
|
+
});
|
|
533
|
+
const sections = [
|
|
534
|
+
["Agent messages", trace.messages.map((message) => message.text).join("\n\n")],
|
|
535
|
+
["Reasoning summaries", trace.reasoning.map((entry) => entry.text).join("\n\n")],
|
|
536
|
+
["Commands", trace.commands.map((command) => `${command.command ?? "command"}\n${command.outputTail ?? ""}`).join("\n\n")],
|
|
537
|
+
["File changes", trace.fileChanges.map((fileChange) => `${fileChange.status ?? "fileChange"} ${fileChange.changeCount ?? 0} change(s)`).join("\n")]
|
|
538
|
+
];
|
|
539
|
+
return sections
|
|
540
|
+
.filter(([, text]) => text.trim() !== "")
|
|
541
|
+
.map(([title, text]) => `## ${title}\n\n${text.trim()}`)
|
|
542
|
+
.join("\n\n");
|
|
543
|
+
}
|
|
544
|
+
appendText(target, message) {
|
|
545
|
+
const itemId = readNestedString(message, ["params", "itemId"]) ?? "unknown";
|
|
546
|
+
const delta = readNestedString(message, ["params", "delta"]) ?? "";
|
|
547
|
+
target.set(itemId, limitTranscript(`${target.get(itemId) ?? ""}${redactText(delta)}`));
|
|
548
|
+
}
|
|
549
|
+
recordItem(message, lifecycle) {
|
|
550
|
+
const item = readNestedRecord(message, ["params", "item"]);
|
|
551
|
+
const id = readString(item, "id") ?? `unknown-${this.trace.items.length + 1}`;
|
|
552
|
+
const type = readString(item, "type") ?? "unknown";
|
|
553
|
+
const status = readString(item, "status");
|
|
554
|
+
this.trace.items.push({
|
|
555
|
+
id,
|
|
556
|
+
type,
|
|
557
|
+
lifecycle,
|
|
558
|
+
title: itemTitle(item, type),
|
|
559
|
+
...(status === undefined ? {} : { status })
|
|
560
|
+
});
|
|
561
|
+
if (lifecycle === "started") {
|
|
562
|
+
this.trace.counts.itemStarts += 1;
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
this.trace.counts.itemCompletions += 1;
|
|
566
|
+
}
|
|
567
|
+
if (type === "commandExecution") {
|
|
568
|
+
this.recordCommand(item, id, status);
|
|
569
|
+
}
|
|
570
|
+
if (type === "fileChange") {
|
|
571
|
+
this.recordFileChange(item, id, status);
|
|
572
|
+
}
|
|
573
|
+
if (type === "mcpToolCall" || type === "dynamicToolCall") {
|
|
574
|
+
this.recordToolCall(item, id, type, status);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
recordCommand(item, itemId, status) {
|
|
578
|
+
const existingIndex = this.trace.commands.findIndex((command) => command.itemId === itemId);
|
|
579
|
+
const commandCwd = readString(item, "cwd");
|
|
580
|
+
const command = {
|
|
581
|
+
itemId,
|
|
582
|
+
...(readString(item, "command") === undefined ? {} : { command: redactText(readString(item, "command") ?? "") }),
|
|
583
|
+
...(commandCwd === undefined ? {} : { cwd: publicPathForTrace(commandCwd, this.rootCwd) }),
|
|
584
|
+
...(status === undefined ? {} : { status }),
|
|
585
|
+
...(typeof item.exitCode === "number" ? { exitCode: item.exitCode } : {}),
|
|
586
|
+
...(readString(item, "aggregatedOutput") === undefined ? {} : { outputTail: tailText(redactText(readString(item, "aggregatedOutput") ?? ""), 2_000) })
|
|
587
|
+
};
|
|
588
|
+
if (existingIndex === -1) {
|
|
589
|
+
this.trace.commands.push(command);
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
this.trace.commands[existingIndex] = { ...this.trace.commands[existingIndex], ...command };
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
recordFileChange(item, itemId, status) {
|
|
596
|
+
this.trace.counts.fileChanges += 1;
|
|
597
|
+
const existingIndex = this.trace.fileChanges.findIndex((fileChange) => fileChange.itemId === itemId);
|
|
598
|
+
const fileChange = {
|
|
599
|
+
itemId,
|
|
600
|
+
...(status === undefined ? {} : { status }),
|
|
601
|
+
...(Array.isArray(item.changes) ? { changeCount: item.changes.length } : {})
|
|
602
|
+
};
|
|
603
|
+
if (existingIndex === -1) {
|
|
604
|
+
this.trace.fileChanges.push(fileChange);
|
|
605
|
+
}
|
|
606
|
+
else {
|
|
607
|
+
this.trace.fileChanges[existingIndex] = { ...this.trace.fileChanges[existingIndex], ...fileChange };
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
recordToolCall(item, itemId, type, status) {
|
|
611
|
+
this.trace.counts.tools += 1;
|
|
612
|
+
const server = readString(item, "server");
|
|
613
|
+
const tool = readString(item, "tool");
|
|
614
|
+
this.trace.tools.push({
|
|
615
|
+
itemId,
|
|
616
|
+
kind: type === "mcpToolCall" ? "mcp" : type === "dynamicToolCall" ? "dynamic" : "unknown",
|
|
617
|
+
...(server === undefined ? {} : { server }),
|
|
618
|
+
...(tool === undefined ? {} : { tool }),
|
|
619
|
+
...(status === undefined ? {} : { status })
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
recordPlan(message) {
|
|
623
|
+
const params = isRecord(message.params) ? message.params : {};
|
|
624
|
+
const explanation = typeof params.explanation === "string" ? params.explanation : undefined;
|
|
625
|
+
const plan = Array.isArray(params.plan) ? params.plan : [];
|
|
626
|
+
this.trace.plans.push({
|
|
627
|
+
...(explanation === undefined ? {} : { explanation: redactText(explanation) }),
|
|
628
|
+
steps: plan.map((step) => summarizePlanStep(step))
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
function readNestedRecord(value, pathParts) {
|
|
633
|
+
let current = value;
|
|
634
|
+
for (const part of pathParts) {
|
|
635
|
+
if (!isRecord(current)) {
|
|
636
|
+
return {};
|
|
637
|
+
}
|
|
638
|
+
current = current[part];
|
|
639
|
+
}
|
|
640
|
+
return isRecord(current) ? current : {};
|
|
641
|
+
}
|
|
642
|
+
function readNestedString(value, pathParts) {
|
|
643
|
+
let current = value;
|
|
644
|
+
for (const part of pathParts) {
|
|
645
|
+
if (!isRecord(current)) {
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
current = current[part];
|
|
649
|
+
}
|
|
650
|
+
return typeof current === "string" ? current : undefined;
|
|
651
|
+
}
|
|
652
|
+
function readString(value, key) {
|
|
653
|
+
return typeof value[key] === "string" ? value[key] : undefined;
|
|
654
|
+
}
|
|
655
|
+
function itemTitle(item, type) {
|
|
656
|
+
if (type === "commandExecution") {
|
|
657
|
+
return redactText(readString(item, "command") ?? "command execution");
|
|
658
|
+
}
|
|
659
|
+
if (type === "agentMessage") {
|
|
660
|
+
return tailText(redactText(readString(item, "text") ?? "agent message"), 120);
|
|
661
|
+
}
|
|
662
|
+
if (type === "mcpToolCall") {
|
|
663
|
+
return redactText([readString(item, "server"), readString(item, "tool")].filter(Boolean).join("/") || "mcp tool call");
|
|
664
|
+
}
|
|
665
|
+
if (type === "dynamicToolCall") {
|
|
666
|
+
return redactText(readString(item, "tool") ?? "dynamic tool call");
|
|
667
|
+
}
|
|
668
|
+
return redactText(type);
|
|
669
|
+
}
|
|
670
|
+
function summarizePlanStep(step) {
|
|
671
|
+
if (!isRecord(step)) {
|
|
672
|
+
return redactText(String(step));
|
|
673
|
+
}
|
|
674
|
+
const text = readString(step, "step") ?? readString(step, "text") ?? readString(step, "description") ?? JSON.stringify(redactJsonValue(step));
|
|
675
|
+
const status = readString(step, "status");
|
|
676
|
+
return status ? `${status}: ${redactText(text)}` : redactText(text);
|
|
677
|
+
}
|
|
678
|
+
function formatJsonRpcError(error) {
|
|
679
|
+
const code = typeof error.code === "number" ? `${error.code}: ` : "";
|
|
680
|
+
const message = typeof error.message === "string" ? error.message : JSON.stringify(redactJsonValue(error));
|
|
681
|
+
return redactText(`${code}${message}`);
|
|
682
|
+
}
|
|
683
|
+
function redactCodexEnvelope(value, rootCwd) {
|
|
684
|
+
const redacted = redactJsonValue(value, "", rootCwd);
|
|
685
|
+
if (!isRecord(value) || !isRecord(redacted) || value.method !== "turn/start") {
|
|
686
|
+
return redacted;
|
|
687
|
+
}
|
|
688
|
+
const rawParams = isRecord(value.params) ? value.params : {};
|
|
689
|
+
const redactedParams = isRecord(redacted.params) ? redacted.params : {};
|
|
690
|
+
const rawInput = Array.isArray(rawParams.input) ? rawParams.input : null;
|
|
691
|
+
const redactedInput = Array.isArray(redactedParams.input) ? redactedParams.input : null;
|
|
692
|
+
if (!rawInput || !redactedInput) {
|
|
693
|
+
return redacted;
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
...redacted,
|
|
697
|
+
params: {
|
|
698
|
+
...redactedParams,
|
|
699
|
+
input: redactedInput.map((entry, index) => redactTurnInputEntry(rawInput[index], entry))
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
function redactTurnInputEntry(rawEntry, redactedEntry) {
|
|
704
|
+
if (!isRecord(redactedEntry)) {
|
|
705
|
+
return redactedEntry;
|
|
706
|
+
}
|
|
707
|
+
const rawText = isRecord(rawEntry) && typeof rawEntry.text === "string" ? rawEntry.text : undefined;
|
|
708
|
+
if (rawText === undefined) {
|
|
709
|
+
return redactedEntry;
|
|
710
|
+
}
|
|
711
|
+
return {
|
|
712
|
+
...redactedEntry,
|
|
713
|
+
text: "[REDACTED_PROMPT_TEXT]",
|
|
714
|
+
textDigest: digestText(rawText),
|
|
715
|
+
textLength: rawText.length,
|
|
716
|
+
...(Array.isArray(redactedEntry.text_elements) ? { text_elements: [] } : {})
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
function redactJsonValue(value, keyHint = "", rootCwd) {
|
|
720
|
+
if (typeof value === "string") {
|
|
721
|
+
if (authLikeKey.test(keyHint)) {
|
|
722
|
+
return "[REDACTED_SECRET]";
|
|
723
|
+
}
|
|
724
|
+
if (rootCwd && pathLikeKey.test(keyHint)) {
|
|
725
|
+
return publicPathForTrace(value, rootCwd);
|
|
726
|
+
}
|
|
727
|
+
return redactText(value);
|
|
728
|
+
}
|
|
729
|
+
if (Array.isArray(value)) {
|
|
730
|
+
return value.map((entry) => redactJsonValue(entry, keyHint, rootCwd));
|
|
731
|
+
}
|
|
732
|
+
if (isRecord(value)) {
|
|
733
|
+
return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, redactJsonValue(entry, key, rootCwd)]));
|
|
734
|
+
}
|
|
735
|
+
return value;
|
|
736
|
+
}
|
|
737
|
+
function limitTranscript(value) {
|
|
738
|
+
const maxChars = 80_000;
|
|
739
|
+
if (value.length <= maxChars) {
|
|
740
|
+
return value;
|
|
741
|
+
}
|
|
742
|
+
return `[...sanitized transcript truncated to last ${maxChars} characters...]\n${value.slice(-maxChars)}`;
|
|
743
|
+
}
|
|
744
|
+
function isRecord(value) {
|
|
745
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
746
|
+
}
|
|
747
|
+
//# sourceMappingURL=codex-app-server.js.map
|