viveworker 0.7.0-beta.0 → 0.7.0-beta.2

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.",
@@ -461,6 +510,70 @@ const translations = {
461
510
  "settings.a2aShare.expired": "Expired",
462
511
  "settings.a2aShare.status.unreachable": "Unreachable",
463
512
  "settings.a2aShare.error": "Upstream fetch failed: {reason}",
513
+ "settings.wallet.title": "Wallet",
514
+ "settings.wallet.subtitle": "Sign-in, passkey, Base addresses",
515
+ "settings.wallet.copy": "Manage your wallet sign-in, passkey, and Base addresses.",
516
+ "settings.wallet.unavailable": "Wallet is not configured for this viveworker instance.",
517
+ "settings.wallet.summary.title": "Current status",
518
+ "settings.wallet.flow.title": "Recommended order",
519
+ "settings.wallet.flow.banner.title": "Let's set up your wallet",
520
+ "settings.wallet.flow.copy": "You need to activate your wallet to exchange task rewards over A2A.",
521
+ "settings.wallet.ready.title": "Ready to use",
522
+ "settings.wallet.ready.copy": "Your email sign-in, passkey, and Base Sepolia wallet are all set.",
523
+ "settings.wallet.ready.payoutIntro": "Agents will use this address for payouts:",
524
+ "settings.wallet.ready.copyAddress": "Tap to copy address",
525
+ "settings.wallet.advanced.title": "Account actions",
526
+ "settings.wallet.stepNumber": ({ count }) => `Step ${count}`,
527
+ "settings.wallet.status.complete": "Done",
528
+ "settings.wallet.status.current": "Do next",
529
+ "settings.wallet.status.pending": "Pending",
530
+ "settings.wallet.status.locked": "Waiting",
531
+ "settings.wallet.status.optional": "Optional",
532
+ "settings.wallet.step.signIn.title": "Sign in with email",
533
+ "settings.wallet.step.signIn.copy": "Send a one-time password to your email, then enter it here to sign in to your wallet.",
534
+ "settings.wallet.step.passkey.title": "Register a passkey",
535
+ "settings.wallet.step.passkey.copy": "Register a passkey on this device. You will use it for wallet issuance and sensitive wallet actions.",
536
+ "settings.wallet.step.baseSepolia.title": "Issue a Base Sepolia wallet",
537
+ "settings.wallet.step.baseSepolia.copy": "Create a testing wallet for the current beta flow. This is the main wallet we recommend issuing first.",
538
+ "settings.wallet.step.base.title": "Issue a Base wallet",
539
+ "settings.wallet.step.base.copy": "Create a Base mainnet wallet when you are ready for production payouts. This step is optional for now.",
540
+ "settings.wallet.mainnet.optIn": "Also set up Base mainnet",
541
+ "settings.wallet.mainnet.optInHint": "Optional — required only for production payouts.",
542
+ "settings.hazbase.title": "Wallet",
543
+ "settings.hazbase.status.signedOut": "Signed out",
544
+ "settings.hazbase.status.signedIn": "Signed in",
545
+ "settings.hazbase.status.otpAwaitingVerify": "Sent. Check your inbox, then enter the code below.",
546
+ "settings.hazbase.passkey.ready": "Registered on this device",
547
+ "settings.hazbase.passkey.missing": "Not registered yet",
548
+ "settings.hazbase.passkey.localHostRequired": "Open viveworker on its .local hostname to register a passkey.",
549
+ "settings.hazbase.wallet.missing": "Not issued yet",
550
+ "settings.hazbase.field.emailLabel": "Email address",
551
+ "settings.hazbase.field.emailPlaceholder": "you@example.com",
552
+ "settings.hazbase.field.otpLabel": "One-time password",
553
+ "settings.hazbase.field.otpPlaceholder": "e.g. 123456",
554
+ "settings.hazbase.action.requestOtp": "Send one-time password",
555
+ "settings.hazbase.action.resendOtp": "Resend",
556
+ "settings.hazbase.action.verifyOtp": "Sign in",
557
+ "settings.hazbase.action.changeEmail": "Change email",
558
+ "settings.hazbase.action.registerPasskey": "Register passkey",
559
+ "settings.hazbase.action.bootstrapBaseSepolia": "Issue Base Sepolia",
560
+ "settings.hazbase.action.bootstrapBase": "Issue Base",
561
+ "settings.hazbase.action.signOut": "Sign out of wallet",
562
+ "settings.hazbase.logout.confirm.title": "Sign out of wallet?",
563
+ "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.",
564
+ "settings.hazbase.logout.confirm.confirmLabel": "Sign out",
565
+ "settings.hazbase.notice.otpRequested": "One-time password sent.",
566
+ "settings.hazbase.notice.otpVerified": "Signed in to wallet.",
567
+ "settings.hazbase.notice.passkeyRegistered": "Passkey registered.",
568
+ "settings.hazbase.notice.walletBootstrapped": "Wallet issued on chain {chainId}.",
569
+ "settings.hazbase.notice.signedOut": "Signed out of wallet.",
570
+ "settings.hazbase.prompt.email": "Wallet email address",
571
+ "settings.hazbase.prompt.otp": "One-time password",
572
+ "settings.row.hazbaseStatus": "Wallet",
573
+ "settings.row.hazbaseEmail": "Wallet email",
574
+ "settings.row.hazbasePasskey": "Passkey",
575
+ "settings.row.hazbaseBaseSepolia": "Base Sepolia wallet",
576
+ "settings.row.hazbaseBase": "Base wallet",
464
577
  "settings.row.a2aShareStatus": "Status",
465
578
  "settings.row.a2aShareEndpoint": "Endpoint",
466
579
  "settings.row.a2aShareUserId": "User ID",
@@ -525,6 +638,12 @@ const translations = {
525
638
  "error.choiceInputReadOnly": "Please answer this type of input on the Mac.",
526
639
  "error.choiceInputAlreadyHandled": "This choice request was already handled.",
527
640
  "error.mkcertRootCaNotFound": "The mkcert root CA file could not be found.",
641
+ "error.hazbaseAuthRequired": "Sign in to your wallet first.",
642
+ "error.hazbaseEmailRequired": "Enter your email address first.",
643
+ "error.hazbaseOtpRequired": "Enter the one-time password.",
644
+ "error.hazbasePasskeyLocalHostRequired": "Open viveworker on its .local hostname to use hazBase passkeys.",
645
+ "error.hazbaseWalletAccountMissing": "Issue a wallet on this chain first.",
646
+ "error.unsupportedChain": "That chain is not supported here.",
528
647
  "server.fallback.codexTask": "{provider} task",
529
648
  "server.title.approval": "Approval",
530
649
  "server.title.complete": "Completed",
@@ -544,6 +663,14 @@ const translations = {
544
663
  "server.action.implement": "Implement",
545
664
  "server.message.commandApprovalNeeded": "Command approval needed.",
546
665
  "server.message.commandLabel": "Command",
666
+ "server.message.filesLabel": "Files",
667
+ "server.message.diffSummaryLabel": "Diff summary:",
668
+ "server.message.autoPilotTrustedReadApproved": "Auto Pilot approved this trusted read command.",
669
+ "server.message.autoPilotTrustedReadSummary": "Auto Pilot approved a trusted read command.",
670
+ "server.message.autoPilotTrustedReadReason": "Auto-approved by viveworker Auto Pilot because it matched the trusted read policy.",
671
+ "server.message.autoPilotTrustedWriteApproved": "Auto Pilot approved this low-risk file change.",
672
+ "server.message.autoPilotTrustedWriteSummary": "Auto Pilot approved a low-risk file change.",
673
+ "server.message.autoPilotTrustedWriteReason": "Auto-approved by viveworker Auto Pilot because it matched the low-risk write policy for the current workspace.",
547
674
  "server.message.fileApprovalNeeded": "File changes need approval.",
548
675
  "server.message.commandPrefix": ({ command }) => `Command: ${command}`,
549
676
  "server.message.pathPrefix": ({ path }) => `Path: ${path}`,
@@ -917,6 +1044,26 @@ const translations = {
917
1044
  "detail.planReady": "プランの確認が必要です。",
918
1045
  "detail.previousMessage": "ひとつ前のメッセージ",
919
1046
  "detail.interruptedTask": "中断されたタスク",
1047
+ "detail.autoPilotManualEyebrow": "Auto Pilot",
1048
+ "detail.autoPilot.manualTitle": "手動承認になった理由",
1049
+ "detail.autoPilot.writeDisabled": "いまは低リスクな書き込みが OFF なので、ファイル変更は手動承認のままです。",
1050
+ "detail.autoPilot.writeMissingWorkspace": "このファイル変更に対して、現在のワークスペースを確認できませんでした。",
1051
+ "detail.autoPilot.writeMissingDiff": "使える diff が含まれていなかったため、Auto Pilot は手動承認へ回しました。",
1052
+ "detail.autoPilot.writeMissingFiles": "Auto Pilot が確認するための対象ファイルが含まれていませんでした。",
1053
+ "detail.autoPilot.writeInvalidFiles": "対象ファイルが現在のワークスペース外か、保護されたパスに当たっていました。",
1054
+ "detail.autoPilot.writeDiffTooLarge": "この diff は、現在の低リスク書き込みの上限を超えています。",
1055
+ "detail.autoPilot.writeDiffUnreadable": "diff を安全なファイル単位の変更として解釈できませんでした。",
1056
+ "detail.autoPilot.writeDangerousDiff": "作成・削除・rename・mode 変更・binary を含むため、手動承認のままです。",
1057
+ "detail.autoPilot.writeDiffMismatch": "diff のファイルヘッダと対象ファイルがきれいに一致しなかったため、手動承認へ回しました。",
1058
+ "detail.autoPilot.writeContentDisabled": "文書・文言に見える変更ですが、そのレーンは現在 OFF です。",
1059
+ "detail.autoPilot.writeUiDisabled": "UI・テストに見える変更ですが、そのレーンは現在 OFF です。",
1060
+ "detail.autoPilot.writeUiUnsafe": "この UI / テスト差分には import や高リスク API の追加が含まれていたため、手動承認のままです。",
1061
+ "detail.autoPilot.writeUiTooLarge": "UI・テストは小さな差分だけが対象で、今回はその範囲を超えています。",
1062
+ "detail.autoPilot.writeSourceDisabled": "小さなコード修正に見える変更ですが、そのベータレーンは現在 OFF です。",
1063
+ "detail.autoPilot.writeSourceUnsafe": "このソース差分には高リスク API または secret に近い追加が含まれていたため、手動承認のままです。",
1064
+ "detail.autoPilot.writeSourceTooLarge": "小さなコード修正は、ごく小さい単一ファイル差分だけが対象です。",
1065
+ "detail.autoPilot.writeSourceContinuity": "小さなコード修正は、このスレッド内で同じファイルを直前に読んでいることが必要です。",
1066
+ "detail.autoPilot.writeNoLaneMatch": "この変更は、現在のワークスペースで有効な低リスク書き込みレーンに当てはまりませんでした。",
920
1067
  "detail.imageAlt": ({ index }) => `添付画像 ${index}`,
921
1068
  "detail.filesTitle": "関連ファイル",
922
1069
  "detail.diffTitle": "差分",
@@ -1091,6 +1238,35 @@ const translations = {
1091
1238
  "settings.awayMode.title": "同期モード",
1092
1239
  "settings.awayMode.copy": "Claude のプラン承認・質問を PC ブラウザとペアリング端末の両方から回答できるようにします。先に回答した方が有効です。",
1093
1240
  "settings.awayMode.codexNote": "Codex は設定不要です。承認や質問は PC とペアリング端末のどちらからでも操作できます。",
1241
+ "settings.autoPilot.title": "承認の自動化",
1242
+ "settings.autoPilot.copy": "現在のワークスペース内で、viveworker に任せる安全な承認の範囲を選べます。",
1243
+ "settings.autoPilot.trustedReadsTitle": "安全な読み取り",
1244
+ "settings.autoPilot.trustedReadsDescription": "rg、find、git diff、git show、sed -n など、ワークスペース内の読み取りを自動承認します。",
1245
+ "settings.autoPilot.trustedWritesTitle": "低リスクな書き込み",
1246
+ "settings.autoPilot.trustedWritesDescription": "現在のワークスペース内で、自動承認する書き込みレーンを選べます。",
1247
+ "settings.autoPilot.writeLaneContentTitle": "文書・文言",
1248
+ "settings.autoPilot.writeLaneContentDescription": "README、Markdown、i18n などの文書や文言の更新。",
1249
+ "settings.autoPilot.writeLaneUiTestsTitle": "UI・テスト",
1250
+ "settings.autoPilot.writeLaneUiTestsDescription": "小さな CSS、web/components の UI 変更、テスト差分。",
1251
+ "settings.autoPilot.writeLaneSourceTitle": "小さなコード修正(ベータ)",
1252
+ "settings.autoPilot.writeLaneSourceDescription": "既存ソースへの小さな単一ファイル修正。より厳しい条件で判定します。",
1253
+ "settings.autoPilot.scopeNote": "秘密情報、設定・デプロイ、認証・決済、ネットワークを伴う変更、ワークスペース外へのアクセスは引き続き手動承認になります。",
1254
+ "settings.autoPilot.recentTitle": "最近の自動承認",
1255
+ "settings.autoPilot.recentEmpty": "まだ自動承認はありません。承認の自動化が動くと、最近の読み取りや書き込みがここに表示されます。",
1256
+ "settings.autoPilot.recentRead": "読み取り",
1257
+ "settings.autoPilot.recentWrite": "書き込み",
1258
+ "settings.autoPilot.recentContent": "文書・文言",
1259
+ "settings.autoPilot.recentUiTests": "UI・テスト",
1260
+ "settings.autoPilot.recentSource": "小さなコード修正",
1261
+ "settings.autoPilot.suggestionsTitle": "おすすめ",
1262
+ "settings.autoPilot.suggestionsEmpty": "普段どおり使っていれば大丈夫です。手動承認が繰り返されると、ここにおすすめが表示されます。",
1263
+ "settings.autoPilot.suggestionEnable": "有効化",
1264
+ "settings.autoPilot.suggestionContentTitle": "文書・文言を有効化",
1265
+ "settings.autoPilot.suggestionContentBody": ({ count }) => `最近の手動承認 ${count} 件が、文書・Markdown・i18n の更新に一致しています。`,
1266
+ "settings.autoPilot.suggestionUiTestsTitle": "UI・テストを有効化",
1267
+ "settings.autoPilot.suggestionUiTestsBody": ({ count }) => `最近の手動承認 ${count} 件が、小さな UI 変更やテスト差分に一致しています。`,
1268
+ "settings.autoPilot.suggestionSourceTitle": "小さなコード修正(ベータ)を有効化",
1269
+ "settings.autoPilot.suggestionSourceBody": ({ count }) => `最近の手動承認 ${count} 件が、小さな単一ファイルのソース修正に一致しています。`,
1094
1270
  "settings.notifications.copy":
1095
1271
  "viveworker がバックグラウンドでも、承認、プラン、質問、完了を確認しやすくします。",
1096
1272
  "settings.notifications.serverDisabled": "サーバー側で Web Push がまだ有効化されていません。",
@@ -1240,6 +1416,70 @@ const translations = {
1240
1416
  "settings.a2aShare.expired": "期限切れ",
1241
1417
  "settings.a2aShare.status.unreachable": "到達不可",
1242
1418
  "settings.a2aShare.error": "上流への問い合わせに失敗しました: {reason}",
1419
+ "settings.wallet.title": "Wallet",
1420
+ "settings.wallet.subtitle": "サインイン / Passkey / Base アドレス",
1421
+ "settings.wallet.copy": "ウォレットへのサインイン、Passkey、Base 系アドレスの管理を行います。",
1422
+ "settings.wallet.unavailable": "この viveworker では Wallet が設定されていません。",
1423
+ "settings.wallet.summary.title": "現在の状態",
1424
+ "settings.wallet.flow.title": "おすすめの順番",
1425
+ "settings.wallet.flow.banner.title": "まずは、セットアップしましょう",
1426
+ "settings.wallet.flow.copy": "A2A でタスクの報酬をやりとりするには、ウォレットを有効化する必要があります。",
1427
+ "settings.wallet.ready.title": "利用準備ができました",
1428
+ "settings.wallet.ready.copy": "メールサインイン、Passkey、Base Sepolia ウォレットの準備ができています。",
1429
+ "settings.wallet.ready.payoutIntro": "エージェントは下記アドレスを利用します:",
1430
+ "settings.wallet.ready.copyAddress": "タップでアドレスをコピー",
1431
+ "settings.wallet.advanced.title": "アカウント操作",
1432
+ "settings.wallet.stepNumber": ({ count }) => `STEP ${count}`,
1433
+ "settings.wallet.status.complete": "完了",
1434
+ "settings.wallet.status.current": "次に実行",
1435
+ "settings.wallet.status.pending": "保留",
1436
+ "settings.wallet.status.locked": "前の step 待ち",
1437
+ "settings.wallet.status.optional": "任意",
1438
+ "settings.wallet.step.signIn.title": "メールでサインイン",
1439
+ "settings.wallet.step.signIn.copy": "メールにワンタイムパスワードを送り、届いた値を入力してウォレットにサインインします。",
1440
+ "settings.wallet.step.passkey.title": "Passkey を登録",
1441
+ "settings.wallet.step.passkey.copy": "この端末に Passkey を登録します。ウォレット発行や重要な操作で使います。",
1442
+ "settings.wallet.step.baseSepolia.title": "Base Sepolia ウォレットを発行",
1443
+ "settings.wallet.step.baseSepolia.copy": "現在の beta フローで使うテスト用ウォレットを発行します。まずはこれを用意するのがおすすめです。",
1444
+ "settings.wallet.step.base.title": "Base ウォレットを発行",
1445
+ "settings.wallet.step.base.copy": "本番の payout が必要になったら Base mainnet ウォレットを発行します。現時点では任意です。",
1446
+ "settings.wallet.mainnet.optIn": "Base mainnet も設定する",
1447
+ "settings.wallet.mainnet.optInHint": "任意 — 本番 payout が必要な場合だけ。",
1448
+ "settings.hazbase.title": "ウォレット",
1449
+ "settings.hazbase.status.signedOut": "未サインイン",
1450
+ "settings.hazbase.status.signedIn": "サインイン済み",
1451
+ "settings.hazbase.status.otpAwaitingVerify": "送信しました。メールを確認して、下のフィールドに入力してください。",
1452
+ "settings.hazbase.passkey.ready": "この端末で登録済み",
1453
+ "settings.hazbase.passkey.missing": "まだ登録されていません",
1454
+ "settings.hazbase.passkey.localHostRequired": ".local ホスト名で viveworker を開くと Passkey を登録できます。",
1455
+ "settings.hazbase.wallet.missing": "未発行",
1456
+ "settings.hazbase.field.emailLabel": "メールアドレス",
1457
+ "settings.hazbase.field.emailPlaceholder": "you@example.com",
1458
+ "settings.hazbase.field.otpLabel": "ワンタイムパスワード",
1459
+ "settings.hazbase.field.otpPlaceholder": "例: 123456",
1460
+ "settings.hazbase.action.requestOtp": "ワンタイムパスワードを送信",
1461
+ "settings.hazbase.action.resendOtp": "再送信",
1462
+ "settings.hazbase.action.verifyOtp": "サインイン",
1463
+ "settings.hazbase.action.changeEmail": "メールアドレスを変更",
1464
+ "settings.hazbase.action.registerPasskey": "Passkey登録",
1465
+ "settings.hazbase.action.bootstrapBaseSepolia": "Base Sepolia発行",
1466
+ "settings.hazbase.action.bootstrapBase": "Base発行",
1467
+ "settings.hazbase.action.signOut": "ウォレットからログアウト",
1468
+ "settings.hazbase.logout.confirm.title": "ウォレットからログアウトしますか?",
1469
+ "settings.hazbase.logout.confirm.copy": "再度アクセスするには、ワンタイムパスワードでのサインインが必要になります。ウォレットアドレスと passkey は同じアカウントに紐付いたまま残ります。",
1470
+ "settings.hazbase.logout.confirm.confirmLabel": "ログアウトする",
1471
+ "settings.hazbase.notice.otpRequested": "ワンタイムパスワードを送信しました。",
1472
+ "settings.hazbase.notice.otpVerified": "ウォレットにサインインしました。",
1473
+ "settings.hazbase.notice.passkeyRegistered": "Passkeyを登録しました。",
1474
+ "settings.hazbase.notice.walletBootstrapped": "chain {chainId} にウォレットを発行しました。",
1475
+ "settings.hazbase.notice.signedOut": "ウォレットからログアウトしました。",
1476
+ "settings.hazbase.prompt.email": "ウォレットのメールアドレス",
1477
+ "settings.hazbase.prompt.otp": "ワンタイムパスワード",
1478
+ "settings.row.hazbaseStatus": "ウォレット",
1479
+ "settings.row.hazbaseEmail": "ウォレットのメール",
1480
+ "settings.row.hazbasePasskey": "Passkey",
1481
+ "settings.row.hazbaseBaseSepolia": "Base Sepoliaウォレット",
1482
+ "settings.row.hazbaseBase": "Baseウォレット",
1243
1483
  "settings.row.a2aShareStatus": "ステータス",
1244
1484
  "settings.row.a2aShareEndpoint": "エンドポイント",
1245
1485
  "settings.row.a2aShareUserId": "ユーザー ID",
@@ -1304,6 +1544,12 @@ const translations = {
1304
1544
  "error.choiceInputReadOnly": "この種類の入力は Mac で回答してください。",
1305
1545
  "error.choiceInputAlreadyHandled": "この選択入力はすでに処理済みです。",
1306
1546
  "error.mkcertRootCaNotFound": "mkcert の root CA ファイルが見つかりません。",
1547
+ "error.hazbaseAuthRequired": "先にウォレットへサインインしてください。",
1548
+ "error.hazbaseEmailRequired": "先にメールアドレスを入力してください。",
1549
+ "error.hazbaseOtpRequired": "ワンタイムパスワードを入力してください。",
1550
+ "error.hazbasePasskeyLocalHostRequired": ".local ホスト名で viveworker を開くと hazBase Passkey を使えます。",
1551
+ "error.hazbaseWalletAccountMissing": "先にこのチェーンのウォレットを発行してください。",
1552
+ "error.unsupportedChain": "このチェーンはここではサポートされていません。",
1307
1553
  "server.fallback.codexTask": "{provider} task",
1308
1554
  "server.title.approval": "要承認",
1309
1555
  "server.title.complete": "完了",
@@ -1323,6 +1569,14 @@ const translations = {
1323
1569
  "server.action.implement": "実装する",
1324
1570
  "server.message.commandApprovalNeeded": "コマンド実行の承認が必要です。",
1325
1571
  "server.message.commandLabel": "コマンド",
1572
+ "server.message.filesLabel": "Files",
1573
+ "server.message.diffSummaryLabel": "差分サマリ:",
1574
+ "server.message.autoPilotTrustedReadApproved": "Auto Pilot がこの Trusted Read コマンドを自動承認しました。",
1575
+ "server.message.autoPilotTrustedReadSummary": "Auto Pilot が Trusted Read コマンドを自動承認しました。",
1576
+ "server.message.autoPilotTrustedReadReason": "viveworker Auto Pilot が Trusted Read ポリシーに一致したため自動承認しました。",
1577
+ "server.message.autoPilotTrustedWriteApproved": "承認の自動化がこの低リスクなファイル変更を自動承認しました。",
1578
+ "server.message.autoPilotTrustedWriteSummary": "承認の自動化が低リスクなファイル変更を自動承認しました。",
1579
+ "server.message.autoPilotTrustedWriteReason": "viveworker が現在のワークスペース向け低リスク書き込みポリシーに一致したため自動承認しました。",
1326
1580
  "server.message.fileApprovalNeeded": "ファイル変更の承認が必要です。",
1327
1581
  "server.message.commandPrefix": ({ command }) => `Command: ${command}`,
1328
1582
  "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-v47";
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
  }