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/filter.tsmodified+20-0
Changes to app/Uplink/filter.ts
@@ -9,6 +9,7 @@ import { CHAT_STYLE_DIRECT } from './messages-db'
99 * rule is an allowlist, never a denylist, and every check fails closed.
1010 *
1111 * A message is a command when ALL of these hold:
12 * - it arrived over iMessage, never SMS (see below),
1213 * - it is in a one-to-one chat (never a group: anyone could be added to one),
1314 * - that chat is with an allowed handle,
1415 * - it was sent BY that handle - or, in a chat with one of this Mac's own
@@ -33,7 +34,26 @@ export type Verdict =
3334 | { accept: true, text: string }
3435 | { accept: false, reason: string }
3536
37/**
38 * Only iMessage carries an identity worth trusting.
39 *
40 * With Text Message Forwarding on, which is the default once an iPhone is
41 * paired and is the setup this product assumes, forwarded SMS lands in chat.db
42 * as a direct chat whose identifier is the sender's number. SMS sender IDs are
43 * spoofable through commodity bulk-SMS gateways; iMessage sender IDs are not.
44 * Without this check, anyone who knows the owner's phone number could send one
45 * SMS that passes every other test here and reaches an agent running with
46 * approvals disabled in the owner's home directory.
47 *
48 * iMessage is also the only service that reaches this Mac over satellite, which
49 * is what Uplink is for, so nothing real is lost.
50 */
51const COMMAND_SERVICE = 'iMessage'
52
3653export function classify(message: IncomingMessage, ctx: FilterContext): Verdict {
54 if (message.service !== COMMAND_SERVICE)
55 return { accept: false, reason: `not ${COMMAND_SERVICE}` }
56
3757 if (message.chatStyle !== CHAT_STYLE_DIRECT)
3858 return { accept: false, reason: 'group chat' }
3959
tests/unit/uplink/messages.test.tsmodified+10-0
Changes to tests/unit/uplink/messages.test.ts
@@ -107,8 +107,18 @@ describe('classify', () => {
107107 expect(classify({ ...base, chatIdentifier: ME, chatGuid: `iMessage;-;${ME}`, sender: FRIEND }, selfCtx)).toEqual({ accept: false, reason: 'sender mismatch' })
108108 })
109109
110 it('refuses SMS even from an allowed handle, because sender IDs are spoofable', () => {
111 // With Text Message Forwarding on, a forwarded SMS is a direct chat whose
112 // identifier is the sender's number, so every other check here passes.
113 expect(classify({ ...base, service: 'SMS' }, ctx)).toEqual({ accept: false, reason: 'not iMessage' })
114 expect(classify({ ...base, service: 'SMS', chatIdentifier: ME, chatGuid: `SMS;-;${ME}`, isFromMe: true, sender: null }, ctx))
115 .toEqual({ accept: false, reason: 'not iMessage' })
116 })
117
110118 it('fails closed on everything else', () => {
111119 const rejected: Array<[Partial<IncomingMessage>, string]> = [
120 [{ service: 'SMS' }, 'not iMessage'],
121 [{ service: 'RCS' }, 'not iMessage'],
112122 [{ chatIdentifier: '+15559999999', sender: '+15559999999' }, 'chat not allowed'],
113123 [{ chatStyle: 43 }, 'group chat'],
114124 [{ isFromMe: true, sender: null }, 'outgoing message'],