wip(13-08): checkpoint before operator session restart — Task 1 GREEN (28/28), Task 2 in progress

Executor stopped deliberately for a session restart (bypass-permissions relaunch).
Executor's final report: 'cargo test assistant confirm-gate suite 28/28 green,
individual nonce test passes; committing Task 1 next — first verify the
tools.rs/grants.rs/backends diffs are formatting-only.'

Task 1 (D-07/D-11 confirm gate, backend) is implemented and test-green but this
checkpoint is verbatim-uncommitted-state, NOT the reviewed atomic Task 1 commit:
continuation executor should verify diffs, then reset --soft or commit-on-top
into proper feat(13-08) task commits. Task 2 (ToolConfirmModal.vue trusted
chrome, Chat.vue + contextBroker.ts wiring) is partially built, tests written.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-05 11:34:31 -04:00
co-authored by Claude Fable 5
parent db11c625c8
commit fc09d7a292
11 changed files with 1011 additions and 53 deletions
+51
View File
@@ -70,6 +70,21 @@
</div>
</div>
<!-- 13-08 (D-11): the destructive-tool confirmation dialog trusted
chrome, mounted as a SIBLING of the iframe, never inside it. The
component Teleports to body with a full-screen backdrop, so it
covers the whole viewport including the area over the iframe, and
no ancestor transform can trap its position: fixed. Its text is
node-authored, fetched by the ContextBroker over the page's own
RPC session — nothing the iframe sends can open or resolve it. -->
<ToolConfirmModal
:show="!!toolConfirm"
:description="toolConfirm?.description ?? ''"
@approve="resolveToolConfirm(true)"
@deny="resolveToolConfirm(false)"
@dismiss="dismissToolConfirm"
/>
</div>
</template>
@@ -78,6 +93,7 @@ import { ref, computed, onActivated, onBeforeUnmount, onDeactivated, onMounted,
import { useRoute, useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { ContextBroker } from '@/services/contextBroker'
import ToolConfirmModal from '@/components/ToolConfirmModal.vue'
import { IS_DEMO } from '@/composables/useDemoIntro'
const { t } = useI18n()
@@ -170,6 +186,37 @@ function closeChat() {
}
}
// 13-08 (D-11): the pending destructive-tool confirmation the trusted
// chrome is currently showing. Set ONLY from the ContextBroker's
// aiui:tool-confirm-request CustomEvent, whose payload is node-fetched
// over the page's own RPC session — never from anything the iframe posts.
const toolConfirm = ref<{ reqId: string; description: string } | null>(null)
function onToolConfirmRequest(e: Event) {
const detail = (e as CustomEvent).detail as { reqId?: string; description?: string }
if (!detail?.reqId || typeof detail.description !== 'string') return
toolConfirm.value = { reqId: detail.reqId, description: detail.description }
}
function resolveToolConfirm(approved: boolean) {
const current = toolConfirm.value
toolConfirm.value = null
if (!current) return
// The decision travels back to the broker (and from there to the node
// over the authenticated RPC session) — never through the iframe.
window.dispatchEvent(
new CustomEvent('aiui:tool-confirm-response', {
detail: { reqId: current.reqId, approved },
}),
)
}
function dismissToolConfirm() {
// Closed without a decision: send nothing. The action stays pending on
// the node until its own timeout declines it — never silently approved.
toolConfirm.value = null
}
function onAiuiMessage(event: MessageEvent) {
if (!aiuiUrl.value) return
// Validate origin — only accept messages from AIUI
@@ -197,6 +244,8 @@ function onAiuiMessage(event: MessageEvent) {
function armChatLive() {
window.removeEventListener('message', onAiuiMessage)
window.addEventListener('message', onAiuiMessage)
window.removeEventListener('aiui:tool-confirm-request', onToolConfirmRequest)
window.addEventListener('aiui:tool-confirm-request', onToolConfirmRequest)
broker?.stop()
broker = null
if (aiuiUrl.value) {
@@ -216,6 +265,7 @@ onActivated(() => armChatLive())
onDeactivated(() => {
window.removeEventListener('message', onAiuiMessage)
window.removeEventListener('aiui:tool-confirm-request', onToolConfirmRequest)
broker?.stop()
broker = null
if (loadTimeout) { clearTimeout(loadTimeout); loadTimeout = null }
@@ -229,6 +279,7 @@ onMounted(() => armChatLive())
onBeforeUnmount(() => {
window.removeEventListener('message', onAiuiMessage)
window.removeEventListener('aiui:tool-confirm-request', onToolConfirmRequest)
broker?.stop()
broker = null
if (loadTimeout) { clearTimeout(loadTimeout); loadTimeout = null }