ReviewOS

also looking at this

stacks/uplink

feat(engine): answer texts with Codex as well as Claude Code

#7
Merged glennmichael123 wants to merge feat/codex-engine into main
18 files +1080 -142

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

app/Uplink/desktop-agent.tsmodified+2-0
Changes to app/Uplink/desktop-agent.ts
@@ -224,6 +224,7 @@ export async function startDesktopAgent(options: { version: string }): Promise<D
224224 name: 'Read Messages',
225225 ok: messages !== null,
226226 detail: messages ? 'Full Disk Access granted' : (messagesError ?? 'Waiting for Full Disk Access'),
227 informational: false,
227228 },
228229 ...allEngines(config).map((engine) => {
229230 const state = engines.get(engine.id)
@@ -245,6 +246,7 @@ export async function startDesktopAgent(options: { version: string }): Promise<D
245246 name: 'Who can text it',
246247 ok: allowed.length > 0,
247248 detail: allowed.length > 0 ? allowed.join(', ') : 'No handles yet. Sign in to Messages, or add your number.',
249 informational: false,
248250 },
249251 ]
250252 // An engine the person has not selected must not hold the app in setup.
tests/unit/uplink/codex-engine.test.tsmodified+15-4
Changes to tests/unit/uplink/codex-engine.test.ts
@@ -64,8 +64,17 @@ function engine(bin: string, overrides: Partial<{ model: string | null, permissi
6464 })
6565}
6666
67/**
68 * The flags, one per line. The prompt is deliberately excluded: it carries the
69 * system prompt and is many lines long, so it would otherwise be scattered
70 * across this array. Use `rawArgsOf` to assert on it.
71 */
6772function argsOf(bin: string): string[] {
68 return readFileSync(`${bin}.args`, 'utf8').trim().split('\n')
73 return rawArgsOf(bin).split('\n').filter(line => line.startsWith('-') || !line.includes(' '))
74}
75
76function rawArgsOf(bin: string): string {
77 return readFileSync(`${bin}.args`, 'utf8').trim()
6978}
7079
7180describe('CodexEngine', () => {
@@ -102,9 +111,11 @@ describe('CodexEngine', () => {
102111 expect(args).toContain('--json')
103112 expect(args.slice(args.indexOf('--cd'), args.indexOf('--cd') + 2)).toEqual(['--cd', dir])
104113 expect(args).toContain('--dangerously-bypass-approvals-and-sandbox')
105 // No --append-system-prompt exists, so the instructions ride on the prompt.
106 expect(args[args.length - 1]).toContain('Answer in plain text.')
107 expect(args[args.length - 1]).toContain('scores?')
114 // No --append-system-prompt exists, so the instructions ride on the prompt,
115 // which is why the prompt is many lines and the last argv line is not it.
116 const raw = rawArgsOf(bin)
117 expect(raw).toContain('Answer in plain text.')
118 expect(raw).toEndWith('scores?')
108119 expect(readFileSync(`${bin}.cwd`, 'utf8').trim()).toEndWith(dir.replace(/^\/private/, ''))
109120 })
110121