Skip to content

Commit dc43112

Browse files
GiniGini
authored andcommitted
feat: expose bounded wallet review intent
1 parent 91bc6cf commit dc43112

3 files changed

Lines changed: 23 additions & 4 deletions

File tree

docs/SECURITY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
- ordered events include `previousHash` and `eventHash`;
2020
- runtime mode and non-production limitations are visible in the UI.
2121
- local wallet decisions produce HMAC receipts for integration testing; each pending approval is bound to a SHA-256 digest of its task, action, expiry, and current evidence head, and that digest is carried into the receipt. The wallet secret is never serialized into task state or evidence. This is still not an OpenVTC asymmetric proof.
22+
- the separately authenticated wallet listing exposes only a bounded intent-review document (title, action, expiry, evidence head, and intent digest). It never exposes the browser session, raw prompt, sandbox handle, connector credential, or mutable workspace capability.
2223
- ONEComputer mode executes Claude through the sandbox API, rejects unsafe artifact paths, caps extraction at 100 files/10 MiB, and deletes the sandbox by default.
2324
- ONEComputer-mode Claude journals remain in the disposable sandbox. ONEVibe stores only bounded projections of tool and transcript events after redacting credential-like fields; the raw `stream-json` journal is excluded from artifact extraction.
2425
- The managed sandbox journal is capped at 4 MiB and must write a terminal exit code. ONEVibe fails the task rather than ingesting an oversized or indeterminate journal; sandbox teardown remains the cancellation backstop.

server/wallet-approval-service.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('WalletApprovalService', () => {
2424
const wallet = new WalletApprovalService(store, token)
2525

2626
expect(() => wallet.authorize('Bearer wrong')).toThrow('authorization failed')
27-
expect(wallet.listPending()).toHaveLength(1)
27+
expect(wallet.listPending()).toMatchObject([{ taskId: task.id, intent: { version: 1, taskId: task.id, action: 'share_artifact', evidenceHash, intentHash } }])
2828
const result = await wallet.decide('approval-test', 'approved', 'test-vti-wallet')
2929

3030
expect(result.share?.id.length).toBeGreaterThan(20)

server/wallet-approval-service.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,27 @@ export class WalletApprovalService {
1414
}
1515

1616
listPending() {
17-
return this.store.listTasks().filter((task) => task.approval?.state === 'pending').map((task) => ({
18-
taskId: task.id, title: task.title, approval: task.approval,
19-
}))
17+
return this.store.listTasks().flatMap((task) => {
18+
const approval = task.approval
19+
if (!approval || approval.state !== 'pending') return []
20+
return [{
21+
taskId: task.id,
22+
title: task.title,
23+
approval,
24+
// This is intentionally the entire wallet-facing review surface. The
25+
// wallet receives no browser session, sandbox handle, raw task prompt,
26+
// connector credential, or mutable workspace capability.
27+
intent: approval.intentHash && approval.evidenceHash ? {
28+
version: 1 as const,
29+
taskId: task.id,
30+
title: task.title,
31+
action: approval.action,
32+
expiresAt: approval.expiresAt,
33+
evidenceHash: approval.evidenceHash,
34+
intentHash: approval.intentHash,
35+
} : undefined,
36+
}]
37+
})
2038
}
2139

2240
async decide(approvalId: string, decision: 'approved' | 'denied', signer: string) {

0 commit comments

Comments
 (0)