Repository navigation
Conversation
…fallthrough guard Selective merge of OSS task-bridge.ts security hardening that stando was missing. Does NOT touch stando's deliberate divergences (state-paths.js layout, watch-tasks-stream.mjs watcher, cloud-client telemetry). 1. Field ordering (PR sonichi#982): all task writers (writeChatTask, the voice workTool writer, context-drop) now emit `task:` LAST and include a `priority:` field (normal for chat/context-drop, urgent for voice). This matches what stando's own agent-api.py already does (it ordered `task:` last at agent-api.py:834) — task-bridge.ts was the lone inconsistent writer. A multi-line body can no longer forge header fields below it. 2. _isVoiceTask stops scanning headers at the first `task:` line — the residual half of sonichi#982. Without it, a body line `channel_id: local-voice` would forge a voice-task classification. Added a regression test (FORGED_VOICE_BODY) proving the guard; updated the existing fixtures to the new `task:`-last format. 3. _shouldFallthrough allowlist guard (issue sonichi#1035): the result-watcher's unconditional onResult() fallthrough is now gated to filenames task-bridge legitimately delivers (task-/voice-/proactive-), so a future per-channel pull namespace won't be injected into voice. Verified: full TS suite green (241/241), incl. task-bridge-format and task-bridge-isvoice; tsc clean. Co-authored-by: Cursor <cursoragent@cursor.com>
bassilkhilo-ag2
left a comment
There was a problem hiding this comment.
Hi Josh — one voice-delivery regression to flag before merge.
PR #70's _shouldFallthrough allowlist is:
return file.startsWith('task-') || file.startsWith('voice-') || file.startsWith('proactive-');This misses four result prefixes that are explicitly written to be spoken aloud when the voice client is connected:
question-— written bysrc/check-pending-questions.py:115insight-— written bysrc/daily-insight.pyfriction-— written bysrc/friction-detector.pybriefing-— written byskills/morning-briefing/SKILL.md:49
The discord bridge already codifies these in FALLBACK_PREFIXES at discord-bridge.py:3145:
FALLBACK_PREFIXES = ("task-", "question-", "briefing-", "insight-", "friction-")As written, this PR would silently stop voice from speaking pending-question pings, daily insights, friction reports, and morning briefings. Suggested one-liner:
export function _shouldFallthrough(file: string): boolean {
return ['task-','voice-','proactive-','question-','briefing-','insight-','friction-']
.some(p => file.startsWith(p));
}PR #94 (unit tests for this guard) also only covers the existing three prefixes — would be good to extend those tests to cover the four new ones too.
Happy to prepare a follow-up commit if helpful.
|
Supporting Bassil's review — here's the exact change needed for export function _shouldFallthrough(file: string): boolean {
return (
file.startsWith('task-') ||
file.startsWith('voice-') ||
file.startsWith('proactive-') ||
file.startsWith('question-') || // check-pending-questions.py:132
file.startsWith('insight-') || // daily-insight.py
file.startsWith('friction-') || // friction-detector.py
file.startsWith('briefing-') // morning-briefing skill
);
}Source verification:
Also note: |
Summary
Phase 5.1 of the OSS sync roadmap — a selective merge of
task-bridge.tssecurity hardening that OSS sutando has but stando was missing. This is not a wholesale port: stando's deliberate divergences (state-paths.jslayout,watch-tasks-stream.mjswatcher,cloud-clienttelemetry) are left untouched.Branches off
main(independent of the UI-extraction PRs #68/#69).What changed (3 hardening items)
writeChatTask, the voiceworkToolwriter, context-drop) now emittask:last and add apriority:field (normalfor chat/context-drop,urgentfor voice). A multi-line user/Gemini body can no longer forge header fields below it.agent-api.py:834, which already orderstask:last. task-bridge.ts was the lone inconsistent writer, andtask_priority.pyalready exists to consume thepriority:field._isVoiceTaskstops at the firsttask:line — the residual half of fix(agent-api): close task-file field injection via /task body sonichi/sutando#982. Without it, a body linechannel_id: local-voicewould forge a voice-task classification (affects the offline DM-forwarding gate). Added aFORGED_VOICE_BODYregression test; updated existing fixtures to the newtask:-last format._shouldFallthroughallowlist guard (issue task-bridge: belt-suspenders guard for non-task .txt files (narrow-scope-followup from #1033) sonichi/sutando#1035) — the result-watcher's unconditionalonResult()fallthrough is now gated to filenames task-bridge legitimately delivers (task-/voice-/proactive-), so a future per-channel pull namespace won't be injected into voice.Deliberately NOT included
main.swiftchange (stando's Swift doesn't writesource: context-drop).Test plan
tests/task-bridge-isvoice.test.ts— 8/8 incl. new forge-guard regressiontests/task-bridge-format.test.ts— 5/5 (field matches are line-anchored, order-independent)tsc --noEmitcleanRelated
src/task-bridge.ts(PR fix(agent-api): close task-file field injection via /task body sonichi/sutando#982, issue task-bridge: belt-suspenders guard for non-task .txt files (narrow-scope-followup from #1033) sonichi/sutando#1035)Made with Cursor