@gakr-gakr/codex 0.1.0 → 0.1.1

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 (115) hide show
  1. package/dist/client-DNN2uyJW.js +642 -0
  2. package/dist/client-factory-Bu9OClHJ.js +9 -0
  3. package/dist/command-formatters-BpPOTePl.js +520 -0
  4. package/dist/command-handlers-BBs7Vws9.js +1533 -0
  5. package/dist/compact-CDboBy7o.js +329 -0
  6. package/dist/computer-use-DCZB46Sw.js +367 -0
  7. package/dist/config-CLMSw0p2.js +510 -0
  8. package/dist/doctor-contract-api.js +53 -0
  9. package/dist/harness.js +51 -0
  10. package/dist/index.js +1171 -0
  11. package/dist/media-understanding-provider.js +335 -0
  12. package/dist/models-jLA2SIvd.js +110 -0
  13. package/dist/node-cli-sessions-BLRDs_US.js +1216 -0
  14. package/dist/plugin-activation-CEy_oYpx.js +452 -0
  15. package/dist/prompt-overlay.js +12 -0
  16. package/dist/protocol-C9UWI98H.js +9 -0
  17. package/dist/protocol-validators-BGBspNmF.js +5988 -0
  18. package/dist/provider-catalog.js +84 -0
  19. package/dist/provider-discovery.js +33 -0
  20. package/dist/provider.js +150 -0
  21. package/dist/rate-limit-cache-9LxQdE0K.js +24 -0
  22. package/dist/request-DbSPeTcV.js +89 -0
  23. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  24. package/dist/run-attempt-BoEwzQCv.js +5463 -0
  25. package/dist/session-binding-e2GFp9VH.js +222 -0
  26. package/dist/shared-client-D7Vy0glq.js +631 -0
  27. package/dist/side-question-BDLuEzFP.js +668 -0
  28. package/dist/test-api.js +49 -0
  29. package/dist/thread-lifecycle-Clo0EHMk.js +1565 -0
  30. package/dist/vision-tools-Cofrv35p.js +1379 -0
  31. package/package.json +16 -1
  32. package/doctor-contract-api.ts +0 -68
  33. package/harness.ts +0 -72
  34. package/index.ts +0 -124
  35. package/media-understanding-provider.ts +0 -521
  36. package/prompt-overlay.ts +0 -21
  37. package/provider-catalog.ts +0 -83
  38. package/provider-discovery.ts +0 -45
  39. package/provider.ts +0 -243
  40. package/src/app-server/app-inventory-cache.ts +0 -324
  41. package/src/app-server/approval-bridge.ts +0 -1211
  42. package/src/app-server/auth-bridge.ts +0 -614
  43. package/src/app-server/capabilities.ts +0 -27
  44. package/src/app-server/client-factory.ts +0 -24
  45. package/src/app-server/client.ts +0 -715
  46. package/src/app-server/compact.ts +0 -512
  47. package/src/app-server/computer-use.ts +0 -683
  48. package/src/app-server/config.ts +0 -1038
  49. package/src/app-server/context-engine-projection.ts +0 -403
  50. package/src/app-server/dynamic-tool-diagnostics.ts +0 -73
  51. package/src/app-server/dynamic-tool-profile.ts +0 -70
  52. package/src/app-server/dynamic-tools.ts +0 -623
  53. package/src/app-server/elicitation-bridge.ts +0 -783
  54. package/src/app-server/event-projector.ts +0 -2065
  55. package/src/app-server/image-payload-sanitizer.ts +0 -167
  56. package/src/app-server/local-runtime-attribution.ts +0 -39
  57. package/src/app-server/managed-binary.ts +0 -193
  58. package/src/app-server/models.ts +0 -172
  59. package/src/app-server/native-hook-relay.ts +0 -150
  60. package/src/app-server/native-subagent-task-mirror.ts +0 -497
  61. package/src/app-server/plugin-activation.ts +0 -283
  62. package/src/app-server/plugin-app-cache-key.ts +0 -74
  63. package/src/app-server/plugin-approval-roundtrip.ts +0 -122
  64. package/src/app-server/plugin-inventory.ts +0 -357
  65. package/src/app-server/plugin-thread-config.ts +0 -455
  66. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +0 -33
  67. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +0 -199
  68. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +0 -102
  69. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +0 -227
  70. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +0 -2630
  71. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +0 -2630
  72. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +0 -1659
  73. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +0 -1655
  74. package/src/app-server/protocol-validators.ts +0 -203
  75. package/src/app-server/protocol.ts +0 -520
  76. package/src/app-server/rate-limit-cache.ts +0 -48
  77. package/src/app-server/rate-limits.ts +0 -583
  78. package/src/app-server/request.ts +0 -73
  79. package/src/app-server/run-attempt.ts +0 -4862
  80. package/src/app-server/session-binding.ts +0 -398
  81. package/src/app-server/session-history.ts +0 -44
  82. package/src/app-server/shared-client.ts +0 -289
  83. package/src/app-server/side-question.ts +0 -1009
  84. package/src/app-server/test-support.ts +0 -48
  85. package/src/app-server/thread-lifecycle.ts +0 -959
  86. package/src/app-server/timeout.ts +0 -9
  87. package/src/app-server/tool-progress-normalization.ts +0 -77
  88. package/src/app-server/trajectory.ts +0 -368
  89. package/src/app-server/transcript-mirror.ts +0 -208
  90. package/src/app-server/transport-stdio.ts +0 -107
  91. package/src/app-server/transport-websocket.ts +0 -90
  92. package/src/app-server/transport.ts +0 -117
  93. package/src/app-server/user-input-bridge.ts +0 -316
  94. package/src/app-server/version.ts +0 -4
  95. package/src/app-server/vision-tools.ts +0 -12
  96. package/src/command-account.ts +0 -544
  97. package/src/command-formatters.ts +0 -426
  98. package/src/command-handlers.ts +0 -2021
  99. package/src/command-plugins-management.ts +0 -137
  100. package/src/command-rpc.ts +0 -142
  101. package/src/commands.ts +0 -65
  102. package/src/conversation-binding-data.ts +0 -124
  103. package/src/conversation-binding.ts +0 -561
  104. package/src/conversation-control.ts +0 -303
  105. package/src/conversation-turn-collector.ts +0 -186
  106. package/src/conversation-turn-input.ts +0 -106
  107. package/src/migration/apply.ts +0 -501
  108. package/src/migration/helpers.ts +0 -55
  109. package/src/migration/plan.ts +0 -461
  110. package/src/migration/provider.ts +0 -41
  111. package/src/migration/source.ts +0 -643
  112. package/src/migration/targets.ts +0 -25
  113. package/src/node-cli-sessions.ts +0 -711
  114. package/test-api.ts +0 -95
  115. package/tsconfig.json +0 -16
@@ -1,9 +0,0 @@
1
- import { withTimeout as withSharedTimeout } from "autobot/plugin-sdk/security-runtime";
2
-
3
- export async function withTimeout<T>(
4
- promise: Promise<T>,
5
- timeoutMs: number,
6
- timeoutMessage: string,
7
- ): Promise<T> {
8
- return await withSharedTimeout(promise, timeoutMs, { message: timeoutMessage });
9
- }
@@ -1,77 +0,0 @@
1
- import {
2
- inferToolMetaFromArgs,
3
- type EmbeddedRunAttemptParams,
4
- type ToolProgressDetailMode,
5
- } from "autobot/plugin-sdk/agent-harness-runtime";
6
- import { redactSensitiveFieldValue, redactToolPayloadText } from "autobot/plugin-sdk/logging-core";
7
- import {
8
- isJsonObject,
9
- type CodexDynamicToolCallParams,
10
- type CodexDynamicToolCallResponse,
11
- type JsonValue,
12
- } from "./protocol.js";
13
-
14
- export function resolveCodexToolProgressDetailMode(
15
- value: EmbeddedRunAttemptParams["toolProgressDetail"],
16
- ): ToolProgressDetailMode {
17
- return value === "raw" ? "raw" : "explain";
18
- }
19
-
20
- export function sanitizeCodexAgentEventValue(
21
- value: unknown,
22
- seen = new WeakSet<object>(),
23
- ): unknown {
24
- if (typeof value === "string") {
25
- return redactToolPayloadText(value);
26
- }
27
- if (Array.isArray(value)) {
28
- if (seen.has(value)) {
29
- return "[Circular]";
30
- }
31
- seen.add(value);
32
- return value.map((entry) => sanitizeCodexAgentEventValue(entry, seen));
33
- }
34
- if (value && typeof value === "object") {
35
- if (seen.has(value)) {
36
- return "[Circular]";
37
- }
38
- seen.add(value);
39
- const out: Record<string, unknown> = {};
40
- for (const [key, child] of Object.entries(value as Record<string, unknown>)) {
41
- out[key] =
42
- typeof child === "string"
43
- ? redactSensitiveFieldValue(key, child)
44
- : sanitizeCodexAgentEventValue(child, seen);
45
- }
46
- return out;
47
- }
48
- return value;
49
- }
50
-
51
- export function sanitizeCodexAgentEventRecord(
52
- value: Record<string, unknown>,
53
- ): Record<string, unknown> {
54
- return sanitizeCodexAgentEventValue(value) as Record<string, unknown>;
55
- }
56
-
57
- export function sanitizeCodexToolArguments(
58
- value: JsonValue | undefined,
59
- ): Record<string, unknown> | undefined {
60
- if (!isJsonObject(value)) {
61
- return undefined;
62
- }
63
- return sanitizeCodexAgentEventRecord(value);
64
- }
65
-
66
- export function sanitizeCodexToolResponse(
67
- response: CodexDynamicToolCallResponse,
68
- ): Record<string, unknown> {
69
- return sanitizeCodexAgentEventRecord(response as unknown as Record<string, unknown>);
70
- }
71
-
72
- export function inferCodexDynamicToolMeta(
73
- call: Pick<CodexDynamicToolCallParams, "tool" | "arguments">,
74
- detailMode: ToolProgressDetailMode,
75
- ): string | undefined {
76
- return inferToolMetaFromArgs(call.tool, call.arguments, { detailMode });
77
- }
@@ -1,368 +0,0 @@
1
- import nodeFs from "node:fs";
2
- import fs from "node:fs/promises";
3
- import path from "node:path";
4
- import { resolveUserPath } from "autobot/plugin-sdk/agent-harness-runtime";
5
- import type {
6
- EmbeddedRunAttemptParams,
7
- EmbeddedRunAttemptResult,
8
- } from "autobot/plugin-sdk/agent-harness-runtime";
9
- import {
10
- appendRegularFile,
11
- resolveRegularFileAppendFlags,
12
- } from "autobot/plugin-sdk/security-runtime";
13
- import { resolveCodexLocalRuntimeAttribution } from "./local-runtime-attribution.js";
14
-
15
- export type CodexTrajectoryRecorder = {
16
- filePath: string;
17
- recordEvent: (type: string, data?: Record<string, unknown>) => void;
18
- flush: () => Promise<void>;
19
- };
20
-
21
- type CodexTrajectoryInit = {
22
- attempt: EmbeddedRunAttemptParams;
23
- cwd: string;
24
- developerInstructions?: string;
25
- prompt?: string;
26
- tools?: Array<{ name?: string; description?: string; inputSchema?: unknown }>;
27
- env?: NodeJS.ProcessEnv;
28
- };
29
-
30
- const SENSITIVE_FIELD_RE = /(?:authorization|cookie|credential|key|password|passwd|secret|token)/iu;
31
- const PRIVATE_PAYLOAD_FIELD_RE = /(?:image|screenshot|attachment|fileData|dataUri)/iu;
32
- const AUTHORIZATION_VALUE_RE = /\b(Bearer|Basic)\s+[A-Za-z0-9+/._~=-]{8,}/giu;
33
- const JWT_VALUE_RE = /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\b/gu;
34
- const COOKIE_PAIR_RE = /\b([A-Za-z][A-Za-z0-9_.-]{1,64})=([A-Za-z0-9+/._~%=-]{16,})(?=;|\s|$)/gu;
35
- const TRAJECTORY_RUNTIME_FILE_MAX_BYTES = 50 * 1024 * 1024;
36
- const TRAJECTORY_RUNTIME_EVENT_MAX_BYTES = 256 * 1024;
37
-
38
- type CodexTrajectoryOpenFlagConstants = Pick<
39
- typeof nodeFs.constants,
40
- "O_APPEND" | "O_CREAT" | "O_TRUNC" | "O_WRONLY"
41
- > &
42
- Partial<Pick<typeof nodeFs.constants, "O_NOFOLLOW">>;
43
-
44
- export function resolveCodexTrajectoryAppendFlags(
45
- constants: CodexTrajectoryOpenFlagConstants = nodeFs.constants,
46
- ): number {
47
- return resolveRegularFileAppendFlags(constants);
48
- }
49
-
50
- export function resolveCodexTrajectoryPointerFlags(
51
- constants: CodexTrajectoryOpenFlagConstants = nodeFs.constants,
52
- ): number {
53
- const noFollow = constants.O_NOFOLLOW;
54
- return (
55
- constants.O_CREAT |
56
- constants.O_TRUNC |
57
- constants.O_WRONLY |
58
- (typeof noFollow === "number" ? noFollow : 0)
59
- );
60
- }
61
-
62
- async function safeAppendTrajectoryFile(filePath: string, line: string): Promise<void> {
63
- await appendRegularFile({
64
- filePath,
65
- content: line,
66
- maxFileBytes: TRAJECTORY_RUNTIME_FILE_MAX_BYTES,
67
- rejectSymlinkParents: true,
68
- });
69
- }
70
-
71
- function boundedTrajectoryLine(event: Record<string, unknown>): string | undefined {
72
- const line = JSON.stringify(event);
73
- const bytes = Buffer.byteLength(line, "utf8");
74
- if (bytes <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) {
75
- return `${line}\n`;
76
- }
77
- const truncated = JSON.stringify({
78
- ...event,
79
- data: {
80
- truncated: true,
81
- originalBytes: bytes,
82
- limitBytes: TRAJECTORY_RUNTIME_EVENT_MAX_BYTES,
83
- reason: "trajectory-event-size-limit",
84
- },
85
- });
86
- if (Buffer.byteLength(truncated, "utf8") <= TRAJECTORY_RUNTIME_EVENT_MAX_BYTES) {
87
- return `${truncated}\n`;
88
- }
89
- return undefined;
90
- }
91
-
92
- function resolveTrajectoryPointerFilePath(sessionFile: string): string {
93
- return sessionFile.endsWith(".jsonl")
94
- ? `${sessionFile.slice(0, -".jsonl".length)}.trajectory-path.json`
95
- : `${sessionFile}.trajectory-path.json`;
96
- }
97
-
98
- function writeTrajectoryPointerBestEffort(params: {
99
- filePath: string;
100
- sessionFile: string;
101
- sessionId: string;
102
- }): void {
103
- const pointerPath = resolveTrajectoryPointerFilePath(params.sessionFile);
104
- try {
105
- const pointerDir = path.resolve(path.dirname(pointerPath));
106
- if (nodeFs.lstatSync(pointerDir).isSymbolicLink()) {
107
- return;
108
- }
109
- try {
110
- if (nodeFs.lstatSync(pointerPath).isSymbolicLink()) {
111
- return;
112
- }
113
- } catch (error) {
114
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
115
- return;
116
- }
117
- }
118
- const fd = nodeFs.openSync(pointerPath, resolveCodexTrajectoryPointerFlags(), 0o600);
119
- try {
120
- nodeFs.writeFileSync(
121
- fd,
122
- `${JSON.stringify(
123
- {
124
- traceSchema: "autobot-trajectory-pointer",
125
- schemaVersion: 1,
126
- sessionId: params.sessionId,
127
- runtimeFile: params.filePath,
128
- },
129
- null,
130
- 2,
131
- )}\n`,
132
- "utf8",
133
- );
134
- nodeFs.fchmodSync(fd, 0o600);
135
- } finally {
136
- nodeFs.closeSync(fd);
137
- }
138
- } catch {
139
- // Pointer files are best-effort; the runtime sidecar itself is authoritative.
140
- }
141
- }
142
-
143
- export function createCodexTrajectoryRecorder(
144
- params: CodexTrajectoryInit,
145
- ): CodexTrajectoryRecorder | null {
146
- const env = params.env ?? process.env;
147
- const enabled = parseTrajectoryEnabled(env);
148
- if (!enabled) {
149
- return null;
150
- }
151
-
152
- const filePath = resolveTrajectoryFilePath({
153
- env,
154
- sessionFile: params.attempt.sessionFile,
155
- sessionId: params.attempt.sessionId,
156
- });
157
- const ready = fs
158
- .mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 })
159
- .catch(() => undefined);
160
- writeTrajectoryPointerBestEffort({
161
- filePath,
162
- sessionFile: params.attempt.sessionFile,
163
- sessionId: params.attempt.sessionId,
164
- });
165
- let queue = Promise.resolve();
166
- let seq = 0;
167
- const attribution = resolveCodexLocalRuntimeAttribution(params.attempt);
168
-
169
- return {
170
- filePath,
171
- recordEvent: (type, data) => {
172
- const event = {
173
- traceSchema: "autobot-trajectory",
174
- schemaVersion: 1,
175
- traceId: params.attempt.sessionId,
176
- source: "runtime",
177
- type,
178
- ts: new Date().toISOString(),
179
- seq: (seq += 1),
180
- sourceSeq: seq,
181
- sessionId: params.attempt.sessionId,
182
- sessionKey: params.attempt.sessionKey,
183
- runId: params.attempt.runId,
184
- workspaceDir: params.cwd,
185
- provider: attribution.provider,
186
- modelId: params.attempt.modelId,
187
- modelApi: attribution.api,
188
- data: data ? sanitizeValue(data) : undefined,
189
- };
190
- const line = boundedTrajectoryLine(event);
191
- if (!line) {
192
- return;
193
- }
194
- queue = queue
195
- .then(() => ready)
196
- .then(() => safeAppendTrajectoryFile(filePath, line))
197
- .catch(() => undefined);
198
- },
199
- flush: async () => {
200
- await queue;
201
- },
202
- };
203
- }
204
-
205
- export function recordCodexTrajectoryContext(
206
- recorder: CodexTrajectoryRecorder | null,
207
- params: CodexTrajectoryInit,
208
- ): void {
209
- if (!recorder) {
210
- return;
211
- }
212
- recorder.recordEvent("context.compiled", {
213
- systemPrompt: params.developerInstructions,
214
- prompt: params.prompt ?? params.attempt.prompt,
215
- imagesCount: params.attempt.images?.length ?? 0,
216
- tools: toTrajectoryToolDefinitions(params.tools),
217
- });
218
- }
219
-
220
- export function recordCodexTrajectoryCompletion(
221
- recorder: CodexTrajectoryRecorder | null,
222
- params: {
223
- attempt: EmbeddedRunAttemptParams;
224
- result: EmbeddedRunAttemptResult;
225
- threadId: string;
226
- turnId: string;
227
- timedOut: boolean;
228
- yieldDetected?: boolean;
229
- },
230
- ): void {
231
- if (!recorder) {
232
- return;
233
- }
234
- recorder.recordEvent("model.completed", {
235
- threadId: params.threadId,
236
- turnId: params.turnId,
237
- timedOut: params.timedOut,
238
- yieldDetected: params.yieldDetected ?? false,
239
- aborted: params.result.aborted,
240
- promptError: normalizeCodexTrajectoryError(params.result.promptError),
241
- usage: params.result.attemptUsage,
242
- assistantTexts: params.result.assistantTexts,
243
- messagesSnapshot: params.result.messagesSnapshot,
244
- });
245
- }
246
-
247
- function parseTrajectoryEnabled(env: NodeJS.ProcessEnv): boolean {
248
- const value = env.AUTOBOT_TRAJECTORY?.trim().toLowerCase();
249
- if (value === "1" || value === "true" || value === "yes" || value === "on") {
250
- return true;
251
- }
252
- if (value === "0" || value === "false" || value === "no" || value === "off") {
253
- return false;
254
- }
255
- return true;
256
- }
257
-
258
- function resolveTrajectoryFilePath(params: {
259
- env: NodeJS.ProcessEnv;
260
- sessionFile: string;
261
- sessionId: string;
262
- }): string {
263
- const dirOverride = params.env.AUTOBOT_TRAJECTORY_DIR?.trim();
264
- if (dirOverride) {
265
- return resolveContainedPath(
266
- resolveUserPath(dirOverride),
267
- `${safeTrajectorySessionFileName(params.sessionId)}.jsonl`,
268
- );
269
- }
270
- return params.sessionFile.endsWith(".jsonl")
271
- ? `${params.sessionFile.slice(0, -".jsonl".length)}.trajectory.jsonl`
272
- : `${params.sessionFile}.trajectory.jsonl`;
273
- }
274
-
275
- function safeTrajectorySessionFileName(sessionId: string): string {
276
- const safe = sessionId.replaceAll(/[^A-Za-z0-9_-]/g, "_").slice(0, 120);
277
- return /[A-Za-z0-9]/u.test(safe) ? safe : "session";
278
- }
279
-
280
- function resolveContainedPath(baseDir: string, fileName: string): string {
281
- const resolvedBase = path.resolve(baseDir);
282
- const resolvedFile = path.resolve(resolvedBase, fileName);
283
- const relative = path.relative(resolvedBase, resolvedFile);
284
- if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
285
- throw new Error("Trajectory file path escaped its configured directory");
286
- }
287
- return resolvedFile;
288
- }
289
-
290
- function toTrajectoryToolDefinitions(
291
- tools: Array<{ name?: string; description?: string; inputSchema?: unknown }> | undefined,
292
- ): Array<{ name: string; description?: string; parameters?: unknown }> | undefined {
293
- if (!tools || tools.length === 0) {
294
- return undefined;
295
- }
296
- return tools
297
- .flatMap((tool) => {
298
- const name = tool.name?.trim();
299
- if (!name) {
300
- return [];
301
- }
302
- return [
303
- {
304
- name,
305
- description: tool.description,
306
- parameters: sanitizeValue(tool.inputSchema),
307
- },
308
- ];
309
- })
310
- .toSorted((left, right) => left.name.localeCompare(right.name));
311
- }
312
-
313
- function sanitizeValue(value: unknown, depth = 0, key = ""): unknown {
314
- if (value == null || typeof value === "boolean" || typeof value === "number") {
315
- return value;
316
- }
317
- if (typeof value === "string") {
318
- if (SENSITIVE_FIELD_RE.test(key)) {
319
- return "<redacted>";
320
- }
321
- if (value.startsWith("data:") && value.length > 256) {
322
- return `<redacted data-uri ${value.slice(0, value.indexOf(",")).length} chars>`;
323
- }
324
- if (PRIVATE_PAYLOAD_FIELD_RE.test(key) && value.length > 256) {
325
- return "<redacted payload>";
326
- }
327
- const redacted = redactSensitiveString(value);
328
- return redacted.length > 20_000 ? `${redacted.slice(0, 20_000)}…` : redacted;
329
- }
330
- if (depth >= 6) {
331
- return "<truncated>";
332
- }
333
- if (Array.isArray(value)) {
334
- return value.slice(0, 100).map((entry) => sanitizeValue(entry, depth + 1, key));
335
- }
336
- if (typeof value === "object") {
337
- const next: Record<string, unknown> = {};
338
- for (const [key, child] of Object.entries(value).slice(0, 100)) {
339
- next[key] = sanitizeValue(child, depth + 1, key);
340
- }
341
- return next;
342
- }
343
- return JSON.stringify(value);
344
- }
345
-
346
- function redactSensitiveString(value: string): string {
347
- return value
348
- .replace(AUTHORIZATION_VALUE_RE, "$1 <redacted>")
349
- .replace(JWT_VALUE_RE, "<redacted-jwt>")
350
- .replace(COOKIE_PAIR_RE, "$1=<redacted>");
351
- }
352
-
353
- export function normalizeCodexTrajectoryError(value: unknown): string | null {
354
- if (!value) {
355
- return null;
356
- }
357
- if (value instanceof Error) {
358
- return value.message;
359
- }
360
- if (typeof value === "string") {
361
- return value;
362
- }
363
- try {
364
- return JSON.stringify(value);
365
- } catch {
366
- return "Unknown error";
367
- }
368
- }
@@ -1,208 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import fs from "node:fs/promises";
3
- import {
4
- acquireSessionWriteLock,
5
- appendSessionTranscriptMessage,
6
- emitSessionTranscriptUpdate,
7
- resolveSessionWriteLockOptions,
8
- runAgentHarnessBeforeMessageWriteHook,
9
- type AgentMessage,
10
- type EmbeddedRunAttemptParams,
11
- type SessionWriteLockAcquireTimeoutConfig,
12
- } from "autobot/plugin-sdk/agent-harness-runtime";
13
-
14
- type MirroredAgentMessage = Extract<AgentMessage, { role: "user" | "assistant" | "toolResult" }>;
15
-
16
- const MIRROR_IDENTITY_META_KEY = "mirrorIdentity" as const;
17
-
18
- function normalizeOptionalString(value: string | null | undefined): string | undefined {
19
- const normalized = value?.trim();
20
- return normalized ? normalized : undefined;
21
- }
22
-
23
- function buildSenderLabel(params: {
24
- senderId?: string;
25
- senderName?: string;
26
- senderUsername?: string;
27
- senderE164?: string;
28
- }): string | undefined {
29
- const label = params.senderName ?? params.senderUsername ?? params.senderE164 ?? params.senderId;
30
- if (!label) {
31
- return undefined;
32
- }
33
- if (!params.senderId || label.includes(params.senderId)) {
34
- return label;
35
- }
36
- return `${label} (${params.senderId})`;
37
- }
38
-
39
- export function buildCodexUserPromptMessage(params: EmbeddedRunAttemptParams): AgentMessage {
40
- const senderId = normalizeOptionalString(params.senderId);
41
- const senderName = normalizeOptionalString(params.senderName);
42
- const senderUsername = normalizeOptionalString(params.senderUsername);
43
- const senderE164 = normalizeOptionalString(params.senderE164);
44
- const senderLabel = buildSenderLabel({ senderId, senderName, senderUsername, senderE164 });
45
- const sourceChannel = normalizeOptionalString(
46
- params.inputProvenance?.sourceChannel ?? params.messageChannel ?? params.messageProvider,
47
- );
48
- return {
49
- role: "user",
50
- content: params.prompt,
51
- timestamp: Date.now(),
52
- ...(params.inputProvenance ? { provenance: params.inputProvenance } : {}),
53
- ...(sourceChannel ? { sourceChannel } : {}),
54
- ...(senderId ? { senderId } : {}),
55
- ...(senderName ? { senderName } : {}),
56
- ...(senderUsername ? { senderUsername } : {}),
57
- ...(senderE164 ? { senderE164 } : {}),
58
- ...(senderLabel ? { senderLabel } : {}),
59
- } as AgentMessage;
60
- }
61
-
62
- /**
63
- * Tag a message with a stable logical identity for mirror dedupe. Callers
64
- * should use a value that is invariant for the same logical message across
65
- * re-emits (e.g. `${turnId}:prompt`, `${turnId}:assistant`) but distinct
66
- * for genuinely-distinct messages (different turns, different kinds). When
67
- * present this identity replaces the role/content fingerprint in the
68
- * idempotency key, so the dedupe survives caller-scope rotation without
69
- * collapsing distinct same-content turns.
70
- */
71
- export function attachCodexMirrorIdentity<T extends AgentMessage>(message: T, identity: string): T {
72
- const record = message as unknown as Record<string, unknown>;
73
- const existing = record["__autobot"];
74
- const baseMeta =
75
- existing && typeof existing === "object" && !Array.isArray(existing)
76
- ? (existing as Record<string, unknown>)
77
- : {};
78
- return {
79
- ...record,
80
- __autobot: { ...baseMeta, [MIRROR_IDENTITY_META_KEY]: identity },
81
- } as unknown as T;
82
- }
83
-
84
- function readMirrorIdentity(message: MirroredAgentMessage): string | undefined {
85
- const record = message as unknown as { __autobot?: unknown };
86
- const meta = record["__autobot"];
87
- if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
88
- return undefined;
89
- }
90
- const id = (meta as Record<string, unknown>)[MIRROR_IDENTITY_META_KEY];
91
- return typeof id === "string" && id.length > 0 ? id : undefined;
92
- }
93
-
94
- // Fallback content fingerprint for callers that did not tag the message
95
- // with a stable mirror identity. Only role and content participate; volatile
96
- // metadata (timestamps, usage, etc.) is intentionally excluded so the
97
- // fingerprint survives snapshot reordering inside a fixed scope. Distinct
98
- // same-content turns are still distinguished by the caller's idempotency
99
- // scope when callers route through this fallback.
100
- function fingerprintMirrorMessageContent(message: MirroredAgentMessage): string {
101
- const payload = JSON.stringify({ role: message.role, content: message.content });
102
- return createHash("sha256").update(payload).digest("hex").slice(0, 16);
103
- }
104
-
105
- function buildMirrorDedupeIdentity(message: MirroredAgentMessage): string {
106
- const explicit = readMirrorIdentity(message);
107
- if (explicit) {
108
- return explicit;
109
- }
110
- return `${message.role}:${fingerprintMirrorMessageContent(message)}`;
111
- }
112
-
113
- export async function mirrorCodexAppServerTranscript(params: {
114
- sessionFile: string;
115
- sessionKey?: string;
116
- agentId?: string;
117
- messages: AgentMessage[];
118
- idempotencyScope?: string;
119
- config?: SessionWriteLockAcquireTimeoutConfig;
120
- }): Promise<void> {
121
- const messages = params.messages.filter(
122
- (message): message is MirroredAgentMessage =>
123
- message.role === "user" || message.role === "assistant" || message.role === "toolResult",
124
- );
125
- if (messages.length === 0) {
126
- return;
127
- }
128
-
129
- const lock = await acquireSessionWriteLock({
130
- sessionFile: params.sessionFile,
131
- ...resolveSessionWriteLockOptions(params.config),
132
- });
133
- try {
134
- const existingIdempotencyKeys = await readTranscriptIdempotencyKeys(params.sessionFile);
135
- for (const message of messages) {
136
- const dedupeIdentity = buildMirrorDedupeIdentity(message);
137
- const idempotencyKey = params.idempotencyScope
138
- ? `${params.idempotencyScope}:${dedupeIdentity}`
139
- : undefined;
140
- if (idempotencyKey && existingIdempotencyKeys.has(idempotencyKey)) {
141
- continue;
142
- }
143
- const transcriptMessage = {
144
- ...message,
145
- ...(idempotencyKey ? { idempotencyKey } : {}),
146
- } as AgentMessage;
147
- const nextMessage = runAgentHarnessBeforeMessageWriteHook({
148
- message: transcriptMessage,
149
- agentId: params.agentId,
150
- sessionKey: params.sessionKey,
151
- });
152
- if (!nextMessage) {
153
- continue;
154
- }
155
- const messageToAppend = (
156
- idempotencyKey
157
- ? {
158
- ...(nextMessage as unknown as Record<string, unknown>),
159
- idempotencyKey,
160
- }
161
- : nextMessage
162
- ) as AgentMessage;
163
- await appendSessionTranscriptMessage({
164
- transcriptPath: params.sessionFile,
165
- message: messageToAppend,
166
- config: params.config,
167
- });
168
- if (idempotencyKey) {
169
- existingIdempotencyKeys.add(idempotencyKey);
170
- }
171
- }
172
- } finally {
173
- await lock.release();
174
- }
175
-
176
- if (params.sessionKey) {
177
- emitSessionTranscriptUpdate({ sessionFile: params.sessionFile, sessionKey: params.sessionKey });
178
- } else {
179
- emitSessionTranscriptUpdate(params.sessionFile);
180
- }
181
- }
182
-
183
- async function readTranscriptIdempotencyKeys(sessionFile: string): Promise<Set<string>> {
184
- const keys = new Set<string>();
185
- let raw: string;
186
- try {
187
- raw = await fs.readFile(sessionFile, "utf8");
188
- } catch (error) {
189
- if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
190
- throw error;
191
- }
192
- return keys;
193
- }
194
- for (const line of raw.split(/\r?\n/)) {
195
- if (!line.trim()) {
196
- continue;
197
- }
198
- try {
199
- const parsed = JSON.parse(line) as { message?: { idempotencyKey?: unknown } };
200
- if (typeof parsed.message?.idempotencyKey === "string") {
201
- keys.add(parsed.message.idempotencyKey);
202
- }
203
- } catch {
204
- continue;
205
- }
206
- }
207
- return keys;
208
- }