freecodego 0.1.6-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/LICENSE +23 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +85 -0
  4. package/README.zh.md +85 -0
  5. package/cordis.patch.yml +56 -0
  6. package/dist/agent-team.js +16296 -0
  7. package/dist/assets/engineering/skills/ask-matt/PHASE-BOUNDARIES.md +55 -0
  8. package/dist/assets/engineering/skills/ask-matt/SKILL.md +89 -0
  9. package/dist/assets/engineering/skills/ask-matt/agents/openai.yaml +5 -0
  10. package/dist/assets/engineering/skills/code-review/SKILL.md +87 -0
  11. package/dist/assets/engineering/skills/code-review/agents/openai.yaml +3 -0
  12. package/dist/assets/engineering/skills/codebase-design/DEEPENING.md +37 -0
  13. package/dist/assets/engineering/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  14. package/dist/assets/engineering/skills/codebase-design/SKILL.md +114 -0
  15. package/dist/assets/engineering/skills/codebase-design/agents/openai.yaml +3 -0
  16. package/dist/assets/engineering/skills/domain-modeling/ADR-FORMAT.md +47 -0
  17. package/dist/assets/engineering/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  18. package/dist/assets/engineering/skills/domain-modeling/SKILL.md +74 -0
  19. package/dist/assets/engineering/skills/domain-modeling/agents/openai.yaml +3 -0
  20. package/dist/assets/engineering/skills/engineering-code-review/SKILL.md +13 -0
  21. package/dist/assets/engineering/skills/engineering-context-control/SKILL.md +13 -0
  22. package/dist/assets/engineering/skills/engineering-release-readiness/SKILL.md +13 -0
  23. package/dist/assets/engineering/skills/engineering-security-review/SKILL.md +13 -0
  24. package/dist/assets/engineering/skills/engineering-silent-failure/SKILL.md +13 -0
  25. package/dist/assets/engineering/skills/engineering-spec-mining/SKILL.md +13 -0
  26. package/dist/assets/engineering/skills/engineering-tdd/SKILL.md +13 -0
  27. package/dist/assets/engineering/skills/grill-with-docs/SKILL.md +7 -0
  28. package/dist/assets/engineering/skills/grill-with-docs/agents/openai.yaml +5 -0
  29. package/dist/assets/engineering/skills/implement/SKILL.md +15 -0
  30. package/dist/assets/engineering/skills/implement/agents/openai.yaml +5 -0
  31. package/dist/assets/engineering/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
  32. package/dist/assets/engineering/skills/improve-codebase-architecture/SKILL.md +71 -0
  33. package/dist/assets/engineering/skills/improve-codebase-architecture/agents/openai.yaml +5 -0
  34. package/dist/assets/engineering/skills/prototype/LOGIC.md +67 -0
  35. package/dist/assets/engineering/skills/prototype/SKILL.md +26 -0
  36. package/dist/assets/engineering/skills/prototype/UI.md +112 -0
  37. package/dist/assets/engineering/skills/prototype/agents/openai.yaml +3 -0
  38. package/dist/assets/engineering/skills/resolving-merge-conflicts/SKILL.md +14 -0
  39. package/dist/assets/engineering/skills/resolving-merge-conflicts/agents/openai.yaml +3 -0
  40. package/dist/assets/engineering/skills/setup-matt-pocock-skills/SKILL.md +116 -0
  41. package/dist/assets/engineering/skills/setup-matt-pocock-skills/agents/openai.yaml +5 -0
  42. package/dist/assets/engineering/skills/setup-matt-pocock-skills/domain.md +51 -0
  43. package/dist/assets/engineering/skills/setup-matt-pocock-skills/issue-tracker-github.md +45 -0
  44. package/dist/assets/engineering/skills/setup-matt-pocock-skills/issue-tracker-gitlab.md +46 -0
  45. package/dist/assets/engineering/skills/setup-matt-pocock-skills/issue-tracker-local.md +30 -0
  46. package/dist/assets/engineering/skills/setup-matt-pocock-skills/triage-labels.md +15 -0
  47. package/dist/assets/engineering/skills/to-spec/SKILL.md +75 -0
  48. package/dist/assets/engineering/skills/to-spec/agents/openai.yaml +5 -0
  49. package/dist/assets/engineering/skills/to-tickets/SKILL.md +105 -0
  50. package/dist/assets/engineering/skills/to-tickets/agents/openai.yaml +5 -0
  51. package/dist/assets/engineering/skills/triage/AGENT-BRIEF.md +207 -0
  52. package/dist/assets/engineering/skills/triage/OUT-OF-SCOPE.md +105 -0
  53. package/dist/assets/engineering/skills/triage/SKILL.md +112 -0
  54. package/dist/assets/engineering/skills/triage/agents/openai.yaml +5 -0
  55. package/dist/assets/engineering/skills/wayfinder/SKILL.md +128 -0
  56. package/dist/assets/engineering/skills/wayfinder/agents/openai.yaml +5 -0
  57. package/dist/assets/engineering/skills/wizard/SKILL.md +44 -0
  58. package/dist/assets/engineering/skills/wizard/agents/openai.yaml +3 -0
  59. package/dist/assets/engineering/skills/wizard/template.sh +204 -0
  60. package/dist/assets/engineering/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
  61. package/dist/assets/engineering/skills/writing-for-agents/SKILL.md +81 -0
  62. package/dist/assets/engineering/skills/writing-for-agents/agents/openai.yaml +3 -0
  63. package/dist/assets/engineering/skills-starter/engineering-debug/SKILL.md +13 -0
  64. package/dist/assets/engineering/skills-starter/engineering-plan/SKILL.md +26 -0
  65. package/dist/assets/engineering/skills-starter/engineering-search-first/SKILL.md +13 -0
  66. package/dist/assets/engineering/skills-starter/engineering-verification/SKILL.md +67 -0
  67. package/dist/assets/engineering/skills-starter/grill-me/SKILL.md +7 -0
  68. package/dist/assets/engineering/skills-starter/grill-me/agents/openai.yaml +5 -0
  69. package/dist/assets/engineering/skills-starter/grilling/SKILL.md +28 -0
  70. package/dist/assets/engineering/skills-starter/grilling/agents/openai.yaml +3 -0
  71. package/dist/assets/engineering/skills-starter/handoff/SKILL.md +16 -0
  72. package/dist/assets/engineering/skills-starter/handoff/agents/openai.yaml +5 -0
  73. package/dist/assets/engineering/skills-starter/prompt-techniques/SKILL.md +84 -0
  74. package/dist/assets/engineering/skills-starter/to-questionnaire/SKILL.md +54 -0
  75. package/dist/assets/engineering/skills-starter/to-questionnaire/agents/openai.yaml +5 -0
  76. package/dist/assets/engineering/skills-starter/wait-what/SKILL.md +7 -0
  77. package/dist/assets/engineering/skills-starter/wait-what/agents/openai.yaml +5 -0
  78. package/dist/assets/engineering/skills-superpowers/brainstorming/SKILL.md +240 -0
  79. package/dist/assets/engineering/skills-superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
  80. package/dist/assets/engineering/skills-superpowers/dispatching-parallel-agents/SKILL.md +167 -0
  81. package/dist/assets/engineering/skills-superpowers/executing-plans/SKILL.md +64 -0
  82. package/dist/assets/engineering/skills-superpowers/finishing-a-development-branch/SKILL.md +225 -0
  83. package/dist/assets/engineering/skills-superpowers/receiving-code-review/SKILL.md +205 -0
  84. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/SKILL.md +567 -0
  85. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/final-reviewer-prompt.md +181 -0
  86. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/implementer-prompt.md +154 -0
  87. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/re-review-prompt.md +115 -0
  88. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/scripts/review-package +46 -0
  89. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/scripts/sdd-workspace +40 -0
  90. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/scripts/task-brief +41 -0
  91. package/dist/assets/engineering/skills-superpowers/subagent-driven-development/task-reviewer-prompt.md +207 -0
  92. package/dist/assets/engineering/skills-superpowers/using-git-worktrees/SKILL.md +167 -0
  93. package/dist/assets/engineering/skills-superpowers/writing-plans/SKILL.md +171 -0
  94. package/dist/assets/engineering/skills-superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
  95. package/dist/assets/presets/augmentcode/agent.cordis.yml +238 -0
  96. package/dist/assets/presets/augmentcode/preset.yml +4 -0
  97. package/dist/assets/presets/freecodego/agent.cordis.yml +220 -0
  98. package/dist/assets/presets/freecodego/preset.yml +4 -0
  99. package/dist/auto-review.js +429 -0
  100. package/dist/bootstrap.js +65223 -0
  101. package/dist/client.cjs +39382 -0
  102. package/dist/schedule.js +15846 -0
  103. package/dist/session-events.js +10 -0
  104. package/dist/tool-agent-team.js +16825 -0
  105. package/dist/workers/codex-worker.js +1127 -0
  106. package/package.json +113 -0
@@ -0,0 +1,1127 @@
1
+ // packages/freecodego/runtime-codex/lib/worker.mjs
2
+ import { access, copyFile, mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { spawn } from "node:child_process";
5
+ import { createInterface } from "node:readline";
6
+ import { randomBytes, randomUUID } from "node:crypto";
7
+ import { createServer } from "node:http";
8
+ import { effectiveSandboxMode, redactSecrets } from "@deepseek-ai/dsh-freecodego-native-runtime-protocol";
9
+ var MAX_WORKER_FRAME_BYTES = 1e6;
10
+ var MAX_TOOL_RESULT_TEXT_BYTES = 9e5;
11
+ var FRAME_TOO_LARGE = "FRAME_TOO_LARGE";
12
+ function bytesOf(text) {
13
+ return Buffer.byteLength(text, "utf8");
14
+ }
15
+ function boundToolResultText(text, budget = MAX_TOOL_RESULT_TEXT_BYTES) {
16
+ const limit = Number.isFinite(budget) && budget > 0 ? Math.max(256, Math.floor(budget)) : MAX_TOOL_RESULT_TEXT_BYTES;
17
+ if (bytesOf(text) <= limit) return text;
18
+ const marker = "\n\n\u2026 [result truncated: this output exceeded the transport limit; ask for a narrower range, a page, or a filter to see the rest]";
19
+ const markerBytes = bytesOf(marker);
20
+ let kept = "";
21
+ let used = 0;
22
+ for (const character of text) {
23
+ const size = bytesOf(character);
24
+ if (used + size + markerBytes > limit) break;
25
+ kept += character;
26
+ used += size;
27
+ }
28
+ return `${kept}${marker}`;
29
+ }
30
+ function workerFrame(message, maxBytes = MAX_WORKER_FRAME_BYTES) {
31
+ const ceiling = Number.isFinite(maxBytes) && maxBytes > 0 ? Math.max(256, Math.floor(maxBytes)) : MAX_WORKER_FRAME_BYTES;
32
+ const line = `${JSON.stringify(message) ?? "null"}
33
+ `;
34
+ const originalBytes = bytesOf(line);
35
+ if (originalBytes <= ceiling) return {
36
+ line,
37
+ replaced: false,
38
+ originalBytes
39
+ };
40
+ const id = message?.id;
41
+ if (typeof id === "string") {
42
+ const reply2 = `${JSON.stringify({
43
+ id,
44
+ error: {
45
+ code: FRAME_TOO_LARGE,
46
+ message: `this reply is ${String(originalBytes)} bytes and the worker transport carries at most ${String(ceiling)}; ask for a narrower range, a page, or a filter and retry`
47
+ }
48
+ })}
49
+ `;
50
+ if (bytesOf(reply2) <= ceiling) return {
51
+ line: reply2,
52
+ replaced: true,
53
+ originalBytes
54
+ };
55
+ return {
56
+ line: `${JSON.stringify({
57
+ id,
58
+ error: {
59
+ code: FRAME_TOO_LARGE,
60
+ message: "the reply exceeded the frame ceiling"
61
+ }
62
+ })}
63
+ `,
64
+ replaced: true,
65
+ originalBytes
66
+ };
67
+ }
68
+ const method = message?.method;
69
+ const notice = `${JSON.stringify({
70
+ ...typeof method === "string" ? { method } : {},
71
+ params: {
72
+ truncated: true,
73
+ originalBytes,
74
+ reason: `this frame was ${String(originalBytes)} bytes, over the ${String(ceiling)}-byte ceiling this worker writes within, so its payload was dropped`
75
+ }
76
+ })}
77
+ `;
78
+ if (bytesOf(notice) <= ceiling) return {
79
+ line: notice,
80
+ replaced: true,
81
+ originalBytes
82
+ };
83
+ return {
84
+ line: `${JSON.stringify({ params: {
85
+ truncated: true,
86
+ originalBytes
87
+ } })}
88
+ `,
89
+ replaced: true,
90
+ originalBytes
91
+ };
92
+ }
93
+ function record$2(value) {
94
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
95
+ }
96
+ function stringify(value) {
97
+ return typeof value === "string" ? value : JSON.stringify(value ?? null);
98
+ }
99
+ function mcpToolResult(value) {
100
+ const result = record$2(value);
101
+ const content = (Array.isArray(result.content) ? result.content : []).flatMap((block) => projectBlock(block)).map((block) => ({
102
+ type: "text",
103
+ text: boundToolResultText(block.text)
104
+ }));
105
+ return {
106
+ content: content.length === 0 ? [{
107
+ type: "text",
108
+ text: boundToolResultText(stringify(value))
109
+ }] : content,
110
+ ...isFailure(result) ? { isError: true } : {}
111
+ };
112
+ }
113
+ function isFailure(result) {
114
+ const error = result.error;
115
+ if (typeof error === "string" ? error !== "" : error !== void 0 && error !== null) return true;
116
+ const flag = result.isError;
117
+ if (flag === void 0 || flag === null || flag === false || flag === 0 || flag === "") return false;
118
+ return true;
119
+ }
120
+ function projectBlock(block) {
121
+ const candidate = record$2(block);
122
+ if (candidate.type === "text" && typeof candidate.text === "string") return [{
123
+ type: "text",
124
+ text: candidate.text
125
+ }];
126
+ if (candidate.type === "image") {
127
+ const attachment = record$2(candidate.attachment);
128
+ return [{
129
+ type: "text",
130
+ text: typeof attachment.attachmentId === "string" ? `Generated image attachment: ${attachment.attachmentId}` : "Generated image (unavailable to this transport)"
131
+ }];
132
+ }
133
+ try {
134
+ return [{
135
+ type: "text",
136
+ text: JSON.stringify(block)
137
+ }];
138
+ } catch {
139
+ return [{
140
+ type: "text",
141
+ text: String(block)
142
+ }];
143
+ }
144
+ }
145
+ var ACCEPT_DECLINE_METHODS = /* @__PURE__ */ new Set(["item/commandExecution/requestApproval", "item/fileChange/requestApproval"]);
146
+ var APPROVED_DENIED_METHODS = /* @__PURE__ */ new Set(["applyPatchApproval", "execCommandApproval"]);
147
+ var PERMISSIONS_METHOD = "item/permissions/requestApproval";
148
+ function isApprovalMethod(method) {
149
+ return ACCEPT_DECLINE_METHODS.has(method) || APPROVED_DENIED_METHODS.has(method) || method === PERMISSIONS_METHOD;
150
+ }
151
+ function record$1(value) {
152
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
153
+ }
154
+ function nonEmptyString(value) {
155
+ return typeof value === "string" && value !== "" ? value : void 0;
156
+ }
157
+ function approvalResponse(method, response, params) {
158
+ const outcome = record$1(response);
159
+ const approved = outcome.type === "approved";
160
+ if (ACCEPT_DECLINE_METHODS.has(method)) return { decision: approved ? "accept" : "decline" };
161
+ if (APPROVED_DENIED_METHODS.has(method)) {
162
+ if (approved) return { decision: "approved" };
163
+ return { decision: { denied: { rejection: nonEmptyString(outcome.message) ?? "User denied this request" } } };
164
+ }
165
+ if (method === PERMISSIONS_METHOD) {
166
+ const requested = record$1(record$1(params).permissions);
167
+ return approved ? {
168
+ permissions: requested,
169
+ scope: "turn"
170
+ } : { permissions: {} };
171
+ }
172
+ return response;
173
+ }
174
+ function record(value) {
175
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
176
+ }
177
+ function questionResponse(answer) {
178
+ const entries = record(answer).answers;
179
+ const answers = {};
180
+ if (!Array.isArray(entries)) return { answers };
181
+ for (const entry of entries) {
182
+ const item = record(entry);
183
+ const id = typeof item.id === "string" ? item.id : "";
184
+ if (id === "") continue;
185
+ const selected = Array.isArray(item.selected) ? item.selected.filter((value) => typeof value === "string") : [];
186
+ const custom = typeof item.custom === "string" && item.custom !== "" ? [item.custom] : [];
187
+ const collected = [...selected, ...custom];
188
+ answers[id] = { answers: [...answers[id]?.answers ?? [], ...collected] };
189
+ }
190
+ return { answers };
191
+ }
192
+ function codexThreadSandboxParams(fields) {
193
+ const mode = effectiveSandboxMode(fields);
194
+ if (mode === void 0) return {};
195
+ if (mode !== "read-only") return { sandbox: mode };
196
+ return fields.readOnly === true ? {
197
+ sandbox: "read-only",
198
+ approvalPolicy: "never"
199
+ } : {
200
+ sandbox: "read-only",
201
+ approvalPolicy: "on-request"
202
+ };
203
+ }
204
+ function harnessMcpRoute(capabilities2, name) {
205
+ if (name === "freecodego_skill_discover" || name === "freecodego_skill_load") {
206
+ if (!capabilities2.skillEnabled) throw new Error(`Harness tool "${name}" is unavailable`);
207
+ return {
208
+ bridge: "skill",
209
+ op: name === "freecodego_skill_discover" ? "list" : "load"
210
+ };
211
+ }
212
+ if (capabilities2.mcpEnabled && capabilities2.mcpTools.some((tool) => tool.name === name)) return {
213
+ bridge: "mcp",
214
+ op: "execute"
215
+ };
216
+ if (!capabilities2.harnessTools.some((tool) => tool.name === name)) throw new Error(`Harness tool "${name}" is unavailable`);
217
+ return {
218
+ bridge: "tool",
219
+ op: "execute"
220
+ };
221
+ }
222
+ var sequence = 0;
223
+ var rpcId = 0;
224
+ var child;
225
+ var threadId;
226
+ var turnId;
227
+ var harnessSessionId;
228
+ var modelId;
229
+ var modelProvider;
230
+ var selectedProvider;
231
+ var stderr = "";
232
+ var pending = /* @__PURE__ */ new Map();
233
+ var approvals = /* @__PURE__ */ new Map();
234
+ var questions = /* @__PURE__ */ new Map();
235
+ var pendingBridges = /* @__PURE__ */ new Map();
236
+ var MANAGED_MCP_START = "# freecodego-managed-mcp:start";
237
+ var MANAGED_MCP_END = "# freecodego-managed-mcp:end";
238
+ var DISPOSE_GRACE_MS = 5e3;
239
+ var capabilities = {
240
+ mcpEnabled: false,
241
+ skillEnabled: false,
242
+ skillRoots: [],
243
+ mcpTools: [],
244
+ harnessTools: []
245
+ };
246
+ var capabilityEnvironment = {};
247
+ var harnessMcpServer;
248
+ var harnessMcpUrl;
249
+ var harnessMcpToken = randomBytes(32).toString("base64url");
250
+ function explicitModel() {
251
+ return modelId === void 0 || modelId === "" || modelId === "codex-auto" ? void 0 : modelId;
252
+ }
253
+ function usesOpenAiBridge() {
254
+ return process.env.FREECODEGO_CODEX_PROVIDER_OVERRIDE === "openai";
255
+ }
256
+ function appServerProvider() {
257
+ return usesOpenAiBridge() ? "openai" : void 0;
258
+ }
259
+ function appServerModel(reasoningEffort) {
260
+ const model = explicitModel();
261
+ if (model === void 0 || !usesOpenAiBridge()) return model;
262
+ const provider = (selectedProvider ?? "freecodego").trim();
263
+ return `freecodego-route:${Buffer.from(provider, "utf8").toString("base64url")}.${Buffer.from(model, "utf8").toString("base64url")}${reasoningEffort === "off" || reasoningEffort === "low" || reasoningEffort === "medium" || reasoningEffort === "high" || reasoningEffort === "xhigh" || reasoningEffort === "max" ? `.${reasoningEffort}` : ""}`;
264
+ }
265
+ function selectedReasoningEffort(value) {
266
+ return value === "off" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max" ? value : "high";
267
+ }
268
+ function codexReasoningEffort(value) {
269
+ const effort = selectedReasoningEffort(value);
270
+ return effort === "off" ? "low" : effort;
271
+ }
272
+ function codexTurnStatus(value) {
273
+ const status = asString(value);
274
+ if (status === "completed") return "completed";
275
+ if (status === "interrupted") return "cancelled";
276
+ return "failed";
277
+ }
278
+ function appServerTurnFailure(error) {
279
+ const detail = asRecord(error);
280
+ const message = asString(detail.message) ?? "";
281
+ const additional = asString(detail.additionalDetails) ?? "";
282
+ if (message === "") return additional === "" ? "Codex turn failed" : redactAppServerDetail(additional);
283
+ return redactAppServerDetail(additional === "" || message.includes(additional) ? message : `${message} (${additional})`);
284
+ }
285
+ function codexConfig(systemPrompt, reasoningEffort) {
286
+ return {
287
+ model_reasoning_effort: reasoningEffort,
288
+ model_reasoning_summary: "detailed",
289
+ ...typeof systemPrompt === "string" && systemPrompt.trim() !== "" ? { developer_instructions: systemPrompt } : {}
290
+ };
291
+ }
292
+ function send(message) {
293
+ const frame = workerFrame(message);
294
+ if (frame.replaced) process.stderr.write(`freecodego-codex-worker: replaced an oversized frame (${String(frame.originalBytes)} bytes over the ${String(MAX_WORKER_FRAME_BYTES)}-byte ceiling)
295
+ `);
296
+ process.stdout.write(frame.line);
297
+ }
298
+ function event(method, params = {}) {
299
+ if (threadId === void 0 || harnessSessionId === void 0) return;
300
+ send({
301
+ method,
302
+ params: {
303
+ runtimeSessionId: threadId,
304
+ harnessSessionId,
305
+ sequence: ++sequence,
306
+ ...params
307
+ }
308
+ });
309
+ }
310
+ function request(method, params) {
311
+ if (child === void 0) return Promise.reject(/* @__PURE__ */ new Error("Codex app-server is not running"));
312
+ const id = ++rpcId;
313
+ const promise = new Promise((resolve, reject) => pending.set(id, {
314
+ resolve,
315
+ reject
316
+ }));
317
+ try {
318
+ child.stdin.write(`${JSON.stringify({
319
+ id,
320
+ method,
321
+ params
322
+ })}
323
+ `);
324
+ } catch (error) {
325
+ pending.delete(id);
326
+ return Promise.reject(error instanceof Error ? error : new Error(String(error)));
327
+ }
328
+ return promise;
329
+ }
330
+ var CODEX_TURN_IDLE_MS = 15 * 6e4;
331
+ function turnIdleMs() {
332
+ const override = Number(process.env.FREECODEGO_CODEX_TURN_IDLE_MS);
333
+ return Number.isFinite(override) && override > 0 ? override : CODEX_TURN_IDLE_MS;
334
+ }
335
+ function armTurnWatchdog() {
336
+ if (turnWatchdog !== void 0) clearTimeout(turnWatchdog);
337
+ turnWatchdog = setTimeout(() => {
338
+ turnWatchdog = void 0;
339
+ const hungTurn = turnId;
340
+ turnId = void 0;
341
+ const message = "Codex turn produced no events within the watchdog window";
342
+ clearPendingBridges(message);
343
+ event("session/completed", {
344
+ status: "failed",
345
+ message
346
+ });
347
+ if (hungTurn !== void 0 && threadId !== void 0) request("turn/interrupt", {
348
+ threadId,
349
+ turnId: hungTurn
350
+ }).catch(() => void 0);
351
+ }, turnIdleMs());
352
+ }
353
+ function disarmTurnWatchdog() {
354
+ if (turnWatchdog !== void 0) {
355
+ clearTimeout(turnWatchdog);
356
+ turnWatchdog = void 0;
357
+ }
358
+ }
359
+ function refreshTurnWatchdog() {
360
+ if (turnWatchdog === void 0 && turnId === void 0) return;
361
+ armTurnWatchdog();
362
+ }
363
+ var turnWatchdog;
364
+ function reply(id, result, error) {
365
+ send(error === void 0 ? {
366
+ id,
367
+ result
368
+ } : {
369
+ id,
370
+ error: {
371
+ code: "CODEX_WORKER",
372
+ message: redactAppServerDetail(error.message)
373
+ }
374
+ });
375
+ }
376
+ function start() {
377
+ if (child !== void 0) return child;
378
+ const executable = process.env.FREECODEGO_CODEX_APP_SERVER;
379
+ if (executable === void 0 || executable.length === 0) throw new Error("FREECODEGO_CODEX_APP_SERVER is required and must be set from a verified runtime manifest");
380
+ const args = process.env.FREECODEGO_CODEX_APP_SERVER_ARGS?.split("").filter(Boolean) ?? [];
381
+ const codexHome = process.env.FREECODEGO_CODEX_HOME;
382
+ if (codexHome === void 0 || codexHome.length === 0) throw new Error("FREECODEGO_CODEX_HOME is required and must be a plugin-owned state directory");
383
+ child = spawn(executable, args, {
384
+ cwd: process.cwd(),
385
+ env: {
386
+ PATH: process.env.PATH ?? "",
387
+ CODEX_HOME: codexHome,
388
+ ...process.env.OPENAI_API_KEY === void 0 ? {} : { OPENAI_API_KEY: process.env.OPENAI_API_KEY },
389
+ ...process.env.OPENAI_BASE_URL === void 0 ? {} : { OPENAI_BASE_URL: process.env.OPENAI_BASE_URL },
390
+ ...capabilityEnvironment
391
+ },
392
+ windowsHide: true,
393
+ stdio: [
394
+ "pipe",
395
+ "pipe",
396
+ "pipe"
397
+ ]
398
+ });
399
+ createInterface({ input: child.stdout }).on("line", (line) => {
400
+ const trimmed = line.trim();
401
+ if (trimmed === "" || !(trimmed.startsWith("{") || trimmed.startsWith("["))) {
402
+ if (trimmed !== "") stderr = `${stderr}${trimmed}
403
+ `.slice(-16 * 1024);
404
+ return;
405
+ }
406
+ try {
407
+ receiveRpc(JSON.parse(trimmed));
408
+ } catch (error) {
409
+ stderr = `${stderr}unparsable stdout frame: ${String(error)}
410
+ `.slice(-16 * 1024);
411
+ }
412
+ });
413
+ child.stderr.setEncoding("utf8");
414
+ child.stderr.on("data", (chunk) => {
415
+ stderr = `${stderr}${chunk}`.slice(-16 * 1024);
416
+ });
417
+ child.stdin.on("error", () => void 0);
418
+ const instance = child;
419
+ child.on("close", () => {
420
+ if (child !== instance) return;
421
+ disarmTurnWatchdog();
422
+ const detail = redactAppServerDetail(stderr.trim());
423
+ const error = /* @__PURE__ */ new Error(detail === "" ? "Codex app-server exited" : `Codex app-server exited: ${detail}`);
424
+ for (const entry of pending.values()) entry.reject(error);
425
+ pending.clear();
426
+ clearPendingBridges("Codex app-server exited");
427
+ child = void 0;
428
+ event("session/failed", { message: error.message });
429
+ });
430
+ return child;
431
+ }
432
+ async function prepareCodexHome() {
433
+ const target = process.env.FREECODEGO_CODEX_HOME;
434
+ if (!target) return;
435
+ await mkdir(target, { recursive: true });
436
+ if (process.env.FREECODEGO_CODEX_SYNC_GLOBAL_AUTH === "1") {
437
+ const home = process.env.CODEX_GLOBAL_HOME?.trim() || join(process.env.USERPROFILE || process.env.HOME || "", ".codex");
438
+ if (home && home !== target) for (const name of ["auth.json"]) {
439
+ const source = join(home, name);
440
+ const destination = join(target, name);
441
+ try {
442
+ await access(destination);
443
+ continue;
444
+ } catch {
445
+ }
446
+ try {
447
+ await copyFile(source, destination);
448
+ } catch {
449
+ }
450
+ }
451
+ }
452
+ await ensureHarnessMcpServer();
453
+ await writeManagedMcpConfig(target);
454
+ }
455
+ async function writeManagedMcpConfig(home) {
456
+ const target = join(home, "config.toml");
457
+ let existing = "";
458
+ try {
459
+ existing = await readFile(target, "utf8");
460
+ } catch {
461
+ }
462
+ const start2 = existing.indexOf(MANAGED_MCP_START);
463
+ const end = existing.indexOf(MANAGED_MCP_END);
464
+ const hasManagedSegment = start2 >= 0 && end >= start2;
465
+ if (hasManagedSegment) existing = `${existing.slice(0, start2)}${existing.slice(end + 28)}`.trimEnd();
466
+ existing = stripMcpServerTables(existing);
467
+ const rendered = renderManagedMcpConfig();
468
+ if (rendered === "" && !hasManagedSegment) return;
469
+ const temporary = `${target}.tmp-${process.pid}`;
470
+ await writeFile(temporary, `${existing}${existing === "" || rendered === "" ? "" : "\n\n"}${rendered}${rendered === "" ? "" : "\n"}`, "utf8");
471
+ await rename(temporary, target);
472
+ }
473
+ function renderManagedMcpConfig() {
474
+ capabilityEnvironment = {};
475
+ if (harnessMcpUrl === void 0) return "";
476
+ capabilityEnvironment.FREECODEGO_CODEX_HARNESS_MCP_TOKEN = harnessMcpToken;
477
+ return [
478
+ MANAGED_MCP_START,
479
+ "[mcp_servers.freecodego-harness]",
480
+ `url = ${tomlString(harnessMcpUrl)}`,
481
+ 'bearer_token_env_var = "FREECODEGO_CODEX_HARNESS_MCP_TOKEN"',
482
+ MANAGED_MCP_END
483
+ ].join("\n\n");
484
+ }
485
+ function tomlString(value) {
486
+ return JSON.stringify(value);
487
+ }
488
+ function stripMcpServerTables(value) {
489
+ let inMcpSection = false;
490
+ const retained = [];
491
+ for (const line of value.split(/\r?\n/)) {
492
+ const header = line.match(/^\s*\[\[?\s*([^\]]+?)\s*\]\]?\s*(?:#.*)?$/);
493
+ if (header !== null) {
494
+ inMcpSection = /^mcp_servers(?:\s*\.|$)/.test((header[1] ?? "").trim());
495
+ if (!inMcpSection) retained.push(line);
496
+ continue;
497
+ }
498
+ if (!inMcpSection && !/^\s*mcp_servers\s*(?:\.|=)/.test(line)) retained.push(line);
499
+ }
500
+ return retained.join("\n").trimEnd();
501
+ }
502
+ function readCapabilities(value) {
503
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return {
504
+ mcpEnabled: false,
505
+ skillEnabled: false,
506
+ skillRoots: [],
507
+ mcpTools: [],
508
+ harnessTools: []
509
+ };
510
+ const input = value;
511
+ return {
512
+ mcpEnabled: input.mcpEnabled === true,
513
+ skillEnabled: input.skillEnabled === true,
514
+ skillRoots: input.skillEnabled === true ? strings(input.skillRoots) : [],
515
+ mcpTools: input.mcpEnabled === true ? readHarnessTools(input.mcpTools) : [],
516
+ harnessTools: readHarnessTools(input.harnessTools)
517
+ };
518
+ }
519
+ function readHarnessTools(value) {
520
+ if (!Array.isArray(value)) return [];
521
+ const tools = [];
522
+ const names = /* @__PURE__ */ new Set();
523
+ for (const item of value) {
524
+ if (typeof item !== "object" || item === null || Array.isArray(item)) continue;
525
+ const tool = item;
526
+ if (typeof tool.name !== "string" || !/^[A-Za-z0-9_-]{1,96}$/.test(tool.name) || names.has(tool.name)) continue;
527
+ names.add(tool.name);
528
+ tools.push({
529
+ name: tool.name,
530
+ description: typeof tool.description === "string" ? tool.description : tool.name,
531
+ parameters: typeof tool.parameters === "object" && tool.parameters !== null && !Array.isArray(tool.parameters) ? tool.parameters : {
532
+ type: "object",
533
+ properties: {}
534
+ }
535
+ });
536
+ }
537
+ return tools;
538
+ }
539
+ function strings(value) {
540
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.trim() !== "") : [];
541
+ }
542
+ function asRecord(value) {
543
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : {};
544
+ }
545
+ function asString(value) {
546
+ return typeof value === "string" ? value : void 0;
547
+ }
548
+ function asMessage(value) {
549
+ const direct = asString(value);
550
+ if (direct !== void 0) return direct;
551
+ if (value === void 0 || value === null) return "";
552
+ try {
553
+ return JSON.stringify(value);
554
+ } catch {
555
+ return "";
556
+ }
557
+ }
558
+ async function configureCodexSkills() {
559
+ await request("skills/extraRoots/set", { extraRoots: capabilities.skillEnabled ? capabilities.skillRoots : [] });
560
+ }
561
+ async function ensureHarnessMcpServer() {
562
+ if (harnessMcpUrl !== void 0 || capabilities.harnessTools.length === 0 && capabilities.mcpTools.length === 0 && !capabilities.skillEnabled) return;
563
+ const server = createServer((request2, response) => {
564
+ handleHarnessMcpRequest(request2, response).catch((error) => {
565
+ mcpError(response, void 0, error);
566
+ });
567
+ });
568
+ await new Promise((resolve, reject) => {
569
+ const onError = (error) => {
570
+ server.close(() => void 0);
571
+ reject(error);
572
+ };
573
+ server.once("error", onError);
574
+ server.listen(0, "127.0.0.1", () => {
575
+ server.off("error", onError);
576
+ const address = server.address();
577
+ if (address === null || typeof address === "string") {
578
+ server.close(() => void 0);
579
+ reject(/* @__PURE__ */ new Error("Codex Harness MCP server has no TCP address"));
580
+ return;
581
+ }
582
+ harnessMcpServer = server;
583
+ harnessMcpUrl = `http://127.0.0.1:${address.port}/mcp`;
584
+ resolve();
585
+ });
586
+ });
587
+ }
588
+ async function handleHarnessMcpRequest(request2, response) {
589
+ if (request2.method !== "POST" || request2.url !== "/mcp" || request2.headers.authorization !== `Bearer ${harnessMcpToken}`) {
590
+ response.writeHead(404);
591
+ response.end();
592
+ return;
593
+ }
594
+ const body = await readHttpBody(request2);
595
+ const message = JSON.parse(body);
596
+ if (message.id === void 0) {
597
+ response.writeHead(202);
598
+ response.end();
599
+ return;
600
+ }
601
+ if (message.method === "initialize") {
602
+ mcpResult(response, message.id, {
603
+ protocolVersion: "2024-11-05",
604
+ capabilities: { tools: {} },
605
+ serverInfo: {
606
+ name: "freecodego-harness",
607
+ version: "1.0.0"
608
+ }
609
+ });
610
+ return;
611
+ }
612
+ if (message.method === "tools/list") {
613
+ mcpResult(response, message.id, { tools: harnessMcpTools() });
614
+ return;
615
+ }
616
+ if (message.method === "tools/call") {
617
+ const params = typeof message.params === "object" && message.params !== null && !Array.isArray(message.params) ? message.params : {};
618
+ const name = typeof params.name === "string" ? params.name : "";
619
+ const args = typeof params.arguments === "object" && params.arguments !== null && !Array.isArray(params.arguments) ? params.arguments : {};
620
+ try {
621
+ const result = await invokeHarnessMcpTool(name, args);
622
+ mcpResult(response, message.id, mcpToolResult(result));
623
+ return;
624
+ } catch (error) {
625
+ mcpResult(response, message.id, {
626
+ content: [{
627
+ type: "text",
628
+ text: redactAppServerDetail(error instanceof Error ? error.message : String(error))
629
+ }],
630
+ isError: true
631
+ });
632
+ return;
633
+ }
634
+ }
635
+ mcpError(response, message.id, /* @__PURE__ */ new Error(`unsupported Harness MCP method ${String(message.method)}`), -32601);
636
+ }
637
+ function harnessMcpTools() {
638
+ const mcpTools = capabilities.mcpEnabled ? capabilities.mcpTools : [];
639
+ const mcpNames = new Set(mcpTools.map((tool) => tool.name));
640
+ return [
641
+ ...capabilities.harnessTools.filter((tool) => !mcpNames.has(tool.name)).map((tool) => ({
642
+ name: tool.name,
643
+ description: tool.description,
644
+ inputSchema: tool.parameters
645
+ })),
646
+ ...mcpTools.map((tool) => ({
647
+ name: tool.name,
648
+ description: tool.description,
649
+ inputSchema: tool.parameters
650
+ })),
651
+ ...capabilities.skillEnabled ? [{
652
+ name: "freecodego_skill_discover",
653
+ description: "List enabled DeepSeek Harness Skills.",
654
+ inputSchema: {
655
+ type: "object",
656
+ properties: {}
657
+ }
658
+ }, {
659
+ name: "freecodego_skill_load",
660
+ description: "Load one enabled DeepSeek Harness Skill.",
661
+ inputSchema: {
662
+ type: "object",
663
+ properties: { name: { type: "string" } },
664
+ required: ["name"]
665
+ }
666
+ }] : []
667
+ ];
668
+ }
669
+ async function invokeHarnessMcpTool(name, args) {
670
+ const route = harnessMcpRoute(capabilities, name);
671
+ if (route.bridge === "skill") return await bridgeCall("skill", route.op, route.op === "load" ? { name: args.name } : {});
672
+ if (route.bridge === "mcp") return await bridgeCall("mcp", "execute", {
673
+ name,
674
+ arguments: args
675
+ });
676
+ return await bridgeCall("tool", "execute", {
677
+ name,
678
+ arguments: args
679
+ });
680
+ }
681
+ async function bridgeCall(bridge, op, input) {
682
+ const requestId = randomUUID();
683
+ const promise = Promise.withResolvers();
684
+ pendingBridges.set(requestId, promise);
685
+ event("bridge/requested", {
686
+ requestId,
687
+ bridge,
688
+ op,
689
+ input
690
+ });
691
+ return await promise.promise;
692
+ }
693
+ function clearPendingBridges(reason) {
694
+ for (const pending2 of pendingBridges.values()) pending2.reject(new Error(reason));
695
+ pendingBridges.clear();
696
+ }
697
+ function mcpResult(response, id, result) {
698
+ response.writeHead(200, { "content-type": "application/json" });
699
+ response.end(JSON.stringify({
700
+ jsonrpc: "2.0",
701
+ id: id ?? null,
702
+ result
703
+ }));
704
+ }
705
+ function mcpError(response, id, error, code = -32e3) {
706
+ response.writeHead(200, { "content-type": "application/json" });
707
+ response.end(JSON.stringify({
708
+ jsonrpc: "2.0",
709
+ id: id ?? null,
710
+ error: {
711
+ code,
712
+ message: redactAppServerDetail(error instanceof Error ? error.message : String(error))
713
+ }
714
+ }));
715
+ }
716
+ function readHttpBody(request2) {
717
+ return new Promise((resolve, reject) => {
718
+ const chunks = [];
719
+ request2.on("data", (chunk) => {
720
+ chunks.push(Buffer.from(chunk));
721
+ });
722
+ request2.on("end", () => {
723
+ resolve(Buffer.concat(chunks).toString("utf8"));
724
+ });
725
+ request2.on("error", reject);
726
+ });
727
+ }
728
+ function receiveRpc(message) {
729
+ if (message.method === "turn/completed") disarmTurnWatchdog();
730
+ else refreshTurnWatchdog();
731
+ if (message.id !== void 0 && (message.result !== void 0 || message.error !== void 0)) {
732
+ const call = pending.get(message.id);
733
+ if (call === void 0) return;
734
+ pending.delete(message.id);
735
+ if (message.error === void 0) call.resolve(message.result);
736
+ else call.reject(new Error(redactAppServerDetail(message.error.message)));
737
+ return;
738
+ }
739
+ if (message.id !== void 0 && message.method !== void 0) {
740
+ const id = message.id;
741
+ const requestedQuestions = normalizeQuestions(message.params);
742
+ if (requestedQuestions !== void 0) {
743
+ const requestId = `question-${id}`;
744
+ questions.set(requestId, (value) => {
745
+ sendRpc({
746
+ id,
747
+ result: questionResponse(value)
748
+ });
749
+ });
750
+ event("question/requested", {
751
+ requestId,
752
+ request: { questions: requestedQuestions }
753
+ });
754
+ return;
755
+ }
756
+ const method = message.method;
757
+ if (!isApprovalMethod(method)) {
758
+ sendRpc({
759
+ id,
760
+ error: {
761
+ code: -32601,
762
+ message: `FreeCodeGo does not implement the Codex App Server request "${method}"`
763
+ }
764
+ });
765
+ return;
766
+ }
767
+ const approvalId = `approval-${id}`;
768
+ const requestParams = message.params;
769
+ approvals.set(approvalId, (value) => {
770
+ sendRpc({
771
+ id,
772
+ result: approvalResponse(method, value, requestParams)
773
+ });
774
+ });
775
+ event("permission/requested", {
776
+ approvalId,
777
+ method,
778
+ detail: message.params ?? {}
779
+ });
780
+ return;
781
+ }
782
+ const params = asRecord(message.params);
783
+ if (message.method === "item/agentMessage/delta") event("assistant/delta", { text: asString(params.delta) ?? "" });
784
+ else if (message.method === "item/reasoning/textDelta" || message.method === "item/reasoning/summaryTextDelta") event("assistant/reasoning/delta", {
785
+ text: asString(params.delta) ?? asString(params.text) ?? "",
786
+ source: "codex-app-server",
787
+ sourceLabel: "Codex App Server"
788
+ });
789
+ else if (message.method === "item/reasoning/summaryPartAdded") {
790
+ const part = asRecord(params.part);
791
+ const added = asString(part.text) ?? asString(part.content) ?? "";
792
+ if (added !== "") event("assistant/reasoning/delta", {
793
+ text: added,
794
+ source: "codex-app-server",
795
+ sourceLabel: "Codex App Server"
796
+ });
797
+ } else if (message.method === "item/completed" && asRecord(params.item).type === "agentMessage") event("assistant/final", { text: asString(asRecord(params.item).text) ?? "" });
798
+ else if (message.method === "item/completed" && asRecord(params.item).type === "reasoning") {
799
+ const item = asRecord(params.item);
800
+ const completed = typeof item.text === "string" ? item.text : Array.isArray(item.summary) ? item.summary.map((part) => asString(asRecord(part).text) ?? "").join("") : "";
801
+ if (completed !== "") event("assistant/reasoning/final", {
802
+ text: completed,
803
+ source: "codex-app-server",
804
+ sourceLabel: "Codex App Server"
805
+ });
806
+ } else if (message.method === "turn/started") turnId = asString(asRecord(params.turn).id) ?? turnId;
807
+ else if (message.method === "turn/completed") {
808
+ disarmTurnWatchdog();
809
+ clearPendingBridges("Codex turn completed");
810
+ turnId = void 0;
811
+ const status = codexTurnStatus(asRecord(params.turn).status);
812
+ const reason = status === "failed" ? appServerTurnFailure(asRecord(params.turn).error) : void 0;
813
+ event("session/completed", reason === void 0 ? { status } : {
814
+ status,
815
+ message: reason
816
+ });
817
+ } else if (message.method !== void 0) event("tool/progress", {
818
+ method: message.method,
819
+ detail: message.params ?? {}
820
+ });
821
+ }
822
+ function sendRpc(message) {
823
+ child?.stdin.write(`${JSON.stringify(message)}
824
+ `);
825
+ }
826
+ function redactAppServerDetail(value) {
827
+ return redactSecrets(value, [
828
+ process.env.OPENAI_API_KEY,
829
+ harnessMcpToken,
830
+ ...Object.values(capabilityEnvironment)
831
+ ]);
832
+ }
833
+ function killAppServerTree(dying) {
834
+ if (process.platform === "win32" && dying.pid !== void 0) {
835
+ spawn("taskkill", [
836
+ "/pid",
837
+ String(dying.pid),
838
+ "/t",
839
+ "/f"
840
+ ], {
841
+ windowsHide: true,
842
+ stdio: "ignore"
843
+ }).on("error", () => void 0);
844
+ return;
845
+ }
846
+ dying.kill();
847
+ }
848
+ async function stopAppServer(dying) {
849
+ const exited = new Promise((resolve) => {
850
+ if (dying.exitCode !== null || dying.signalCode !== null) {
851
+ resolve();
852
+ return;
853
+ }
854
+ dying.once("close", () => {
855
+ resolve();
856
+ });
857
+ });
858
+ dying.stdin.end();
859
+ const deadline = setTimeout(() => {
860
+ killAppServerTree(dying);
861
+ }, DISPOSE_GRACE_MS);
862
+ try {
863
+ await exited;
864
+ } finally {
865
+ clearTimeout(deadline);
866
+ }
867
+ }
868
+ async function handle(message) {
869
+ if (message.method === "initialize") return {
870
+ protocolAbi: message.params.protocolVersion,
871
+ engines: ["codex"],
872
+ runtimeVersion: "codex-app-server-worker/1"
873
+ };
874
+ if (message.method === "host/configure") {
875
+ capabilities = readCapabilities(message.params);
876
+ if (child !== void 0) {
877
+ await ensureHarnessMcpServer();
878
+ const home = process.env.FREECODEGO_CODEX_HOME;
879
+ if (home !== void 0 && home !== "") await writeManagedMcpConfig(home);
880
+ await configureCodexSkills();
881
+ }
882
+ return null;
883
+ }
884
+ if (message.method === "session/create") {
885
+ await prepareCodexHome();
886
+ start();
887
+ harnessSessionId = asString(message.params.harnessSessionId);
888
+ modelId = asString(message.params.modelId);
889
+ selectedProvider = asString(message.params.provider);
890
+ modelProvider = appServerProvider();
891
+ const selectedEffort = selectedReasoningEffort(message.params.reasoningEffort);
892
+ await request("initialize", {
893
+ clientInfo: {
894
+ name: "DeepSeek Harness",
895
+ version: "1"
896
+ },
897
+ capabilities: {}
898
+ });
899
+ sendRpc({
900
+ method: "initialized",
901
+ params: {}
902
+ });
903
+ await configureCodexSkills();
904
+ const started = asRecord(await request("thread/start", {
905
+ cwd: message.params.workspace,
906
+ ...codexThreadSandboxParams(message.params),
907
+ ...appServerModel(selectedEffort) === void 0 ? {} : { model: appServerModel(selectedEffort) },
908
+ ...modelProvider ? { modelProvider } : {},
909
+ config: codexConfig(message.params.systemPrompt, codexReasoningEffort(selectedEffort))
910
+ }));
911
+ const startedThread = asRecord(started.thread);
912
+ const startedThreadId = startedThread.id;
913
+ if (typeof startedThreadId !== "string" || startedThreadId === "") throw new Error("Codex app-server returned no thread id for thread/start");
914
+ threadId = startedThreadId;
915
+ if (!usesOpenAiBridge()) {
916
+ modelId = asString(started.model) ?? asString(startedThread.model) ?? modelId;
917
+ modelProvider = asString(started.modelProvider) ?? asString(startedThread.modelProvider) ?? modelProvider;
918
+ }
919
+ event("session/started", {
920
+ model: modelId ?? "",
921
+ provider: selectedProvider ?? modelProvider ?? ""
922
+ });
923
+ return {
924
+ runtimeSessionId: threadId,
925
+ modelId,
926
+ provider: selectedProvider ?? modelProvider
927
+ };
928
+ }
929
+ if (message.method === "session/resume") {
930
+ await prepareCodexHome();
931
+ start();
932
+ harnessSessionId = asString(message.params.harnessSessionId);
933
+ modelId = asString(message.params.modelId);
934
+ selectedProvider = asString(message.params.provider);
935
+ modelProvider = appServerProvider();
936
+ const resumeId = message.params.runtimeSessionId;
937
+ if (typeof resumeId !== "string" || resumeId.length === 0) throw new Error("Codex resume requires a persisted runtimeSessionId");
938
+ const selectedEffort = selectedReasoningEffort(message.params.reasoningEffort);
939
+ await request("initialize", {
940
+ clientInfo: {
941
+ name: "DeepSeek Harness",
942
+ version: "1"
943
+ },
944
+ capabilities: {}
945
+ });
946
+ sendRpc({
947
+ method: "initialized",
948
+ params: {}
949
+ });
950
+ await configureCodexSkills();
951
+ const resumed = asRecord(await request("thread/resume", {
952
+ threadId: resumeId,
953
+ cwd: message.params.workspace,
954
+ ...codexThreadSandboxParams(message.params),
955
+ ...appServerModel(selectedEffort) === void 0 ? {} : { model: appServerModel(selectedEffort) },
956
+ ...modelProvider ? { modelProvider } : {},
957
+ config: codexConfig(message.params.systemPrompt, codexReasoningEffort(selectedEffort))
958
+ }));
959
+ const resumedThread = asRecord(resumed.thread);
960
+ threadId = asString(resumedThread.id) ?? resumeId;
961
+ if (!usesOpenAiBridge()) {
962
+ modelId = asString(resumed.model) ?? asString(resumedThread.model) ?? modelId;
963
+ modelProvider = asString(resumed.modelProvider) ?? asString(resumedThread.modelProvider) ?? modelProvider;
964
+ }
965
+ event("session/started", {
966
+ model: modelId ?? "",
967
+ provider: selectedProvider ?? modelProvider ?? ""
968
+ });
969
+ return {
970
+ runtimeSessionId: threadId,
971
+ modelId,
972
+ provider: selectedProvider ?? modelProvider
973
+ };
974
+ }
975
+ if (message.method === "session/prompt") {
976
+ if (!threadId) throw new Error("Codex session is not initialized");
977
+ if (typeof message.params.modelId === "string" && message.params.modelId.trim() !== "") modelId = message.params.modelId;
978
+ if (typeof message.params.provider === "string" && message.params.provider.trim() !== "") selectedProvider = message.params.provider;
979
+ const selectedEffort = selectedReasoningEffort(message.params.reasoningEffort);
980
+ armTurnWatchdog();
981
+ try {
982
+ turnId = asString(asRecord(asRecord(await request("turn/start", {
983
+ threadId,
984
+ input: [{
985
+ type: "text",
986
+ text: message.params.content,
987
+ text_elements: []
988
+ }],
989
+ ...message.params.workspace ? { cwd: message.params.workspace } : {},
990
+ ...appServerModel(selectedEffort) === void 0 ? {} : { model: appServerModel(selectedEffort) },
991
+ effort: codexReasoningEffort(selectedEffort),
992
+ summary: "detailed"
993
+ })).turn).id) ?? turnId;
994
+ } catch (error) {
995
+ disarmTurnWatchdog();
996
+ throw error;
997
+ }
998
+ return null;
999
+ }
1000
+ if (message.method === "session/cancel") {
1001
+ disarmTurnWatchdog();
1002
+ clearPendingBridges("Codex turn cancelled");
1003
+ if (turnId === void 0) {
1004
+ event("session/completed", { status: "cancelled" });
1005
+ return null;
1006
+ }
1007
+ try {
1008
+ await request("turn/interrupt", {
1009
+ threadId,
1010
+ turnId
1011
+ });
1012
+ } catch (error) {
1013
+ event("session/completed", {
1014
+ status: "failed",
1015
+ message: error instanceof Error ? redactAppServerDetail(error.message) : "Codex turn interrupt failed"
1016
+ });
1017
+ return null;
1018
+ }
1019
+ turnId = void 0;
1020
+ disarmTurnWatchdog();
1021
+ event("session/completed", { status: "cancelled" });
1022
+ return null;
1023
+ }
1024
+ if (message.method === "permission/respond") {
1025
+ const requestId = asString(message.params.requestId) ?? "";
1026
+ const resolve = approvals.get(requestId);
1027
+ if (resolve === void 0) throw new Error("unknown Codex approval request");
1028
+ approvals.delete(requestId);
1029
+ resolve(message.params.response);
1030
+ return null;
1031
+ }
1032
+ if (message.method === "question/respond") {
1033
+ const requestId = asString(message.params.requestId) ?? "";
1034
+ const resolve = questions.get(requestId);
1035
+ if (resolve === void 0) throw new Error("unknown Codex question request");
1036
+ questions.delete(requestId);
1037
+ resolve(message.params.response);
1038
+ return null;
1039
+ }
1040
+ if (message.method === "bridge/respond") {
1041
+ const bridgeId = asString(message.params.requestId) ?? "";
1042
+ const pending2 = pendingBridges.get(bridgeId);
1043
+ if (pending2 === void 0) return null;
1044
+ pendingBridges.delete(bridgeId);
1045
+ const failure = message.params.error;
1046
+ if (failure !== void 0 && failure !== null) pending2.reject(new Error(asMessage(failure) || "bridge call failed"));
1047
+ else pending2.resolve(message.params.result);
1048
+ return null;
1049
+ }
1050
+ if (message.method === "session/dispose") {
1051
+ disarmTurnWatchdog();
1052
+ const dying = child;
1053
+ child = void 0;
1054
+ threadId = void 0;
1055
+ harnessSessionId = void 0;
1056
+ approvals.clear();
1057
+ questions.clear();
1058
+ clearPendingBridges("Codex session disposed");
1059
+ const server = harnessMcpServer;
1060
+ harnessMcpServer = void 0;
1061
+ harnessMcpUrl = void 0;
1062
+ if (dying !== void 0) await stopAppServer(dying);
1063
+ await new Promise((resolve) => {
1064
+ if (server === void 0) resolve();
1065
+ else server.close(() => {
1066
+ resolve();
1067
+ });
1068
+ });
1069
+ return null;
1070
+ }
1071
+ throw new Error(`unsupported Codex worker method ${message.method}`);
1072
+ }
1073
+ createInterface({ input: process.stdin }).on("line", (line) => {
1074
+ try {
1075
+ const message = parseHostFrame(line);
1076
+ if (message === void 0) throw new Error("Host frame is not a { id, method, params } request object");
1077
+ handle(message).then((result) => {
1078
+ reply(message.id, result);
1079
+ }, (error) => {
1080
+ reply(message.id, void 0, error instanceof Error ? error : new Error(String(error)));
1081
+ });
1082
+ } catch (error) {
1083
+ send({ error: {
1084
+ code: "INVALID_JSON",
1085
+ message: String(error)
1086
+ } });
1087
+ }
1088
+ });
1089
+ function parseHostFrame(line) {
1090
+ const parsed = JSON.parse(line);
1091
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
1092
+ const frame = parsed;
1093
+ if (typeof frame.id !== "string" || typeof frame.method !== "string") return void 0;
1094
+ const params = typeof frame.params === "object" && frame.params !== null && !Array.isArray(frame.params) ? frame.params : {};
1095
+ return {
1096
+ id: frame.id,
1097
+ method: frame.method,
1098
+ params
1099
+ };
1100
+ }
1101
+ function normalizeQuestions(value) {
1102
+ const candidate = typeof value === "object" && value !== null && !Array.isArray(value) ? value.questions : void 0;
1103
+ if (!Array.isArray(candidate) || candidate.length === 0) return void 0;
1104
+ const out = [];
1105
+ for (const item of candidate) {
1106
+ if (typeof item !== "object" || item === null || Array.isArray(item)) return void 0;
1107
+ const record2 = item;
1108
+ if (typeof record2.question !== "string" || record2.question.trim() === "") return void 0;
1109
+ const options = Array.isArray(record2.options) ? record2.options.flatMap((option) => {
1110
+ if (typeof option !== "object" || option === null || Array.isArray(option)) return [];
1111
+ const value2 = option;
1112
+ return typeof value2.label === "string" && value2.label.trim() !== "" ? [{
1113
+ label: value2.label,
1114
+ ...typeof value2.description === "string" ? { description: value2.description } : {}
1115
+ }] : [];
1116
+ }) : void 0;
1117
+ out.push({
1118
+ id: typeof record2.id === "string" && record2.id.trim() !== "" ? record2.id : `question-${out.length + 1}`,
1119
+ question: record2.question,
1120
+ ...typeof record2.header === "string" ? { header: record2.header } : {},
1121
+ ...typeof record2.detail === "string" ? { detail: record2.detail } : {},
1122
+ ...typeof record2.multiSelect === "boolean" ? { multiSelect: record2.multiSelect } : {},
1123
+ ...options === void 0 || options.length === 0 ? {} : { options }
1124
+ });
1125
+ }
1126
+ return out;
1127
+ }