viveworker 0.7.0-beta.1 → 0.7.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,107 @@
1
+ function ensureBrowserSupport() {
2
+ if (typeof window === 'undefined' || typeof navigator === 'undefined' || !navigator.credentials) {
3
+ throw new Error('WebAuthn is only available in a browser environment');
4
+ }
5
+ }
6
+
7
+ function toBase64url(input) {
8
+ if (!input) throw new Error('Expected binary data from WebAuthn response');
9
+ const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
10
+ let binary = '';
11
+ for (const byte of bytes) binary += String.fromCharCode(byte);
12
+ return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, '');
13
+ }
14
+
15
+ function fromBase64url(input) {
16
+ const normalized = String(input || '').replace(/-/g, '+').replace(/_/g, '/');
17
+ const padding = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));
18
+ const binary = atob(normalized + padding);
19
+ const bytes = new Uint8Array(binary.length);
20
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
21
+ return bytes;
22
+ }
23
+
24
+ function algorithmFromCode(code) {
25
+ if (code === -257) return 'RS256';
26
+ return 'ES256';
27
+ }
28
+
29
+ export async function createPasskeyRegistrationCredential(challenge) {
30
+ ensureBrowserSupport();
31
+ const credential = await navigator.credentials.create({
32
+ publicKey: {
33
+ challenge: fromBase64url(challenge.challenge),
34
+ rp: { id: challenge.rpId, name: challenge.rpName },
35
+ user: {
36
+ id: fromBase64url(challenge.userHandle),
37
+ name: challenge.userName,
38
+ displayName: challenge.userDisplayName,
39
+ },
40
+ pubKeyCredParams: [
41
+ { type: 'public-key', alg: -7 },
42
+ { type: 'public-key', alg: -257 },
43
+ ],
44
+ timeout: challenge.timeoutMs,
45
+ authenticatorSelection: {
46
+ userVerification: 'required',
47
+ residentKey: 'preferred',
48
+ },
49
+ attestation: 'none',
50
+ excludeCredentials: (challenge.excludeCredentialIds || []).map((credentialId) => ({
51
+ id: fromBase64url(credentialId),
52
+ type: 'public-key',
53
+ })),
54
+ },
55
+ });
56
+
57
+ if (!(credential instanceof PublicKeyCredential)) {
58
+ throw new Error('Passkey registration failed');
59
+ }
60
+ const response = credential.response;
61
+ const publicKey = response.getPublicKey?.();
62
+ const algorithm = response.getPublicKeyAlgorithm?.();
63
+ const authenticatorData = response.getAuthenticatorData?.();
64
+ if (!publicKey || algorithm == null || !authenticatorData) {
65
+ throw new Error('This browser does not expose the WebAuthn attestation details required by hazBase');
66
+ }
67
+
68
+ return {
69
+ username: challenge.userName,
70
+ credential: {
71
+ id: credential.id,
72
+ publicKey: toBase64url(publicKey),
73
+ algorithm: algorithmFromCode(algorithm),
74
+ },
75
+ authenticatorData: toBase64url(authenticatorData),
76
+ clientData: toBase64url(response.clientDataJSON),
77
+ attestationData: toBase64url(response.attestationObject),
78
+ };
79
+ }
80
+
81
+ export async function createPasskeyAssertionCredential(challenge) {
82
+ ensureBrowserSupport();
83
+ const credential = await navigator.credentials.get({
84
+ publicKey: {
85
+ challenge: fromBase64url(challenge.challenge),
86
+ rpId: challenge.rpId,
87
+ userVerification: 'required',
88
+ timeout: challenge.timeoutMs,
89
+ allowCredentials: (challenge.credentialIds || []).map((credentialId) => ({
90
+ id: fromBase64url(credentialId),
91
+ type: 'public-key',
92
+ })),
93
+ },
94
+ });
95
+
96
+ if (!(credential instanceof PublicKeyCredential)) {
97
+ throw new Error('Passkey assertion failed');
98
+ }
99
+ const response = credential.response;
100
+ return {
101
+ credentialId: credential.id,
102
+ authenticatorData: toBase64url(response.authenticatorData),
103
+ clientData: toBase64url(response.clientDataJSON),
104
+ signature: toBase64url(response.signature),
105
+ ...(response.userHandle ? { userHandle: toBase64url(response.userHandle) } : {}),
106
+ };
107
+ }
package/web/i18n.js CHANGED
@@ -135,6 +135,26 @@ const translations = {
135
135
  "detail.planReady": "Plan is ready.",
136
136
  "detail.previousMessage": "Previous message",
137
137
  "detail.interruptedTask": "Interrupted task",
138
+ "detail.autoPilotManualEyebrow": "Auto Pilot",
139
+ "detail.autoPilot.manualTitle": "Why this stayed manual",
140
+ "detail.autoPilot.writeDisabled": "Low-risk writes is off right now, so file changes still wait for manual approval.",
141
+ "detail.autoPilot.writeMissingWorkspace": "Auto Pilot could not confirm a current workspace for this file change.",
142
+ "detail.autoPilot.writeMissingDiff": "The file change did not include a usable diff, so Auto Pilot left it for manual review.",
143
+ "detail.autoPilot.writeMissingFiles": "The file change did not include any referenced files for Auto Pilot to check.",
144
+ "detail.autoPilot.writeInvalidFiles": "The referenced files were outside the current workspace or matched a protected path.",
145
+ "detail.autoPilot.writeDiffTooLarge": "This diff is larger than the current low-risk write limits.",
146
+ "detail.autoPilot.writeDiffUnreadable": "Auto Pilot could not parse the diff into a safe file-level change.",
147
+ "detail.autoPilot.writeDangerousDiff": "This diff included create, delete, rename, mode, or binary markers that stay manual.",
148
+ "detail.autoPilot.writeDiffMismatch": "The diff headers did not line up cleanly with the referenced files, so Auto Pilot left it for manual review.",
149
+ "detail.autoPilot.writeContentDisabled": "This looks like a Content & copy change, but that lane is currently off.",
150
+ "detail.autoPilot.writeUiDisabled": "This looks like a UI & tests change, but that lane is currently off.",
151
+ "detail.autoPilot.writeUiUnsafe": "This UI/test diff added imports or higher-risk APIs, so it stayed manual.",
152
+ "detail.autoPilot.writeUiTooLarge": "UI & tests only covers small diffs, and this one was larger than that lane allows.",
153
+ "detail.autoPilot.writeSourceDisabled": "This looks like a Small code patch, but that beta lane is currently off.",
154
+ "detail.autoPilot.writeSourceUnsafe": "This source diff added a higher-risk API or secret-like token, so it stayed manual.",
155
+ "detail.autoPilot.writeSourceTooLarge": "Small code patches only covers very small single-file diffs.",
156
+ "detail.autoPilot.writeSourceContinuity": "Small code patches needs a recent read of the same file in this thread before Auto Pilot will approve it.",
157
+ "detail.autoPilot.writeNoLaneMatch": "This change did not fit any enabled low-risk write lane in the current workspace.",
138
158
  "detail.imageAlt": ({ index }) => `Attached image ${index}`,
139
159
  "detail.filesTitle": "Files",
140
160
  "detail.diffTitle": "Diff",
@@ -312,6 +332,35 @@ const translations = {
312
332
  "settings.awayMode.title": "Sync mode",
313
333
  "settings.awayMode.copy": "Let Claude's plan approvals and questions be answered from either your PC browser or any paired device, whichever you reach first.",
314
334
  "settings.awayMode.codexNote": "Codex requires no configuration: approvals and prompts can already be handled from either the desktop or any paired device.",
335
+ "settings.autoPilot.title": "Auto Pilot",
336
+ "settings.autoPilot.copy": "Choose which safe approvals viveworker can handle for you in the current workspace.",
337
+ "settings.autoPilot.trustedReadsTitle": "Safe reads",
338
+ "settings.autoPilot.trustedReadsDescription": "Auto-approve workspace-only reads such as rg, find, git diff, git show, and sed -n.",
339
+ "settings.autoPilot.trustedWritesTitle": "Low-risk writes",
340
+ "settings.autoPilot.trustedWritesDescription": "Choose which write lanes Auto Pilot can handle in the current workspace.",
341
+ "settings.autoPilot.writeLaneContentTitle": "Content & copy",
342
+ "settings.autoPilot.writeLaneContentDescription": "Docs, markdown, and i18n/copy files.",
343
+ "settings.autoPilot.writeLaneUiTestsTitle": "UI & tests",
344
+ "settings.autoPilot.writeLaneUiTestsDescription": "Small CSS, web/component UI changes, and test-only diffs.",
345
+ "settings.autoPilot.writeLaneSourceTitle": "Small code patches (beta)",
346
+ "settings.autoPilot.writeLaneSourceDescription": "Single-file source edits with tighter diff and safety rules.",
347
+ "settings.autoPilot.scopeNote": "Secrets, config, deploy, auth, payments, networked changes, and anything outside the current workspace still require manual approval.",
348
+ "settings.autoPilot.recentTitle": "Recent auto-approved actions",
349
+ "settings.autoPilot.recentEmpty": "Nothing has been auto-approved yet. Recent reads and writes will appear here once Auto Pilot starts handling them.",
350
+ "settings.autoPilot.recentRead": "Read",
351
+ "settings.autoPilot.recentWrite": "Write",
352
+ "settings.autoPilot.recentContent": "Content & copy",
353
+ "settings.autoPilot.recentUiTests": "UI & tests",
354
+ "settings.autoPilot.recentSource": "Small code patch",
355
+ "settings.autoPilot.suggestionsTitle": "Suggested next",
356
+ "settings.autoPilot.suggestionsEmpty": "Keep using viveworker normally. Suggestions will appear here once repeated manual approvals show a clear pattern.",
357
+ "settings.autoPilot.suggestionEnable": "Enable",
358
+ "settings.autoPilot.suggestionContentTitle": "Turn on Content & copy",
359
+ "settings.autoPilot.suggestionContentBody": ({ count }) => `${count} recent manual approvals matched docs, markdown, or i18n/copy edits.`,
360
+ "settings.autoPilot.suggestionUiTestsTitle": "Turn on UI & tests",
361
+ "settings.autoPilot.suggestionUiTestsBody": ({ count }) => `${count} recent manual approvals matched small UI or test diffs.`,
362
+ "settings.autoPilot.suggestionSourceTitle": "Turn on Small code patches (beta)",
363
+ "settings.autoPilot.suggestionSourceBody": ({ count }) => `${count} recent manual approvals matched small single-file source patches.`,
315
364
  "settings.notifications.copy":
316
365
  "Keep approvals, plans, questions, and completions within reach even when viveworker is in the background.",
317
366
  "settings.notifications.serverDisabled": "Web Push is not enabled on the server yet.",
@@ -374,6 +423,8 @@ const translations = {
374
423
  "settings.row.currentDeviceSubscribed": "This device subscribed",
375
424
  "settings.row.lastSuccessfulDelivery": "Last successful delivery",
376
425
  "settings.row.version": "Version",
426
+ "settings.updateAvailable.title": "New version available",
427
+ "settings.updateAvailable.copy": "viveworker v{latest} is available. You are currently using v{current}.",
377
428
  "settings.row.currentLanguage": "Current language",
378
429
  "settings.row.languageSource": "Language source",
379
430
  "settings.row.defaultLanguage": "Setup default",
@@ -461,6 +512,77 @@ const translations = {
461
512
  "settings.a2aShare.expired": "Expired",
462
513
  "settings.a2aShare.status.unreachable": "Unreachable",
463
514
  "settings.a2aShare.error": "Upstream fetch failed: {reason}",
515
+ "settings.wallet.title": "Wallet",
516
+ "settings.wallet.subtitle": "Sign-in, passkey, Base addresses",
517
+ "settings.wallet.copy": "Manage your wallet sign-in, passkey, and Base addresses.",
518
+ "settings.wallet.unavailable": "Wallet is not configured for this viveworker instance.",
519
+ "settings.wallet.summary.title": "Current status",
520
+ "settings.wallet.flow.title": "Recommended order",
521
+ "settings.wallet.flow.banner.title": "Let's set up your wallet",
522
+ "settings.wallet.flow.copy": "You need to activate your wallet to exchange task rewards over A2A.",
523
+ "settings.wallet.sessionExpired.title": "Refresh your wallet session",
524
+ "settings.wallet.sessionExpired.copy": "Your wallet session expired. Sign in again with a one-time password; your passkey and Base Sepolia address stay linked.",
525
+ "settings.wallet.ready.title": "Ready to use",
526
+ "settings.wallet.ready.copy": "Your email sign-in, passkey, and Base Sepolia wallet are all set.",
527
+ "settings.wallet.ready.payoutIntro": "Agents will use this address for payouts:",
528
+ "settings.wallet.ready.copyAddress": "Tap to copy address",
529
+ "settings.wallet.advanced.title": "Account actions",
530
+ "settings.wallet.stepNumber": ({ count }) => `Step ${count}`,
531
+ "settings.wallet.status.complete": "Done",
532
+ "settings.wallet.status.current": "Do next",
533
+ "settings.wallet.status.pending": "Pending",
534
+ "settings.wallet.status.locked": "Waiting",
535
+ "settings.wallet.status.optional": "Optional",
536
+ "settings.wallet.step.signIn.title": "Sign in with email",
537
+ "settings.wallet.step.signIn.copy": "Send a one-time password to your email, then enter it here to sign in to your wallet.",
538
+ "settings.wallet.step.refreshSession.title": "Refresh session",
539
+ "settings.wallet.step.refreshSession.copy": "Send a new one-time password and sign in again before continuing wallet actions.",
540
+ "settings.wallet.step.passkey.title": "Register a passkey",
541
+ "settings.wallet.step.passkey.copy": "Register a passkey on this device. You will use it for wallet issuance and sensitive wallet actions.",
542
+ "settings.wallet.step.baseSepolia.title": "Issue a Base Sepolia wallet",
543
+ "settings.wallet.step.baseSepolia.copy": "Create a testing wallet for the current beta flow. This is the main wallet we recommend issuing first.",
544
+ "settings.wallet.step.base.title": "Issue a Base wallet",
545
+ "settings.wallet.step.base.copy": "Create a Base mainnet wallet when you are ready for production payouts. This step is optional for now.",
546
+ "settings.wallet.mainnet.optIn": "Also set up Base mainnet",
547
+ "settings.wallet.mainnet.optInHint": "Optional — required only for production payouts.",
548
+ "settings.hazbase.title": "Wallet",
549
+ "settings.hazbase.status.signedOut": "Signed out",
550
+ "settings.hazbase.status.signedIn": "Signed in",
551
+ "settings.hazbase.status.sessionExpired": "Session expired",
552
+ "settings.hazbase.status.otpAwaitingVerify": "Sent. Check your inbox, then enter the code below.",
553
+ "settings.hazbase.passkey.ready": "Registered on this device",
554
+ "settings.hazbase.passkey.missing": "Not registered yet",
555
+ "settings.hazbase.passkey.localHostRequired": "Open viveworker on its .local hostname to register a passkey.",
556
+ "settings.hazbase.wallet.missing": "Not issued yet",
557
+ "settings.hazbase.field.emailLabel": "Email address",
558
+ "settings.hazbase.field.emailPlaceholder": "you@example.com",
559
+ "settings.hazbase.field.otpLabel": "One-time password",
560
+ "settings.hazbase.field.otpPlaceholder": "e.g. 123456",
561
+ "settings.hazbase.action.requestOtp": "Send one-time password",
562
+ "settings.hazbase.action.refreshSession": "Refresh session",
563
+ "settings.hazbase.action.resendOtp": "Resend",
564
+ "settings.hazbase.action.verifyOtp": "Sign in",
565
+ "settings.hazbase.action.changeEmail": "Change email",
566
+ "settings.hazbase.action.registerPasskey": "Register passkey",
567
+ "settings.hazbase.action.bootstrapBaseSepolia": "Issue Base Sepolia",
568
+ "settings.hazbase.action.bootstrapBase": "Issue Base",
569
+ "settings.hazbase.action.signOut": "Sign out of wallet",
570
+ "settings.hazbase.logout.confirm.title": "Sign out of wallet?",
571
+ "settings.hazbase.logout.confirm.copy": "You'll need to sign back in with a one-time password to access this wallet again. Your wallet address and passkey stay linked to this account.",
572
+ "settings.hazbase.logout.confirm.confirmLabel": "Sign out",
573
+ "settings.hazbase.notice.otpRequested": "One-time password sent.",
574
+ "settings.hazbase.notice.otpVerified": "Signed in to wallet.",
575
+ "settings.hazbase.notice.passkeyRegistered": "Passkey registered.",
576
+ "settings.hazbase.notice.walletBootstrapped": "Wallet issued on chain {chainId}.",
577
+ "settings.hazbase.notice.signedOut": "Signed out of wallet.",
578
+ "settings.hazbase.notice.sessionRefreshStarted": "Session refresh started. Send a one-time password to sign in again.",
579
+ "settings.hazbase.prompt.email": "Wallet email address",
580
+ "settings.hazbase.prompt.otp": "One-time password",
581
+ "settings.row.hazbaseStatus": "Wallet",
582
+ "settings.row.hazbaseEmail": "Wallet email",
583
+ "settings.row.hazbasePasskey": "Passkey",
584
+ "settings.row.hazbaseBaseSepolia": "Base Sepolia wallet",
585
+ "settings.row.hazbaseBase": "Base wallet",
464
586
  "settings.row.a2aShareStatus": "Status",
465
587
  "settings.row.a2aShareEndpoint": "Endpoint",
466
588
  "settings.row.a2aShareUserId": "User ID",
@@ -525,6 +647,13 @@ const translations = {
525
647
  "error.choiceInputReadOnly": "Please answer this type of input on the Mac.",
526
648
  "error.choiceInputAlreadyHandled": "This choice request was already handled.",
527
649
  "error.mkcertRootCaNotFound": "The mkcert root CA file could not be found.",
650
+ "error.hazbaseAuthRequired": "Sign in to your wallet first.",
651
+ "error.hazbaseSessionExpired": "Your wallet session expired. Open Wallet settings and sign in again.",
652
+ "error.hazbaseEmailRequired": "Enter your email address first.",
653
+ "error.hazbaseOtpRequired": "Enter the one-time password.",
654
+ "error.hazbasePasskeyLocalHostRequired": "Open viveworker on its .local hostname to use hazBase passkeys.",
655
+ "error.hazbaseWalletAccountMissing": "Issue a wallet on this chain first.",
656
+ "error.unsupportedChain": "That chain is not supported here.",
528
657
  "server.fallback.codexTask": "{provider} task",
529
658
  "server.title.approval": "Approval",
530
659
  "server.title.complete": "Completed",
@@ -540,10 +669,19 @@ const translations = {
540
669
  "server.action.detail": "Detail",
541
670
  "server.action.select": "Choose",
542
671
  "server.action.approve": "Approve",
672
+ "server.action.payWithWallet": "Pay with wallet",
543
673
  "server.action.reject": "Reject",
544
674
  "server.action.implement": "Implement",
545
675
  "server.message.commandApprovalNeeded": "Command approval needed.",
546
676
  "server.message.commandLabel": "Command",
677
+ "server.message.filesLabel": "Files",
678
+ "server.message.diffSummaryLabel": "Diff summary:",
679
+ "server.message.autoPilotTrustedReadApproved": "Auto Pilot approved this trusted read command.",
680
+ "server.message.autoPilotTrustedReadSummary": "Auto Pilot approved a trusted read command.",
681
+ "server.message.autoPilotTrustedReadReason": "Auto-approved by viveworker Auto Pilot because it matched the trusted read policy.",
682
+ "server.message.autoPilotTrustedWriteApproved": "Auto Pilot approved this low-risk file change.",
683
+ "server.message.autoPilotTrustedWriteSummary": "Auto Pilot approved a low-risk file change.",
684
+ "server.message.autoPilotTrustedWriteReason": "Auto-approved by viveworker Auto Pilot because it matched the low-risk write policy for the current workspace.",
547
685
  "server.message.fileApprovalNeeded": "File changes need approval.",
548
686
  "server.message.commandPrefix": ({ command }) => `Command: ${command}`,
549
687
  "server.message.pathPrefix": ({ path }) => `Path: ${path}`,
@@ -917,6 +1055,26 @@ const translations = {
917
1055
  "detail.planReady": "プランの確認が必要です。",
918
1056
  "detail.previousMessage": "ひとつ前のメッセージ",
919
1057
  "detail.interruptedTask": "中断されたタスク",
1058
+ "detail.autoPilotManualEyebrow": "Auto Pilot",
1059
+ "detail.autoPilot.manualTitle": "手動承認になった理由",
1060
+ "detail.autoPilot.writeDisabled": "いまは低リスクな書き込みが OFF なので、ファイル変更は手動承認のままです。",
1061
+ "detail.autoPilot.writeMissingWorkspace": "このファイル変更に対して、現在のワークスペースを確認できませんでした。",
1062
+ "detail.autoPilot.writeMissingDiff": "使える diff が含まれていなかったため、Auto Pilot は手動承認へ回しました。",
1063
+ "detail.autoPilot.writeMissingFiles": "Auto Pilot が確認するための対象ファイルが含まれていませんでした。",
1064
+ "detail.autoPilot.writeInvalidFiles": "対象ファイルが現在のワークスペース外か、保護されたパスに当たっていました。",
1065
+ "detail.autoPilot.writeDiffTooLarge": "この diff は、現在の低リスク書き込みの上限を超えています。",
1066
+ "detail.autoPilot.writeDiffUnreadable": "diff を安全なファイル単位の変更として解釈できませんでした。",
1067
+ "detail.autoPilot.writeDangerousDiff": "作成・削除・rename・mode 変更・binary を含むため、手動承認のままです。",
1068
+ "detail.autoPilot.writeDiffMismatch": "diff のファイルヘッダと対象ファイルがきれいに一致しなかったため、手動承認へ回しました。",
1069
+ "detail.autoPilot.writeContentDisabled": "文書・文言に見える変更ですが、そのレーンは現在 OFF です。",
1070
+ "detail.autoPilot.writeUiDisabled": "UI・テストに見える変更ですが、そのレーンは現在 OFF です。",
1071
+ "detail.autoPilot.writeUiUnsafe": "この UI / テスト差分には import や高リスク API の追加が含まれていたため、手動承認のままです。",
1072
+ "detail.autoPilot.writeUiTooLarge": "UI・テストは小さな差分だけが対象で、今回はその範囲を超えています。",
1073
+ "detail.autoPilot.writeSourceDisabled": "小さなコード修正に見える変更ですが、そのベータレーンは現在 OFF です。",
1074
+ "detail.autoPilot.writeSourceUnsafe": "このソース差分には高リスク API または secret に近い追加が含まれていたため、手動承認のままです。",
1075
+ "detail.autoPilot.writeSourceTooLarge": "小さなコード修正は、ごく小さい単一ファイル差分だけが対象です。",
1076
+ "detail.autoPilot.writeSourceContinuity": "小さなコード修正は、このスレッド内で同じファイルを直前に読んでいることが必要です。",
1077
+ "detail.autoPilot.writeNoLaneMatch": "この変更は、現在のワークスペースで有効な低リスク書き込みレーンに当てはまりませんでした。",
920
1078
  "detail.imageAlt": ({ index }) => `添付画像 ${index}`,
921
1079
  "detail.filesTitle": "関連ファイル",
922
1080
  "detail.diffTitle": "差分",
@@ -1091,6 +1249,35 @@ const translations = {
1091
1249
  "settings.awayMode.title": "同期モード",
1092
1250
  "settings.awayMode.copy": "Claude のプラン承認・質問を PC ブラウザとペアリング端末の両方から回答できるようにします。先に回答した方が有効です。",
1093
1251
  "settings.awayMode.codexNote": "Codex は設定不要です。承認や質問は PC とペアリング端末のどちらからでも操作できます。",
1252
+ "settings.autoPilot.title": "承認の自動化",
1253
+ "settings.autoPilot.copy": "現在のワークスペース内で、viveworker に任せる安全な承認の範囲を選べます。",
1254
+ "settings.autoPilot.trustedReadsTitle": "安全な読み取り",
1255
+ "settings.autoPilot.trustedReadsDescription": "rg、find、git diff、git show、sed -n など、ワークスペース内の読み取りを自動承認します。",
1256
+ "settings.autoPilot.trustedWritesTitle": "低リスクな書き込み",
1257
+ "settings.autoPilot.trustedWritesDescription": "現在のワークスペース内で、自動承認する書き込みレーンを選べます。",
1258
+ "settings.autoPilot.writeLaneContentTitle": "文書・文言",
1259
+ "settings.autoPilot.writeLaneContentDescription": "README、Markdown、i18n などの文書や文言の更新。",
1260
+ "settings.autoPilot.writeLaneUiTestsTitle": "UI・テスト",
1261
+ "settings.autoPilot.writeLaneUiTestsDescription": "小さな CSS、web/components の UI 変更、テスト差分。",
1262
+ "settings.autoPilot.writeLaneSourceTitle": "小さなコード修正(ベータ)",
1263
+ "settings.autoPilot.writeLaneSourceDescription": "既存ソースへの小さな単一ファイル修正。より厳しい条件で判定します。",
1264
+ "settings.autoPilot.scopeNote": "秘密情報、設定・デプロイ、認証・決済、ネットワークを伴う変更、ワークスペース外へのアクセスは引き続き手動承認になります。",
1265
+ "settings.autoPilot.recentTitle": "最近の自動承認",
1266
+ "settings.autoPilot.recentEmpty": "まだ自動承認はありません。承認の自動化が動くと、最近の読み取りや書き込みがここに表示されます。",
1267
+ "settings.autoPilot.recentRead": "読み取り",
1268
+ "settings.autoPilot.recentWrite": "書き込み",
1269
+ "settings.autoPilot.recentContent": "文書・文言",
1270
+ "settings.autoPilot.recentUiTests": "UI・テスト",
1271
+ "settings.autoPilot.recentSource": "小さなコード修正",
1272
+ "settings.autoPilot.suggestionsTitle": "おすすめ",
1273
+ "settings.autoPilot.suggestionsEmpty": "普段どおり使っていれば大丈夫です。手動承認が繰り返されると、ここにおすすめが表示されます。",
1274
+ "settings.autoPilot.suggestionEnable": "有効化",
1275
+ "settings.autoPilot.suggestionContentTitle": "文書・文言を有効化",
1276
+ "settings.autoPilot.suggestionContentBody": ({ count }) => `最近の手動承認 ${count} 件が、文書・Markdown・i18n の更新に一致しています。`,
1277
+ "settings.autoPilot.suggestionUiTestsTitle": "UI・テストを有効化",
1278
+ "settings.autoPilot.suggestionUiTestsBody": ({ count }) => `最近の手動承認 ${count} 件が、小さな UI 変更やテスト差分に一致しています。`,
1279
+ "settings.autoPilot.suggestionSourceTitle": "小さなコード修正(ベータ)を有効化",
1280
+ "settings.autoPilot.suggestionSourceBody": ({ count }) => `最近の手動承認 ${count} 件が、小さな単一ファイルのソース修正に一致しています。`,
1094
1281
  "settings.notifications.copy":
1095
1282
  "viveworker がバックグラウンドでも、承認、プラン、質問、完了を確認しやすくします。",
1096
1283
  "settings.notifications.serverDisabled": "サーバー側で Web Push がまだ有効化されていません。",
@@ -1153,6 +1340,8 @@ const translations = {
1153
1340
  "settings.row.currentDeviceSubscribed": "この端末の購読状態",
1154
1341
  "settings.row.lastSuccessfulDelivery": "最後の通知成功時刻",
1155
1342
  "settings.row.version": "バージョン",
1343
+ "settings.updateAvailable.title": "新しいバージョンがあります",
1344
+ "settings.updateAvailable.copy": "viveworker v{latest} が利用できます。現在は v{current} です。",
1156
1345
  "settings.row.currentLanguage": "現在の言語",
1157
1346
  "settings.row.languageSource": "言語ソース",
1158
1347
  "settings.row.defaultLanguage": "setup 時の既定値",
@@ -1240,6 +1429,77 @@ const translations = {
1240
1429
  "settings.a2aShare.expired": "期限切れ",
1241
1430
  "settings.a2aShare.status.unreachable": "到達不可",
1242
1431
  "settings.a2aShare.error": "上流への問い合わせに失敗しました: {reason}",
1432
+ "settings.wallet.title": "Wallet",
1433
+ "settings.wallet.subtitle": "サインイン / Passkey / Base アドレス",
1434
+ "settings.wallet.copy": "ウォレットへのサインイン、Passkey、Base 系アドレスの管理を行います。",
1435
+ "settings.wallet.unavailable": "この viveworker では Wallet が設定されていません。",
1436
+ "settings.wallet.summary.title": "現在の状態",
1437
+ "settings.wallet.flow.title": "おすすめの順番",
1438
+ "settings.wallet.flow.banner.title": "まずは、セットアップしましょう",
1439
+ "settings.wallet.flow.copy": "A2A でタスクの報酬をやりとりするには、ウォレットを有効化する必要があります。",
1440
+ "settings.wallet.sessionExpired.title": "ウォレットセッションを更新しましょう",
1441
+ "settings.wallet.sessionExpired.copy": "ウォレットのセッションが切れています。ワンタイムパスワードで再サインインすれば、Passkey と Base Sepolia アドレスはそのまま使えます。",
1442
+ "settings.wallet.ready.title": "利用準備ができました",
1443
+ "settings.wallet.ready.copy": "メールサインイン、Passkey、Base Sepolia ウォレットの準備ができています。",
1444
+ "settings.wallet.ready.payoutIntro": "エージェントは下記アドレスを利用します:",
1445
+ "settings.wallet.ready.copyAddress": "タップでアドレスをコピー",
1446
+ "settings.wallet.advanced.title": "アカウント操作",
1447
+ "settings.wallet.stepNumber": ({ count }) => `STEP ${count}`,
1448
+ "settings.wallet.status.complete": "完了",
1449
+ "settings.wallet.status.current": "次に実行",
1450
+ "settings.wallet.status.pending": "保留",
1451
+ "settings.wallet.status.locked": "前の step 待ち",
1452
+ "settings.wallet.status.optional": "任意",
1453
+ "settings.wallet.step.signIn.title": "メールでサインイン",
1454
+ "settings.wallet.step.signIn.copy": "メールにワンタイムパスワードを送り、届いた値を入力してウォレットにサインインします。",
1455
+ "settings.wallet.step.refreshSession.title": "セッションを更新",
1456
+ "settings.wallet.step.refreshSession.copy": "ウォレット操作を続ける前に、新しいワンタイムパスワードで再サインインします。",
1457
+ "settings.wallet.step.passkey.title": "Passkey を登録",
1458
+ "settings.wallet.step.passkey.copy": "この端末に Passkey を登録します。ウォレット発行や重要な操作で使います。",
1459
+ "settings.wallet.step.baseSepolia.title": "Base Sepolia ウォレットを発行",
1460
+ "settings.wallet.step.baseSepolia.copy": "現在の beta フローで使うテスト用ウォレットを発行します。まずはこれを用意するのがおすすめです。",
1461
+ "settings.wallet.step.base.title": "Base ウォレットを発行",
1462
+ "settings.wallet.step.base.copy": "本番の payout が必要になったら Base mainnet ウォレットを発行します。現時点では任意です。",
1463
+ "settings.wallet.mainnet.optIn": "Base mainnet も設定する",
1464
+ "settings.wallet.mainnet.optInHint": "任意 — 本番 payout が必要な場合だけ。",
1465
+ "settings.hazbase.title": "ウォレット",
1466
+ "settings.hazbase.status.signedOut": "未サインイン",
1467
+ "settings.hazbase.status.signedIn": "サインイン済み",
1468
+ "settings.hazbase.status.sessionExpired": "セッション切れ",
1469
+ "settings.hazbase.status.otpAwaitingVerify": "送信しました。メールを確認して、下のフィールドに入力してください。",
1470
+ "settings.hazbase.passkey.ready": "この端末で登録済み",
1471
+ "settings.hazbase.passkey.missing": "まだ登録されていません",
1472
+ "settings.hazbase.passkey.localHostRequired": ".local ホスト名で viveworker を開くと Passkey を登録できます。",
1473
+ "settings.hazbase.wallet.missing": "未発行",
1474
+ "settings.hazbase.field.emailLabel": "メールアドレス",
1475
+ "settings.hazbase.field.emailPlaceholder": "you@example.com",
1476
+ "settings.hazbase.field.otpLabel": "ワンタイムパスワード",
1477
+ "settings.hazbase.field.otpPlaceholder": "例: 123456",
1478
+ "settings.hazbase.action.requestOtp": "ワンタイムパスワードを送信",
1479
+ "settings.hazbase.action.refreshSession": "セッションを更新",
1480
+ "settings.hazbase.action.resendOtp": "再送信",
1481
+ "settings.hazbase.action.verifyOtp": "サインイン",
1482
+ "settings.hazbase.action.changeEmail": "メールアドレスを変更",
1483
+ "settings.hazbase.action.registerPasskey": "Passkey登録",
1484
+ "settings.hazbase.action.bootstrapBaseSepolia": "Base Sepolia発行",
1485
+ "settings.hazbase.action.bootstrapBase": "Base発行",
1486
+ "settings.hazbase.action.signOut": "ウォレットからログアウト",
1487
+ "settings.hazbase.logout.confirm.title": "ウォレットからログアウトしますか?",
1488
+ "settings.hazbase.logout.confirm.copy": "再度アクセスするには、ワンタイムパスワードでのサインインが必要になります。ウォレットアドレスと passkey は同じアカウントに紐付いたまま残ります。",
1489
+ "settings.hazbase.logout.confirm.confirmLabel": "ログアウトする",
1490
+ "settings.hazbase.notice.otpRequested": "ワンタイムパスワードを送信しました。",
1491
+ "settings.hazbase.notice.otpVerified": "ウォレットにサインインしました。",
1492
+ "settings.hazbase.notice.passkeyRegistered": "Passkeyを登録しました。",
1493
+ "settings.hazbase.notice.walletBootstrapped": "chain {chainId} にウォレットを発行しました。",
1494
+ "settings.hazbase.notice.signedOut": "ウォレットからログアウトしました。",
1495
+ "settings.hazbase.notice.sessionRefreshStarted": "セッション更新を開始しました。ワンタイムパスワードを送信して再サインインしてください。",
1496
+ "settings.hazbase.prompt.email": "ウォレットのメールアドレス",
1497
+ "settings.hazbase.prompt.otp": "ワンタイムパスワード",
1498
+ "settings.row.hazbaseStatus": "ウォレット",
1499
+ "settings.row.hazbaseEmail": "ウォレットのメール",
1500
+ "settings.row.hazbasePasskey": "Passkey",
1501
+ "settings.row.hazbaseBaseSepolia": "Base Sepoliaウォレット",
1502
+ "settings.row.hazbaseBase": "Baseウォレット",
1243
1503
  "settings.row.a2aShareStatus": "ステータス",
1244
1504
  "settings.row.a2aShareEndpoint": "エンドポイント",
1245
1505
  "settings.row.a2aShareUserId": "ユーザー ID",
@@ -1304,6 +1564,13 @@ const translations = {
1304
1564
  "error.choiceInputReadOnly": "この種類の入力は Mac で回答してください。",
1305
1565
  "error.choiceInputAlreadyHandled": "この選択入力はすでに処理済みです。",
1306
1566
  "error.mkcertRootCaNotFound": "mkcert の root CA ファイルが見つかりません。",
1567
+ "error.hazbaseAuthRequired": "先にウォレットへサインインしてください。",
1568
+ "error.hazbaseSessionExpired": "ウォレットのセッションが切れています。Wallet 設定を開いて再サインインしてください。",
1569
+ "error.hazbaseEmailRequired": "先にメールアドレスを入力してください。",
1570
+ "error.hazbaseOtpRequired": "ワンタイムパスワードを入力してください。",
1571
+ "error.hazbasePasskeyLocalHostRequired": ".local ホスト名で viveworker を開くと hazBase Passkey を使えます。",
1572
+ "error.hazbaseWalletAccountMissing": "先にこのチェーンのウォレットを発行してください。",
1573
+ "error.unsupportedChain": "このチェーンはここではサポートされていません。",
1307
1574
  "server.fallback.codexTask": "{provider} task",
1308
1575
  "server.title.approval": "要承認",
1309
1576
  "server.title.complete": "完了",
@@ -1319,10 +1586,19 @@ const translations = {
1319
1586
  "server.action.detail": "詳細",
1320
1587
  "server.action.select": "選ぶ",
1321
1588
  "server.action.approve": "承認",
1589
+ "server.action.payWithWallet": "ウォレットで支払う",
1322
1590
  "server.action.reject": "拒否",
1323
1591
  "server.action.implement": "実装する",
1324
1592
  "server.message.commandApprovalNeeded": "コマンド実行の承認が必要です。",
1325
1593
  "server.message.commandLabel": "コマンド",
1594
+ "server.message.filesLabel": "Files",
1595
+ "server.message.diffSummaryLabel": "差分サマリ:",
1596
+ "server.message.autoPilotTrustedReadApproved": "Auto Pilot がこの Trusted Read コマンドを自動承認しました。",
1597
+ "server.message.autoPilotTrustedReadSummary": "Auto Pilot が Trusted Read コマンドを自動承認しました。",
1598
+ "server.message.autoPilotTrustedReadReason": "viveworker Auto Pilot が Trusted Read ポリシーに一致したため自動承認しました。",
1599
+ "server.message.autoPilotTrustedWriteApproved": "承認の自動化がこの低リスクなファイル変更を自動承認しました。",
1600
+ "server.message.autoPilotTrustedWriteSummary": "承認の自動化が低リスクなファイル変更を自動承認しました。",
1601
+ "server.message.autoPilotTrustedWriteReason": "viveworker が現在のワークスペース向け低リスク書き込みポリシーに一致したため自動承認しました。",
1326
1602
  "server.message.fileApprovalNeeded": "ファイル変更の承認が必要です。",
1327
1603
  "server.message.commandPrefix": ({ command }) => `Command: ${command}`,
1328
1604
  "server.message.pathPrefix": ({ path }) => `Path: ${path}`,
package/web/sw.js CHANGED
@@ -1,4 +1,4 @@
1
- const CACHE_NAME = "viveworker-v25";
1
+ const CACHE_NAME = "viveworker-v56";
2
2
  const NOTIFICATION_INTENT_CACHE = "viveworker-notification-intent-v1";
3
3
  const NOTIFICATION_INTENT_PATH = "/__viveworker_notification_intent__";
4
4
  const APP_ASSETS = ["/app.css", "/app.js", "/i18n.js"];
@@ -44,12 +44,12 @@ self.addEventListener("fetch", (event) => {
44
44
  }
45
45
 
46
46
  if (APP_ROUTES.has(url.pathname)) {
47
- event.respondWith(networkFirst(event.request, "/app"));
47
+ event.respondWith(staleWhileRevalidate(event, "/app"));
48
48
  return;
49
49
  }
50
50
 
51
51
  if (CACHED_PATHS.has(url.pathname)) {
52
- event.respondWith(networkFirst(event.request, url.pathname));
52
+ event.respondWith(staleWhileRevalidate(event, url.pathname));
53
53
  }
54
54
  });
55
55
 
@@ -182,19 +182,37 @@ async function notifyClients(type) {
182
182
  }
183
183
  }
184
184
 
185
- async function networkFirst(request, cacheKey) {
185
+ // Cache-first with background revalidation. Flips the previous networkFirst
186
+ // strategy: instead of blocking first paint on a fresh fetch of the ~450KB
187
+ // app shell (HTML + app.js + app.css + i18n.js) every launch, we serve the
188
+ // cached copy immediately and refresh it in the background for the next
189
+ // visit. Updates still land quickly because the SW itself is served fresh
190
+ // from the bridge (`/sw.js` is excluded from this handler above), and a new
191
+ // SW's `install` event pre-populates the cache with the latest assets
192
+ // before `activate`/`clients.claim()` triggers a reload in page script.
193
+ async function staleWhileRevalidate(event, cacheKey) {
186
194
  const cache = await caches.open(CACHE_NAME);
187
- try {
188
- const response = await fetch(request, { cache: "no-store" });
189
- if (response && response.ok) {
190
- await cache.put(cacheKey, response.clone());
191
- }
195
+ const cached = await cache.match(cacheKey);
196
+
197
+ const networkPromise = fetch(event.request, { cache: "no-store" })
198
+ .then(async (response) => {
199
+ if (response && response.ok) {
200
+ await cache.put(cacheKey, response.clone());
201
+ }
202
+ return response;
203
+ })
204
+ .catch(() => null);
205
+
206
+ if (cached) {
207
+ // Keep the SW alive long enough to persist the background refresh even
208
+ // if the page navigates away right after first paint.
209
+ event.waitUntil(networkPromise);
210
+ return cached;
211
+ }
212
+
213
+ const response = await networkPromise;
214
+ if (response) {
192
215
  return response;
193
- } catch {
194
- const cached = await cache.match(cacheKey);
195
- if (cached) {
196
- return cached;
197
- }
198
- return Response.error();
199
216
  }
217
+ return Response.error();
200
218
  }