@buzzni/saycode-cli 0.9.3 → 0.9.4

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.
Files changed (2) hide show
  1. package/index.mjs +1598 -474
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -49,7 +49,7 @@ function _mergeNamespaces(n, m) {
49
49
  }
50
50
  return Object.freeze(Object.defineProperty(n, Symbol.toStringTag, { value: "Module" }));
51
51
  }
52
- const version$3 = "0.9.3";
52
+ const version$3 = "0.9.4";
53
53
  const manifest = {
54
54
  version: version$3
55
55
  };
@@ -831,6 +831,33 @@ const managedClaudeKo = {
831
831
  "managedClaude.offline": "작업PC에 연결 중이에요.",
832
832
  "managedClaude.loading": "AI 연결 중…"
833
833
  };
834
+ const workTemplateKo = {
835
+ "work.gallery": "문서 템플릿",
836
+ "work.templates": "템플릿",
837
+ "work.create": "내 템플릿 만들기",
838
+ "work.closeGallery": "템플릿 목록 닫기",
839
+ "work.edit": "템플릿 편집",
840
+ "work.delete": "템플릿 삭제",
841
+ "work.deleteConfirm": "{name} 템플릿을 삭제할까요?",
842
+ "work.description": "기존 문서의 서식을 저장해 다음 Work에서 다시 사용할 수 있습니다.",
843
+ "work.name": "템플릿 이름",
844
+ "work.file": "원본 파일",
845
+ "work.chooseFile": "파일 선택",
846
+ "work.noFileSelected": "선택한 파일 없음",
847
+ "work.preserve": "유지할 서식과 작성 지침",
848
+ "work.save": "템플릿 저장",
849
+ "work.saving": "저장 중…",
850
+ "work.instructions": "작성 지침",
851
+ "work.saveChanges": "변경 저장",
852
+ "work.templateInvalid": "선택한 템플릿 원본을 확인할 수 없습니다.",
853
+ "work.templateChanged": "템플릿이 변경됐습니다. 다시 선택해 주세요.",
854
+ "work.templatesUnavailable": "템플릿 목록을 불러오지 못했습니다.",
855
+ "work.clearTemplate": "템플릿 선택 해제",
856
+ "work.templatesLoading": "템플릿 불러오는 중…",
857
+ "work.templateSaveFailed": "템플릿을 저장하지 못했습니다. 내용을 확인하고 다시 시도해 주세요.",
858
+ "work.templatePrompt": "{format} 문서를 {name} 템플릿 스킬로 작성해줘. 원본 양식의 디자인과 구조를 유지해줘.",
859
+ "work.taskCue": "내 작업은"
860
+ };
834
861
  const workKo = {
835
862
  "work.mediaChanged": "원본 파일이 변경됐습니다. 다시 열어주세요.",
836
863
  "work.mediaUnsupported": "이 컴퓨터에서는 미디어 스트리밍을 지원하지 않습니다. 원본을 저장해 열어주세요.",
@@ -1034,6 +1061,7 @@ const organizationStartKo = {
1034
1061
  "organizationStart.welcome": "조직에서 바로 시작해요",
1035
1062
  "organizationStart.welcomeBody": "필요한 도구와 이 컴퓨터 연결을 자동으로 준비할게요.",
1036
1063
  "organizationStart.chooseTitle": "어느 조직에서 작업할까요?",
1064
+ "organizationStart.resumeVerified": "확인된 조직으로 계속",
1037
1065
  "organizationStart.next": "다음",
1038
1066
  "organizationStart.switching": "조직 연결 중…",
1039
1067
  "organizationStart.confirmConnectionTitle": "PC 연결 확인",
@@ -1186,11 +1214,30 @@ const organizationStartKo = {
1186
1214
  "companyInvite.declineConfirm": "거절 확인",
1187
1215
  "companyInvite.declineBody": "{name}의 초대를 거절할까요? 참여하려면 새 초대가 필요해요.",
1188
1216
  "companyInvite.cancel": "취소",
1189
- "companyInvite.loading": "초대 확인 중…",
1217
+ "companyInvite.loading": "조직과 초대 확인 중…",
1190
1218
  "companyInvite.acceptLabel": "{name} 초대 수락",
1191
1219
  "companyInvite.declineLabel": "{name} 초대 거절",
1192
1220
  "companyInvite.declineConfirmLabel": "{name} 초대 거절 확인",
1193
- "companyInvite.refreshFailed": "초대 목록을 확인하지 못했어요. 다시 확인해주세요."
1221
+ "companyInvite.refreshFailed": "조직과 초대를 확인하지 못했어요. 다시 확인해주세요.",
1222
+ "organizationEnrollment.join": "초대 코드로 참여",
1223
+ "organizationEnrollment.create": "조직 만들기",
1224
+ "organizationEnrollment.code": "초대 코드",
1225
+ "organizationEnrollment.name": "이름",
1226
+ "organizationEnrollment.joinSubmit": "조직에 참여",
1227
+ "organizationEnrollment.createSubmit": "만들기",
1228
+ "organizationEnrollment.back": "조직 참여 방법으로 돌아가기",
1229
+ "organizationEnrollment.joinHelp": "조직 관리자에게 받은 초대 코드를 입력해주세요.",
1230
+ "organizationEnrollment.working": "조직 정보를 확인 중…",
1231
+ "organizationEnrollment.joined": "조직이 확인됐어요. 다음을 눌러 시작 준비를 이어가세요.",
1232
+ "organizationEnrollment.unconfirmed": "처리 결과를 아직 확인하지 못했어요. 중복 요청 없이 조직 목록을 다시 확인한 뒤 참여한 조직을 선택해주세요.",
1233
+ "organizationEnrollment.recheck": "조직 목록 다시 확인",
1234
+ "organizationEnrollment.invalidCode": "초대 코드를 확인해주세요. 만료되거나 취소된 코드는 관리자에게 다시 요청해주세요.",
1235
+ "organizationEnrollment.slugTaken": "조직 주소를 준비하지 못했어요. 다시 시도해주세요.",
1236
+ "organizationEnrollment.rateLimited": "요청이 많아요. 잠시 후 다시 시도해주세요.",
1237
+ "organizationEnrollment.signInAgain": "로그인이 만료됐어요. 다시 로그인한 뒤 참여해주세요.",
1238
+ "organizationEnrollment.failed": "요청을 처리하지 못했어요. 입력 내용을 확인하고 다시 시도해주세요.",
1239
+ "organizationEnrollment.welcomeBody": "함께 작업할 조직을 선택하거나 새로 만들어주세요.",
1240
+ "organizationEnrollment.other": "다른 조직 선택·참여"
1194
1241
  };
1195
1242
  const usageEntryKo = {
1196
1243
  ...selfHostConnectKo,
@@ -1334,7 +1381,38 @@ const usageEntryKo = {
1334
1381
  "onboarding.additionalStep": "추가 단계",
1335
1382
  "onboarding.requiredSettings": "필수 설정",
1336
1383
  "onboarding.additionalSettings": "추가 설정 — 선택",
1337
- "onboarding.cli.bundledPreparation": "Saycode 도구는 앱에 포함되어 있어요. 필수 도구와 이 컴퓨터의 연결을 확인해요."
1384
+ "onboarding.cli.bundledPreparation": "Saycode 도구는 앱에 포함되어 있어요. 필수 도구와 이 컴퓨터의 연결을 확인해요.",
1385
+ "signup.open": "계정 만들기",
1386
+ "signup.title": "계정 만들기",
1387
+ "signup.sendCode": "인증 코드 받기",
1388
+ "signup.sendingCode": "코드 보내는 중…",
1389
+ "signup.code": "메일로 발송된 인증코드를 입력해주세요",
1390
+ "signup.resend": "다시 보내기",
1391
+ "signup.password": "비밀번호",
1392
+ "signup.confirmation": "비밀번호 확인",
1393
+ "signup.minimum": "12자 이상",
1394
+ "signup.mismatch": "비밀번호가 일치하지 않습니다.",
1395
+ "signup.submit": "가입하고 시작하기",
1396
+ "signup.creating": "계정 만드는 중…",
1397
+ "signup.back": "로그인",
1398
+ "signup.registered": "이미 가입된 이메일이에요. 로그인해 주세요.",
1399
+ "signup.rateLimited": "요청이 많아요. 잠시 후 다시 시도해 주세요.",
1400
+ "signup.requestFailed": "코드를 요청하지 못했어요. 연결을 확인하고 다시 시도해 주세요.",
1401
+ "signup.invalidCode": "인증 코드가 올바르지 않거나 만료됐어요. 다시 확인해 주세요.",
1402
+ "signup.recoveryUnavailable": "계정 복구 기능이 준비되지 않았어요. 잠시 후 다시 시도해 주세요.",
1403
+ "signup.failed": "가입을 완료하지 못했어요. 입력 내용을 확인하고 다시 시도해 주세요.",
1404
+ "signup.unconfirmed": "가입 결과를 확인하지 못했어요. 로그인으로 계정을 확인해 주세요.",
1405
+ "signup.complete": "가입이 완료됐어요. 로그인해 시작하세요.",
1406
+ "signup.continueLogin": "로그인해서 시작하기",
1407
+ "signup.terms": "계속 진행하면 서비스 이용약관에 동의하는 것으로 간주됩니다.",
1408
+ "signup.showPassword": "비밀번호 보기",
1409
+ "signup.hidePassword": "비밀번호 숨기기",
1410
+ "signup.enterCode": "인증 코드 입력",
1411
+ "signup.digit": "인증 코드 {number}번째 자리",
1412
+ "signup.verify": "인증하고 계속",
1413
+ "signup.verifying": "인증 확인 중…",
1414
+ "signup.previous": "이전",
1415
+ "signup.verifyFailed": "인증을 확인하지 못했어요. 연결을 확인하고 다시 시도해 주세요."
1338
1416
  };
1339
1417
  const homeTabsKo = {
1340
1418
  "homeTabs.collaboratingTitle": "함께 작업 프로젝트",
@@ -1392,10 +1470,10 @@ const homeTabsKo = {
1392
1470
  "homeTabs.startingWork": "작업 시작 중",
1393
1471
  "homeTabs.startWorkFailed": "작업을 시작하지 못했습니다.",
1394
1472
  "homeTabs.syncedBeforeImplementation": "구현 시작 전 워크스페이스 기획안 동기화",
1395
- "homeTabs.quick": "새 프로젝트",
1396
- "homeTabs.git": "저장소 가져오기",
1397
- "homeTabs.zip": "파일 가져오기",
1398
- "homeTabs.folder": "폴더 연결",
1473
+ "homeTabs.quick": "프로젝트 만들기",
1474
+ "homeTabs.git": "코드 저장소 가져오기",
1475
+ "homeTabs.zip": "Zip/파일 가져오기",
1476
+ "homeTabs.folder": "머신 폴더 사용하기",
1399
1477
  "homeTabs.categories": "프로젝트 범주"
1400
1478
  };
1401
1479
  const navigationLoadingKo = {
@@ -1696,7 +1774,15 @@ const workAreaKo = {
1696
1774
  "workArea.panelFailed": "이 화면을 표시하지 못했어요",
1697
1775
  "workArea.panelFailedHint": "다시 시도해도 같으면 이 탭을 닫았다가 목록에서 다시 열어 주세요. 대화와 작업 내용은 그대로 남아 있어요.",
1698
1776
  "workArea.panelRetry": "다시 시도",
1699
- "workArea.panelClose": "탭 닫기"
1777
+ "workArea.panelClose": "탭 닫기",
1778
+ "workArea.nameSearch.hint": "목록 필터 · Enter로 전체 이름 검색",
1779
+ "workArea.nameSearch.loading": "프로젝트 파일 이름을 찾는 중…",
1780
+ "workArea.nameSearch.partial": "일부 파일만 확인했습니다 · Enter로 다시 검색",
1781
+ "workArea.nameSearch.results": "프로젝트 검색 결과 · Esc로 해제",
1782
+ "workArea.nameSearch.empty": "일치하는 파일 없음 · Esc로 해제",
1783
+ "workArea.revealFile": "파일 목록에서 위치 보기",
1784
+ "workArea.revealFailed": "파일 위치를 확인하지 못했습니다. 위치 보기를 다시 눌러주세요.",
1785
+ "workArea.fileListFailed": "목록을 불러오지 못했습니다."
1700
1786
  };
1701
1787
  const redesignKo = {
1702
1788
  ...sessionShareKo,
@@ -2844,7 +2930,7 @@ const personalChatsKo = {
2844
2930
  "personalChats.creating": "채팅을 시작하는 중…",
2845
2931
  "personalChats.noOnlineMachine": "온라인 작업PC가 없습니다. 채팅을 시작하려면 작업PC를 시작해주세요.",
2846
2932
  "personalChats.localMachinePreparing": "이 컴퓨터를 Saycode에 연결하는 중이에요. 잠시 후 바로 시작할 수 있어요.",
2847
- "personalChats.localMachineAsk": "이 컴퓨터에 Saycode CLI를 설치하면 바로 채팅을 시작할 수 있어요.",
2933
+ "personalChats.localMachineAsk": "채팅을 시작하려면 이 컴퓨터를 작업PC로 연결해주세요.",
2848
2934
  "personalChats.localMachineFailed": "이 컴퓨터 자동 연결에 실패했어요. 다른 방법으로 연결할 수 있어요.",
2849
2935
  "personalChats.pickMachine": "이 채팅을 실행할 작업PC를 선택하세요",
2850
2936
  "personalChats.createFailed": "채팅을 시작하지 못했습니다.",
@@ -4234,7 +4320,24 @@ const organizationTeamsKo = {
4234
4320
  "organizationTeams.shareApply": "적용"
4235
4321
  };
4236
4322
  const gitAccountsKo = {
4237
- "settingsTab.gitAccounts": "Git 계정",
4323
+ "projectStart.url": "주소로 가져오기",
4324
+ "projectStart.clearRepository": "저장소 선택 해제",
4325
+ "projectStart.providerPicker": "{provider} 저장소 선택",
4326
+ "projectStart.manageAccess": "저장소 접근 관리",
4327
+ "projectStart.repositoryPlaceholder": "저장소를 선택하세요",
4328
+ "projectStart.localAccount": "이 컴퓨터의 GitHub 계정 · @{account}",
4329
+ "projectStart.localGit": "이 컴퓨터의 Git 설정",
4330
+ "projectStart.machineGit": "작업PC의 Git 설정",
4331
+ "projectStart.githubAccount": "GitHub 연결 계정",
4332
+ "projectStart.gitlabAccount": "GitLab 연결 계정",
4333
+ "projectStart.organizationPat": "조직 PAT · {name}",
4334
+ "projectStart.personalPat": "개인 PAT",
4335
+ "projectStart.selectedConnectionHint": "선택한 저장소의 연결 계정을 사용합니다.",
4336
+ "projectStart.localAccountHint": "이 컴퓨터에 로그인한 GitHub 계정으로 가져옵니다.",
4337
+ "projectStart.machineGitHint": "작업PC에 설정된 Git 인증을 사용합니다.",
4338
+ "projectStart.accountHint": "선택한 연결 계정으로 저장소에 접근합니다.",
4339
+ "projectStart.connectionUnavailable": "선택한 저장소 연결을 사용할 수 없습니다. 연결 상태를 다시 확인해 주세요.",
4340
+ "settingsTab.gitAccounts": "저장소",
4238
4341
  "gitAccounts.title": "GitHub · GitLab 계정",
4239
4342
  "gitAccounts.description": "저장소 가져오기·트리거·PR 작업에 쓸 계정을 연결합니다. 토큰은 서버에만 보관되고 이 기기에는 저장되지 않습니다.",
4240
4343
  "gitAccounts.refresh": "상태 새로고침",
@@ -4250,7 +4353,27 @@ const gitAccountsKo = {
4250
4353
  "gitAccounts.connectFailed": "연결을 시작하지 못했습니다. 잠시 후 다시 시도하세요.",
4251
4354
  "gitAccounts.disconnectFailed": "연결을 해제하지 못했습니다. 잠시 후 다시 시도하세요.",
4252
4355
  "gitAccounts.warnings": "해제는 완료됐지만 확인이 필요한 항목이 있습니다:",
4253
- "gitAccounts.patHint": "개인 액세스 토큰(PAT)은 보안 탭에서 관리합니다."
4356
+ "projectStart.repositorySettings": "저장소 설정",
4357
+ "projectStart.allRepositories": "모든 저장소",
4358
+ "projectStart.selectedRepositories": "선택한 저장소",
4359
+ "projectStart.lastConnected": "마지막 연결",
4360
+ "projectStart.patTitle": "개인 코드 저장소 Personal Access Token",
4361
+ "projectStart.patIssue": "PAT 발급 페이지 열기",
4362
+ "projectStart.patRegistered": "등록됨 · 개인",
4363
+ "projectStart.patUnregistered": "등록 안 됨",
4364
+ "projectStart.patUpdatedAt": "마지막 갱신",
4365
+ "projectStart.patConfirmDelete": "삭제 확인",
4366
+ "projectStart.patDelete": "삭제",
4367
+ "projectStart.patUpdate": "새 PAT (갱신)",
4368
+ "projectStart.patNew": "새 PAT",
4369
+ "projectStart.patPlaceholder": "토큰 값을 입력하세요",
4370
+ "projectStart.patHide": "토큰 숨기기",
4371
+ "projectStart.patShow": "토큰 표시",
4372
+ "projectStart.patSaveUpdate": "갱신",
4373
+ "projectStart.patSaveNew": "등록",
4374
+ "projectStart.patSaved": "토큰을 저장했습니다.",
4375
+ "projectStart.patDeleted": "토큰을 삭제했습니다.",
4376
+ "projectStart.loadingConnections": "저장소 연결 정보 불러오는 중…"
4254
4377
  };
4255
4378
  const agentBrowserConsoleKo = {
4256
4379
  "browserPanel.agentBrowserConsole": "AI 브라우저 작업 콘솔",
@@ -4821,6 +4944,51 @@ const designGuideKo = {
4821
4944
  "designGuide.entry.body": "앱 화면을 만들 때 쓰는 색, 글자, 부품, 대화 화면 기준을 확인합니다.",
4822
4945
  "designGuide.entry.open": "디자인 가이드 열기"
4823
4946
  };
4947
+ const prFollowThroughKo = {
4948
+ "prFollow.readyTitle": "PR #{number} 머지 가능",
4949
+ "prFollow.readyBody": "CI·리뷰 봇·충돌 확인을 모두 통과했습니다. 머지는 직접 해 주세요.",
4950
+ "prFollow.mergeGuard.stacked": "이 PR 브랜치에 아직 반영되지 않은 스택 PR이 있습니다: {list}. 지금 머지하면 그 PR은 고아가 되어 따로 옮겨야 합니다.",
4951
+ "prFollow.mergeGuard.botReviewing": "리뷰 봇이 아직 이 PR을 검토하거나 수정 PR을 만드는 중입니다.",
4952
+ "prFollow.mergeGuard.unknown": "남은 스택 PR이 있는지 확인하지 못했습니다.",
4953
+ "prFollow.mergeGuard.confirm": "그래도 머지할까요?",
4954
+ "prFollow.mergeGuard.title": "머지 전에 확인해 주세요",
4955
+ "prFollow.mergeGuard.mergeAnyway": "그래도 머지",
4956
+ "prFollow.enable": "PR 지킴이",
4957
+ "prFollow.enableTitle": "이 PR의 CI·리뷰 봇·충돌을 지켜보다가 대화가 쉬고 있을 때 고칠 일을 대신 지시합니다. 머지 가능해지면 알려 줍니다.",
4958
+ "prFollow.disable": "PR 지킴이 끄기",
4959
+ "prFollow.dismiss": "PR 지킴이 기록 지우기",
4960
+ "prFollow.badge.prefix": "지킴이 · {status}",
4961
+ "prFollow.badge.awaitingPr": "PR 기다리는 중",
4962
+ "prFollow.badge.watching": "지켜보는 중",
4963
+ "prFollow.badge.checksPending": "체크 대기",
4964
+ "prFollow.badge.botReviewing": "리뷰 봇 대기",
4965
+ "prFollow.badge.conflict": "충돌",
4966
+ "prFollow.badge.checksFailed": "CI 실패",
4967
+ "prFollow.badge.stackedPr": "스택 PR 있음",
4968
+ "prFollow.badge.botFindings": "봇 지적 반영 필요",
4969
+ "prFollow.badge.ready": "머지 가능",
4970
+ "prFollow.badge.merged": "머지됨",
4971
+ "prFollow.badge.lookupFailed": "PR 상태 확인 실패",
4972
+ "prFollow.badge.stopped.user": "꺼짐",
4973
+ "prFollow.badge.stopped.noPr": "PR이 만들어지지 않음",
4974
+ "prFollow.badge.stopped.closed": "PR 닫힘",
4975
+ "prFollow.badge.stopped.budget": "자동 지시 한도 도달",
4976
+ "prFollow.badge.stopped.repeatedFailure": "같은 실패 반복 — 확인 필요",
4977
+ "prFollow.badge.stopped.sessionRemoved": "대화 없음",
4978
+ "prFollow.settings.saveFailed": "설정을 저장하지 못했습니다.",
4979
+ "prFollow.settings.followThrough": "오토파일럿 Commit & PR 뒤 PR 지킴이 켜기",
4980
+ "prFollow.settings.followThroughHelp": "PR의 CI 실패·리뷰 봇 수정 PR·충돌을 1분마다 확인하고, 대화가 쉬고 있을 때 고칠 일을 대신 지시합니다. 머지 가능해지면 알림을 보냅니다. base 브랜치로의 머지는 하지 않습니다.",
4981
+ "prFollow.settings.continueAfterMerge": "머지되면 남은 작업 이어가기",
4982
+ "prFollow.settings.continueAfterMergeHelp": "PR이 머지되면 같은 대화에 spec의 남은 작업을 최신 base 기준 새 브랜치에서 이어가라고 지시합니다. 남은 스택 PR이 있으면 그 처리도 함께 지시합니다.",
4983
+ "prFollow.settings.autoAnswer": "오토파일럿이 추천안 질문에 대신 답하기",
4984
+ "prFollow.settings.autoAnswerHelp": '오토파일럿 중에 AI가 추천안을 제시하며 "진행할까요?"로 멈추면 "추천안대로 진행"으로 답합니다(한 번 켤 때 최대 2회). 권한·비용·외부 전송·되돌리기 어려운 변경은 진행하지 말라고 함께 지시합니다.',
4985
+ "prFollow.settings.reviewer": "오토파일럿 독립 리뷰 AI",
4986
+ "prFollow.settings.reviewerDefault": "기본값",
4987
+ "prFollow.settings.reviewerModel": "리뷰 모델",
4988
+ "prFollow.settings.reviewerEffort": "리뷰 Effort",
4989
+ "prFollow.settings.reviewerHelp": "오토파일럿을 독립 리뷰로 켤 때 쓸 AI입니다. 켤 때의 값이 그 실행에 고정됩니다.",
4990
+ "prFollow.badge.title": "PR 지킴이 · {status}"
4991
+ };
4824
4992
  const catalogExtensionsKo = {
4825
4993
  ...chatSchedulesKo,
4826
4994
  ...mySettingsKo,
@@ -4901,9 +5069,20 @@ const catalogExtensionsKo = {
4901
5069
  ...lessonCandidateKo,
4902
5070
  ...gitAccountsKo,
4903
5071
  ...agentBrowserConsoleKo,
4904
- ...designGuideKo
5072
+ ...designGuideKo,
5073
+ ...prFollowThroughKo
4905
5074
  };
4906
5075
  const onboardingKo = {
5076
+ "folderBrowser.title": "폴더 선택",
5077
+ "folderBrowser.parent": "상위 폴더",
5078
+ "folderBrowser.folders": "폴더 목록",
5079
+ "folderBrowser.loading": "폴더 확인 중…",
5080
+ "folderBrowser.empty": "하위 폴더가 없습니다.",
5081
+ "folderBrowser.newPath": "새로운 경로에서 시작",
5082
+ "folderBrowser.choose": "이 폴더 선택",
5083
+ "projectImport.drop": "zip · 폴더 · 파일을 끌어다 놓거나 클릭해서 선택",
5084
+ "projectImport.excludeHint": "node_modules / .git / dist 등 무거운 폴더는 자동으로 제외돼요",
5085
+ "projectImport.folder": "폴더 선택",
4907
5086
  "onboarding.page.instructions": "Saycode 지시 사용",
4908
5087
  "onboarding.instructions.body": "AI가 작업을 정리하고 결과를 안내할 때 Saycode의 기본 지시를 사용할까요?",
4909
5088
  "onboarding.instructions.enable": "지시 사용하기",
@@ -5103,7 +5282,8 @@ const onboardingKo = {
5103
5282
  "onboarding.groups.error.capabilityRequired": "이 머신의 Saycode CLI가 조직 인증 세트를 지원하지 않습니다. CLI를 업데이트한 뒤 다시 시도해 주세요.",
5104
5283
  "onboarding.groups.error.setupTokenRuntimeRequired": "이 컴퓨터에 조직용 Claude setup-token runtime이 없습니다. Saycode가 제공한 cswap runtime을 설치한 뒤 다시 시도해 주세요.",
5105
5284
  "onboarding.groups.error.happyDaemonUpdateRequired": "이 컴퓨터의 Saycode 데몬이 조직 인증 세트를 지원하지 않습니다. Desktop을 업데이트한 뒤 다시 시도해 주세요.",
5106
- "onboarding.groups.error.unknown": "조직 인증 세트를 연결하지 못했습니다. 잠시 후 다시 시도해 주세요."
5285
+ "onboarding.groups.error.unknown": "조직 인증 세트를 연결하지 못했습니다. 잠시 후 다시 시도해 주세요.",
5286
+ "folderBrowser.path": "경로"
5107
5287
  };
5108
5288
  const standaloneRemoteOnboardingKo = {
5109
5289
  "onboarding.optional": " (선택)",
@@ -5188,6 +5368,7 @@ const ko = {
5188
5368
  "assetWorkflow.filesSelect": "직접 선택한 파일 읽기",
5189
5369
  "assetWorkflow.draftsPrepare": "선택한 대화의 초안과 첨부 준비",
5190
5370
  ...workKo,
5371
+ ...workTemplateKo,
5191
5372
  ...FILE_ACCESS_TRANSLATIONS_KO,
5192
5373
  ...chatWorkKo,
5193
5374
  ...usageEntryKo,
@@ -7061,7 +7242,7 @@ const ko = {
7061
7242
  "saycodePromptPreference.eyebrow": "AI도구 지시 선택",
7062
7243
  "saycodePromptPreference.title": "Saycode 기본 지시를 사용할까요?",
7063
7244
  "saycodePromptPreference.body": "내가 만든 Claude/Codex 스킬과 하네스를 그대로 쓰려면 꺼진 상태를 유지하세요. 나중에 환경설정에서 언제든 바꿀 수 있습니다.",
7064
- "saycodePromptPreference.defaultOnNotice": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인 미리보기는 기본으로 계속 켜집니다. 이 기능도 끄려면 환경설정 → 시스템 프롬프트에서 각각 해제하세요.",
7245
+ "saycodePromptPreference.defaultOnNotice": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인 미리보기, Saycode 사용 안내는 기본으로 계속 켜집니다. 이 기능도 끄려면 환경설정 → 시스템 프롬프트에서 각각 해제하세요.",
7065
7246
  "saycodePromptPreference.keepOff": "꺼진 상태 유지",
7066
7247
  "saycodePromptPreference.enable": "Saycode 지시 켜기",
7067
7248
  "saycodePromptPreference.saving": "저장 중…",
@@ -7070,7 +7251,7 @@ const ko = {
7070
7251
  "saycodePromptPreference.settingsEnable": "Saycode가 제공하는 기본 행동 지시 사용",
7071
7252
  "saycodePromptPreference.settingsHelp": "사용자 시스템 프롬프트, 프로젝트 지침, 설치한 스킬과 하네스는 이 설정과 관계없이 유지됩니다.",
7072
7253
  "saycodePromptPreference.blocksTitle": "개별 지시 선택",
7073
- "saycodePromptPreference.blocksHelp": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인 미리보기는 기본으로 켜지며, 나머지 항목은 위의 전체 설정을 따릅니다.",
7254
+ "saycodePromptPreference.blocksHelp": "자식 AI도구 호출, 내부 작업 위임, 결과물 인라인 미리보기, Saycode 사용 안내는 기본으로 켜지며, 나머지 항목은 위의 전체 설정을 따릅니다.",
7074
7255
  "saycodePromptPreference.alwaysOnNotice": "채팅 제목 생성은 항상 켜져 있으며 끌 수 없습니다. 채팅 목록에서 대화를 다시 찾으려면 필요합니다.",
7075
7256
  "saycodePromptPreference.block.agentOrchestration.label": "자식 AI도구 호출",
7076
7257
  "saycodePromptPreference.block.agentOrchestration.description": "자연어로 요청한 작업을 독립된 자식 AI도구 세션에 맡기고 결과를 회수하도록 지시합니다.",
@@ -7082,6 +7263,8 @@ const ko = {
7082
7263
  "saycodePromptPreference.block.coAuthoredCredit.description": "커밋 메시지에 Claude와 Happy 공동 작성자 표시를 추가합니다.",
7083
7264
  "saycodePromptPreference.block.inlinePreview.label": "결과물 인라인 미리보기",
7084
7265
  "saycodePromptPreference.block.inlinePreview.description": "HTML 결과물을 만들면 답변 안에 미리보기를 띄우도록 지시합니다.",
7266
+ "saycodePromptPreference.block.saycodeGuide.label": "Saycode 사용 안내",
7267
+ "saycodePromptPreference.block.saycodeGuide.description": "Saycode 사용법을 물으면 관련 안내와 앱 상황 일부(버전·사용 형태·화면 표시 설정)를 이번 대화의 AI에게 함께 보냅니다. 전체 안내서는 대화 작업 폴더의 .aplus/saycode-guide에 두며 저장소에는 올라가지 않습니다. 질문인지 판단하는 일은 이 기기에서만 합니다.",
7085
7268
  "saycodePromptPreference.loading": "계정 설정을 불러오는 중…",
7086
7269
  "standalone.zrokStatus": "zrok 상태",
7087
7270
  "standalone.zrokNeeded": "zrok2 설정이 필요합니다",
@@ -8476,10 +8659,10 @@ In a chat
8476
8659
  /model · /effort · /voice [path] · /detach · /exit live chat commands
8477
8660
 
8478
8661
  Leaving never stops a remote session — it keeps running on the machine.`;
8479
- const record$j = (value2) => !!value2 && typeof value2 === "object" && !Array.isArray(value2);
8662
+ const record$l = (value2) => !!value2 && typeof value2 === "object" && !Array.isArray(value2);
8480
8663
  const ids = (value2) => Array.isArray(value2) && value2.length <= 1e3 && value2.every((id2) => typeof id2 === "string" && id2.trim().length > 0);
8481
8664
  function parseMachineSharing(value2) {
8482
- if (!record$j(value2) || !record$j(value2.machine)) throw new Error("Invalid machine sharing response");
8665
+ if (!record$l(value2) || !record$l(value2.machine)) throw new Error("Invalid machine sharing response");
8483
8666
  const m = value2.machine;
8484
8667
  if (typeof value2.revision !== "string" || !value2.revision || !["canShare", "canMakeDedicated", "canManageAccess"].every((key) => typeof value2[key] === "boolean") || !["machineId", "ownerId", "companyId", "infraMode"].every((key) => typeof m[key] === "string" && !!m[key]) || !["dedicated", "shared"].includes(String(m.type)) || !ids(m.accessList) || m.accessList.length === 0 || m.accessList.includes("*") && m.accessList.length !== 1 || m.assignedUserId != null && typeof m.assignedUserId !== "string" || value2.missingUserIds !== void 0 && !ids(value2.missingUserIds)) throw new Error("Invalid machine sharing response");
8485
8668
  return {
@@ -8744,7 +8927,7 @@ function parseCompanyMemberDirectoryPage(value2) {
8744
8927
  nextCursor: rawCursor
8745
8928
  };
8746
8929
  }
8747
- function record$i(value2) {
8930
+ function record$k(value2) {
8748
8931
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid team collaboration response");
8749
8932
  return value2;
8750
8933
  }
@@ -8757,12 +8940,12 @@ function choice(value2, values) {
8757
8940
  return value2;
8758
8941
  }
8759
8942
  function parseCollaboration(value2) {
8760
- const row2 = record$i(value2);
8943
+ const row2 = record$k(value2);
8761
8944
  if (!Array.isArray(row2.teamIds) || row2.teamIds.length > 1e3 || !Array.isArray(row2.bindings) || row2.bindings.length > 1e3) throw new Error("Invalid team collaboration list");
8762
8945
  return {
8763
8946
  teamIds: row2.teamIds.map(identifier$4),
8764
8947
  bindings: row2.bindings.map((value22) => {
8765
- const binding = record$i(value22);
8948
+ const binding = record$k(value22);
8766
8949
  return {
8767
8950
  accountId: identifier$4(binding.accountId),
8768
8951
  membershipOrigin: choice(binding.membershipOrigin, ["unresolved", "team", "preexisting"]),
@@ -8774,8 +8957,8 @@ function parseCollaboration(value2) {
8774
8957
  };
8775
8958
  }
8776
8959
  function parseMutation(value2) {
8777
- const row2 = record$i(value2);
8778
- const sync = row2.sync === null ? null : record$i(row2.sync);
8960
+ const row2 = record$k(value2);
8961
+ const sync = row2.sync === null ? null : record$k(row2.sync);
8779
8962
  if (sync && [sync.synced, sync.failed, sync.rejected].some((value22) => !Number.isSafeInteger(value22) || Number(value22) < 0)) throw new Error("Invalid team synchronization counts");
8780
8963
  return { collaboration: parseCollaboration(row2.collaboration), sync, ...parseProjectMemberRemovalResult(row2) };
8781
8964
  }
@@ -8783,7 +8966,7 @@ function createProjectTeamCollaborationApi(request2) {
8783
8966
  const path2 = (id2) => `/api/projects/${encodeURIComponent(id2)}/collaboration/teams`;
8784
8967
  return {
8785
8968
  async getProjectTeamCollaboration(id2) {
8786
- return parseCollaboration(record$i(await request2(path2(id2))).collaboration);
8969
+ return parseCollaboration(record$k(await request2(path2(id2))).collaboration);
8787
8970
  },
8788
8971
  async updateProjectTeamCollaboration(id2, teamIds) {
8789
8972
  if (teamIds.length > 1e3) throw new Error("Too many collaboration teams");
@@ -8795,6 +8978,27 @@ function createProjectTeamCollaborationApi(request2) {
8795
8978
  }
8796
8979
  };
8797
8980
  }
8981
+ function record$j(value2) {
8982
+ if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid organization enrollment response");
8983
+ return value2;
8984
+ }
8985
+ function parseResult(value2, kind) {
8986
+ const row2 = record$j(value2);
8987
+ if (row2.reauthenticationRequired === true) return { companyId: null, reauthenticationRequired: true };
8988
+ const id2 = kind === "join" ? record$j(row2.member).companyId : row2.id;
8989
+ if (typeof id2 !== "string" || !id2.trim()) throw new Error("Invalid organization enrollment response");
8990
+ return { companyId: id2, reauthenticationRequired: false };
8991
+ }
8992
+ function createCompanyEnrollmentApi(request2) {
8993
+ return {
8994
+ async joinCompanyByCode(joinCode, mfaSessionToken) {
8995
+ return parseResult(await request2("/api/companies/join", { method: "POST", body: JSON.stringify({ joinCode: joinCode.trim() }), headers: mfaSessionToken ? { "X-Saycode-Mfa-Session": mfaSessionToken } : {} }), "join");
8996
+ },
8997
+ async createCompany(input) {
8998
+ return parseResult(await request2("/api/companies", { method: "POST", body: JSON.stringify({ displayName: input.displayName.trim(), slug: input.slug.trim() }) }), "create");
8999
+ }
9000
+ };
9001
+ }
8798
9002
  const AI_GROUP_ERROR_KINDS = [
8799
9003
  "AI_GROUP_ACCOUNT_IDENTITY_UNAVAILABLE",
8800
9004
  "AI_GROUP_ACCOUNT_KEY_LOCKED",
@@ -8856,7 +9060,7 @@ function parseAiGroupErrorKind(value2) {
8856
9060
  return typeof value2 === "string" && AI_GROUP_ERROR_KINDS.includes(value2) ? value2 : "UNKNOWN_ERROR";
8857
9061
  }
8858
9062
  const MANAGED_ACCOUNT_ID = /^[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}$/i;
8859
- function record$h(value2) {
9063
+ function record$i(value2) {
8860
9064
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) throw new Error("Invalid AI group metadata");
8861
9065
  return value2;
8862
9066
  }
@@ -8885,23 +9089,23 @@ function receiptMetadata(result2) {
8885
9089
  ...result2.assignmentGeneration == null ? {} : { assignmentGeneration: positiveInteger(result2.assignmentGeneration) },
8886
9090
  ...result2.fingerprint == null ? {} : { fingerprint: digest$2(result2.fingerprint) },
8887
9091
  ...result2.appliedCredentials == null ? {} : { appliedCredentials: list$1(result2.appliedCredentials, 500).map((entry) => {
8888
- const credential = record$h(entry), managedAccountId = text$9(credential.managedAccountId, 80);
9092
+ const credential = record$i(entry), managedAccountId = text$9(credential.managedAccountId, 80);
8889
9093
  if (!MANAGED_ACCOUNT_ID.test(managedAccountId)) throw new Error("Invalid AI group metadata");
8890
9094
  return { managedAccountId, credentialGeneration: positiveInteger(credential.credentialGeneration) };
8891
9095
  }) }
8892
9096
  };
8893
9097
  }
8894
9098
  function parseAiGroupAssignments(value2, companyId) {
8895
- const body = record$h(value2);
9099
+ const body = record$i(value2);
8896
9100
  if (body.version !== 1 || body.companyId !== companyId) throw new Error("Invalid AI group scope");
8897
9101
  return { version: 1, companyId, groups: list$1(body.groups, 1e3).map((value22) => {
8898
- const group = record$h(value22);
9102
+ const group = record$i(value22);
8899
9103
  return {
8900
9104
  credentialGroupId: text$9(group.credentialGroupId, 128),
8901
9105
  credentialGroupName: text$9(group.credentialGroupName, 80),
8902
9106
  userGroupName: text$9(group.userGroupName, 80),
8903
9107
  accounts: list$1(group.accounts, 500).map((value3) => {
8904
- const account = record$h(value3), identity = text$9(account.identity);
9108
+ const account = record$i(value3), identity = text$9(account.identity);
8905
9109
  if (!/^[a-f0-9]{64}$/.test(identity) || !Number.isSafeInteger(account.bundleVersion) || Number(account.bundleVersion) < 1 || account.kind !== "oauth" && account.kind !== "api_key") throw new Error("Invalid AI group account");
8906
9110
  const managed = account.credentialType === "setup_token";
8907
9111
  if (managed && (account.provider !== "claude" || account.kind !== "oauth" || typeof account.managedAccountId !== "string" || !MANAGED_ACCOUNT_ID.test(account.managedAccountId) || !Number.isSafeInteger(account.credentialGeneration) || Number(account.credentialGeneration) < 1)) throw new Error("Invalid AI group managed account");
@@ -8917,7 +9121,7 @@ function parseAiGroupAssignments(value2, companyId) {
8917
9121
  })
8918
9122
  };
8919
9123
  }), results: list$1(body.results, 5e3).map((value22) => {
8920
- const result2 = record$h(value22), status = result2.status;
9124
+ const result2 = record$i(value22), status = result2.status;
8921
9125
  if (status !== "pending" && status !== "applied" && status !== "revoked" && status !== "failed" && status !== "action_required") throw new Error("Invalid AI group result");
8922
9126
  const errorKind = parseAiGroupErrorKind(result2.errorKind);
8923
9127
  return {
@@ -9047,15 +9251,15 @@ function createClaudeCollectorApi(transport) {
9047
9251
  }
9048
9252
  };
9049
9253
  }
9050
- const record$g = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
9254
+ const record$h = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
9051
9255
  const timestamp$1 = (value2) => Number.isSafeInteger(value2) && value2 >= 0;
9052
9256
  function parseWindow(value2) {
9053
- const row2 = record$g(value2);
9257
+ const row2 = record$h(value2);
9054
9258
  if (!row2 || typeof row2.id !== "string" || !row2.id || !timestamp$1(row2.openedAt) || !timestamp$1(row2.endsAt) || !timestamp$1(row2.waitMinutes)) return null;
9055
9259
  return { id: row2.id, openedAt: row2.openedAt, endsAt: row2.endsAt, waitMinutes: row2.waitMinutes };
9056
9260
  }
9057
9261
  function parseMe(value2) {
9058
- const row2 = record$g(value2);
9262
+ const row2 = record$h(value2);
9059
9263
  if (!row2) return null;
9060
9264
  const { hasLatestGrant, pending: pending2, autoEligible, autoAt, held } = row2;
9061
9265
  if (typeof hasLatestGrant !== "boolean" || typeof pending2 !== "boolean" || typeof autoEligible !== "boolean" || typeof held !== "boolean") return null;
@@ -9066,7 +9270,7 @@ function companyKeyAutoWindowHeaders(options) {
9066
9270
  return options?.distributor ? { "X-Saycode-Auto-Grant-Client": "desktop" } : {};
9067
9271
  }
9068
9272
  function parseCompanyKeyAutoWindow(raw) {
9069
- const body = record$g(raw);
9273
+ const body = record$h(raw);
9070
9274
  return { window: parseWindow(body?.window), me: parseMe(body?.me) };
9071
9275
  }
9072
9276
  function companyKeyGrantFailure(error) {
@@ -9076,7 +9280,7 @@ function companyKeyGrantFailure(error) {
9076
9280
  const body = "body" in error ? error.body : void 0;
9077
9281
  if (typeof body === "string") {
9078
9282
  try {
9079
- const parsed2 = record$g(JSON.parse(body));
9283
+ const parsed2 = record$h(JSON.parse(body));
9080
9284
  if (typeof parsed2?.error === "string") reason = parsed2.error;
9081
9285
  } catch {
9082
9286
  }
@@ -11747,32 +11951,32 @@ function invalidateRendererQueries(kind, scope2) {
11747
11951
  }
11748
11952
  const ROUTING_POLICY = "org-shared-difficulty-routing.v1";
11749
11953
  const ROUTING_AGENTS = ["claude", "codex"];
11750
- function record$f(value2) {
11954
+ function record$g(value2) {
11751
11955
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
11752
11956
  }
11753
11957
  function id$2(value2) {
11754
11958
  return typeof value2 === "string" && value2.length > 0 && value2.length <= 256;
11755
11959
  }
11756
11960
  function parseRoutingResult(value2) {
11757
- const r = record$f(value2);
11758
- const baseRoute = record$f(r?.baseRoute);
11759
- const previousApplied = record$f(r?.previousApplied);
11961
+ const r = record$g(value2);
11962
+ const baseRoute = record$g(r?.baseRoute);
11963
+ const previousApplied = record$g(r?.previousApplied);
11760
11964
  if (!r || r.version !== 1 || r.mode !== "auto" || r.policyVersion !== ROUTING_POLICY || !id$2(r.clientRequestId) || !id$2(r.model) || !(r.effort === null || id$2(r.effort)) || !(r.policyRevision === null || Number.isSafeInteger(r.policyRevision) && r.policyRevision >= 0) || !["trivial", "routine", "hard", "escalated"].includes(String(r.difficulty)) || !["p1-local", "p2-org-shared", "p2-local", "fallback-p1", "manual-legacy"].includes(String(r.classifierSource)) || r.stage !== void 0 && !["selected", "queued", "applied", "failed", "cancelled", "unknown"].includes(String(r.stage)) || r.revision !== void 0 && (!Number.isSafeInteger(r.revision) || r.revision < 0) || r.evidence !== void 0 && !["engine-applied", "legacy-selection", "unknown"].includes(String(r.evidence)) || r.executionId !== void 0 && !id$2(r.executionId) || r.clientRequestIds !== void 0 && (!Array.isArray(r.clientRequestIds) || r.clientRequestIds.length > 256 || !r.clientRequestIds.every(id$2)) || r.candidateDifficulty !== void 0 && !["trivial", "routine", "hard", "escalated"].includes(String(r.candidateDifficulty)) || r.baseRoute !== void 0 && (!baseRoute || !["trivial", "routine", "hard", "escalated"].includes(String(baseRoute.difficulty)) || !id$2(baseRoute.model) || !(baseRoute.effort === null || id$2(baseRoute.effort))) || r.temporaryEscalation !== void 0 && typeof r.temporaryEscalation !== "boolean" || r.decisionReasons !== void 0 && (!Array.isArray(r.decisionReasons) || r.decisionReasons.length > 16 || !r.decisionReasons.every((reason) => typeof reason === "string" && reason.length > 0 && reason.length <= 64)) || r.providerConfirmedModel !== void 0 && !id$2(r.providerConfirmedModel) || r.previousApplied !== void 0 && (!previousApplied || !id$2(previousApplied.model) || !(previousApplied.effort === null || id$2(previousApplied.effort)) || !["trivial", "routine", "hard", "escalated"].includes(String(previousApplied.difficulty)) || !["auto", "local-auto-bootstrap", "manual"].includes(String(previousApplied.kind)))) return null;
11761
11965
  return r;
11762
11966
  }
11763
11967
  function parseRoutingPolicy(value2) {
11764
- const p = record$f(value2);
11968
+ const p = record$g(value2);
11765
11969
  if (!p || p.schemaVersion !== 1 || typeof p.enabled !== "boolean" || p.policyVersion !== ROUTING_POLICY || !Number.isSafeInteger(p.revision) || p.revision < 0 || typeof p.appliesToSharedMachines !== "boolean" || !(p.hostMachineId === null || id$2(p.hostMachineId)) || !["off", "ready", "host-offline", "host-unsupported", "not-authorized"].includes(String(p.status))) return null;
11766
11970
  if (p.enabled && !id$2(p.hostMachineId)) return null;
11767
11971
  return p;
11768
11972
  }
11769
11973
  function parseRoutingSettings(value2) {
11770
- const v = record$f(value2), s = record$f(v?.settings), r = record$f(v?.rollout), h = v?.host === null ? null : record$f(v?.host);
11974
+ const v = record$g(value2), s = record$g(v?.settings), r = record$g(v?.rollout), h = v?.host === null ? null : record$g(v?.host);
11771
11975
  if (!s || s.schemaVersion !== 1 || s.policyVersion !== ROUTING_POLICY || typeof s.enabled !== "boolean" || !(s.hostMachineId === null || id$2(s.hostMachineId)) || s.enabled && !id$2(s.hostMachineId) || !Number.isSafeInteger(s.revision) || s.revision < 0 || !(s.updatedById === null || id$2(s.updatedById)) || !(s.updatedAt === null || typeof s.updatedAt === "number" && Number.isFinite(s.updatedAt)) || !r || !["sharedMachineCount", "capableSharedMachineCount", "unsupportedSharedMachineCount"].every((k) => Number.isSafeInteger(r[k]) && r[k] >= 0) || v?.host !== null && (!h || !id$2(h.machineId) || !(h.displayName === null || typeof h.displayName === "string") || typeof h.online !== "boolean" || typeof h.shared !== "boolean" || !["ready", "missing", "unsupported", "not-ready"].includes(String(h.capability)) || !(h.hostProcessKeyId === null || id$2(h.hostProcessKeyId)))) throw new Error("Invalid organization routing settings");
11772
11976
  return v;
11773
11977
  }
11774
11978
  function canNegotiateOrgRouting(input) {
11775
- const capability = record$f(input.machine?.difficultyRouting);
11979
+ const capability = record$g(input.machine?.difficultyRouting);
11776
11980
  return !input.manual && id$2(input.requestId) && !!input.companyId && ROUTING_AGENTS.includes(input.agent ?? "") && input.machine?.registry?.type === "shared" && input.machine.registry.companyId === input.companyId && capability?.version === 1 && capability.protocol === ROUTING_POLICY;
11777
11981
  }
11778
11982
  function prepareRoutingIntent(input) {
@@ -11970,9 +12174,9 @@ const globalConfig = {};
11970
12174
  function config(newConfig) {
11971
12175
  return globalConfig;
11972
12176
  }
11973
- function getEnumValues(entries) {
11974
- const numericValues = Object.values(entries).filter((v) => typeof v === "number");
11975
- const values = Object.entries(entries).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
12177
+ function getEnumValues(entries2) {
12178
+ const numericValues = Object.values(entries2).filter((v) => typeof v === "number");
12179
+ const values = Object.entries(entries2).filter(([k, _]) => numericValues.indexOf(+k) === -1).map(([_, v]) => v);
11976
12180
  return values;
11977
12181
  }
11978
12182
  function jsonStringifyReplacer(_, value2) {
@@ -15235,7 +15439,7 @@ const ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => {
15235
15439
  inst.keyType = def.keyType;
15236
15440
  inst.valueType = def.valueType;
15237
15441
  });
15238
- function record$e(keyType, valueType, params) {
15442
+ function record$f(keyType, valueType, params) {
15239
15443
  return new ZodRecord({
15240
15444
  type: "record",
15241
15445
  keyType,
@@ -15281,10 +15485,10 @@ const ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => {
15281
15485
  };
15282
15486
  });
15283
15487
  function _enum(values, params) {
15284
- const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
15488
+ const entries2 = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values;
15285
15489
  return new ZodEnum({
15286
15490
  type: "enum",
15287
- entries,
15491
+ entries: entries2,
15288
15492
  ...normalizeParams(params)
15289
15493
  });
15290
15494
  }
@@ -15469,7 +15673,7 @@ function superRefine(fn) {
15469
15673
  }
15470
15674
  function json(params) {
15471
15675
  const jsonSchema = lazy(() => {
15472
- return union([string$1(params), number(), boolean(), _null(), array$4(jsonSchema), record$e(string$1(), jsonSchema)]);
15676
+ return union([string$1(params), number(), boolean(), _null(), array$4(jsonSchema), record$f(string$1(), jsonSchema)]);
15473
15677
  });
15474
15678
  return jsonSchema;
15475
15679
  }
@@ -17767,14 +17971,14 @@ const scriptAutomationPayloadSchema = strictObject({
17767
17971
  entrypoint: entrypointSchema,
17768
17972
  args: array$4(string$1().max(4e3)).max(64),
17769
17973
  timeoutSeconds: number().int().min(1).max(1800),
17770
- secretRefs: record$e(secretName, string$1().min(1).max(200)),
17974
+ secretRefs: record$f(secretName, string$1().min(1).max(200)),
17771
17975
  allowedOrigins: array$4(originSchema).max(100)
17772
17976
  }),
17773
17977
  schedule: scriptScheduleSchema.nullable(),
17774
17978
  externalEnabled: boolean(),
17775
- inputSchema: record$e(string$1(), json())
17979
+ inputSchema: record$f(string$1(), json())
17776
17980
  });
17777
- strictObject({ input: record$e(string$1(), json()) });
17981
+ strictObject({ input: record$f(string$1(), json()) });
17778
17982
  const scriptAdmissionSchema = scriptAutomationPayloadSchema.pick({
17779
17983
  schedule: true,
17780
17984
  externalEnabled: true,
@@ -17801,7 +18005,7 @@ discriminatedUnion("operation", [
17801
18005
  strictObject({ operation: literal("upsert"), registration: scriptRegistrationRequestSchema }),
17802
18006
  strictObject({ operation: literal("runs"), automationId: managementId, runId: managementId.optional() }),
17803
18007
  strictObject({ operation: literal("artifact"), artifactId: managementId }),
17804
- strictObject({ operation: literal("run"), automationId: managementId, input: record$e(string$1(), json()), idempotencyKey: managementId }),
18008
+ strictObject({ operation: literal("run"), automationId: managementId, input: record$f(string$1(), json()), idempotencyKey: managementId }),
17805
18009
  strictObject({ operation: literal("cancel"), automationId: managementId, runId: managementId })
17806
18010
  ]);
17807
18011
  const scriptAutomationRowSchema = object$3({
@@ -18007,10 +18211,10 @@ function parsePasswordLoginResponse(raw) {
18007
18211
  }
18008
18212
  function parseKdfParams(value2) {
18009
18213
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return void 0;
18010
- const entries = Object.entries(value2);
18011
- if (entries.length > 8) return void 0;
18214
+ const entries2 = Object.entries(value2);
18215
+ if (entries2.length > 8) return void 0;
18012
18216
  const parsed2 = {};
18013
- for (const [key, entry] of entries) {
18217
+ for (const [key, entry] of entries2) {
18014
18218
  if (!/^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(key)) return void 0;
18015
18219
  if (typeof entry === "string") {
18016
18220
  if (entry.length > 128) return void 0;
@@ -18092,14 +18296,14 @@ function parseCloudLoginResult(raw) {
18092
18296
  }
18093
18297
  const HANDOFF_ID_PATTERN = /^mph-[A-Za-z0-9_-]{22}$/;
18094
18298
  const CAPABILITY_PATTERN = /^[A-Za-z0-9_-]{43}$/;
18095
- function record$d(value2) {
18299
+ function record$e(value2) {
18096
18300
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
18097
18301
  }
18098
18302
  function validExpiry$1(value2) {
18099
18303
  return typeof value2 === "number" && Number.isSafeInteger(value2) && value2 > 0;
18100
18304
  }
18101
18305
  function parseMfaPasskeyHandoff(raw, apiBaseUrl) {
18102
- const value2 = record$d(raw);
18306
+ const value2 = record$e(raw);
18103
18307
  if (!value2 || typeof value2.handoffId !== "string" || !HANDOFF_ID_PATTERN.test(value2.handoffId) || typeof value2.exchangeToken !== "string" || !CAPABILITY_PATTERN.test(value2.exchangeToken) || typeof value2.authorizationUrl !== "string" || !validExpiry$1(value2.expiresAt)) {
18104
18308
  throw new Error("MFA_PASSKEY_HANDOFF_RESPONSE_INVALID");
18105
18309
  }
@@ -18123,7 +18327,7 @@ function parseMfaPasskeyHandoff(raw, apiBaseUrl) {
18123
18327
  };
18124
18328
  }
18125
18329
  function parsePendingMfaPasskeyExchange(raw) {
18126
- const value2 = record$d(raw);
18330
+ const value2 = record$e(raw);
18127
18331
  if (!value2 || value2.status !== "pending" || !validExpiry$1(value2.expiresAt)) {
18128
18332
  throw new Error("MFA_PASSKEY_HANDOFF_RESPONSE_INVALID");
18129
18333
  }
@@ -18325,7 +18529,7 @@ class SessionWriteLockedError extends Error {
18325
18529
  this.name = "SessionWriteLockedError";
18326
18530
  }
18327
18531
  }
18328
- const MAX_ENTRIES = 3e3;
18532
+ const MAX_ENTRIES$1 = 3e3;
18329
18533
  const headerCache = /* @__PURE__ */ new Map();
18330
18534
  function cacheGet(store, sessionId, ciphertext) {
18331
18535
  return store.get(sessionId)?.get(ciphertext) ?? null;
@@ -18337,7 +18541,7 @@ function cacheSet(store, sessionId, ciphertext, plaintext) {
18337
18541
  store.set(sessionId, perSession);
18338
18542
  }
18339
18543
  perSession.set(ciphertext, plaintext);
18340
- if (perSession.size > MAX_ENTRIES) {
18544
+ if (perSession.size > MAX_ENTRIES$1) {
18341
18545
  const oldest = perSession.keys().next().value;
18342
18546
  if (oldest !== void 0) perSession.delete(oldest);
18343
18547
  }
@@ -18678,7 +18882,7 @@ function personalMachinesPath(companyId) {
18678
18882
  function myMachinesPath(companyId) {
18679
18883
  return `/api/admin/companies/${encodeURIComponent(companyId)}/my-machines`;
18680
18884
  }
18681
- function record$c(value2) {
18885
+ function record$d(value2) {
18682
18886
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
18683
18887
  }
18684
18888
  function array$3(value2) {
@@ -18697,7 +18901,7 @@ function provider(value2) {
18697
18901
  return PROVIDERS.includes(str$1(value2)) ? str$1(value2) : null;
18698
18902
  }
18699
18903
  function parseBundle(value2) {
18700
- const row2 = record$c(value2);
18904
+ const row2 = record$d(value2);
18701
18905
  const id2 = str$1(row2.id);
18702
18906
  const bundleProvider = provider(row2.provider);
18703
18907
  if (!id2 || !bundleProvider) return null;
@@ -18716,7 +18920,7 @@ function parseBundle(value2) {
18716
18920
  };
18717
18921
  }
18718
18922
  function parseDeployment(value2) {
18719
- const row2 = record$c(value2);
18923
+ const row2 = record$d(value2);
18720
18924
  const machineId = str$1(row2.machineId);
18721
18925
  const bundleId = str$1(row2.bundleId);
18722
18926
  if (!machineId || !bundleId) return null;
@@ -18736,10 +18940,10 @@ function parseDeployment(value2) {
18736
18940
  };
18737
18941
  }
18738
18942
  function parseStatus(value2) {
18739
- const row2 = record$c(value2);
18943
+ const row2 = record$d(value2);
18740
18944
  const statusProvider = provider(row2.provider);
18741
18945
  if (!statusProvider) return void 0;
18742
- const rotationRow = record$c(row2.rotation);
18946
+ const rotationRow = record$d(row2.rotation);
18743
18947
  const state = str$1(rotationRow.state);
18744
18948
  const warningKinds = Array.isArray(rotationRow.warningKinds) ? [...new Set(rotationRow.warningKinds.filter(
18745
18949
  (kind) => kind === "ACCOUNT_NEEDS_REAUTH" || kind === "NO_COMPARISON"
@@ -18764,7 +18968,7 @@ function parseStatus(value2) {
18764
18968
  };
18765
18969
  }
18766
18970
  function parseMachineResult(value2) {
18767
- const row2 = record$c(value2);
18971
+ const row2 = record$d(value2);
18768
18972
  const machineId = str$1(row2.machineId);
18769
18973
  if (!machineId) return null;
18770
18974
  if (row2.ok !== true) {
@@ -18779,28 +18983,28 @@ function parseMachineResult(value2) {
18779
18983
  };
18780
18984
  }
18781
18985
  function parseAiCredentialVaultSnapshot(value2) {
18782
- const body = record$c(value2);
18986
+ const body = record$d(value2);
18783
18987
  return {
18784
18988
  bundles: array$3(body.bundles).map(parseBundle).filter((bundle) => bundle !== null),
18785
18989
  deployments: array$3(body.deployments).map(parseDeployment).filter((deployment) => deployment !== null)
18786
18990
  };
18787
18991
  }
18788
18992
  function parseAiCredentialCaptureResponse(value2) {
18789
- const body = record$c(value2);
18993
+ const body = record$d(value2);
18790
18994
  const bundle = parseBundle(body.bundle);
18791
18995
  if (!bundle) throw new Error("AI credential capture response did not contain a bundle");
18792
18996
  return { bundle, warnings: array$3(body.warnings).filter((warning) => typeof warning === "string") };
18793
18997
  }
18794
18998
  function parseAiCredentialBatchResult(value2) {
18795
- const body = record$c(value2);
18999
+ const body = record$d(value2);
18796
19000
  return {
18797
19001
  results: array$3(body.results).map(parseMachineResult).filter((result2) => result2 !== null),
18798
19002
  warnings: array$3(body.warnings).filter((warning) => typeof warning === "string")
18799
19003
  };
18800
19004
  }
18801
19005
  function parseSelfServiceMachines(value2) {
18802
- return array$3(record$c(value2).machines).map((entry) => {
18803
- const row2 = record$c(entry);
19006
+ return array$3(record$d(value2).machines).map((entry) => {
19007
+ const row2 = record$d(entry);
18804
19008
  return {
18805
19009
  machineId: str$1(row2.machineId),
18806
19010
  type: row2.type === "shared" ? "shared" : "personal",
@@ -18813,8 +19017,8 @@ function parseSelfServiceMachines(value2) {
18813
19017
  }).filter((machine) => machine.machineId !== "");
18814
19018
  }
18815
19019
  function parsePersonalMachines(value2) {
18816
- return array$3(record$c(value2).machines).map((entry) => {
18817
- const row2 = record$c(entry);
19020
+ return array$3(record$d(value2).machines).map((entry) => {
19021
+ const row2 = record$d(entry);
18818
19022
  const type = row2.type === "shared" ? "shared" : "personal";
18819
19023
  return {
18820
19024
  machineId: str$1(row2.machineId),
@@ -18849,7 +19053,7 @@ const orgSearchPaths = {
18849
19053
  cursors: (companyId, projectId) => `/api/companies/${encodeURIComponent(companyId)}/search-index/cursors${projectId === void 0 ? "" : `?projectId=${encodeURIComponent(projectId)}`}`,
18850
19054
  progress: (companyId) => `/api/companies/${encodeURIComponent(companyId)}/search-index/progress`
18851
19055
  };
18852
- function record$b(value2) {
19056
+ function record$c(value2) {
18853
19057
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : {};
18854
19058
  }
18855
19059
  function str(value2, fallback = "") {
@@ -18860,9 +19064,9 @@ function num(value2, fallback = 0) {
18860
19064
  }
18861
19065
  const PHASES = ["idle", "waiting", "indexing", "done"];
18862
19066
  function parseOrgSearchCursors(raw) {
18863
- const list2 = Array.isArray(record$b(raw).cursors) ? record$b(raw).cursors : [];
19067
+ const list2 = Array.isArray(record$c(raw).cursors) ? record$c(raw).cursors : [];
18864
19068
  return list2.flatMap((item) => {
18865
- const c = record$b(item);
19069
+ const c = record$c(item);
18866
19070
  if (typeof c.sessionId !== "string" || typeof c.lastSeq !== "number") return [];
18867
19071
  return [{
18868
19072
  sessionId: c.sessionId,
@@ -18874,7 +19078,7 @@ function parseOrgSearchCursors(raw) {
18874
19078
  });
18875
19079
  }
18876
19080
  function parseProjectProgress(raw) {
18877
- const p = record$b(raw);
19081
+ const p = record$c(raw);
18878
19082
  return {
18879
19083
  projectId: str(p.projectId),
18880
19084
  sessionsTotal: num(p.sessionsTotal),
@@ -18887,7 +19091,7 @@ function parseProjectProgress(raw) {
18887
19091
  };
18888
19092
  }
18889
19093
  function parseOrgSearchProgress(raw) {
18890
- const r = record$b(raw);
19094
+ const r = record$c(raw);
18891
19095
  return {
18892
19096
  phase: PHASES.includes(r.phase) ? r.phase : "idle",
18893
19097
  sessionsTotal: num(r.sessionsTotal),
@@ -19364,7 +19568,7 @@ const PUBLICATION_KEYS = /* @__PURE__ */ new Set([
19364
19568
  function fail$9(detail) {
19365
19569
  throw new Error(`artifact publication response is invalid: ${detail}`);
19366
19570
  }
19367
- function record$a(value2, label) {
19571
+ function record$b(value2, label) {
19368
19572
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$9(`${label} must be an object`);
19369
19573
  return value2;
19370
19574
  }
@@ -19410,7 +19614,7 @@ function sourceIdentity(source2) {
19410
19614
  ]) : JSON.stringify([source2.chatId, source2.path]);
19411
19615
  }
19412
19616
  function validatePublication(value2, apiBaseUrl, operation, expectedSource) {
19413
- const publication = record$a(value2, "publication");
19617
+ const publication = record$b(value2, "publication");
19414
19618
  if (Object.keys(publication).some((key) => !PUBLICATION_KEYS.has(key))) {
19415
19619
  fail$9("publication contains an unexpected field");
19416
19620
  }
@@ -19443,7 +19647,7 @@ function validatePublication(value2, apiBaseUrl, operation, expectedSource) {
19443
19647
  };
19444
19648
  }
19445
19649
  function parseStaticArtifactPublicationResult(value2, options) {
19446
- const response2 = record$a(value2, "response");
19650
+ const response2 = record$b(value2, "response");
19447
19651
  if (options.operation === "publish") {
19448
19652
  exactKeys$3(response2, ["publication", "publicUrl", "reused"]);
19449
19653
  if (typeof response2.reused !== "boolean") fail$9("reused is invalid");
@@ -19497,6 +19701,60 @@ function parseLessonCandidate(value2) {
19497
19701
  }
19498
19702
  };
19499
19703
  }
19704
+ const MAX_ENTRIES = 1e4;
19705
+ const entries = /* @__PURE__ */ new Map();
19706
+ const MAX_KEY_SETS = 8;
19707
+ let keySets = [];
19708
+ function touchKeySet(set) {
19709
+ const index = keySets.indexOf(set);
19710
+ if (index >= 0 && index < keySets.length - 1) keySets.push(...keySets.splice(index, 1));
19711
+ }
19712
+ function sharedKeyCopy(keys2) {
19713
+ const existing = keySets.find((set) => sameKeyBytes(set, keys2));
19714
+ if (existing) {
19715
+ touchKeySet(existing);
19716
+ return existing;
19717
+ }
19718
+ const copy = keys2.map((key) => Uint8Array.from(key));
19719
+ keySets.push(copy);
19720
+ const evicted = keySets.length > MAX_KEY_SETS ? keySets.shift() : void 0;
19721
+ if (evicted) {
19722
+ for (const [id2, entry] of entries) if (entry.keys === evicted) entries.delete(id2);
19723
+ for (const key of evicted) key.fill(0);
19724
+ }
19725
+ return copy;
19726
+ }
19727
+ function sameKeyBytes(a, b) {
19728
+ if (a.length !== b.length) return false;
19729
+ return a.every((key, index) => {
19730
+ const other = b[index];
19731
+ if (key === other) return true;
19732
+ if (key.length !== other.length) return false;
19733
+ for (let i = 0; i < key.length; i += 1) if (key[i] !== other[i]) return false;
19734
+ return true;
19735
+ });
19736
+ }
19737
+ function memoizeSessionHeaders(api, keys2, decode2) {
19738
+ const agentStateBlob = api.agentState || null;
19739
+ const cached2 = entries.get(api.id);
19740
+ if (cached2 && cached2.metadata === api.metadata && cached2.agentState === agentStateBlob && sameKeyBytes(cached2.keys, keys2) && cached2.dataKeyBlobs.every((blob) => getCachedDataKeyMetadataJson(api.id, blob) !== null)) {
19741
+ entries.delete(api.id);
19742
+ entries.set(api.id, cached2);
19743
+ touchKeySet(cached2.keys);
19744
+ return cached2.headers;
19745
+ }
19746
+ const headers2 = decode2();
19747
+ entries.delete(api.id);
19748
+ if (headers2.metadata !== null && (agentStateBlob === null || headers2.agentState !== null)) {
19749
+ const dataKeyBlobs = [api.metadata, agentStateBlob].filter((blob) => !!blob && getCachedDataKeyMetadataJson(api.id, blob) !== null);
19750
+ entries.set(api.id, { metadata: api.metadata, agentState: agentStateBlob, keys: sharedKeyCopy(keys2), dataKeyBlobs, headers: headers2 });
19751
+ if (entries.size > MAX_ENTRIES) {
19752
+ const oldest = entries.keys().next().value;
19753
+ if (oldest !== void 0) entries.delete(oldest);
19754
+ }
19755
+ }
19756
+ return headers2;
19757
+ }
19500
19758
  function asRecord(value2) {
19501
19759
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
19502
19760
  }
@@ -28326,6 +28584,12 @@ function sessionTitleFromMetadata(metadata) {
28326
28584
  const text2 = summary.text;
28327
28585
  return typeof text2 === "string" && text2.trim() ? text2 : void 0;
28328
28586
  }
28587
+ function summaryTextFromMetadata(metadata) {
28588
+ const summary = metadata?.summary;
28589
+ if (!summary || typeof summary !== "object" || Array.isArray(summary)) return void 0;
28590
+ const text2 = summary.text;
28591
+ return typeof text2 === "string" && text2.trim() ? text2 : void 0;
28592
+ }
28329
28593
  function branchSlugFromMetadata(metadata) {
28330
28594
  const summary = metadata?.summary;
28331
28595
  if (!summary || typeof summary !== "object" || Array.isArray(summary)) return void 0;
@@ -28731,8 +28995,8 @@ function nonEmptyString$1(value2) {
28731
28995
  }
28732
28996
  function coerceStringArray(value2) {
28733
28997
  if (!Array.isArray(value2)) return void 0;
28734
- const entries = value2.filter((entry) => typeof entry === "string" && entry.trim() !== "");
28735
- return entries.length > 0 ? entries : void 0;
28998
+ const entries2 = value2.filter((entry) => typeof entry === "string" && entry.trim() !== "");
28999
+ return entries2.length > 0 ? entries2 : void 0;
28736
29000
  }
28737
29001
  function optionalTimestamp(value2) {
28738
29002
  if (typeof value2 !== "string" && typeof value2 !== "number" && !(value2 instanceof Date) && value2 !== null && value2 !== void 0) {
@@ -28762,7 +29026,7 @@ const MCP_RUNTIME_STATUSES = /* @__PURE__ */ new Set([
28762
29026
  ]);
28763
29027
  function coerceMcpServerStatuses(value2) {
28764
29028
  if (!Array.isArray(value2)) return void 0;
28765
- const entries = [];
29029
+ const entries2 = [];
28766
29030
  for (const raw of value2) {
28767
29031
  if (!isRecord$3(raw)) continue;
28768
29032
  const name = nonEmptyString$1(raw.name);
@@ -28770,9 +29034,9 @@ function coerceMcpServerStatuses(value2) {
28770
29034
  const checkedAt = optionalTimestamp(raw.checkedAt);
28771
29035
  if (!name || !status || checkedAt === void 0) continue;
28772
29036
  const error = nonEmptyString$1(raw.error);
28773
- entries.push({ name, status, checkedAt, ...error ? { error } : {} });
29037
+ entries2.push({ name, status, checkedAt, ...error ? { error } : {} });
28774
29038
  }
28775
- return entries.length > 0 ? entries : void 0;
29039
+ return entries2.length > 0 ? entries2 : void 0;
28776
29040
  }
28777
29041
  function coerceCreatedBy(value2) {
28778
29042
  if (!isRecord$3(value2)) return void 0;
@@ -28794,16 +29058,20 @@ function decryptSessionJsonSync(sessionId, blob, candidateKeys) {
28794
29058
  return decryptJsonWithCandidates(blob, candidateKeys);
28795
29059
  }
28796
29060
  function apiSessionToSession(api, candidateKeys, existing) {
28797
- const metadata = decryptSessionJsonSync(api.id, api.metadata, candidateKeys);
29061
+ const decoded = memoizeSessionHeaders(api, candidateKeys, () => ({
29062
+ metadata: decryptSessionJsonSync(api.id, api.metadata, candidateKeys),
29063
+ summaryText: decryptSummaryText(api.metadata, candidateKeys),
29064
+ remoteThinking: apiSessionAgentStateThinking(api.agentState, candidateKeys),
29065
+ pendingAskUserQuestion: apiSessionAgentStatePendingAskUserQuestion(api.agentState, candidateKeys),
29066
+ pendingExitPlanMode: apiSessionAgentStatePendingExitPlanMode(api.agentState, candidateKeys),
29067
+ pendingPermissionRequests: apiSessionAgentStatePendingPermissions(api.agentState, candidateKeys),
29068
+ agentControlledByUser: apiSessionAgentStateControlledByUser(api.agentState, candidateKeys),
29069
+ agentState: api.agentState ? decryptSessionJsonSync(api.id, api.agentState, candidateKeys) : null
29070
+ }));
29071
+ const { metadata, remoteThinking, pendingAskUserQuestion, pendingExitPlanMode, pendingPermissionRequests, agentControlledByUser, agentState } = decoded;
28798
29072
  const updatedAt = mergeUpdatedAt(api.updatedAt);
28799
- const summaryText = decryptSummaryText(api.metadata, candidateKeys) ?? existing?.summaryText;
29073
+ const summaryText = summaryTextFromMetadata(metadata) ?? decoded.summaryText ?? existing?.summaryText;
28800
29074
  const machineId = nonEmptyString$1(metadata?.machineId) ?? existing?.machineId;
28801
- const remoteThinking = apiSessionAgentStateThinking(api.agentState, candidateKeys);
28802
- const pendingAskUserQuestion = apiSessionAgentStatePendingAskUserQuestion(api.agentState, candidateKeys);
28803
- const pendingExitPlanMode = apiSessionAgentStatePendingExitPlanMode(api.agentState, candidateKeys);
28804
- const pendingPermissionRequests = apiSessionAgentStatePendingPermissions(api.agentState, candidateKeys);
28805
- const agentControlledByUser = apiSessionAgentStateControlledByUser(api.agentState, candidateKeys);
28806
- const agentState = api.agentState ? decryptSessionJsonSync(api.id, api.agentState, candidateKeys) : null;
28807
29075
  const agent = agentFromFlavor(typeof metadata?.flavor === "string" ? metadata.flavor : void 0) ?? existing?.agent;
28808
29076
  const models = coerceSessionModels(metadata?.models) ?? existing?.models;
28809
29077
  const worktree = coerceSessionWorktree(metadata?.worktree) ?? coerceSessionWorktree(metadata) ?? existing?.worktree;
@@ -28928,7 +29196,7 @@ async function prepareMcpCallerGrantEnvelope(input, deps) {
28928
29196
  return { ok: false, reason: "invalid-key" };
28929
29197
  }
28930
29198
  }
28931
- function record$9(value2) {
29199
+ function record$a(value2) {
28932
29200
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
28933
29201
  }
28934
29202
  function text$7(value2) {
@@ -28943,7 +29211,7 @@ function timestamp(value2, fallback) {
28943
29211
  return fallback;
28944
29212
  }
28945
29213
  function parseInvite(value2) {
28946
- const row2 = record$9(value2);
29214
+ const row2 = record$a(value2);
28947
29215
  if (!row2) return null;
28948
29216
  const inviteId = text$7(row2.inviteId ?? row2.id);
28949
29217
  const companyId = text$7(row2.companyId);
@@ -28963,7 +29231,7 @@ function parseInvite(value2) {
28963
29231
  };
28964
29232
  }
28965
29233
  function parsePendingCompanyInvites(value2) {
28966
- const rows = Array.isArray(value2) ? value2 : record$9(value2)?.invites;
29234
+ const rows = Array.isArray(value2) ? value2 : record$a(value2)?.invites;
28967
29235
  if (!Array.isArray(rows)) throw new Error("Invalid invitation response");
28968
29236
  const seen = /* @__PURE__ */ new Set();
28969
29237
  const result2 = [];
@@ -29067,9 +29335,9 @@ const STANDALONE_USER_ID = "standalone-local";
29067
29335
  function isStandaloneAuthSession(session) {
29068
29336
  return session?.userId === STANDALONE_USER_ID;
29069
29337
  }
29070
- function indexMachineRegistry(entries) {
29338
+ function indexMachineRegistry(entries2) {
29071
29339
  const byId = {};
29072
- for (const entry of entries) {
29340
+ for (const entry of entries2) {
29073
29341
  if (!entry?.machineId) continue;
29074
29342
  const { machineId, ...info } = entry;
29075
29343
  byId[machineId] = info;
@@ -29176,7 +29444,7 @@ const TERMINAL_CODES = /* @__PURE__ */ new Set([
29176
29444
  function invalid$1() {
29177
29445
  throw new Error("session-followup-response-invalid");
29178
29446
  }
29179
- function record$8(value2) {
29447
+ function record$9(value2) {
29180
29448
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) invalid$1();
29181
29449
  return value2;
29182
29450
  }
@@ -29210,7 +29478,7 @@ function validateSessionFollowupCreateInput(input) {
29210
29478
  }
29211
29479
  function parseSessionFollowupPublic(value2) {
29212
29480
  try {
29213
- const item = record$8(value2);
29481
+ const item = record$9(value2);
29214
29482
  if (!STATUSES$1.has(item.status)) invalid$1();
29215
29483
  if (item.terminalCode !== null && !TERMINAL_CODES.has(item.terminalCode)) invalid$1();
29216
29484
  if (item.payloadVersion !== 1) invalid$1();
@@ -29481,9 +29749,9 @@ function rememberRows(scope2, requestedIds, rows, now) {
29481
29749
  for (const id2 of requestedIds) cache2.delete(id2);
29482
29750
  for (const entry of rows) cache2.set(entry.row.id, entry);
29483
29751
  const idleBefore = now - SESSION_ROW_CACHE_IDLE_MS;
29484
- for (const [otherScope, entries] of rowsByScope) {
29485
- for (const [id2, entry] of entries) if (entry.seenAt <= idleBefore) entries.delete(id2);
29486
- if (entries.size === 0) rowsByScope.delete(otherScope);
29752
+ for (const [otherScope, entries2] of rowsByScope) {
29753
+ for (const [id2, entry] of entries2) if (entry.seenAt <= idleBefore) entries2.delete(id2);
29754
+ if (entries2.size === 0) rowsByScope.delete(otherScope);
29487
29755
  }
29488
29756
  for (const [otherScope, until] of versionUnsupportedUntilByScope) {
29489
29757
  if (until <= now) versionUnsupportedUntilByScope.delete(otherScope);
@@ -29626,7 +29894,8 @@ function normalizeOptionalIsoDate(value2) {
29626
29894
  function normalizeGithubPatStatus(raw) {
29627
29895
  return {
29628
29896
  hasPat: raw.hasPat ?? raw.registered ?? false,
29629
- updatedAt: normalizeOptionalIsoDate(raw.updatedAt)
29897
+ updatedAt: normalizeOptionalIsoDate(raw.updatedAt),
29898
+ ...raw.provider === "github" || raw.provider === "gitlab" ? { provider: raw.provider } : {}
29630
29899
  };
29631
29900
  }
29632
29901
  function normalizeGitProviderPage(provider2, rawRepositories, hasMore) {
@@ -30151,7 +30420,18 @@ class ApiClient {
30151
30420
  }
30152
30421
  async getCompanies() {
30153
30422
  const data = await this.request("/api/companies/me", { boundary: "api" });
30154
- return Array.isArray(data) ? data : data.companies ?? [];
30423
+ const companies = Array.isArray(data) ? data : data?.companies;
30424
+ if (!Array.isArray(companies) || companies.some((row2) => !row2 || typeof row2.id !== "string" || !row2.id.trim() || typeof row2.slug !== "string" || !row2.slug.trim() || typeof row2.displayName !== "string" || !row2.displayName.trim() || !["owner", "admin", "member"].includes(row2.myRole))) throw new Error("Invalid company membership response");
30425
+ return companies;
30426
+ }
30427
+ companyEnrollmentApi() {
30428
+ return createCompanyEnrollmentApi((path2, init) => this.request(path2, { ...init, boundary: "api" }));
30429
+ }
30430
+ joinCompanyByCode(code, mfaSessionToken) {
30431
+ return this.companyEnrollmentApi().joinCompanyByCode(code, mfaSessionToken);
30432
+ }
30433
+ createCompany(input) {
30434
+ return this.companyEnrollmentApi().createCompany(input);
30155
30435
  }
30156
30436
  async getPendingCompanyInvites() {
30157
30437
  const invites = [], cursors = /* @__PURE__ */ new Set();
@@ -30458,6 +30738,9 @@ class ApiClient {
30458
30738
  async startGithubIntegrationConnect() {
30459
30739
  return this.request("/api/integrations/github/connect-url", { method: "POST", boundary: "api" });
30460
30740
  }
30741
+ async startGithubIntegrationInstallation() {
30742
+ return this.request("/api/integrations/github/install-url", { method: "POST", boundary: "api" });
30743
+ }
30461
30744
  async searchGitLabIntegrationProjects(search, page = 1) {
30462
30745
  const params = new URLSearchParams({ search, page: String(page) });
30463
30746
  const data = await this.request(
@@ -31602,6 +31885,12 @@ class ApiClient {
31602
31885
  if (list2.length === 0) return null;
31603
31886
  return list2.find((pr) => pr.state === "open") ?? list2.find((pr) => pr.state === "merged") ?? list2[0];
31604
31887
  }
31888
+ /** specs/pr-follow-through R3-2 — 머지 가드가 머지와 같은 권위 경로로 열린 PR 을 본다. */
31889
+ async listOpenGithubPullRequests(projectId) {
31890
+ const params = new URLSearchParams({ state: "open", perPage: "100" });
31891
+ const raw = await this.request(`/api/projects/${encodeURIComponent(projectId)}/pull-requests?${params.toString()}`, { boundary: "api" });
31892
+ return extractPullRequestList(raw).map((entry) => normalizeGithubPullRequest(entry));
31893
+ }
31605
31894
  async mergeGithubPullRequest(input, tokenOverride) {
31606
31895
  const raw = await this.request(
31607
31896
  `/api/projects/${encodeURIComponent(input.projectId)}/pull-requests/${encodeURIComponent(String(input.number))}/merge`,
@@ -32441,12 +32730,22 @@ function parseAccessKeyFile(raw) {
32441
32730
  }
32442
32731
  if (!parsed2 || typeof parsed2 !== "object") return null;
32443
32732
  const { token, secret } = parsed2;
32444
- if (!isNonEmptyString$2(token) || !isNonEmptyString$2(secret)) return null;
32445
- if (decodedByteLength(secret) !== SECRETBOX_KEY_BYTES) return null;
32733
+ if (!isNonEmptyString$2(token)) return null;
32734
+ let keys2;
32735
+ if ("secret" in parsed2) {
32736
+ if (!isNonEmptyString$2(secret) || decodedByteLength(secret) !== SECRETBOX_KEY_BYTES) return null;
32737
+ keys2 = { secret };
32738
+ } else {
32739
+ const encryption = parsed2.encryption;
32740
+ if (!encryption || typeof encryption !== "object") return null;
32741
+ const { publicKey, machineKey } = encryption;
32742
+ if (!isNonEmptyString$2(publicKey) || decodedByteLength(publicKey) !== 32 || !isNonEmptyString$2(machineKey) || decodedByteLength(machineKey) !== 32) return null;
32743
+ keys2 = { encryption: { publicKey, machineKey } };
32744
+ }
32446
32745
  const { apiToken, serverUrl } = parsed2;
32447
32746
  return {
32448
32747
  token,
32449
- secret,
32748
+ ...keys2,
32450
32749
  ...isNonEmptyString$2(apiToken) ? { apiToken } : {},
32451
32750
  ...isOriginUrl(serverUrl) ? { serverUrl } : {}
32452
32751
  };
@@ -32513,6 +32812,10 @@ function missingCredentialMessage() {
32513
32812
  "Set SAYCODE_HAPPY_HOME to point at a different Happy home directory."
32514
32813
  ].join("\n");
32515
32814
  }
32815
+ function requireLegacySecret(credential) {
32816
+ if (!credential.secret) throw new Error("This operation requires a legacy secret; dataKey credentials need a scoped encryption key.");
32817
+ return credential.secret;
32818
+ }
32516
32819
  const PERSONAL_WORKSPACE_ID = "personal";
32517
32820
  const PERSONAL_WORKSPACE = { id: PERSONAL_WORKSPACE_ID, name: "Personal", kind: "personal" };
32518
32821
  function text$6(value2) {
@@ -32707,7 +33010,8 @@ class RealtimeSocket {
32707
33010
  return new Error("Realtime WebSocket connection was cancelled because the transport was disposed.");
32708
33011
  }
32709
33012
  safeErrorMessage(error) {
32710
- return error.message.replaceAll(this.credential.token, "[redacted]").replaceAll(this.credential.secret, "[redacted]");
33013
+ const secrets = [this.credential.token, this.credential.secret, this.credential.encryption?.machineKey];
33014
+ return secrets.reduce((message, secret) => secret ? message.replaceAll(secret, "[redacted]") : message, error.message);
32711
33015
  }
32712
33016
  on(event, listener) {
32713
33017
  this.socket.on(event, listener);
@@ -32742,13 +33046,13 @@ class CliTransport {
32742
33046
  constructor(credential, serverUrl = DEFAULT_SERVER_URL) {
32743
33047
  this.credential = credential;
32744
33048
  this.serverUrl = serverUrl;
32745
- this.secretKey = decodeBase64$1(credential.secret);
33049
+ this.secretKey = credential.secret ? decodeBase64$1(credential.secret) : null;
32746
33050
  this.realtime = new RealtimeSocket(credential, serverUrl);
32747
33051
  }
32748
33052
  realtime;
32749
33053
  secretKey;
32750
33054
  get key() {
32751
- return this.secretKey;
33055
+ return this.secretKey ?? decodeBase64$1(requireLegacySecret(this.credential));
32752
33056
  }
32753
33057
  connect(timeoutMs = DEFAULT_CONNECT_TIMEOUT_MS) {
32754
33058
  return this.realtime.connect(timeoutMs);
@@ -32764,21 +33068,28 @@ class CliTransport {
32764
33068
  * `callDesktopMachineRpc` (src/sync/sessionCreation.ts). `target` is the
32765
33069
  * machine id for daemon methods and the session id for session methods.
32766
33070
  */
32767
- async rpc(target, method, params, ackTimeoutMs = DEFAULT_ACK_TIMEOUT_MS) {
32768
- const encrypted = this.secretKey ? encryptSecretBox(JSON.stringify(params), this.secretKey) : plainJsonToBase64(params);
33071
+ async rpc(target, method, params, ackTimeoutMs = DEFAULT_ACK_TIMEOUT_MS, machineKey) {
33072
+ const nonce = Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString("base64");
33073
+ const encrypted = machineKey ? await encryptDataKeyContent(machineKey, { rpcBinding: 1, method, scope: target, issuedAt: Date.now(), nonce, params }) : encryptSecretBox(JSON.stringify(params), this.key);
32769
33074
  const response2 = await this.realtime.emitWithAck("rpc-call", {
32770
33075
  method: `${target}:${method}`,
32771
33076
  params: encrypted
32772
33077
  }, ackTimeoutMs);
32773
33078
  if (!response2.ok) throw new Error(response2.error || `RPC ${method} was rejected`);
33079
+ if (machineKey) {
33080
+ const opened = response2.result ? await decryptDataKeyJson(machineKey, response2.result) : null;
33081
+ if (opened?.rpcBinding !== 1 || opened.nonce !== nonce) throw new Error("MACHINE_RPC_BINDING_FAILED");
33082
+ const result2 = opened.result;
33083
+ if (result2 && typeof result2 === "object" && "error" in result2) throw new Error(String(result2.error));
33084
+ return result2 ?? null;
33085
+ }
32774
33086
  if (!response2.result) return null;
32775
33087
  return this.decodePayload(response2.result);
32776
33088
  }
32777
- /** Session/machine payloads arrive either as plaintext JSON, base64 JSON, or secretbox. */
33089
+ /** Decode plaintext JSON or legacy secretbox; dataKey payloads require their scoped key. */
32778
33090
  decodePayload(raw) {
32779
33091
  if (/^[{["]/.test(raw)) return JSON.parse(raw);
32780
- if (this.secretKey) return JSON.parse(decryptSecretBox(raw, this.secretKey));
32781
- return JSON.parse(Buffer.from(raw, "base64").toString("utf8"));
33092
+ return JSON.parse(decryptSecretBox(raw, this.key));
32782
33093
  }
32783
33094
  dispose() {
32784
33095
  this.realtime.dispose();
@@ -33507,19 +33818,19 @@ function reduceSessionReadState(state, change, merge2, fingerprintLimit = DEFAUL
33507
33818
  fingerprints: fingerprints2
33508
33819
  };
33509
33820
  }
33510
- function record$7(value2) {
33821
+ function record$8(value2) {
33511
33822
  return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
33512
33823
  }
33513
33824
  function unwrapEncryptedField(value2) {
33514
33825
  if (typeof value2 === "string") return value2;
33515
- const envelope = record$7(value2);
33826
+ const envelope = record$8(value2);
33516
33827
  if (!envelope) return null;
33517
33828
  return typeof envelope.c === "string" ? envelope.c : null;
33518
33829
  }
33519
33830
  function readNewMessage(update, sessionId) {
33520
- const body = record$7(record$7(update)?.body);
33831
+ const body = record$8(record$8(update)?.body);
33521
33832
  if (!body || body.t !== "new-message" || body.sid !== sessionId) return null;
33522
- const message = record$7(body.message);
33833
+ const message = record$8(body.message);
33523
33834
  if (!message) return null;
33524
33835
  const ciphertext = unwrapEncryptedField(message.content);
33525
33836
  if (ciphertext === null) return null;
@@ -33530,9 +33841,9 @@ function readNewMessage(update, sessionId) {
33530
33841
  };
33531
33842
  }
33532
33843
  function readSessionAgentState(update, sessionId) {
33533
- const body = record$7(record$7(update)?.body);
33844
+ const body = record$8(record$8(update)?.body);
33534
33845
  if (!body || body.t !== "update-session" || body.id !== sessionId) return null;
33535
- const agentState = record$7(body.agentState);
33846
+ const agentState = record$8(body.agentState);
33536
33847
  if (!agentState) return null;
33537
33848
  return unwrapEncryptedField(agentState.value);
33538
33849
  }
@@ -34413,6 +34724,40 @@ function readMachineId(input, readFile2) {
34413
34724
  function spawnBudgetKey(env) {
34414
34725
  return env.agentId ?? env.root;
34415
34726
  }
34727
+ function readLocalMachineRpcKey(input, machineId, readFile2) {
34728
+ for (const path2 of machineSettingsPaths(input)) {
34729
+ const raw = readFile2(path2);
34730
+ if (!raw) continue;
34731
+ let id2;
34732
+ try {
34733
+ id2 = JSON.parse(raw)?.machineId;
34734
+ } catch {
34735
+ continue;
34736
+ }
34737
+ if (typeof id2 !== "string" || !id2) continue;
34738
+ if (id2 !== machineId) throw new Error("MACHINE_RPC_SCOPE_MISMATCH");
34739
+ const credential = readFile2(path2.replace(/settings\.json$/, "access.key"));
34740
+ if (!credential) return null;
34741
+ let parsed2;
34742
+ try {
34743
+ parsed2 = JSON.parse(credential);
34744
+ } catch {
34745
+ throw new Error("MACHINE_RPC_KEY_INVALID");
34746
+ }
34747
+ if (!parsed2 || typeof parsed2 !== "object") throw new Error("MACHINE_RPC_KEY_INVALID");
34748
+ if ("secret" in parsed2) {
34749
+ if (!parseAccessKeyFile(credential)) throw new Error("MACHINE_RPC_KEY_INVALID");
34750
+ return null;
34751
+ }
34752
+ if (!("encryption" in parsed2)) return null;
34753
+ const encoded = parsed2.encryption?.machineKey;
34754
+ if (typeof encoded !== "string") throw new Error("MACHINE_RPC_KEY_INVALID");
34755
+ const key = Buffer.from(encoded, "base64");
34756
+ if (key.length !== 32 || key.toString("base64") !== encoded) throw new Error("MACHINE_RPC_KEY_INVALID");
34757
+ return new Uint8Array(key);
34758
+ }
34759
+ throw new Error("MACHINE_RPC_SCOPE_MISMATCH");
34760
+ }
34416
34761
  const CLI_MODEL_CATALOG_TIMEOUT_MS = 3e3;
34417
34762
  const CLI_MODEL_CATALOG_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
34418
34763
  const CATALOG_FILE_MODE = 384;
@@ -34492,7 +34837,7 @@ function createCliSessionConnection(bound, deps = REAL_CLI_SESSION_CONNECTION_DE
34492
34837
  model: input.model ?? bound.settings.model,
34493
34838
  effort: input.effort === void 0 ? bound.settings.effort : input.effort
34494
34839
  }, input.autoRoute);
34495
- const key = new Uint8Array(Buffer.from(bound.credential.secret, "base64"));
34840
+ const key = new Uint8Array(Buffer.from(requireLegacySecret(bound.credential), "base64"));
34496
34841
  await bound.rest.sendMessage(bound.sessionId, localId, encodeMessageContent(content, key));
34497
34842
  return { localId };
34498
34843
  },
@@ -34718,18 +35063,21 @@ function normalizeSessionRows(rows, candidateKeys) {
34718
35063
  for (const row2 of rows) {
34719
35064
  const metadata = decryptJsonWithCandidates(row2.metadata, candidateKeys);
34720
35065
  if (!metadata) continue;
34721
- sessions.push({
34722
- id: row2.id,
34723
- machineId: readString$4(metadata, "machineId") ?? "",
34724
- directory: readString$4(metadata, "path") ?? "",
34725
- summary: readSummary(metadata),
34726
- active: row2.active === true,
34727
- updatedAt: row2.updatedAt ?? 0,
34728
- agentState: decryptJsonWithCandidates(row2.agentState, candidateKeys)
34729
- });
35066
+ sessions.push(normalizeDecryptedSessionRow(row2, metadata, decryptJsonWithCandidates(row2.agentState, candidateKeys)));
34730
35067
  }
34731
35068
  return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
34732
35069
  }
35070
+ function normalizeDecryptedSessionRow(row2, metadata, agentState) {
35071
+ return {
35072
+ id: row2.id,
35073
+ machineId: readString$4(metadata, "machineId") ?? "",
35074
+ directory: readString$4(metadata, "path") ?? "",
35075
+ summary: readSummary(metadata),
35076
+ active: row2.active === true,
35077
+ updatedAt: row2.updatedAt ?? 0,
35078
+ agentState
35079
+ };
35080
+ }
34733
35081
  function filterSessions(sessions, options) {
34734
35082
  return sessions.filter((session) => {
34735
35083
  if (!options.all && !session.active) return false;
@@ -34746,7 +35094,7 @@ async function resumeStoppedSession(context, transport, sessionId) {
34746
35094
  const rows = await context.rest.lookupSessions([sessionId]).catch((error) => {
34747
35095
  throw new CliError(`Saycode could not check whether this conversation is running: ${error instanceof Error ? error.message : String(error)}`);
34748
35096
  });
34749
- const machineId = stoppedSessionMachine(rows, sessionId, [decodeBase64$1(context.syncCredential.secret)]);
35097
+ const machineId = stoppedSessionMachine(rows, sessionId, [decodeBase64$1(requireLegacySecret(context.syncCredential))]);
34750
35098
  if (!machineId) return;
34751
35099
  const result2 = await transport.rpc(machineId, "resume-happy-session", { sessionId }, 6e4).catch((error) => {
34752
35100
  throw new CliError(`This conversation is stopped and could not be started again on its machine: ${error instanceof Error ? error.message : String(error)}`);
@@ -35097,8 +35445,8 @@ function collectRelatedProjectIds(links, activeProjectId) {
35097
35445
  }
35098
35446
  function mergeRelatedProjectDirectorySkipCounts(...counts) {
35099
35447
  const merged = {};
35100
- for (const entries of counts) {
35101
- for (const [reason, count] of Object.entries(entries)) {
35448
+ for (const entries2 of counts) {
35449
+ for (const [reason, count] of Object.entries(entries2)) {
35102
35450
  if (count) incrementSkip(merged, reason, count);
35103
35451
  }
35104
35452
  }
@@ -35545,6 +35893,44 @@ async function resolveSpawnBrowserAttestation(input) {
35545
35893
  return void 0;
35546
35894
  }
35547
35895
  }
35896
+ async function sessionControlRpc(sessionId, method, params, resolveTransport, beforeSend) {
35897
+ const encryption = resolveSessionWriteEncryption(sessionId);
35898
+ if (encryption.kind === "blocked") throw new SessionWriteLockedError();
35899
+ const guard = () => {
35900
+ const current2 = resolveSessionWriteEncryption(sessionId);
35901
+ if (current2.kind !== encryption.kind || current2.kind === "dek" && encryption.kind === "dek" && current2.dek !== encryption.dek) {
35902
+ throw new SessionWriteLockedError();
35903
+ }
35904
+ };
35905
+ const transport = await resolveTransport();
35906
+ try {
35907
+ guard();
35908
+ const nonce = Buffer$1.from(crypto.getRandomValues(new Uint8Array(16))).toString("base64");
35909
+ const encrypted = encryption.kind === "dek" ? await encryptDataKeyContent(encryption.dek, { rpcBinding: 1, method, scope: sessionId, issuedAt: Date.now(), nonce, params }) : transport.secretKey ? encryptSecretBox(JSON.stringify(params), transport.secretKey) : plainJsonToBase64(params);
35910
+ await beforeSend?.();
35911
+ guard();
35912
+ const response2 = await transport.emitWithAck("rpc-call", { method: `${sessionId}:${method}`, params: encrypted });
35913
+ guard();
35914
+ if (!response2.ok) throw new Error(response2.error || "SESSION_RPC_FAILED");
35915
+ let result2 = null;
35916
+ if (encryption.kind === "dek") {
35917
+ const opened = response2.result ? await decryptDataKeyJson(encryption.dek, response2.result) : null;
35918
+ guard();
35919
+ if (!opened || opened.rpcBinding !== 1 || opened.nonce !== nonce || !Object.hasOwn(opened, "result") || Object.hasOwn(opened, "params") || opened.result === null && method !== "permission") throw new Error("SESSION_RPC_BINDING_FAILED");
35920
+ result2 = opened.result;
35921
+ } else if (response2.result) {
35922
+ result2 = JSON.parse(/^[{[\"]/.test(response2.result) ? response2.result : transport.secretKey ? decryptSecretBox(response2.result, transport.secretKey) : Buffer$1.from(response2.result, "base64").toString("utf8"));
35923
+ }
35924
+ if (result2 && typeof result2 === "object") {
35925
+ const value2 = result2;
35926
+ if (value2.error || value2.success === false || value2.ok === false) throw new Error(typeof value2.error === "string" ? value2.error : "SESSION_RPC_FAILED");
35927
+ return value2;
35928
+ }
35929
+ return null;
35930
+ } finally {
35931
+ transport.dispose();
35932
+ }
35933
+ }
35548
35934
  async function mapWithConcurrency(values, concurrency, mapper) {
35549
35935
  const results = new Array(values.length);
35550
35936
  let nextIndex = 0;
@@ -36449,58 +36835,32 @@ function isRpcMethodNotAvailableError(error) {
36449
36835
  function sessionPermissionUpgradeMessage() {
36450
36836
  return translateCurrent("sessionCreation.daemonUpgrade");
36451
36837
  }
36452
- function parsePermissionReplyAck(response2, key) {
36453
- if (!response2.result) return { delivered: null };
36454
- try {
36455
- const ack = parseRpcResult(response2.result, key);
36456
- if (typeof ack?.delivered !== "boolean") return { delivered: null };
36457
- return { delivered: ack.delivered };
36458
- } catch {
36459
- return { delivered: null };
36460
- }
36461
- }
36462
36838
  async function sendSessionPermission(input, approved) {
36463
- const transport = await resolveSessionRpcTransport(input.auth, input.machine);
36464
- try {
36465
- if (input.beforeSend) {
36466
- try {
36467
- await input.beforeSend();
36468
- } catch {
36469
- const error = new Error("the permission answer was withdrawn before it was sent");
36470
- error.code = "PERMISSION_FENCED";
36471
- throw error;
36472
- }
36473
- }
36474
- const params = {
36475
- id: input.permissionId,
36476
- approved,
36477
- decision: approved ? "approved" : "denied",
36478
- ...approved && input.updatedInput ? { updatedInput: input.updatedInput } : {},
36479
- ...approved && input.mode ? { mode: input.mode } : {},
36480
- ...input.channelClaim ? { channelClaim: input.channelClaim } : {}
36481
- };
36482
- if (input.beforeSend) {
36483
- try {
36484
- await input.beforeSend();
36485
- } catch {
36486
- const error = new Error("the permission answer was withdrawn before it was sent");
36487
- error.code = "PERMISSION_FENCED";
36488
- throw error;
36489
- }
36490
- }
36491
- const paramsText = JSON.stringify(params);
36492
- const encryptedParams = transport.secretKey ? encryptSecretBox(paramsText, transport.secretKey) : plainJsonToBase64(params);
36493
- const response2 = await transport.emitWithAck("rpc-call", {
36494
- method: `${input.sessionId}:permission`,
36495
- params: encryptedParams
36496
- });
36497
- if (!response2.ok) {
36498
- throw new Error(response2.error || translateCurrent("sessionCreation.permissionRejected"));
36839
+ const beforeSend = async () => {
36840
+ try {
36841
+ await input.beforeSend?.();
36842
+ } catch {
36843
+ const error = new Error("the permission answer was withdrawn before it was sent");
36844
+ error.code = "PERMISSION_FENCED";
36845
+ throw error;
36499
36846
  }
36500
- return parsePermissionReplyAck(response2, transport.secretKey);
36501
- } finally {
36502
- transport.dispose();
36503
- }
36847
+ };
36848
+ const params = {
36849
+ id: input.permissionId,
36850
+ approved,
36851
+ decision: approved ? "approved" : "denied",
36852
+ ...approved && input.updatedInput ? { updatedInput: input.updatedInput } : {},
36853
+ ...approved && input.mode ? { mode: input.mode } : {},
36854
+ ...input.channelClaim ? { channelClaim: input.channelClaim } : {}
36855
+ };
36856
+ const ack = await sessionControlRpc(
36857
+ input.sessionId,
36858
+ "permission",
36859
+ params,
36860
+ () => resolveSessionRpcTransport(input.auth, input.machine),
36861
+ beforeSend
36862
+ );
36863
+ return { delivered: typeof ack?.delivered === "boolean" ? ack.delivered : null };
36504
36864
  }
36505
36865
  async function replyToSessionPermission(input, approved) {
36506
36866
  try {
@@ -36625,41 +36985,22 @@ async function resumeDesktopSessionOnMachine(input, deps = {}) {
36625
36985
  }
36626
36986
  async function abortDesktopSessionResponse(input) {
36627
36987
  if (!input.machine.online) throw new Error(translateCurrent("sessionCreation.machineOfflineInterrupt"));
36628
- const transport = await resolveSessionRpcTransport(input.auth, input.machine);
36629
36988
  try {
36630
- const paramsText = JSON.stringify({});
36631
- const encryptedParams = transport.secretKey ? encryptSecretBox(paramsText, transport.secretKey) : plainJsonToBase64({});
36632
- const response2 = await transport.emitWithAck("rpc-call", {
36633
- method: `${input.sessionId}:abort`,
36634
- params: encryptedParams
36635
- });
36636
- if (!response2.ok) {
36637
- throw new Error(response2.error || translateCurrent("sessionCreation.interruptFailed"));
36638
- }
36989
+ await sessionControlRpc(input.sessionId, "abort", {}, () => resolveSessionRpcTransport(input.auth, input.machine));
36639
36990
  } catch (error) {
36640
36991
  const message = error instanceof Error ? error.message : String(error);
36641
36992
  throw new Error(message || translateCurrent("sessionCreation.interruptFailed"));
36642
- } finally {
36643
- transport.dispose();
36644
36993
  }
36645
36994
  }
36646
36995
  async function steerDesktopSessionResponse(input) {
36647
36996
  if (!input.machine.online) {
36648
36997
  return { success: false, error: translateCurrent("sessionCreation.machineOfflineInterrupt") };
36649
36998
  }
36650
- const transport = await resolveSessionRpcTransport(input.auth, input.machine);
36651
36999
  try {
36652
- const paramsText = JSON.stringify({ text: input.text });
36653
- const encryptedParams = transport.secretKey ? encryptSecretBox(paramsText, transport.secretKey) : plainJsonToBase64({ text: input.text });
36654
- const response2 = await transport.emitWithAck("rpc-call", {
36655
- method: `${input.sessionId}:steer`,
36656
- params: encryptedParams
36657
- });
36658
- return response2.ok ? { success: true } : { success: false, error: response2.error || translateCurrent("sessionCreation.interruptFailed") };
37000
+ await sessionControlRpc(input.sessionId, "steer", { text: input.text }, () => resolveSessionRpcTransport(input.auth, input.machine));
37001
+ return { success: true };
36659
37002
  } catch (error) {
36660
37003
  return { success: false, error: error instanceof Error ? error.message : String(error) };
36661
- } finally {
36662
- transport.dispose();
36663
37004
  }
36664
37005
  }
36665
37006
  const HUB_MAX_SPAWN = 32;
@@ -38257,7 +38598,7 @@ function matchMachine(machines2, query) {
38257
38598
  return { type: "not-found" };
38258
38599
  }
38259
38600
  async function runMachines(context) {
38260
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
38601
+ const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
38261
38602
  const machines2 = normalizeMachineRows(await context.rest.listMachines(), keys2);
38262
38603
  if (machines2.length === 0) {
38263
38604
  context.out.raw("No machines registered. Run `happy auth login` on a machine to add one.");
@@ -38268,7 +38609,7 @@ async function runMachines(context) {
38268
38609
  for (const machine of sorted) context.out.raw(formatMachineRow(machine, now));
38269
38610
  }
38270
38611
  async function resolveMachineId(context, query) {
38271
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
38612
+ const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
38272
38613
  const machines2 = normalizeMachineRows(await context.rest.listMachines(), keys2);
38273
38614
  const match = matchMachine(machines2, query);
38274
38615
  if (match.type === "match") return match.machine.id;
@@ -38280,7 +38621,7 @@ ${candidates2}
38280
38621
  Use a longer prefix or the machine id.`);
38281
38622
  }
38282
38623
  async function runLs(context, options) {
38283
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
38624
+ const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
38284
38625
  const machineId = options.machine ? await resolveMachineId(context, options.machine) : void 0;
38285
38626
  const [machineRows, sessionRows] = await Promise.all([
38286
38627
  context.rest.listMachines(),
@@ -38387,7 +38728,7 @@ async function runProjects(context, options) {
38387
38728
  context.out.raw(options.machine ? "No projects on that machine." : "No projects in this workspace.");
38388
38729
  return;
38389
38730
  }
38390
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
38731
+ const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
38391
38732
  const hostById = new Map(
38392
38733
  normalizeMachineRows(await context.rest.listMachines(), keys2).map((machine) => [machine.id, machine.host])
38393
38734
  );
@@ -38621,7 +38962,7 @@ async function runChat(context, options, providedTransport) {
38621
38962
  let liveSettings = newConversationOptions ? createLiveChatSettings(newConversationOptions) : liveChatSettingsFromSessionRows(
38622
38963
  await context.rest.listSessions().catch(() => []),
38623
38964
  sessionId,
38624
- [decodeBase64$1(context.syncCredential.secret)],
38965
+ [decodeBase64$1(requireLegacySecret(context.syncCredential))],
38625
38966
  options.agent
38626
38967
  );
38627
38968
  const sessionConnection = () => createCliSessionConnection({
@@ -38815,7 +39156,7 @@ Leaving. Reattach with \`saycode chat -s ${sessionId}\`.`);
38815
39156
  liveSettings,
38816
39157
  await context.rest.listSessions().catch(() => []),
38817
39158
  sessionId,
38818
- [decodeBase64$1(context.syncCredential.secret)]
39159
+ [decodeBase64$1(requireLegacySecret(context.syncCredential))]
38819
39160
  );
38820
39161
  }
38821
39162
  const liveCommand = localPrompt === null ? { handled: false, settings: liveSettings } : await handleLiveChatCommand(
@@ -39080,7 +39421,7 @@ function paint$1(context, rows, cursor, selectable) {
39080
39421
  context.out.raw(out.join("\n"));
39081
39422
  }
39082
39423
  async function runWatch(context, options) {
39083
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
39424
+ const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
39084
39425
  const selectable = process.stdin.isTTY === true;
39085
39426
  await withTransport(context, async (transport) => {
39086
39427
  let activity = createActivityIndex();
@@ -39267,7 +39608,7 @@ function screenHeight() {
39267
39608
  }
39268
39609
  async function loadLevel(context, state) {
39269
39610
  const level = currentLevel(state);
39270
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
39611
+ const keys2 = [decodeBase64$1(requireLegacySecret(context.syncCredential))];
39271
39612
  if (level === "workspace") {
39272
39613
  const workspaces = await context.listWorkspaces();
39273
39614
  return workspaces.map((workspace) => ({ id: workspace.id, label: workspace.name, detail: workspace.kind }));
@@ -40957,10 +41298,10 @@ function serverLogWriter(layout, name) {
40957
41298
  };
40958
41299
  }
40959
41300
  const happyCliPackageName = "@buzzni/happy-cli";
40960
- const happyCliVersion = "1.1.10-aplus.319";
40961
- const happyCliTag = "happy-cli-v1.1.10-aplus.319";
40962
- const vendorCommit = "4cc930893e9530c5d88ab0280575979b433738a3";
40963
- const saycodeCliVersion = "0.8.2";
41301
+ const happyCliVersion = "1.1.10-aplus.321";
41302
+ const happyCliTag = "happy-cli-v1.1.10-aplus.321";
41303
+ const vendorCommit = "6cd0106d427421c1661c24b18a1351f1e1046f8c";
41304
+ const saycodeCliVersion = "0.9.3";
40964
41305
  const happyServerVersion = "0.1.2";
40965
41306
  const releaseFeatures = { "mcpRuntimeRecovery": { "enabled": true, "releaseState": "released", "sourceBranch": "main", "sourceHead": "a79a10e288cf7775cb15955f716693c6a7df1640", "requiredCommit": "975e32cf" } };
40966
41307
  const pin = {
@@ -41289,7 +41630,7 @@ class ProcessLedger {
41289
41630
  } catch {
41290
41631
  return [];
41291
41632
  }
41292
- const entries = [];
41633
+ const entries2 = [];
41293
41634
  for (const name of names) {
41294
41635
  if (!name.endsWith(".json")) continue;
41295
41636
  let raw;
@@ -41307,9 +41648,9 @@ class ProcessLedger {
41307
41648
  this.log(`ignoring ledger entry ${name} whose recorded id is ${entry.id}`);
41308
41649
  continue;
41309
41650
  }
41310
- entries.push(entry);
41651
+ entries2.push(entry);
41311
41652
  }
41312
- return entries;
41653
+ return entries2;
41313
41654
  }
41314
41655
  /**
41315
41656
  * A record we cannot read is one no sweep will ever act on, so it is moved
@@ -41392,11 +41733,11 @@ class ProcessLedger {
41392
41733
  return this.reconciliation;
41393
41734
  }
41394
41735
  async runReconciliation() {
41395
- const entries = await this.readEntries();
41736
+ const entries2 = await this.readEntries();
41396
41737
  const probe = this.options.probe ?? ((entry) => probeProcess(entry));
41397
41738
  const terminate = this.options.terminate ?? terminateProcess;
41398
41739
  const probes = {};
41399
- await Promise.all(entries.map(async (entry) => {
41740
+ await Promise.all(entries2.map(async (entry) => {
41400
41741
  try {
41401
41742
  probes[entry.id] = await probe(entry);
41402
41743
  } catch (error) {
@@ -41404,7 +41745,7 @@ class ProcessLedger {
41404
41745
  probes[entry.id] = "unknown";
41405
41746
  }
41406
41747
  }));
41407
- const plan = planLedgerReconciliation({ entries, probes, selfPid: process.pid });
41748
+ const plan = planLedgerReconciliation({ entries: entries2, probes, selfPid: process.pid });
41408
41749
  for (const action of plan.dropOnly) {
41409
41750
  this.log(`dropping ${action.entry.kind} pid ${action.entry.pid} (${action.entry.id}): ${action.reason}`);
41410
41751
  await this.release(action.entry.id);
@@ -41481,7 +41822,7 @@ function parseSessionWriteScopeRequest(value2) {
41481
41822
  ...typeof v.error === "string" && /^[A-Z_]{1,80}$/.test(v.error) ? { error: v.error } : {}
41482
41823
  };
41483
41824
  }
41484
- function record$6(value2) {
41825
+ function record$7(value2) {
41485
41826
  return value2 && typeof value2 === "object" ? value2 : {};
41486
41827
  }
41487
41828
  class SessionWriteScopeHost {
@@ -41505,8 +41846,8 @@ class SessionWriteScopeHost {
41505
41846
  };
41506
41847
  }
41507
41848
  async list(machineId, sessionId) {
41508
- const body = record$6(await this.post("/session-write-scope", { action: "list", sessionId }));
41509
- const capability = record$6(record$6(body.capabilities).sessionWriteScope);
41849
+ const body = record$7(await this.post("/session-write-scope", { action: "list", sessionId }));
41850
+ const capability = record$7(record$7(body.capabilities).sessionWriteScope);
41510
41851
  if (body.supported !== true || capability.version !== 1 || capability.incarnation !== this.incarnation || capability.hostKey !== this.keyFingerprint || capability.accountId !== this.accountId || capability.machineId !== machineId) {
41511
41852
  return { supported: false, requests: [] };
41512
41853
  }
@@ -41540,7 +41881,7 @@ class SessionWriteScopeHost {
41540
41881
  decision.sessionId,
41541
41882
  decision.action
41542
41883
  ]));
41543
- const response2 = record$6(await this.post("/session-write-scope/decide", {
41884
+ const response2 = record$7(await this.post("/session-write-scope/decide", {
41544
41885
  decision,
41545
41886
  signature: sign(null, bytes, this.keys.privateKey).toString("base64url")
41546
41887
  }));
@@ -41554,7 +41895,7 @@ class SessionWriteScopeHost {
41554
41895
  async requestRevocation(machineId, sessionId, requestId) {
41555
41896
  const request2 = (await this.list(machineId, sessionId)).requests.find((item) => item.id === requestId);
41556
41897
  if (!request2 || request2.kind !== "grant" || request2.grantActive !== true) throw new Error("GRANT_NOT_ACTIVE");
41557
- const body = record$6(await this.post("/session-write-scope", {
41898
+ const body = record$7(await this.post("/session-write-scope", {
41558
41899
  action: "request",
41559
41900
  sessionId,
41560
41901
  path: request2.root,
@@ -41624,8 +41965,8 @@ function parsePersistedPathOutput(stdout, env) {
41624
41965
  try {
41625
41966
  const parsed2 = JSON.parse(Buffer.from(stdout.trim(), "base64").toString("utf8"));
41626
41967
  const raw = [parsed2.machine, parsed2.user].filter((value2) => typeof value2 === "string").join(";");
41627
- const entries = mergeWindowsPathEntries([], raw.split(";").map((entry) => expandWindowsEnvReferences(entry.trim(), env)));
41628
- return entries.length > 0 ? entries : null;
41968
+ const entries2 = mergeWindowsPathEntries([], raw.split(";").map((entry) => expandWindowsEnvReferences(entry.trim(), env)));
41969
+ return entries2.length > 0 ? entries2 : null;
41629
41970
  } catch {
41630
41971
  return null;
41631
41972
  }
@@ -41648,14 +41989,14 @@ function mergeWindowsPathEntries(current2, extra) {
41648
41989
  }
41649
41990
  function createWindowsPersistedPathReader(input) {
41650
41991
  const now = input.now ?? Date.now;
41651
- let entries = null;
41992
+ let entries2 = null;
41652
41993
  let readAt = -Infinity;
41653
41994
  let failedAt = -Infinity;
41654
41995
  let pending2 = null;
41655
41996
  return {
41656
41997
  /** Single flight; a failed read keeps the last good value so a flaky PowerShell never drops known entries. */
41657
41998
  refresh() {
41658
- if (now() - readAt < FRESH_MS || now() - failedAt < FAILURE_COOLDOWN_MS) return Promise.resolve(entries);
41999
+ if (now() - readAt < FRESH_MS || now() - failedAt < FAILURE_COOLDOWN_MS) return Promise.resolve(entries2);
41659
42000
  pending2 ??= (async () => {
41660
42001
  const env = input.env();
41661
42002
  const powershell = win32.join(env.SystemRoot ?? "C:\\Windows", "System32", "WindowsPowerShell", "v1.0", "powershell.exe");
@@ -41663,20 +42004,20 @@ function createWindowsPersistedPathReader(input) {
41663
42004
  const stdout = await input.run(powershell, ["-NoProfile", "-NonInteractive", "-EncodedCommand", Buffer.from(SCRIPT, "utf16le").toString("base64")], { timeout: TIMEOUT_MS, env });
41664
42005
  const next = parsePersistedPathOutput(stdout, env);
41665
42006
  if (next) {
41666
- entries = next;
42007
+ entries2 = next;
41667
42008
  readAt = now();
41668
42009
  } else failedAt = now();
41669
42010
  } catch {
41670
42011
  failedAt = now();
41671
42012
  }
41672
- return entries;
42013
+ return entries2;
41673
42014
  })().finally(() => {
41674
42015
  pending2 = null;
41675
42016
  });
41676
42017
  return pending2;
41677
42018
  },
41678
42019
  cached() {
41679
- return entries;
42020
+ return entries2;
41680
42021
  }
41681
42022
  };
41682
42023
  }
@@ -41722,9 +42063,9 @@ function commandEnvironment(env, paths = [], platform2 = process.platform, persi
41722
42063
  const keys2 = Object.keys(next).filter((key2) => windows ? key2.toLowerCase() === "path" : key2 === "PATH");
41723
42064
  const key = windows && keys2.includes("Path") ? "Path" : "PATH";
41724
42065
  const delimiter2 = windows ? ";" : ":";
41725
- const entries = keys2.flatMap((name) => (next[name] ?? "").split(delimiter2)).filter(Boolean);
42066
+ const entries2 = keys2.flatMap((name) => (next[name] ?? "").split(delimiter2)).filter(Boolean);
41726
42067
  for (const name of keys2) delete next[name];
41727
- next[key] = [.../* @__PURE__ */ new Set([...windows ? mergeWindowsPathEntries(entries, persisted ?? []) : entries, ...discoveredPaths, ...paths])].join(delimiter2);
42068
+ next[key] = [.../* @__PURE__ */ new Set([...windows ? mergeWindowsPathEntries(entries2, persisted ?? []) : entries2, ...discoveredPaths, ...paths])].join(delimiter2);
41728
42069
  return next;
41729
42070
  }
41730
42071
  function rememberCommandPaths(paths) {
@@ -41792,8 +42133,8 @@ function parseUserShellPath(stdout) {
41792
42133
  const start = stdout.indexOf(START);
41793
42134
  const end = stdout.indexOf(END, start + START.length);
41794
42135
  if (start < 0 || end < 0) return null;
41795
- const entries = stdout.slice(start + START.length, end).split(":").filter((entry) => entry.startsWith("/"));
41796
- return entries.length > 0 ? entries.join(":") : null;
42136
+ const entries2 = stdout.slice(start + START.length, end).split(":").filter((entry) => entry.startsWith("/"));
42137
+ return entries2.length > 0 ? entries2.join(":") : null;
41797
42138
  }
41798
42139
  function resolveUserShell(env, platform2) {
41799
42140
  if (env.SHELL?.startsWith("/")) return env.SHELL;
@@ -41898,8 +42239,8 @@ const GLOBAL_HAPPY_CLI_PACKAGE = "@buzzni/happy-cli";
41898
42239
  function happyCliEnv(userShellPath = cachedUserShellPath()) {
41899
42240
  if (process.platform === "win32") return commandEnvironment(process.env);
41900
42241
  const extraPaths = ["/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"];
41901
- const entries = [userShellPath, process.env.PATH, ...extraPaths].filter(Boolean).join(":").split(":");
41902
- return commandEnvironment({ ...process.env, PATH: [...new Set(entries)].join(":") });
42242
+ const entries2 = [userShellPath, process.env.PATH, ...extraPaths].filter(Boolean).join(":").split(":");
42243
+ return commandEnvironment({ ...process.env, PATH: [...new Set(entries2)].join(":") });
41903
42244
  }
41904
42245
  async function happyCliEnvWithUserShell() {
41905
42246
  await ensureUserShellPath();
@@ -42112,7 +42453,7 @@ function validateCandidateInventory(inventory) {
42112
42453
  return inventory;
42113
42454
  }
42114
42455
  async function verifyCandidateFiles(root, inventory) {
42115
- const entries = validateCandidateInventory(inventory);
42456
+ const entries2 = validateCandidateInventory(inventory);
42116
42457
  const directories = /* @__PURE__ */ new Map();
42117
42458
  const checkDirectory = (directory) => {
42118
42459
  if (!directories.has(directory)) directories.set(directory, fs.promises.lstat(directory).then((stat2) => {
@@ -42120,8 +42461,8 @@ async function verifyCandidateFiles(root, inventory) {
42120
42461
  }));
42121
42462
  return directories.get(directory);
42122
42463
  };
42123
- for (let offset = 0; offset < entries.length; offset += 16) {
42124
- await Promise.all(entries.slice(offset, offset + 16).map(async (entry) => {
42464
+ for (let offset = 0; offset < entries2.length; offset += 16) {
42465
+ await Promise.all(entries2.slice(offset, offset + 16).map(async (entry) => {
42125
42466
  let directory = root;
42126
42467
  await checkDirectory(directory);
42127
42468
  const parts2 = entry.file.split("/");
@@ -42380,7 +42721,7 @@ function waitExit(child, timeout) {
42380
42721
  });
42381
42722
  }
42382
42723
  const identifier$2 = (value2) => typeof value2 === "string" && /^[a-zA-Z0-9._-]{1,128}$/.test(value2);
42383
- const record$5 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
42724
+ const record$6 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
42384
42725
  const pause = (ms2) => new Promise((resolve2) => setTimeout(resolve2, ms2));
42385
42726
  class StandaloneDaemonRecoveryRequiredError extends Error {
42386
42727
  #recover;
@@ -42419,7 +42760,7 @@ async function drainAndStopStandaloneDaemon(child, state, {
42419
42760
  reason = "app-quit",
42420
42761
  isDaemonIncarnationGone = async () => false
42421
42762
  } = {}) {
42422
- if (!record$5(state) || !Number.isInteger(state.pid) || state.pid < 1 || child && state.pid !== child.pid || !Number.isInteger(state.httpPort) || state.httpPort < 1 || state.httpPort > 65535 || typeof state.controlSecret !== "string" || !state.controlSecret || !["app-quit", "update", "mode-switch"].includes(reason)) {
42763
+ if (!record$6(state) || !Number.isInteger(state.pid) || state.pid < 1 || child && state.pid !== child.pid || !Number.isInteger(state.httpPort) || state.httpPort < 1 || state.httpPort > 65535 || typeof state.controlSecret !== "string" || !state.controlSecret || !["app-quit", "update", "mode-switch"].includes(reason)) {
42423
42764
  throw new Error("Invalid standalone daemon control state");
42424
42765
  }
42425
42766
  const { httpPort, controlSecret } = state;
@@ -42462,13 +42803,13 @@ async function drainAndStopStandaloneDaemon(child, state, {
42462
42803
  }
42463
42804
  };
42464
42805
  const capability = await post("capabilities", {});
42465
- if (!record$5(capability) || capability.supported !== true || capability.capability !== "standalone-session-drain-v1" || !identifier$2(capability.instanceId) || !Array.isArray(capability.targets) || capability.targets.length < 1 || capability.targets.length > 8 || capability.targets.some((target) => !record$5(target) || target.platform !== "win32" || !["x64", "arm64"].includes(target.arch) || !["codex", "claude", "opencode", "grok"].includes(target.provider) || target.mode !== "standard")) throw new Error("Standalone drain unsupported or invalid capability");
42806
+ if (!record$6(capability) || capability.supported !== true || capability.capability !== "standalone-session-drain-v1" || !identifier$2(capability.instanceId) || !Array.isArray(capability.targets) || capability.targets.length < 1 || capability.targets.length > 8 || capability.targets.some((target) => !record$6(target) || target.platform !== "win32" || !["x64", "arm64"].includes(target.arch) || !["codex", "claude", "opencode", "grok"].includes(target.provider) || target.mode !== "standard")) throw new Error("Standalone drain unsupported or invalid capability");
42466
42807
  const expectedInstanceId = capability.instanceId;
42467
42808
  const requestId = randomUUID();
42468
42809
  let operationId;
42469
42810
  const observedLaunches = /* @__PURE__ */ new Set();
42470
42811
  const snapshot = (value2) => {
42471
- if (!record$5(value2) || value2.version !== 1 || value2.instanceId !== expectedInstanceId || !identifier$2(value2.operationId) || operationId && value2.operationId !== operationId || !["draining", "blocked", "completed"].includes(value2.state) || !(value2.error === null || typeof value2.error === "string" && value2.error.length <= 1024) || typeof value2.retryable !== "boolean" || !Array.isArray(value2.launches) || value2.launches.length > 256 || value2.launches.some((launch) => !record$5(launch) || !identifier$2(launch.launchId) || !["pending", "exited", "unknown", "failed"].includes(launch.state) || launch.canTerminate !== void 0 && typeof launch.canTerminate !== "boolean" || launch.state === "exited" && launch.canTerminate === true) || new Set(value2.launches.map((launch) => launch.launchId)).size !== value2.launches.length) throw new Error("Invalid standalone drain snapshot");
42812
+ if (!record$6(value2) || value2.version !== 1 || value2.instanceId !== expectedInstanceId || !identifier$2(value2.operationId) || operationId && value2.operationId !== operationId || !["draining", "blocked", "completed"].includes(value2.state) || !(value2.error === null || typeof value2.error === "string" && value2.error.length <= 1024) || typeof value2.retryable !== "boolean" || !Array.isArray(value2.launches) || value2.launches.length > 256 || value2.launches.some((launch) => !record$6(launch) || !identifier$2(launch.launchId) || !["pending", "exited", "unknown", "failed"].includes(launch.state) || launch.canTerminate !== void 0 && typeof launch.canTerminate !== "boolean" || launch.state === "exited" && launch.canTerminate === true) || new Set(value2.launches.map((launch) => launch.launchId)).size !== value2.launches.length) throw new Error("Invalid standalone drain snapshot");
42472
42813
  const launchIds = new Set(value2.launches.map((launch) => launch.launchId));
42473
42814
  if ([...observedLaunches].some((launchId) => !launchIds.has(launchId))) throw new Error("Standalone drain lost a launch");
42474
42815
  for (const launchId of launchIds) observedLaunches.add(launchId);
@@ -42477,7 +42818,7 @@ async function drainAndStopStandaloneDaemon(child, state, {
42477
42818
  const blockedOperationId = value2.operationId;
42478
42819
  throw new StandaloneDrainBlockedError(value2.launches, async (launchId) => {
42479
42820
  const result2 = await post("terminate", { expectedInstanceId, operationId: blockedOperationId, launchId }, performance.now() + 3e4);
42480
- if (!record$5(result2) || result2.accepted !== true) throw new Error("Standalone owner termination unconfirmed");
42821
+ if (!record$6(result2) || result2.accepted !== true) throw new Error("Standalone owner termination unconfirmed");
42481
42822
  });
42482
42823
  }
42483
42824
  if (value2.state === "completed" && (value2.retryable !== false || value2.error !== null || value2.launches.some((launch) => launch.state !== "exited"))) {
@@ -42491,7 +42832,7 @@ async function drainAndStopStandaloneDaemon(child, state, {
42491
42832
  status = snapshot(await post("status", { expectedInstanceId, operationId }));
42492
42833
  }
42493
42834
  const committed = await post("commit", { expectedInstanceId, operationId });
42494
- if (!record$5(committed) || committed.accepted !== true) throw new Error("Standalone drain commit unconfirmed");
42835
+ if (!record$6(committed) || committed.accepted !== true) throw new Error("Standalone drain commit unconfirmed");
42495
42836
  if (child) {
42496
42837
  if (!await waitExit(child, 5e3)) throw new Error("Standalone daemon exit timed out");
42497
42838
  if (child.exitCode !== 0 || child.signalCode !== null) throw new Error("Standalone daemon did not exit cleanly");
@@ -42514,7 +42855,7 @@ async function stopWindowsStandaloneDaemon({ child, homeDir, external: external2
42514
42855
  let state;
42515
42856
  try {
42516
42857
  state = JSON.parse(await readFile(join(homeDir, "daemon.state.json"), "utf8"));
42517
- if (!record$5(state)) throw new Error("Invalid standalone daemon control state");
42858
+ if (!record$6(state)) throw new Error("Invalid standalone daemon control state");
42518
42859
  } catch (error) {
42519
42860
  if (error?.code !== "ENOENT") throw new Error("Unreadable standalone daemon control state");
42520
42861
  state = null;
@@ -54410,11 +54751,11 @@ class StandalonePlanningSpec {
54410
54751
  if (!project.workspaceDir) return { files: [], selected: empty2 };
54411
54752
  const requested = rawFilename ? specFilename(rawFilename) : null;
54412
54753
  const specsDir = join(project.workspaceDir, "specs");
54413
- const entries = await readdir(specsDir, { withFileTypes: true }).catch((error) => {
54754
+ const entries2 = await readdir(specsDir, { withFileTypes: true }).catch((error) => {
54414
54755
  if (error.code === "ENOENT") return [];
54415
54756
  throw error;
54416
54757
  });
54417
- const candidates2 = entries.filter((entry) => entry.isDirectory()).flatMap((entry) => ["prd.md", "spec.md"].map((file) => `specs/${entry.name}/${file}`)).filter((filename) => {
54758
+ const candidates2 = entries2.filter((entry) => entry.isDirectory()).flatMap((entry) => ["prd.md", "spec.md"].map((file) => `specs/${entry.name}/${file}`)).filter((filename) => {
54418
54759
  try {
54419
54760
  specFilename(filename);
54420
54761
  return true;
@@ -55159,6 +55500,33 @@ const managedClaudeEn = {
55159
55500
  "managedClaude.offline": "Connecting to your PC.",
55160
55501
  "managedClaude.loading": "Connecting AI…"
55161
55502
  };
55503
+ const workTemplateEn = {
55504
+ "work.gallery": "Document templates",
55505
+ "work.templates": "Templates",
55506
+ "work.create": "Create my template",
55507
+ "work.closeGallery": "Close templates",
55508
+ "work.edit": "Edit template",
55509
+ "work.delete": "Delete template",
55510
+ "work.deleteConfirm": "Delete the {name} template?",
55511
+ "work.description": "Save an existing document’s formatting to reuse in your next Work.",
55512
+ "work.name": "Template name",
55513
+ "work.file": "Source file",
55514
+ "work.chooseFile": "Choose file",
55515
+ "work.noFileSelected": "No file selected",
55516
+ "work.preserve": "Formatting and instructions to preserve",
55517
+ "work.save": "Save template",
55518
+ "work.saving": "Saving…",
55519
+ "work.instructions": "Instructions",
55520
+ "work.saveChanges": "Save changes",
55521
+ "work.templateInvalid": "The selected template could not be verified.",
55522
+ "work.templateChanged": "The template changed. Select it again.",
55523
+ "work.templatesUnavailable": "Could not load templates.",
55524
+ "work.clearTemplate": "Clear template",
55525
+ "work.templatesLoading": "Loading templates…",
55526
+ "work.templateSaveFailed": "Could not save the template. Check the contents and try again.",
55527
+ "work.templatePrompt": "Create a {format} document using the {name} template skill. Preserve the original design and structure.",
55528
+ "work.taskCue": "My task is"
55529
+ };
55162
55530
  const workEn = {
55163
55531
  "work.mediaChanged": "The original file changed. Reopen it to continue.",
55164
55532
  "work.mediaUnsupported": "Media streaming is unavailable on this computer. Save the original and open it locally.",
@@ -55362,6 +55730,7 @@ const organizationStartEn = {
55362
55730
  "organizationStart.welcome": "Start working with your organization",
55363
55731
  "organizationStart.welcomeBody": "We’ll prepare the tools and connect this computer automatically.",
55364
55732
  "organizationStart.chooseTitle": "Which organization will you work in?",
55733
+ "organizationStart.resumeVerified": "Continue with verified organization",
55365
55734
  "organizationStart.next": "Next",
55366
55735
  "organizationStart.switching": "Connecting organization…",
55367
55736
  "organizationStart.confirmConnectionTitle": "Confirm PC connection",
@@ -55514,11 +55883,30 @@ const organizationStartEn = {
55514
55883
  "companyInvite.declineConfirm": "Confirm decline",
55515
55884
  "companyInvite.declineBody": "Decline the invitation from {name}? Joining later requires a new invitation.",
55516
55885
  "companyInvite.cancel": "Cancel",
55517
- "companyInvite.loading": "Checking invitations…",
55886
+ "companyInvite.loading": "Checking organizations and invitations…",
55518
55887
  "companyInvite.acceptLabel": "Accept invitation from {name}",
55519
55888
  "companyInvite.declineLabel": "Decline invitation from {name}",
55520
55889
  "companyInvite.declineConfirmLabel": "Confirm decline of invitation from {name}",
55521
- "companyInvite.refreshFailed": "Could not refresh invitations. Check again."
55890
+ "companyInvite.refreshFailed": "Could not check organizations and invitations. Try again.",
55891
+ "organizationEnrollment.join": "Join with an invite code",
55892
+ "organizationEnrollment.create": "Create an organization",
55893
+ "organizationEnrollment.code": "Invite code",
55894
+ "organizationEnrollment.name": "Name",
55895
+ "organizationEnrollment.joinSubmit": "Join organization",
55896
+ "organizationEnrollment.createSubmit": "Create",
55897
+ "organizationEnrollment.back": "Back to organization options",
55898
+ "organizationEnrollment.joinHelp": "Enter the invite code from your organization administrator.",
55899
+ "organizationEnrollment.working": "Checking organization…",
55900
+ "organizationEnrollment.joined": "Organization confirmed. Choose Next to continue setup.",
55901
+ "organizationEnrollment.unconfirmed": "The result is not confirmed yet. Recheck your organizations without repeating the request, then select the organization you joined.",
55902
+ "organizationEnrollment.recheck": "Recheck organizations",
55903
+ "organizationEnrollment.invalidCode": "Check the invite code. Ask your administrator for a new code if it expired or was revoked.",
55904
+ "organizationEnrollment.slugTaken": "Could not prepare the organization address. Try again.",
55905
+ "organizationEnrollment.rateLimited": "Too many requests. Please try again shortly.",
55906
+ "organizationEnrollment.signInAgain": "Your session expired. Sign in again to join.",
55907
+ "organizationEnrollment.failed": "We could not process the request. Check your input and try again.",
55908
+ "organizationEnrollment.welcomeBody": "Choose an organization to work with, or create a new one.",
55909
+ "organizationEnrollment.other": "Choose or join another organization"
55522
55910
  };
55523
55911
  const usageEntryEn = {
55524
55912
  ...selfHostConnectEn,
@@ -55662,7 +56050,38 @@ const usageEntryEn = {
55662
56050
  "onboarding.additionalStep": "Additional step",
55663
56051
  "onboarding.requiredSettings": "Required setup",
55664
56052
  "onboarding.additionalSettings": "Additional setup — optional",
55665
- "onboarding.cli.bundledPreparation": "Saycode CLI is bundled with the app. Prepare the required tools, then register this computer and verify its daemon."
56053
+ "onboarding.cli.bundledPreparation": "Saycode CLI is bundled with the app. Prepare the required tools, then register this computer and verify its daemon.",
56054
+ "signup.open": "Create an account",
56055
+ "signup.title": "Create an account",
56056
+ "signup.sendCode": "Send verification code",
56057
+ "signup.sendingCode": "Sending code…",
56058
+ "signup.code": "Enter the verification code sent to your email",
56059
+ "signup.resend": "Resend",
56060
+ "signup.password": "Password",
56061
+ "signup.confirmation": "Confirm password",
56062
+ "signup.minimum": "12+ characters",
56063
+ "signup.mismatch": "Passwords do not match.",
56064
+ "signup.submit": "Sign up and start",
56065
+ "signup.creating": "Creating your account…",
56066
+ "signup.back": "Sign in",
56067
+ "signup.registered": "This email is already registered. Please log in.",
56068
+ "signup.rateLimited": "Too many requests. Try again later.",
56069
+ "signup.requestFailed": "Could not request a code. Check your connection and try again.",
56070
+ "signup.invalidCode": "The code is invalid or expired. Please check it again.",
56071
+ "signup.recoveryUnavailable": "Account recovery is unavailable. Try again later.",
56072
+ "signup.failed": "Could not complete signup. Check your details and try again.",
56073
+ "signup.unconfirmed": "Signup could not be confirmed. Check your account by logging in.",
56074
+ "signup.complete": "Your account is ready. Log in to start.",
56075
+ "signup.continueLogin": "Log in to start",
56076
+ "signup.terms": "By continuing, you agree to the Terms of Service.",
56077
+ "signup.showPassword": "Show passwords",
56078
+ "signup.hidePassword": "Hide passwords",
56079
+ "signup.enterCode": "Enter verification code",
56080
+ "signup.digit": "Verification code digit {number}",
56081
+ "signup.verify": "Verify and continue",
56082
+ "signup.verifying": "Verifying…",
56083
+ "signup.previous": "Back",
56084
+ "signup.verifyFailed": "Could not verify the code. Check your connection and try again."
55666
56085
  };
55667
56086
  const homeTabsEn = {
55668
56087
  "homeTabs.collaboratingTitle": "Collaborating projects",
@@ -55720,8 +56139,8 @@ const homeTabsEn = {
55720
56139
  "homeTabs.startingWork": "Starting work",
55721
56140
  "homeTabs.startWorkFailed": "Could not start work.",
55722
56141
  "homeTabs.syncedBeforeImplementation": "Synced the workspace plan before implementation",
55723
- "homeTabs.quick": "Start a new project",
55724
- "homeTabs.git": "Import repository",
56142
+ "homeTabs.quick": "Create a project",
56143
+ "homeTabs.git": "Import code repository",
55725
56144
  "homeTabs.zip": "Import ZIP/files",
55726
56145
  "homeTabs.folder": "Use a machine folder",
55727
56146
  "homeTabs.categories": "Project categories"
@@ -56024,7 +56443,15 @@ const workAreaEn = {
56024
56443
  "workArea.panelFailed": "Couldn't display this view",
56025
56444
  "workArea.panelFailedHint": "If retrying doesn't help, close this tab and reopen it from the list. Your conversation and work are kept.",
56026
56445
  "workArea.panelRetry": "Try again",
56027
- "workArea.panelClose": "Close tab"
56446
+ "workArea.panelClose": "Close tab",
56447
+ "workArea.nameSearch.hint": "Filtering list · Enter to search all names",
56448
+ "workArea.nameSearch.loading": "Finding project file names…",
56449
+ "workArea.nameSearch.partial": "Partial file results · Enter to search again",
56450
+ "workArea.nameSearch.results": "Project results · Esc to clear",
56451
+ "workArea.nameSearch.empty": "No matching files · Esc to clear",
56452
+ "workArea.revealFile": "Reveal in file list",
56453
+ "workArea.revealFailed": "Could not locate the file. Try Reveal again.",
56454
+ "workArea.fileListFailed": "Could not load the file list."
56028
56455
  };
56029
56456
  const redesignEn = {
56030
56457
  "newSession.aiAuto": "AI Auto",
@@ -57172,7 +57599,7 @@ const personalChatsEn = {
57172
57599
  "personalChats.creating": "Starting the chat…",
57173
57600
  "personalChats.noOnlineMachine": "No machine is online. Start one to begin a chat.",
57174
57601
  "personalChats.localMachinePreparing": "Connecting this computer to Saycode. You can start in a moment.",
57175
- "personalChats.localMachineAsk": "Install Saycode CLI on this computer to start chatting.",
57602
+ "personalChats.localMachineAsk": "Connect this computer as a work PC to start chatting.",
57176
57603
  "personalChats.localMachineFailed": "Automatic connection for this computer failed. You can connect another way.",
57177
57604
  "personalChats.pickMachine": "Choose a machine to run this chat on",
57178
57605
  "personalChats.createFailed": "Could not start the chat.",
@@ -58562,7 +58989,24 @@ const organizationTeamsEn = {
58562
58989
  "organizationTeams.shareApply": "Apply"
58563
58990
  };
58564
58991
  const gitAccountsEn = {
58565
- "settingsTab.gitAccounts": "Git accounts",
58992
+ "projectStart.url": "Import from URL",
58993
+ "projectStart.clearRepository": "Clear repository selection",
58994
+ "projectStart.providerPicker": "Choose a {provider} repository",
58995
+ "projectStart.manageAccess": "Manage repository access",
58996
+ "projectStart.repositoryPlaceholder": "Choose a repository",
58997
+ "projectStart.localAccount": "This computer’s GitHub account · @{account}",
58998
+ "projectStart.localGit": "This computer’s Git configuration",
58999
+ "projectStart.machineGit": "Work computer’s Git configuration",
59000
+ "projectStart.githubAccount": "Connected GitHub account",
59001
+ "projectStart.gitlabAccount": "Connected GitLab account",
59002
+ "projectStart.organizationPat": "Organization PAT · {name}",
59003
+ "projectStart.personalPat": "Personal PAT",
59004
+ "projectStart.selectedConnectionHint": "Uses the selected repository’s connected account.",
59005
+ "projectStart.localAccountHint": "Imports using the GitHub account signed in on this computer.",
59006
+ "projectStart.machineGitHint": "Uses Git authentication configured on the work computer.",
59007
+ "projectStart.accountHint": "Accesses the repository using the selected connection.",
59008
+ "projectStart.connectionUnavailable": "The selected repository connection is unavailable. Check its connection status.",
59009
+ "settingsTab.gitAccounts": "Repositories",
58566
59010
  "gitAccounts.title": "GitHub · GitLab accounts",
58567
59011
  "gitAccounts.description": "Connect the accounts used for repository import, triggers, and PR work. Tokens are kept on the server only and never stored on this device.",
58568
59012
  "gitAccounts.refresh": "Refresh status",
@@ -58578,7 +59022,27 @@ const gitAccountsEn = {
58578
59022
  "gitAccounts.connectFailed": "Could not start connecting. Try again in a moment.",
58579
59023
  "gitAccounts.disconnectFailed": "Could not disconnect. Try again in a moment.",
58580
59024
  "gitAccounts.warnings": "Disconnected, but some items need attention:",
58581
- "gitAccounts.patHint": "Personal access tokens (PAT) are managed in the Security tab."
59025
+ "projectStart.repositorySettings": "Repository settings",
59026
+ "projectStart.allRepositories": "All repositories",
59027
+ "projectStart.selectedRepositories": "Selected repositories",
59028
+ "projectStart.lastConnected": "Last connected",
59029
+ "projectStart.patTitle": "Personal repository access token",
59030
+ "projectStart.patIssue": "Open PAT issuance page",
59031
+ "projectStart.patRegistered": "Registered · Personal",
59032
+ "projectStart.patUnregistered": "Not registered",
59033
+ "projectStart.patUpdatedAt": "Last updated",
59034
+ "projectStart.patConfirmDelete": "Confirm delete",
59035
+ "projectStart.patDelete": "Delete",
59036
+ "projectStart.patUpdate": "New PAT (update)",
59037
+ "projectStart.patNew": "New PAT",
59038
+ "projectStart.patPlaceholder": "Enter a token",
59039
+ "projectStart.patHide": "Hide token",
59040
+ "projectStart.patShow": "Show token",
59041
+ "projectStart.patSaveUpdate": "Update",
59042
+ "projectStart.patSaveNew": "Register",
59043
+ "projectStart.patSaved": "Token saved.",
59044
+ "projectStart.patDeleted": "Token deleted.",
59045
+ "projectStart.loadingConnections": "Loading repository connection information…"
58582
59046
  };
58583
59047
  const agentBrowserConsoleEn = {
58584
59048
  "browserPanel.agentBrowserConsole": "AI Browser task console",
@@ -59149,6 +59613,51 @@ const designGuideEn = {
59149
59613
  "designGuide.entry.body": "See the color, type, component, and chat standards used to build app screens.",
59150
59614
  "designGuide.entry.open": "Open design guide"
59151
59615
  };
59616
+ const prFollowThroughEn = {
59617
+ "prFollow.readyTitle": "PR #{number} is ready to merge",
59618
+ "prFollow.readyBody": "CI, the review bot and conflict checks all passed. Merge it when you're ready.",
59619
+ "prFollow.mergeGuard.stacked": "Stacked PRs on this branch are still open: {list}. Merging now leaves them orphaned and they will need to be moved separately.",
59620
+ "prFollow.mergeGuard.botReviewing": "The review bot is still reviewing this PR or preparing a fix PR.",
59621
+ "prFollow.mergeGuard.unknown": "Couldn't check whether stacked PRs remain.",
59622
+ "prFollow.mergeGuard.confirm": "Merge anyway?",
59623
+ "prFollow.mergeGuard.title": "Check before merging",
59624
+ "prFollow.mergeGuard.mergeAnyway": "Merge anyway",
59625
+ "prFollow.enable": "PR watch",
59626
+ "prFollow.enableTitle": "Watches this PR's CI, review bot and conflicts, and tells the conversation what to fix while it is idle. Notifies you when it is ready to merge.",
59627
+ "prFollow.disable": "Stop PR watch",
59628
+ "prFollow.dismiss": "Clear PR watch",
59629
+ "prFollow.badge.prefix": "Watch · {status}",
59630
+ "prFollow.badge.awaitingPr": "waiting for the PR",
59631
+ "prFollow.badge.watching": "watching",
59632
+ "prFollow.badge.checksPending": "checks running",
59633
+ "prFollow.badge.botReviewing": "review bot working",
59634
+ "prFollow.badge.conflict": "conflict",
59635
+ "prFollow.badge.checksFailed": "CI failed",
59636
+ "prFollow.badge.stackedPr": "stacked PR open",
59637
+ "prFollow.badge.botFindings": "bot findings to apply",
59638
+ "prFollow.badge.ready": "ready to merge",
59639
+ "prFollow.badge.merged": "merged",
59640
+ "prFollow.badge.lookupFailed": "couldn't read the PR",
59641
+ "prFollow.badge.stopped.user": "off",
59642
+ "prFollow.badge.stopped.noPr": "no PR was opened",
59643
+ "prFollow.badge.stopped.closed": "PR closed",
59644
+ "prFollow.badge.stopped.budget": "instruction limit reached",
59645
+ "prFollow.badge.stopped.repeatedFailure": "same failure again — needs you",
59646
+ "prFollow.badge.stopped.sessionRemoved": "conversation gone",
59647
+ "prFollow.settings.saveFailed": "Couldn't save the setting.",
59648
+ "prFollow.settings.followThrough": "Start PR watch after Autopilot's Commit & PR",
59649
+ "prFollow.settings.followThroughHelp": "Checks the PR's CI failures, review bot fix PRs and conflicts every minute, and tells the conversation what to fix while it is idle. Sends a notification when the PR is ready to merge. It never merges into the base branch.",
59650
+ "prFollow.settings.continueAfterMerge": "Continue remaining work after merge",
59651
+ "prFollow.settings.continueAfterMergeHelp": "When the PR is merged, tells the same conversation to continue the spec's remaining tasks on a new branch from the latest base, along with handling any leftover stacked PRs.",
59652
+ "prFollow.settings.autoAnswer": "Let Autopilot answer questions that come with a recommendation",
59653
+ "prFollow.settings.autoAnswerHelp": 'During Autopilot, when the AI stops with a recommendation and asks whether to proceed, it answers "go with the recommendation" (up to twice per run). It also tells the AI not to proceed with permissions, costs, external sends or hard-to-undo changes.',
59654
+ "prFollow.settings.reviewer": "Autopilot independent review AI",
59655
+ "prFollow.settings.reviewerDefault": "Default",
59656
+ "prFollow.settings.reviewerModel": "Review model",
59657
+ "prFollow.settings.reviewerEffort": "Review effort",
59658
+ "prFollow.settings.reviewerHelp": "The AI used when Autopilot runs an independent review. The value at the time you turn Autopilot on is kept for that run.",
59659
+ "prFollow.badge.title": "PR watch · {status}"
59660
+ };
59152
59661
  const catalogExtensionsEn = {
59153
59662
  ...chatSchedulesEn,
59154
59663
  ...mySettingsEn,
@@ -59229,9 +59738,20 @@ const catalogExtensionsEn = {
59229
59738
  ...lessonCandidateEn,
59230
59739
  ...gitAccountsEn,
59231
59740
  ...agentBrowserConsoleEn,
59232
- ...designGuideEn
59741
+ ...designGuideEn,
59742
+ ...prFollowThroughEn
59233
59743
  };
59234
59744
  const onboardingEn = {
59745
+ "folderBrowser.title": "Choose folder",
59746
+ "folderBrowser.parent": "Parent folder",
59747
+ "folderBrowser.folders": "Folders",
59748
+ "folderBrowser.loading": "Loading folders…",
59749
+ "folderBrowser.empty": "No subfolders.",
59750
+ "folderBrowser.newPath": "Start from a new path",
59751
+ "folderBrowser.choose": "Select this folder",
59752
+ "projectImport.drop": "Drop a zip, folder or files here, or click to choose",
59753
+ "projectImport.excludeHint": "Heavy folders such as node_modules, .git and dist are excluded automatically",
59754
+ "projectImport.folder": "Choose folder",
59235
59755
  "onboarding.page.instructions": "Saycode instructions",
59236
59756
  "onboarding.instructions.body": "Saycode’s built-in instructions help the AI organize work and explain results. Choose whether to use them.",
59237
59757
  "onboarding.instructions.enable": "Use instructions",
@@ -59431,7 +59951,8 @@ const onboardingEn = {
59431
59951
  "onboarding.groups.error.capabilityRequired": "This machine's Saycode CLI does not support organization credentials. Update the CLI, then retry.",
59432
59952
  "onboarding.groups.error.setupTokenRuntimeRequired": "This computer is missing the organization setup-token runtime. Install the Saycode cswap runtime, then retry.",
59433
59953
  "onboarding.groups.error.happyDaemonUpdateRequired": "This computer's Saycode daemon does not support organization credentials. Update Desktop, then retry.",
59434
- "onboarding.groups.error.unknown": "The organization credentials could not be connected. Try again shortly."
59954
+ "onboarding.groups.error.unknown": "The organization credentials could not be connected. Try again shortly.",
59955
+ "folderBrowser.path": "Path"
59435
59956
  };
59436
59957
  const standaloneRemoteOnboardingEn = {
59437
59958
  "onboarding.optional": " (optional)",
@@ -59516,6 +60037,7 @@ const en = {
59516
60037
  "assetWorkflow.filesSelect": "Read explicitly selected files",
59517
60038
  "assetWorkflow.draftsPrepare": "Prepare draft text and attachments in the selected conversation",
59518
60039
  ...workEn,
60040
+ ...workTemplateEn,
59519
60041
  ...FILE_ACCESS_TRANSLATIONS_EN,
59520
60042
  ...chatWorkEn,
59521
60043
  ...usageEntryEn,
@@ -61327,7 +61849,7 @@ const en = {
61327
61849
  "saycodePromptPreference.eyebrow": "Choose agent instructions",
61328
61850
  "saycodePromptPreference.title": "Use Saycode’s default instructions?",
61329
61851
  "saycodePromptPreference.body": "Keep them off to use your own Claude/Codex skills and harnesses as-is. You can change this anytime in Settings.",
61330
- "saycodePromptPreference.defaultOnNotice": "Child Agent calls, internal task delegation, and inline result previews remain on by default. To turn any of them off, go to Settings → System Prompt and disable it there.",
61852
+ "saycodePromptPreference.defaultOnNotice": "Child Agent calls, internal task delegation, inline result previews, and the Saycode usage guide remain on by default. To turn any of them off, go to Settings → System Prompt and disable it there.",
61331
61853
  "saycodePromptPreference.keepOff": "Keep them off",
61332
61854
  "saycodePromptPreference.enable": "Enable Saycode instructions",
61333
61855
  "saycodePromptPreference.saving": "Saving…",
@@ -61336,7 +61858,7 @@ const en = {
61336
61858
  "saycodePromptPreference.settingsEnable": "Use default behavioral instructions provided by Saycode",
61337
61859
  "saycodePromptPreference.settingsHelp": "Your system prompt, project instructions, installed skills, and harnesses remain available regardless of this setting.",
61338
61860
  "saycodePromptPreference.blocksTitle": "Individual instructions",
61339
- "saycodePromptPreference.blocksHelp": "Child Agent calls, internal task delegation, and inline result previews are on by default. Other unset items follow the setting above.",
61861
+ "saycodePromptPreference.blocksHelp": "Child Agent calls, internal task delegation, inline result previews, and the Saycode usage guide are on by default. Other unset items follow the setting above.",
61340
61862
  "saycodePromptPreference.alwaysOnNotice": "Chat title generation is always on and cannot be turned off. It is what lets you find a conversation again in the chat list.",
61341
61863
  "saycodePromptPreference.block.agentOrchestration.label": "Child Agent calls",
61342
61864
  "saycodePromptPreference.block.agentOrchestration.description": "Instructs the agent to delegate natural-language requests to an independent child Agent session and collect its result.",
@@ -61348,6 +61870,8 @@ const en = {
61348
61870
  "saycodePromptPreference.block.coAuthoredCredit.description": "Adds the Claude and Happy co-author trailers to commit messages.",
61349
61871
  "saycodePromptPreference.block.inlinePreview.label": "Inline result previews",
61350
61872
  "saycodePromptPreference.block.inlinePreview.description": "Tells the agent to show HTML deliverables as inline previews in its replies.",
61873
+ "saycodePromptPreference.block.saycodeGuide.label": "Saycode usage guide",
61874
+ "saycodePromptPreference.block.saycodeGuide.description": "When you ask how to use Saycode, the matching guide entries and a few app details (version, usage type, display settings) are sent to this conversation's AI. The full guide is placed in the conversation's working folder under .aplus/saycode-guide and kept out of your repository. Deciding whether it is such a question happens on this device.",
61351
61875
  "saycodePromptPreference.loading": "Loading account settings…",
61352
61876
  "standalone.zrokStatus": "zrok status",
61353
61877
  "standalone.zrokNeeded": "zrok2 setup is required",
@@ -62646,6 +63170,33 @@ const managedClaudeZh = {
62646
63170
  "managedClaude.offline": "正在连接工作电脑。",
62647
63171
  "managedClaude.loading": "正在连接AI…"
62648
63172
  };
63173
+ const workTemplateZh = {
63174
+ "work.gallery": "文档模板",
63175
+ "work.templates": "模板",
63176
+ "work.create": "创建我的模板",
63177
+ "work.closeGallery": "关闭模板列表",
63178
+ "work.edit": "编辑模板",
63179
+ "work.delete": "删除模板",
63180
+ "work.deleteConfirm": "删除模板“{name}”?",
63181
+ "work.description": "保存现有文档的格式,以便在下次Work中使用。",
63182
+ "work.name": "模板名称",
63183
+ "work.file": "源文件",
63184
+ "work.chooseFile": "选择文件",
63185
+ "work.noFileSelected": "未选择文件",
63186
+ "work.preserve": "要保留的格式和编写说明",
63187
+ "work.save": "保存模板",
63188
+ "work.saving": "正在保存…",
63189
+ "work.instructions": "编写说明",
63190
+ "work.saveChanges": "保存更改",
63191
+ "work.templateInvalid": "无法验证所选模板。",
63192
+ "work.templateChanged": "模板已更改,请重新选择。",
63193
+ "work.templatesUnavailable": "无法加载模板列表。",
63194
+ "work.clearTemplate": "取消模板选择",
63195
+ "work.templatesLoading": "正在加载模板…",
63196
+ "work.templateSaveFailed": "无法保存模板,请检查内容后重试。",
63197
+ "work.templatePrompt": "使用 {name} 模板技能创建 {format} 文档,保留原有设计与结构。",
63198
+ "work.taskCue": "我的任务是"
63199
+ };
62649
63200
  const workZh = {
62650
63201
  "work.mediaChanged": "原文件已更改。请重新打开。",
62651
63202
  "work.mediaUnsupported": "此计算机不支持媒体流式播放。请保存原文件并在本地打开。",
@@ -62849,6 +63400,7 @@ const organizationStartZh = {
62849
63400
  "organizationStart.welcome": "立即在组织中开始",
62850
63401
  "organizationStart.welcomeBody": "我们会自动准备工具并连接这台电脑。",
62851
63402
  "organizationStart.chooseTitle": "您要在哪个组织工作?",
63403
+ "organizationStart.resumeVerified": "使用已确认的组织继续",
62852
63404
  "organizationStart.next": "下一步",
62853
63405
  "organizationStart.switching": "正在连接组织…",
62854
63406
  "organizationStart.confirmConnectionTitle": "确认电脑连接",
@@ -63001,11 +63553,30 @@ const organizationStartZh = {
63001
63553
  "companyInvite.declineConfirm": "确认拒绝",
63002
63554
  "companyInvite.declineBody": "拒绝{name}的邀请?之后加入需要新的邀请。",
63003
63555
  "companyInvite.cancel": "取消",
63004
- "companyInvite.loading": "正在检查邀请…",
63556
+ "companyInvite.loading": "正在检查组织和邀请…",
63005
63557
  "companyInvite.acceptLabel": "接受{name}的邀请",
63006
63558
  "companyInvite.declineLabel": "拒绝{name}的邀请",
63007
63559
  "companyInvite.declineConfirmLabel": "确认拒绝{name}的邀请",
63008
- "companyInvite.refreshFailed": "无法刷新邀请,请重新检查。"
63560
+ "companyInvite.refreshFailed": "无法检查组织和邀请,请重试。",
63561
+ "organizationEnrollment.join": "使用邀请码加入",
63562
+ "organizationEnrollment.create": "创建组织",
63563
+ "organizationEnrollment.code": "邀请码",
63564
+ "organizationEnrollment.name": "名称",
63565
+ "organizationEnrollment.joinSubmit": "加入组织",
63566
+ "organizationEnrollment.createSubmit": "创建",
63567
+ "organizationEnrollment.back": "返回组织选项",
63568
+ "organizationEnrollment.joinHelp": "请输入组织管理员提供的邀请码。",
63569
+ "organizationEnrollment.working": "正在确认组织…",
63570
+ "organizationEnrollment.joined": "已确认组织。请选择下一步继续准备。",
63571
+ "organizationEnrollment.unconfirmed": "尚未确认处理结果。请重新检查组织列表而不重复提交,然后选择已加入的组织。",
63572
+ "organizationEnrollment.recheck": "重新检查组织列表",
63573
+ "organizationEnrollment.invalidCode": "请检查邀请码。如已过期或撤销,请联系管理员重新获取。",
63574
+ "organizationEnrollment.slugTaken": "无法准备组织地址,请重试。",
63575
+ "organizationEnrollment.rateLimited": "请求过多,请稍后重试。",
63576
+ "organizationEnrollment.signInAgain": "登录已过期,请重新登录后加入。",
63577
+ "organizationEnrollment.failed": "无法处理请求。请检查输入并重试。",
63578
+ "organizationEnrollment.welcomeBody": "请选择要一起工作的组织,或创建新组织。",
63579
+ "organizationEnrollment.other": "选择或加入其他组织"
63009
63580
  };
63010
63581
  const usageEntryZh = {
63011
63582
  ...selfHostConnectZh,
@@ -63149,7 +63720,38 @@ const usageEntryZh = {
63149
63720
  "onboarding.additionalStep": "附加步骤",
63150
63721
  "onboarding.requiredSettings": "必需设置",
63151
63722
  "onboarding.additionalSettings": "附加设置 — 可选",
63152
- "onboarding.cli.bundledPreparation": "Saycode CLI 已内置。准备必需工具后,注册此计算机并验证守护进程。"
63723
+ "onboarding.cli.bundledPreparation": "Saycode CLI 已内置。准备必需工具后,注册此计算机并验证守护进程。",
63724
+ "signup.open": "创建账户",
63725
+ "signup.title": "创建账户",
63726
+ "signup.sendCode": "发送验证码",
63727
+ "signup.sendingCode": "正在发送验证码…",
63728
+ "signup.code": "请输入邮件中收到的验证码",
63729
+ "signup.resend": "重新发送",
63730
+ "signup.password": "密码",
63731
+ "signup.confirmation": "确认密码",
63732
+ "signup.minimum": "至少12个字符",
63733
+ "signup.mismatch": "两次密码不一致。",
63734
+ "signup.submit": "注册并开始",
63735
+ "signup.creating": "正在创建账户…",
63736
+ "signup.back": "登录",
63737
+ "signup.registered": "此邮箱已注册,请登录。",
63738
+ "signup.rateLimited": "请求过多,请稍后重试。",
63739
+ "signup.requestFailed": "无法请求验证码,请检查连接后重试。",
63740
+ "signup.invalidCode": "验证码无效或已过期,请重新确认。",
63741
+ "signup.recoveryUnavailable": "账户恢复暂不可用,请稍后重试。",
63742
+ "signup.failed": "无法完成注册,请检查输入后重试。",
63743
+ "signup.unconfirmed": "无法确认注册结果,请登录确认账户。",
63744
+ "signup.complete": "注册已完成,请登录开始。",
63745
+ "signup.continueLogin": "登录并开始",
63746
+ "signup.terms": "继续即表示您同意服务条款。",
63747
+ "signup.showPassword": "显示密码",
63748
+ "signup.hidePassword": "隐藏密码",
63749
+ "signup.enterCode": "输入验证码",
63750
+ "signup.digit": "验证码第 {number} 位",
63751
+ "signup.verify": "验证并继续",
63752
+ "signup.verifying": "正在验证…",
63753
+ "signup.previous": "上一步",
63754
+ "signup.verifyFailed": "无法验证验证码。请检查网络连接后重试。"
63153
63755
  };
63154
63756
  const homeTabsZh = {
63155
63757
  "homeTabs.collaboratingTitle": "协作项目",
@@ -63207,7 +63809,7 @@ const homeTabsZh = {
63207
63809
  "homeTabs.startingWork": "正在开始工作",
63208
63810
  "homeTabs.startWorkFailed": "无法开始工作。",
63209
63811
  "homeTabs.syncedBeforeImplementation": "在开始实施前同步工作区计划",
63210
- "homeTabs.quick": "开始新项目",
63812
+ "homeTabs.quick": "创建项目",
63211
63813
  "homeTabs.git": "导入代码仓库",
63212
63814
  "homeTabs.zip": "导入 ZIP/文件",
63213
63815
  "homeTabs.folder": "使用机器文件夹",
@@ -63511,7 +64113,15 @@ const workAreaZh = {
63511
64113
  "workArea.panelFailed": "无法显示此界面",
63512
64114
  "workArea.panelFailedHint": "如果重试后仍然如此,请关闭此标签页并从列表中重新打开。对话和工作内容都会保留。",
63513
64115
  "workArea.panelRetry": "重试",
63514
- "workArea.panelClose": "关闭标签页"
64116
+ "workArea.panelClose": "关闭标签页",
64117
+ "workArea.nameSearch.hint": "筛选列表 · 按 Enter 搜索所有文件名",
64118
+ "workArea.nameSearch.loading": "正在查找项目文件名…",
64119
+ "workArea.nameSearch.partial": "仅确认部分文件 · 按 Enter 重新搜索",
64120
+ "workArea.nameSearch.results": "项目搜索结果 · 按 Esc 清除",
64121
+ "workArea.nameSearch.empty": "没有匹配的文件 · 按 Esc 清除",
64122
+ "workArea.revealFile": "在文件列表中显示位置",
64123
+ "workArea.revealFailed": "无法确认文件位置。请再次点击显示位置。",
64124
+ "workArea.fileListFailed": "无法加载文件列表。"
63515
64125
  };
63516
64126
  const redesignZh = {
63517
64127
  "newSession.aiAuto": "AI 自动",
@@ -64659,7 +65269,7 @@ const personalChatsZh = {
64659
65269
  "personalChats.creating": "正在开始对话…",
64660
65270
  "personalChats.noOnlineMachine": "没有可用的在线机器。请启动一台机器来开始对话。",
64661
65271
  "personalChats.localMachinePreparing": "正在将这台电脑连接到 Saycode,稍后即可开始。",
64662
- "personalChats.localMachineAsk": "在这台电脑上安装 Saycode CLI 后即可开始聊天。",
65272
+ "personalChats.localMachineAsk": "请将这台电脑连接为工作电脑以开始聊天。",
64663
65273
  "personalChats.localMachineFailed": "这台电脑自动连接失败。可以用其他方式连接。",
64664
65274
  "personalChats.pickMachine": "选择用于此对话的机器",
64665
65275
  "personalChats.createFailed": "无法开始对话。",
@@ -66049,7 +66659,24 @@ const organizationTeamsZh = {
66049
66659
  "organizationTeams.shareApply": "应用"
66050
66660
  };
66051
66661
  const gitAccountsZh = {
66052
- "settingsTab.gitAccounts": "Git 账号",
66662
+ "projectStart.url": "从地址导入",
66663
+ "projectStart.clearRepository": "清除仓库选择",
66664
+ "projectStart.providerPicker": "选择 {provider} 仓库",
66665
+ "projectStart.manageAccess": "管理仓库访问权限",
66666
+ "projectStart.repositoryPlaceholder": "请选择仓库",
66667
+ "projectStart.localAccount": "此电脑的 GitHub 账号 · @{account}",
66668
+ "projectStart.localGit": "此电脑的 Git 配置",
66669
+ "projectStart.machineGit": "工作电脑的 Git 配置",
66670
+ "projectStart.githubAccount": "已连接的 GitHub 账号",
66671
+ "projectStart.gitlabAccount": "已连接的 GitLab 账号",
66672
+ "projectStart.organizationPat": "组织 PAT · {name}",
66673
+ "projectStart.personalPat": "个人 PAT",
66674
+ "projectStart.selectedConnectionHint": "使用所选仓库的连接账号。",
66675
+ "projectStart.localAccountHint": "使用此电脑登录的 GitHub 账号导入。",
66676
+ "projectStart.machineGitHint": "使用工作电脑配置的 Git 认证。",
66677
+ "projectStart.accountHint": "使用所选连接账号访问仓库。",
66678
+ "projectStart.connectionUnavailable": "所选仓库连接不可用,请检查连接状态。",
66679
+ "settingsTab.gitAccounts": "代码仓库",
66053
66680
  "gitAccounts.title": "GitHub · GitLab 账号",
66054
66681
  "gitAccounts.description": "连接用于导入仓库、触发器和 PR 工作的账号。令牌仅保存在服务器上,不会存储在本设备。",
66055
66682
  "gitAccounts.refresh": "刷新状态",
@@ -66065,7 +66692,27 @@ const gitAccountsZh = {
66065
66692
  "gitAccounts.connectFailed": "无法开始连接,请稍后重试。",
66066
66693
  "gitAccounts.disconnectFailed": "无法断开连接,请稍后重试。",
66067
66694
  "gitAccounts.warnings": "已断开,但有需要确认的项目:",
66068
- "gitAccounts.patHint": "个人访问令牌 (PAT) 在安全标签页中管理。"
66695
+ "projectStart.repositorySettings": "仓库设置",
66696
+ "projectStart.allRepositories": "所有仓库",
66697
+ "projectStart.selectedRepositories": "选定仓库",
66698
+ "projectStart.lastConnected": "上次连接",
66699
+ "projectStart.patTitle": "个人代码仓库 Personal Access Token",
66700
+ "projectStart.patIssue": "打开PAT签发页面",
66701
+ "projectStart.patRegistered": "已注册 · 个人",
66702
+ "projectStart.patUnregistered": "未注册",
66703
+ "projectStart.patUpdatedAt": "上次更新",
66704
+ "projectStart.patConfirmDelete": "确认删除",
66705
+ "projectStart.patDelete": "删除",
66706
+ "projectStart.patUpdate": "新PAT(更新)",
66707
+ "projectStart.patNew": "新PAT",
66708
+ "projectStart.patPlaceholder": "输入令牌",
66709
+ "projectStart.patHide": "隐藏令牌",
66710
+ "projectStart.patShow": "显示令牌",
66711
+ "projectStart.patSaveUpdate": "更新",
66712
+ "projectStart.patSaveNew": "注册",
66713
+ "projectStart.patSaved": "令牌已保存。",
66714
+ "projectStart.patDeleted": "令牌已删除。",
66715
+ "projectStart.loadingConnections": "正在加载仓库连接信息…"
66069
66716
  };
66070
66717
  const agentBrowserConsoleZh = {
66071
66718
  "browserPanel.agentBrowserConsole": "AI 浏览器任务控制台",
@@ -66636,6 +67283,51 @@ const designGuideZh = {
66636
67283
  "designGuide.entry.body": "查看构建应用界面时使用的颜色、文字、组件和聊天界面标准。",
66637
67284
  "designGuide.entry.open": "打开设计指南"
66638
67285
  };
67286
+ const prFollowThroughZh = {
67287
+ "prFollow.readyTitle": "PR #{number} 可以合并",
67288
+ "prFollow.readyBody": "CI、审查机器人和冲突检查均已通过。请自行合并。",
67289
+ "prFollow.mergeGuard.stacked": "此 PR 分支上仍有未合入的堆叠 PR:{list}。现在合并会使它们成为孤立 PR,需要另行迁移。",
67290
+ "prFollow.mergeGuard.botReviewing": "审查机器人仍在审查此 PR 或准备修复 PR。",
67291
+ "prFollow.mergeGuard.unknown": "无法确认是否还有堆叠 PR。",
67292
+ "prFollow.mergeGuard.confirm": "仍要合并吗?",
67293
+ "prFollow.mergeGuard.title": "合并前请确认",
67294
+ "prFollow.mergeGuard.mergeAnyway": "仍然合并",
67295
+ "prFollow.enable": "PR 守护",
67296
+ "prFollow.enableTitle": "监视此 PR 的 CI、审查机器人和冲突,在对话空闲时代为下达修复指示。可以合并时通知你。",
67297
+ "prFollow.disable": "停止 PR 守护",
67298
+ "prFollow.dismiss": "清除 PR 守护记录",
67299
+ "prFollow.badge.prefix": "守护 · {status}",
67300
+ "prFollow.badge.awaitingPr": "等待 PR",
67301
+ "prFollow.badge.watching": "监视中",
67302
+ "prFollow.badge.checksPending": "等待检查",
67303
+ "prFollow.badge.botReviewing": "等待审查机器人",
67304
+ "prFollow.badge.conflict": "冲突",
67305
+ "prFollow.badge.checksFailed": "CI 失败",
67306
+ "prFollow.badge.stackedPr": "存在堆叠 PR",
67307
+ "prFollow.badge.botFindings": "需处理机器人意见",
67308
+ "prFollow.badge.ready": "可以合并",
67309
+ "prFollow.badge.merged": "已合并",
67310
+ "prFollow.badge.lookupFailed": "无法读取 PR 状态",
67311
+ "prFollow.badge.stopped.user": "已关闭",
67312
+ "prFollow.badge.stopped.noPr": "未创建 PR",
67313
+ "prFollow.badge.stopped.closed": "PR 已关闭",
67314
+ "prFollow.badge.stopped.budget": "已达自动指示上限",
67315
+ "prFollow.badge.stopped.repeatedFailure": "同样的失败反复出现 — 需要你处理",
67316
+ "prFollow.badge.stopped.sessionRemoved": "对话已不存在",
67317
+ "prFollow.settings.saveFailed": "无法保存设置。",
67318
+ "prFollow.settings.followThrough": "自动驾驶 Commit & PR 后启动 PR 守护",
67319
+ "prFollow.settings.followThroughHelp": "每分钟检查 PR 的 CI 失败、审查机器人修复 PR 和冲突,在对话空闲时代为下达修复指示。可以合并时发送通知。不会合并到 base 分支。",
67320
+ "prFollow.settings.continueAfterMerge": "合并后继续剩余工作",
67321
+ "prFollow.settings.continueAfterMergeHelp": "PR 合并后,指示同一对话基于最新 base 在新分支上继续 spec 的剩余任务,如有遗留的堆叠 PR 也一并处理。",
67322
+ "prFollow.settings.autoAnswer": "让自动驾驶代为回答附带推荐方案的问题",
67323
+ "prFollow.settings.autoAnswerHelp": "自动驾驶期间,AI 给出推荐方案并停下询问是否继续时,会代为回答“按推荐方案进行”(每次最多 2 次),同时要求不要执行权限、费用、外部发送或难以撤销的更改。",
67324
+ "prFollow.settings.reviewer": "自动驾驶独立审查 AI",
67325
+ "prFollow.settings.reviewerDefault": "默认",
67326
+ "prFollow.settings.reviewerModel": "审查模型",
67327
+ "prFollow.settings.reviewerEffort": "审查 Effort",
67328
+ "prFollow.settings.reviewerHelp": "以独立审查模式启用自动驾驶时使用的 AI。启用时的设置会固定用于该次运行。",
67329
+ "prFollow.badge.title": "PR 守护 · {status}"
67330
+ };
66639
67331
  const catalogExtensionsZh = {
66640
67332
  ...chatSchedulesZh,
66641
67333
  ...mySettingsZh,
@@ -66716,9 +67408,20 @@ const catalogExtensionsZh = {
66716
67408
  ...lessonCandidateZh,
66717
67409
  ...gitAccountsZh,
66718
67410
  ...agentBrowserConsoleZh,
66719
- ...designGuideZh
67411
+ ...designGuideZh,
67412
+ ...prFollowThroughZh
66720
67413
  };
66721
67414
  const onboardingZh = {
67415
+ "folderBrowser.title": "选择文件夹",
67416
+ "folderBrowser.parent": "上级文件夹",
67417
+ "folderBrowser.folders": "文件夹列表",
67418
+ "folderBrowser.loading": "正在读取文件夹…",
67419
+ "folderBrowser.empty": "没有子文件夹。",
67420
+ "folderBrowser.newPath": "从新路径开始",
67421
+ "folderBrowser.choose": "选择此文件夹",
67422
+ "projectImport.drop": "拖入 zip、文件夹或文件,或点击选择",
67423
+ "projectImport.excludeHint": "自动排除 node_modules / .git / dist 等大型文件夹",
67424
+ "projectImport.folder": "选择文件夹",
66722
67425
  "onboarding.page.instructions": "Saycode 指令",
66723
67426
  "onboarding.instructions.body": "Saycode 的默认指令帮助 AI 整理工作并说明结果。请选择是否使用。",
66724
67427
  "onboarding.instructions.enable": "使用指令",
@@ -66918,7 +67621,8 @@ const onboardingZh = {
66918
67621
  "onboarding.groups.error.capabilityRequired": "这台机器的 Saycode CLI 不支持组织认证信息。请更新 CLI 后重试。",
66919
67622
  "onboarding.groups.error.setupTokenRuntimeRequired": "这台电脑缺少组织 setup-token runtime。请安装 Saycode cswap runtime 后重试。",
66920
67623
  "onboarding.groups.error.happyDaemonUpdateRequired": "这台电脑上的 Saycode 守护进程不支持组织认证信息。请更新 Desktop 后重试。",
66921
- "onboarding.groups.error.unknown": "无法连接组织认证信息,请稍后重试。"
67624
+ "onboarding.groups.error.unknown": "无法连接组织认证信息,请稍后重试。",
67625
+ "folderBrowser.path": "路径"
66922
67626
  };
66923
67627
  const standaloneRemoteOnboardingZh = {
66924
67628
  "onboarding.optional": "(可选)",
@@ -67003,6 +67707,7 @@ const zh = {
67003
67707
  "assetWorkflow.filesSelect": "读取明确选择的文件",
67004
67708
  "assetWorkflow.draftsPrepare": "准备所选会话的草稿与附件",
67005
67709
  ...workZh,
67710
+ ...workTemplateZh,
67006
67711
  ...FILE_ACCESS_TRANSLATIONS_ZH,
67007
67712
  ...chatWorkZh,
67008
67713
  ...usageEntryZh,
@@ -68781,7 +69486,7 @@ const zh = {
68781
69486
  "saycodePromptPreference.eyebrow": "选择智能体指令",
68782
69487
  "saycodePromptPreference.title": "要使用 Saycode 默认指令吗?",
68783
69488
  "saycodePromptPreference.body": "如果要原样使用自己的 Claude/Codex 技能和工作流,请保持关闭。之后可随时在设置中更改。",
68784
- "saycodePromptPreference.defaultOnNotice": "子 Agent 调用、内部任务委派和结果内联预览默认保持开启。如需关闭任一功能,请前往设置 → 系统提示并单独禁用。",
69489
+ "saycodePromptPreference.defaultOnNotice": "子 Agent 调用、内部任务委派、结果内联预览和 Saycode 使用指南默认保持开启。如需关闭任一功能,请前往设置 → 系统提示并单独禁用。",
68785
69490
  "saycodePromptPreference.keepOff": "保持关闭",
68786
69491
  "saycodePromptPreference.enable": "开启 Saycode 指令",
68787
69492
  "saycodePromptPreference.saving": "正在保存…",
@@ -68790,7 +69495,7 @@ const zh = {
68790
69495
  "saycodePromptPreference.settingsEnable": "使用 Saycode 提供的默认行为指令",
68791
69496
  "saycodePromptPreference.settingsHelp": "无论此设置如何,您的系统提示、项目指令、已安装技能和工作流都会保留。",
68792
69497
  "saycodePromptPreference.blocksTitle": "单项指令",
68793
- "saycodePromptPreference.blocksHelp": "子 Agent 调用、内部任务委派和结果内联预览默认开启。其他未设置项目将遵循上方的总开关。",
69498
+ "saycodePromptPreference.blocksHelp": "子 Agent 调用、内部任务委派、结果内联预览和 Saycode 使用指南默认开启。其他未设置项目将遵循上方的总开关。",
68794
69499
  "saycodePromptPreference.alwaysOnNotice": "聊天标题生成始终开启且无法关闭。它让您能在聊天列表中重新找到对话。",
68795
69500
  "saycodePromptPreference.block.agentOrchestration.label": "子 Agent 调用",
68796
69501
  "saycodePromptPreference.block.agentOrchestration.description": "指示代理将自然语言请求委派给独立的子 Agent 会话并收集结果。",
@@ -68802,6 +69507,8 @@ const zh = {
68802
69507
  "saycodePromptPreference.block.coAuthoredCredit.description": "在提交信息中添加 Claude 与 Happy 的共同作者署名。",
68803
69508
  "saycodePromptPreference.block.inlinePreview.label": "结果内联预览",
68804
69509
  "saycodePromptPreference.block.inlinePreview.description": "指示智能体在回复中以内联预览展示 HTML 成果。",
69510
+ "saycodePromptPreference.block.saycodeGuide.label": "Saycode 使用指南",
69511
+ "saycodePromptPreference.block.saycodeGuide.description": "当你询问如何使用 Saycode 时,会把相关指南和部分应用状态(版本、使用方式、显示设置)发送给本次对话的 AI。完整指南放在对话工作文件夹的 .aplus/saycode-guide 中,不会进入你的仓库。是否属于此类问题只在本设备上判断。",
68805
69512
  "saycodePromptPreference.loading": "正在加载账户设置…",
68806
69513
  "standalone.zrokStatus": "zrok 状态",
68807
69514
  "standalone.zrokNeeded": "需要设置 zrok2",
@@ -70133,6 +70840,33 @@ const managedClaudeJa = {
70133
70840
  "managedClaude.offline": "作業PCに接続中です。",
70134
70841
  "managedClaude.loading": "AIに接続中…"
70135
70842
  };
70843
+ const workTemplateJa = {
70844
+ "work.gallery": "文書テンプレート",
70845
+ "work.templates": "テンプレート",
70846
+ "work.create": "マイテンプレートを作成",
70847
+ "work.closeGallery": "テンプレート一覧を閉じる",
70848
+ "work.edit": "テンプレートを編集",
70849
+ "work.delete": "テンプレートを削除",
70850
+ "work.deleteConfirm": "テンプレート「{name}」を削除しますか?",
70851
+ "work.description": "既存の文書の書式を保存して、次のWorkで再利用できます。",
70852
+ "work.name": "テンプレート名",
70853
+ "work.file": "元のファイル",
70854
+ "work.chooseFile": "ファイルを選択",
70855
+ "work.noFileSelected": "ファイル未選択",
70856
+ "work.preserve": "保持する書式と作成指示",
70857
+ "work.save": "テンプレートを保存",
70858
+ "work.saving": "保存中…",
70859
+ "work.instructions": "作成指示",
70860
+ "work.saveChanges": "変更を保存",
70861
+ "work.templateInvalid": "選択したテンプレートを確認できません。",
70862
+ "work.templateChanged": "テンプレートが変更されました。再度選択してください。",
70863
+ "work.templatesUnavailable": "テンプレートを読み込めませんでした。",
70864
+ "work.clearTemplate": "テンプレートの選択を解除",
70865
+ "work.templatesLoading": "テンプレートを読み込み中…",
70866
+ "work.templateSaveFailed": "保存できませんでした。内容を確認して再試行してください。",
70867
+ "work.templatePrompt": "{name} テンプレートスキルで {format} 文書を作成してください。元のデザインと構造を維持してください。",
70868
+ "work.taskCue": "作業内容は"
70869
+ };
70136
70870
  const workJa = {
70137
70871
  "work.mediaChanged": "元のファイルが変更されました。もう一度開いてください。",
70138
70872
  "work.mediaUnsupported": "このコンピューターではメディアストリーミングを利用できません。元のファイルを保存して開いてください。",
@@ -70336,6 +71070,7 @@ const organizationStartJa = {
70336
71070
  "organizationStart.welcome": "組織ですぐに始めましょう",
70337
71071
  "organizationStart.welcomeBody": "必要なツールとこのコンピュータの接続を自動で準備します。",
70338
71072
  "organizationStart.chooseTitle": "どの組織で作業しますか?",
71073
+ "organizationStart.resumeVerified": "確認済みの組織で続ける",
70339
71074
  "organizationStart.next": "次へ",
70340
71075
  "organizationStart.switching": "組織に接続中…",
70341
71076
  "organizationStart.confirmConnectionTitle": "PC接続の確認",
@@ -70488,11 +71223,30 @@ const organizationStartJa = {
70488
71223
  "companyInvite.declineConfirm": "辞退を確認",
70489
71224
  "companyInvite.declineBody": "{name}からの招待を辞退しますか?後から参加するには新しい招待が必要です。",
70490
71225
  "companyInvite.cancel": "キャンセル",
70491
- "companyInvite.loading": "招待を確認中…",
71226
+ "companyInvite.loading": "組織と招待を確認中…",
70492
71227
  "companyInvite.acceptLabel": "{name}の招待を承認",
70493
71228
  "companyInvite.declineLabel": "{name}の招待を辞退",
70494
71229
  "companyInvite.declineConfirmLabel": "{name}の招待の辞退を確認",
70495
- "companyInvite.refreshFailed": "招待一覧を確認できませんでした。再確認してください。"
71230
+ "companyInvite.refreshFailed": "組織と招待を確認できませんでした。もう一度お試しください。",
71231
+ "organizationEnrollment.join": "招待コードで参加",
71232
+ "organizationEnrollment.create": "組織を作成",
71233
+ "organizationEnrollment.code": "招待コード",
71234
+ "organizationEnrollment.name": "名前",
71235
+ "organizationEnrollment.joinSubmit": "組織に参加",
71236
+ "organizationEnrollment.createSubmit": "作成",
71237
+ "organizationEnrollment.back": "組織の選択肢に戻る",
71238
+ "organizationEnrollment.joinHelp": "組織の管理者から受け取った招待コードを入力してください。",
71239
+ "organizationEnrollment.working": "組織情報を確認中…",
71240
+ "organizationEnrollment.joined": "組織を確認しました。「次へ」で準備を続けてください。",
71241
+ "organizationEnrollment.unconfirmed": "処理結果をまだ確認できません。リクエストを繰り返さず組織一覧を再確認し、参加した組織を選択してください。",
71242
+ "organizationEnrollment.recheck": "組織一覧を再確認",
71243
+ "organizationEnrollment.invalidCode": "招待コードを確認してください。期限切れや取り消しの場合は管理者に再発行を依頼してください。",
71244
+ "organizationEnrollment.slugTaken": "組織アドレスを準備できませんでした。もう一度お試しください。",
71245
+ "organizationEnrollment.rateLimited": "リクエストが多すぎます。しばらくしてから再試行してください。",
71246
+ "organizationEnrollment.signInAgain": "ログインの有効期限が切れました。再ログインして参加してください。",
71247
+ "organizationEnrollment.failed": "処理できませんでした。入力内容を確認して再試行してください。",
71248
+ "organizationEnrollment.welcomeBody": "一緒に作業する組織を選ぶか、新しく作成してください。",
71249
+ "organizationEnrollment.other": "別の組織を選択・参加"
70496
71250
  };
70497
71251
  const usageEntryJa = {
70498
71252
  ...selfHostConnectJa,
@@ -70636,7 +71390,38 @@ const usageEntryJa = {
70636
71390
  "onboarding.additionalStep": "追加ステップ",
70637
71391
  "onboarding.requiredSettings": "必須設定",
70638
71392
  "onboarding.additionalSettings": "追加設定 — 任意",
70639
- "onboarding.cli.bundledPreparation": "Saycode CLI は同梱されています。必須ツールを準備してから、このコンピューターを登録してデーモンを確認します。"
71393
+ "onboarding.cli.bundledPreparation": "Saycode CLI は同梱されています。必須ツールを準備してから、このコンピューターを登録してデーモンを確認します。",
71394
+ "signup.open": "アカウントを作成",
71395
+ "signup.title": "アカウントを作成",
71396
+ "signup.sendCode": "認証コードを送信",
71397
+ "signup.sendingCode": "コードを送信中…",
71398
+ "signup.code": "メールで届いた認証コードを入力してください",
71399
+ "signup.resend": "再送信",
71400
+ "signup.password": "パスワード",
71401
+ "signup.confirmation": "パスワードを確認",
71402
+ "signup.minimum": "12文字以上",
71403
+ "signup.mismatch": "パスワードが一致しません。",
71404
+ "signup.submit": "登録して開始",
71405
+ "signup.creating": "アカウントを作成中…",
71406
+ "signup.back": "ログイン",
71407
+ "signup.registered": "登録済みのメールです。ログインしてください。",
71408
+ "signup.rateLimited": "リクエストが多すぎます。しばらくしてから再試行してください。",
71409
+ "signup.requestFailed": "コードを送信できませんでした。接続を確認して再試行してください。",
71410
+ "signup.invalidCode": "コードが正しくないか有効期限が切れています。確認してください。",
71411
+ "signup.recoveryUnavailable": "アカウント復旧を利用できません。しばらくしてから再試行してください。",
71412
+ "signup.failed": "登録できませんでした。入力内容を確認して再試行してください。",
71413
+ "signup.unconfirmed": "登録結果を確認できません。ログインして確認してください。",
71414
+ "signup.complete": "登録が完了しました。ログインして開始してください。",
71415
+ "signup.continueLogin": "ログインして開始",
71416
+ "signup.terms": "続行すると、利用規約に同意したものとみなされます。",
71417
+ "signup.showPassword": "パスワードを表示",
71418
+ "signup.hidePassword": "パスワードを隠す",
71419
+ "signup.enterCode": "認証コードを入力",
71420
+ "signup.digit": "認証コードの{number}桁目",
71421
+ "signup.verify": "認証して次へ",
71422
+ "signup.verifying": "認証を確認中…",
71423
+ "signup.previous": "戻る",
71424
+ "signup.verifyFailed": "認証を確認できませんでした。接続を確認して再試行してください。"
70640
71425
  };
70641
71426
  const homeTabsJa = {
70642
71427
  "homeTabs.collaboratingTitle": "共同作業プロジェクト",
@@ -70694,7 +71479,7 @@ const homeTabsJa = {
70694
71479
  "homeTabs.startingWork": "作業を開始中",
70695
71480
  "homeTabs.startWorkFailed": "作業を開始できませんでした。",
70696
71481
  "homeTabs.syncedBeforeImplementation": "実装開始前にワークスペース計画を同期",
70697
- "homeTabs.quick": "新しいプロジェクトを開始",
71482
+ "homeTabs.quick": "プロジェクトを作成",
70698
71483
  "homeTabs.git": "リポジトリをインポート",
70699
71484
  "homeTabs.zip": "ZIP/ファイルをインポート",
70700
71485
  "homeTabs.folder": "マシンのフォルダーを使用",
@@ -70998,7 +71783,15 @@ const workAreaJa = {
70998
71783
  "workArea.panelFailed": "この画面を表示できませんでした",
70999
71784
  "workArea.panelFailedHint": "再試行しても変わらない場合は、このタブを閉じて一覧から開き直してください。会話と作業内容はそのまま残っています。",
71000
71785
  "workArea.panelRetry": "再試行",
71001
- "workArea.panelClose": "タブを閉じる"
71786
+ "workArea.panelClose": "タブを閉じる",
71787
+ "workArea.nameSearch.hint": "一覧を絞り込み · Enterで全体の名前を検索",
71788
+ "workArea.nameSearch.loading": "プロジェクトのファイル名を検索中…",
71789
+ "workArea.nameSearch.partial": "一部のファイルのみ確認 · Enterで再検索",
71790
+ "workArea.nameSearch.results": "プロジェクトの検索結果 · Escで解除",
71791
+ "workArea.nameSearch.empty": "一致するファイルなし · Escで解除",
71792
+ "workArea.revealFile": "ファイル一覧で場所を表示",
71793
+ "workArea.revealFailed": "ファイルの場所を確認できませんでした。もう一度場所を表示してください。",
71794
+ "workArea.fileListFailed": "一覧を読み込めませんでした。"
71002
71795
  };
71003
71796
  const redesignJa = {
71004
71797
  "newSession.aiAuto": "AI 自動",
@@ -72146,7 +72939,7 @@ const personalChatsJa = {
72146
72939
  "personalChats.creating": "チャットを開始しています…",
72147
72940
  "personalChats.noOnlineMachine": "オンラインのマシンがありません。チャットを開始するにはマシンを起動してください。",
72148
72941
  "personalChats.localMachinePreparing": "このコンピューターをSaycodeに接続しています。まもなく開始できます。",
72149
- "personalChats.localMachineAsk": "このコンピューターにSaycode CLIをインストールすると、すぐにチャットを開始できます。",
72942
+ "personalChats.localMachineAsk": "チャットを開始するには、このコンピューターを作業PCとして接続してください。",
72150
72943
  "personalChats.localMachineFailed": "このコンピューターの自動接続に失敗しました。別の方法で接続できます。",
72151
72944
  "personalChats.pickMachine": "このチャットを実行するマシンを選択してください",
72152
72945
  "personalChats.createFailed": "チャットを開始できませんでした。",
@@ -73536,7 +74329,24 @@ const organizationTeamsJa = {
73536
74329
  "organizationTeams.shareApply": "適用"
73537
74330
  };
73538
74331
  const gitAccountsJa = {
73539
- "settingsTab.gitAccounts": "Git アカウント",
74332
+ "projectStart.url": "URLからインポート",
74333
+ "projectStart.clearRepository": "リポジトリ選択を解除",
74334
+ "projectStart.providerPicker": "{provider} リポジトリを選択",
74335
+ "projectStart.manageAccess": "リポジトリアクセス管理",
74336
+ "projectStart.repositoryPlaceholder": "リポジトリを選択してください",
74337
+ "projectStart.localAccount": "このコンピューターのGitHubアカウント · @{account}",
74338
+ "projectStart.localGit": "このコンピューターのGit設定",
74339
+ "projectStart.machineGit": "作業PCのGit設定",
74340
+ "projectStart.githubAccount": "連携済みGitHubアカウント",
74341
+ "projectStart.gitlabAccount": "連携済みGitLabアカウント",
74342
+ "projectStart.organizationPat": "組織PAT · {name}",
74343
+ "projectStart.personalPat": "個人PAT",
74344
+ "projectStart.selectedConnectionHint": "選択したリポジトリの連携アカウントを使用します。",
74345
+ "projectStart.localAccountHint": "このコンピューターにログインしたGitHubアカウントでインポートします。",
74346
+ "projectStart.machineGitHint": "作業PCに設定されたGit認証を使用します。",
74347
+ "projectStart.accountHint": "選択した連携アカウントでリポジトリにアクセスします。",
74348
+ "projectStart.connectionUnavailable": "選択したリポジトリ接続を使用できません。接続状態を確認してください。",
74349
+ "settingsTab.gitAccounts": "リポジトリ",
73540
74350
  "gitAccounts.title": "GitHub · GitLab アカウント",
73541
74351
  "gitAccounts.description": "リポジトリのインポート・トリガー・PR 作業に使うアカウントを連携します。トークンはサーバーにのみ保管され、この端末には保存されません。",
73542
74352
  "gitAccounts.refresh": "状態を更新",
@@ -73552,7 +74362,27 @@ const gitAccountsJa = {
73552
74362
  "gitAccounts.connectFailed": "連携を開始できませんでした。しばらくしてから再試行してください。",
73553
74363
  "gitAccounts.disconnectFailed": "連携を解除できませんでした。しばらくしてから再試行してください。",
73554
74364
  "gitAccounts.warnings": "解除しましたが、確認が必要な項目があります:",
73555
- "gitAccounts.patHint": "個人アクセストークン (PAT) はセキュリティタブで管理します。"
74365
+ "projectStart.repositorySettings": "リポジトリ設定",
74366
+ "projectStart.allRepositories": "すべてのリポジトリ",
74367
+ "projectStart.selectedRepositories": "選択したリポジトリ",
74368
+ "projectStart.lastConnected": "最終接続",
74369
+ "projectStart.patTitle": "個人コードリポジトリ Personal Access Token",
74370
+ "projectStart.patIssue": "PAT発行ページを開く",
74371
+ "projectStart.patRegistered": "登録済み · 個人",
74372
+ "projectStart.patUnregistered": "未登録",
74373
+ "projectStart.patUpdatedAt": "最終更新",
74374
+ "projectStart.patConfirmDelete": "削除を確認",
74375
+ "projectStart.patDelete": "削除",
74376
+ "projectStart.patUpdate": "新しいPAT(更新)",
74377
+ "projectStart.patNew": "新しいPAT",
74378
+ "projectStart.patPlaceholder": "トークンを入力",
74379
+ "projectStart.patHide": "トークンを隠す",
74380
+ "projectStart.patShow": "トークンを表示",
74381
+ "projectStart.patSaveUpdate": "更新",
74382
+ "projectStart.patSaveNew": "登録",
74383
+ "projectStart.patSaved": "トークンを保存しました。",
74384
+ "projectStart.patDeleted": "トークンを削除しました。",
74385
+ "projectStart.loadingConnections": "リポジトリの接続情報を読み込み中…"
73556
74386
  };
73557
74387
  const agentBrowserConsoleJa = {
73558
74388
  "browserPanel.agentBrowserConsole": "AIブラウザのタスクコンソール",
@@ -74123,6 +74953,51 @@ const designGuideJa = {
74123
74953
  "designGuide.entry.body": "アプリの画面を作るときに使う色、文字、部品、チャット画面の基準を確認します。",
74124
74954
  "designGuide.entry.open": "デザインガイドを開く"
74125
74955
  };
74956
+ const prFollowThroughJa = {
74957
+ "prFollow.readyTitle": "PR #{number} はマージ可能です",
74958
+ "prFollow.readyBody": "CI・レビューボット・競合の確認をすべて通過しました。マージはご自身で行ってください。",
74959
+ "prFollow.mergeGuard.stacked": "このPRブランチにまだ反映されていないスタックPRがあります: {list}。今マージするとそれらのPRは孤立し、別途移す必要があります。",
74960
+ "prFollow.mergeGuard.botReviewing": "レビューボットがまだこのPRを確認中、または修正PRを作成中です。",
74961
+ "prFollow.mergeGuard.unknown": "残っているスタックPRがあるか確認できませんでした。",
74962
+ "prFollow.mergeGuard.confirm": "それでもマージしますか?",
74963
+ "prFollow.mergeGuard.title": "マージ前に確認してください",
74964
+ "prFollow.mergeGuard.mergeAnyway": "それでもマージ",
74965
+ "prFollow.enable": "PRウォッチ",
74966
+ "prFollow.enableTitle": "このPRのCI・レビューボット・競合を見守り、会話が待機中のときに修正すべきことを代わりに指示します。マージ可能になったらお知らせします。",
74967
+ "prFollow.disable": "PRウォッチを停止",
74968
+ "prFollow.dismiss": "PRウォッチの記録を消去",
74969
+ "prFollow.badge.prefix": "ウォッチ · {status}",
74970
+ "prFollow.badge.awaitingPr": "PR待ち",
74971
+ "prFollow.badge.watching": "見守り中",
74972
+ "prFollow.badge.checksPending": "チェック待ち",
74973
+ "prFollow.badge.botReviewing": "レビューボット待ち",
74974
+ "prFollow.badge.conflict": "競合",
74975
+ "prFollow.badge.checksFailed": "CI失敗",
74976
+ "prFollow.badge.stackedPr": "スタックPRあり",
74977
+ "prFollow.badge.botFindings": "ボット指摘の反映が必要",
74978
+ "prFollow.badge.ready": "マージ可能",
74979
+ "prFollow.badge.merged": "マージ済み",
74980
+ "prFollow.badge.lookupFailed": "PR状態を確認できません",
74981
+ "prFollow.badge.stopped.user": "オフ",
74982
+ "prFollow.badge.stopped.noPr": "PRが作成されませんでした",
74983
+ "prFollow.badge.stopped.closed": "PRがクローズされました",
74984
+ "prFollow.badge.stopped.budget": "自動指示の上限に到達",
74985
+ "prFollow.badge.stopped.repeatedFailure": "同じ失敗の繰り返し — 確認が必要",
74986
+ "prFollow.badge.stopped.sessionRemoved": "会話がありません",
74987
+ "prFollow.settings.saveFailed": "設定を保存できませんでした。",
74988
+ "prFollow.settings.followThrough": "オートパイロットのCommit & PR後にPRウォッチを開始",
74989
+ "prFollow.settings.followThroughHelp": "PRのCI失敗・レビューボットの修正PR・競合を1分ごとに確認し、会話が待機中のときに修正すべきことを代わりに指示します。マージ可能になると通知します。baseブランチへのマージは行いません。",
74990
+ "prFollow.settings.continueAfterMerge": "マージ後に残りの作業を続ける",
74991
+ "prFollow.settings.continueAfterMergeHelp": "PRがマージされると、同じ会話にspecの残りの作業を最新baseから新しいブランチで続けるよう指示します。残ったスタックPRがあればその処理も指示します。",
74992
+ "prFollow.settings.autoAnswer": "推奨案付きの質問にオートパイロットが代わりに答える",
74993
+ "prFollow.settings.autoAnswerHelp": "オートパイロット中にAIが推奨案を示して「進めますか?」で止まると「推奨案で進めて」と答えます(1回の実行で最大2回)。権限・費用・外部送信・元に戻しにくい変更は進めないよう併せて指示します。",
74994
+ "prFollow.settings.reviewer": "オートパイロットの独立レビューAI",
74995
+ "prFollow.settings.reviewerDefault": "デフォルト",
74996
+ "prFollow.settings.reviewerModel": "レビューモデル",
74997
+ "prFollow.settings.reviewerEffort": "レビューEffort",
74998
+ "prFollow.settings.reviewerHelp": "オートパイロットを独立レビューで有効にするときに使うAIです。有効にした時点の値がその実行に固定されます。",
74999
+ "prFollow.badge.title": "PRウォッチ · {status}"
75000
+ };
74126
75001
  const catalogExtensionsJa = {
74127
75002
  ...chatSchedulesJa,
74128
75003
  ...mySettingsJa,
@@ -74203,9 +75078,20 @@ const catalogExtensionsJa = {
74203
75078
  ...lessonCandidateJa,
74204
75079
  ...gitAccountsJa,
74205
75080
  ...agentBrowserConsoleJa,
74206
- ...designGuideJa
75081
+ ...designGuideJa,
75082
+ ...prFollowThroughJa
74207
75083
  };
74208
75084
  const onboardingJa = {
75085
+ "folderBrowser.title": "フォルダーを選択",
75086
+ "folderBrowser.parent": "親フォルダー",
75087
+ "folderBrowser.folders": "フォルダー一覧",
75088
+ "folderBrowser.loading": "フォルダーを確認中…",
75089
+ "folderBrowser.empty": "サブフォルダーがありません。",
75090
+ "folderBrowser.newPath": "新しいパスから開始",
75091
+ "folderBrowser.choose": "このフォルダーを選択",
75092
+ "projectImport.drop": "zip・フォルダー・ファイルをドロップするかクリックして選択",
75093
+ "projectImport.excludeHint": "node_modules / .git / dist などの大きなフォルダーは自動的に除外されます",
75094
+ "projectImport.folder": "フォルダーを選択",
74209
75095
  "onboarding.page.instructions": "Saycodeの指示",
74210
75096
  "onboarding.instructions.body": "Saycodeの標準指示は、AIが作業を整理して結果を説明するのを助けます。使用するか選んでください。",
74211
75097
  "onboarding.instructions.enable": "指示を使用する",
@@ -74405,7 +75291,8 @@ const onboardingJa = {
74405
75291
  "onboarding.groups.error.capabilityRequired": "このマシンのSaycode CLIは組織の認証情報に対応していません。CLIを更新してからもう一度お試しください。",
74406
75292
  "onboarding.groups.error.setupTokenRuntimeRequired": "このコンピューターに組織用setup-token runtimeがありません。Saycodeのcswap runtimeをインストールしてからもう一度お試しください。",
74407
75293
  "onboarding.groups.error.happyDaemonUpdateRequired": "このコンピューターのSaycodeデーモンは組織の認証情報に対応していません。Desktopを更新してからもう一度お試しください。",
74408
- "onboarding.groups.error.unknown": "組織の認証情報を接続できませんでした。しばらくしてからもう一度お試しください。"
75294
+ "onboarding.groups.error.unknown": "組織の認証情報を接続できませんでした。しばらくしてからもう一度お試しください。",
75295
+ "folderBrowser.path": "パス"
74409
75296
  };
74410
75297
  const standaloneRemoteOnboardingJa = {
74411
75298
  "onboarding.optional": "(任意)",
@@ -74490,6 +75377,7 @@ const ja = {
74490
75377
  "assetWorkflow.filesSelect": "選択したファイルを読む",
74491
75378
  "assetWorkflow.draftsPrepare": "選択した会話の下書きと添付を準備",
74492
75379
  ...workJa,
75380
+ ...workTemplateJa,
74493
75381
  ...FILE_ACCESS_TRANSLATIONS_JA,
74494
75382
  ...chatWorkJa,
74495
75383
  ...usageEntryJa,
@@ -76268,7 +77156,7 @@ const ja = {
76268
77156
  "saycodePromptPreference.eyebrow": "エージェント指示を選択",
76269
77157
  "saycodePromptPreference.title": "Saycode の既定指示を使用しますか?",
76270
77158
  "saycodePromptPreference.body": "独自の Claude/Codex スキルやハーネスをそのまま使う場合はオフのままにしてください。後で設定から変更できます。",
76271
- "saycodePromptPreference.defaultOnNotice": "子 Agent の呼び出し、内部作業の委任、成果物のインラインプレビューは既定でオンのままです。オフにするには、設定 → システムプロンプトで個別に無効にしてください。",
77159
+ "saycodePromptPreference.defaultOnNotice": "子 Agent の呼び出し、内部作業の委任、成果物のインラインプレビュー、Saycode 使い方ガイドは既定でオンのままです。オフにするには、設定 → システムプロンプトで個別に無効にしてください。",
76272
77160
  "saycodePromptPreference.keepOff": "オフのままにする",
76273
77161
  "saycodePromptPreference.enable": "Saycode 指示をオンにする",
76274
77162
  "saycodePromptPreference.saving": "保存中…",
@@ -76277,7 +77165,7 @@ const ja = {
76277
77165
  "saycodePromptPreference.settingsEnable": "Saycode が提供する既定の行動指示を使用",
76278
77166
  "saycodePromptPreference.settingsHelp": "ユーザーのシステムプロンプト、プロジェクト指示、インストール済みスキルとハーネスはこの設定に関係なく維持されます。",
76279
77167
  "saycodePromptPreference.blocksTitle": "個別の指示",
76280
- "saycodePromptPreference.blocksHelp": "子 Agent の呼び出し、内部作業の委任、成果物のインラインプレビューは既定でオンです。その他の未設定項目は上の設定に従います。",
77168
+ "saycodePromptPreference.blocksHelp": "子 Agent の呼び出し、内部作業の委任、成果物のインラインプレビュー、Saycode 使い方ガイドは既定でオンです。その他の未設定項目は上の設定に従います。",
76281
77169
  "saycodePromptPreference.alwaysOnNotice": "チャットタイトルの生成は常に有効で、オフにできません。チャット一覧で会話を再び見つけるために必要です。",
76282
77170
  "saycodePromptPreference.block.agentOrchestration.label": "子 Agent の呼び出し",
76283
77171
  "saycodePromptPreference.block.agentOrchestration.description": "自然言語で依頼された作業を独立した子 Agent セッションに委任し、結果を回収するよう指示します。",
@@ -76289,6 +77177,8 @@ const ja = {
76289
77177
  "saycodePromptPreference.block.coAuthoredCredit.description": "コミットメッセージに Claude と Happy の共同作成者表示を追加します。",
76290
77178
  "saycodePromptPreference.block.inlinePreview.label": "成果物のインラインプレビュー",
76291
77179
  "saycodePromptPreference.block.inlinePreview.description": "HTML の成果物を回答内にプレビュー表示するよう指示します。",
77180
+ "saycodePromptPreference.block.saycodeGuide.label": "Saycode 使い方ガイド",
77181
+ "saycodePromptPreference.block.saycodeGuide.description": "Saycode の使い方を尋ねると、関連するガイドとアプリの状況の一部(バージョン・利用形態・表示設定)をこの会話の AI に送ります。ガイド全体は会話の作業フォルダーの .aplus/saycode-guide に置き、リポジトリには含めません。質問かどうかの判定はこの端末内で行います。",
76292
77182
  "saycodePromptPreference.loading": "アカウント設定を読み込み中…",
76293
77183
  "standalone.zrokStatus": "zrok の状態",
76294
77184
  "standalone.zrokNeeded": "zrok2 の設定が必要です",
@@ -80472,8 +81362,8 @@ function requireParser() {
80472
81362
  };
80473
81363
  Parser.prototype._parsePLTE = function(data) {
80474
81364
  this._crc.write(data);
80475
- let entries = Math.floor(data.length / 3);
80476
- for (let i = 0; i < entries; i++) {
81365
+ let entries2 = Math.floor(data.length / 3);
81366
+ for (let i = 0; i < entries2; i++) {
80477
81367
  this._palette.push([data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 255]);
80478
81368
  }
80479
81369
  this.palette(this._palette);
@@ -83159,7 +84049,7 @@ async function runMachinesApprove(code, out) {
83159
84049
  const outcome = await approvePairing({
83160
84050
  serverUrl: credential.serverUrl,
83161
84051
  ownerToken: credential.token,
83162
- secret: decodeBase64$1(credential.secret),
84052
+ secret: decodeBase64$1(requireLegacySecret(credential)),
83163
84053
  terminalPublicKey
83164
84054
  }).catch((error) => {
83165
84055
  throw new CliError(error instanceof Error ? error.message : String(error));
@@ -83345,8 +84235,8 @@ function mergeExtensionRevocations(known, incoming) {
83345
84235
  }
83346
84236
  return [...merged.values()].sort((left, right) => revocationKey(left).localeCompare(revocationKey(right)));
83347
84237
  }
83348
- function isExtensionRevoked(entries, id2, version2) {
83349
- return entries.some((entry) => entry.id === id2 && entry.version === version2);
84238
+ function isExtensionRevoked(entries2, id2, version2) {
84239
+ return entries2.some((entry) => entry.id === id2 && entry.version === version2);
83350
84240
  }
83351
84241
  const DEFAULT_MAX_ARCHIVE_BYTES = 32 * 1024 * 1024;
83352
84242
  const DEFAULT_MAX_CATALOG_BYTES = 1024 * 1024;
@@ -83985,7 +84875,7 @@ const ID_PATTERN$3 = /^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
83985
84875
  function fail$7(path2, detail) {
83986
84876
  throw new Error(`${path2}: ${detail}`);
83987
84877
  }
83988
- function record$4(value2, path2) {
84878
+ function record$5(value2, path2) {
83989
84879
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$7(path2, "expected object");
83990
84880
  return value2;
83991
84881
  }
@@ -84011,7 +84901,7 @@ function assertOnlyKeys(value2, allowed, path2) {
84011
84901
  function contributionList(value2, path2, allowedKeys, parse2) {
84012
84902
  return array$1(value2, path2).map((raw, index) => {
84013
84903
  const entryPath = `${path2}[${index}]`;
84014
- const entry = record$4(raw, entryPath);
84904
+ const entry = record$5(raw, entryPath);
84015
84905
  assertOnlyKeys(entry, new Set(allowedKeys), entryPath);
84016
84906
  return parse2(entry, entryPath);
84017
84907
  });
@@ -84030,7 +84920,7 @@ function assertExtensionFileViewers(extensionId, viewers, panels, permissions) {
84030
84920
  }
84031
84921
  function parseMachineCommands(value2, extensionId, apiVersion) {
84032
84922
  if (apiVersion < 3) fail$7("manifest.machineCommands", "requires Extension API version 3");
84033
- const root = record$4(value2, "manifest.machineCommands");
84923
+ const root = record$5(value2, "manifest.machineCommands");
84034
84924
  assertOnlyKeys(root, /* @__PURE__ */ new Set(["version", "profiles"]), "manifest.machineCommands");
84035
84925
  if (root.version !== 1) fail$7("manifest.machineCommands.version", "unsupported version");
84036
84926
  const profiles = contributionList(
@@ -84051,9 +84941,9 @@ function parseMachineCommands(value2, extensionId, apiVersion) {
84051
84941
  });
84052
84942
  if (!argv.length || argv.length > 32) fail$7(`${path2}.argv`, "expected 1-32 arguments");
84053
84943
  const parameters = {};
84054
- for (const [name, raw] of Object.entries(record$4(entry.parameters, `${path2}.parameters`))) {
84944
+ for (const [name, raw] of Object.entries(record$5(entry.parameters, `${path2}.parameters`))) {
84055
84945
  if (!/^[a-z][a-zA-Z0-9_]{0,63}$/.test(name)) fail$7(`${path2}.parameters`, "invalid parameter name");
84056
- const parameter = record$4(raw, `${path2}.parameters.${name}`);
84946
+ const parameter = record$5(raw, `${path2}.parameters.${name}`);
84057
84947
  if (parameter.type === "string") {
84058
84948
  assertOnlyKeys(parameter, /* @__PURE__ */ new Set(["type", "maxLength", "values"]), `${path2}.parameters.${name}`);
84059
84949
  if (!Number.isInteger(parameter.maxLength) || Number(parameter.maxLength) < 1 || Number(parameter.maxLength) > 4096) fail$7(`${path2}.parameters.${name}.maxLength`, "invalid maxLength");
@@ -84249,7 +85139,7 @@ const MAX_EXTENSIONS = 16;
84249
85139
  function fail$5(path2, detail) {
84250
85140
  throw new Error(`${path2}: ${detail}`);
84251
85141
  }
84252
- function record$3(value2, path2) {
85142
+ function record$4(value2, path2) {
84253
85143
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$5(path2, "expected object");
84254
85144
  return value2;
84255
85145
  }
@@ -84276,12 +85166,12 @@ function uniqueList(values, path2) {
84276
85166
  return values;
84277
85167
  }
84278
85168
  function parseLocalizations(value2, path2) {
84279
- const localizations = record$3(value2, path2);
85169
+ const localizations = record$4(value2, path2);
84280
85170
  if (Object.keys(localizations).length > MAX_LOCALES) fail$5(path2, "too many locales");
84281
85171
  return Object.fromEntries(Object.entries(localizations).map(([locale, raw]) => {
84282
85172
  if (!LOCALE_PATTERN$1.test(locale)) fail$5(path2, `invalid locale ${locale}`);
84283
85173
  const localizationPath = `${path2}.${locale}`;
84284
- const localization = record$3(raw, localizationPath);
85174
+ const localization = record$4(raw, localizationPath);
84285
85175
  exactKeys$2(localization, ["title"], [], localizationPath);
84286
85176
  return [locale, { title: title(localization.title, `${localizationPath}.title`) }];
84287
85177
  }));
@@ -84312,14 +85202,14 @@ function parseExtensionArtifactActions(value2, path2 = "manifest.contributes.art
84312
85202
  if (!Array.isArray(value2) || value2.length > MAX_ACTIONS) fail$5(path2, "expected at most 32 actions");
84313
85203
  return value2.map((raw, index) => {
84314
85204
  const actionPath = `${path2}[${index}]`;
84315
- const action = record$3(raw, actionPath);
85205
+ const action = record$4(raw, actionPath);
84316
85206
  exactKeys$2(action, ["id", "title", "operation", "when"], ["localizations"], actionPath);
84317
85207
  const parsedId = text$2(action.id, `${actionPath}.id`);
84318
85208
  if (!ID_PATTERN$2.test(parsedId) || parsedId.includes("..")) fail$5(`${actionPath}.id`, "invalid stable identifier");
84319
85209
  if (!EXTENSION_ARTIFACT_ACTION_OPERATIONS.includes(action.operation)) {
84320
85210
  fail$5(`${actionPath}.operation`, "unsupported operation");
84321
85211
  }
84322
- const when = record$3(action.when, `${actionPath}.when`);
85212
+ const when = record$4(action.when, `${actionPath}.when`);
84323
85213
  exactKeys$2(when, ["sourceTypes", "extensions"], [], `${actionPath}.when`);
84324
85214
  return {
84325
85215
  id: parsedId,
@@ -84405,12 +85295,12 @@ function assertDepth(value2, path2, depth) {
84405
85295
  function scalarParams(value2, path2) {
84406
85296
  const source2 = plainRecord$1(value2, path2);
84407
85297
  assertDepth(source2, path2, 1);
84408
- const entries = Object.entries(source2);
84409
- if (entries.length > MAX_CHANNEL_PARAM_FIELDS) {
84410
- fail$4(`${path2} has ${entries.length} fields, over the ${MAX_CHANNEL_PARAM_FIELDS} field limit`);
85298
+ const entries2 = Object.entries(source2);
85299
+ if (entries2.length > MAX_CHANNEL_PARAM_FIELDS) {
85300
+ fail$4(`${path2} has ${entries2.length} fields, over the ${MAX_CHANNEL_PARAM_FIELDS} field limit`);
84411
85301
  }
84412
85302
  const result2 = {};
84413
- for (const [key, item] of entries) {
85303
+ for (const [key, item] of entries2) {
84414
85304
  text$1(key, `${path2} field name`, 64);
84415
85305
  if (typeof item === "string") result2[key] = text$1(item, `${path2}.${key}`);
84416
85306
  else if (typeof item === "number") result2[key] = integer(item, `${path2}.${key}`, Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER);
@@ -84836,7 +85726,7 @@ function parseChannelApiVersion(value2, apiVersion) {
84836
85726
  return value2;
84837
85727
  }
84838
85728
  function parseContributions(value2, apiVersion, channelApiVersion) {
84839
- const parsed2 = record$4(value2, "manifest.contributes");
85729
+ const parsed2 = record$5(value2, "manifest.contributes");
84840
85730
  assertOnlyKeys(parsed2, CONTRIBUTION_KEYS, "manifest.contributes");
84841
85731
  if (parsed2.channels !== void 0) {
84842
85732
  if (apiVersion < 3) fail$7("manifest.contributes.channels", "requires Extension API version 3");
@@ -84908,12 +85798,12 @@ function parseContributions(value2, apiVersion, channelApiVersion) {
84908
85798
  (entry, path2) => {
84909
85799
  let localizations;
84910
85800
  if (entry.localizations !== void 0) {
84911
- const rawLocalizations = record$4(entry.localizations, `${path2}.localizations`);
85801
+ const rawLocalizations = record$5(entry.localizations, `${path2}.localizations`);
84912
85802
  localizations = {};
84913
85803
  for (const [locale, rawLocalization] of Object.entries(rawLocalizations)) {
84914
85804
  if (!LOCALE_PATTERN.test(locale)) fail$7(`${path2}.localizations`, `invalid locale ${locale}`);
84915
85805
  const localizationPath = `${path2}.localizations.${locale}`;
84916
- const localization = record$4(rawLocalization, localizationPath);
85806
+ const localization = record$5(rawLocalization, localizationPath);
84917
85807
  assertOnlyKeys(
84918
85808
  localization,
84919
85809
  /* @__PURE__ */ new Set(["title", "description", "firstPrompt"]),
@@ -84945,7 +85835,7 @@ function parseContributions(value2, apiVersion, channelApiVersion) {
84945
85835
  "manifest.contributes.machineActions",
84946
85836
  ["id", "title", "command", "when"],
84947
85837
  (entry, path2) => {
84948
- const when = record$4(entry.when, `${path2}.when`);
85838
+ const when = record$5(entry.when, `${path2}.when`);
84949
85839
  assertOnlyKeys(when, /* @__PURE__ */ new Set(["online"]), `${path2}.when`);
84950
85840
  if (when.online !== true) fail$7(`${path2}.when.online`, "expected true");
84951
85841
  return {
@@ -84978,14 +85868,14 @@ function parseContributions(value2, apiVersion, channelApiVersion) {
84978
85868
  return result2;
84979
85869
  }
84980
85870
  function parseExtensionManifest(value2, options) {
84981
- const manifest2 = record$4(value2, "manifest");
85871
+ const manifest2 = record$5(value2, "manifest");
84982
85872
  assertOnlyKeys(manifest2, TOP_LEVEL_KEYS, "manifest");
84983
85873
  const parsedId = id$1(manifest2.id, "manifest.id");
84984
85874
  const version2 = string(manifest2.version, "manifest.version");
84985
85875
  if (!SEMVER_PATTERN$1.test(version2)) fail$7("manifest.version", "expected semantic version");
84986
85876
  const apiVersion = parseApiVersion(manifest2.apiVersion, options);
84987
85877
  const channelApiVersion = parseChannelApiVersion(manifest2.channelApiVersion, apiVersion);
84988
- const engines = record$4(manifest2.engines, "manifest.engines");
85878
+ const engines = record$5(manifest2.engines, "manifest.engines");
84989
85879
  assertOnlyKeys(engines, /* @__PURE__ */ new Set(["saycode"]), "manifest.engines");
84990
85880
  const saycodeEngine = string(engines.saycode, "manifest.engines.saycode");
84991
85881
  if (!ENGINE_RANGE_PATTERN.test(saycodeEngine)) {
@@ -85798,7 +86688,7 @@ function syncSocketTokens(auth) {
85798
86688
  }
85799
86689
  function createChannelAdapterCandidateSource(deps) {
85800
86690
  const manifests = /* @__PURE__ */ new Map();
85801
- const entries = () => {
86691
+ const entries2 = () => {
85802
86692
  try {
85803
86693
  return deps.entries();
85804
86694
  } catch {
@@ -85806,7 +86696,7 @@ function createChannelAdapterCandidateSource(deps) {
85806
86696
  }
85807
86697
  };
85808
86698
  return {
85809
- candidates: () => Object.freeze(entries().flatMap(({ id: id2, state }) => {
86699
+ candidates: () => Object.freeze(entries2().flatMap(({ id: id2, state }) => {
85810
86700
  const manifest2 = manifests.get(`${id2}@${state.installedVersion}`);
85811
86701
  if (!manifest2) return [];
85812
86702
  return [{
@@ -85817,7 +86707,7 @@ function createChannelAdapterCandidateSource(deps) {
85817
86707
  }];
85818
86708
  })),
85819
86709
  refresh: async () => {
85820
- for (const { id: id2, state } of entries()) {
86710
+ for (const { id: id2, state } of entries2()) {
85821
86711
  const key = `${id2}@${state.installedVersion}`;
85822
86712
  if (manifests.has(key)) continue;
85823
86713
  try {
@@ -89955,6 +90845,37 @@ const SLACK_FILE_UPLOAD_OPERATIONS = Object.freeze({
89955
90845
  allowedParams: Object.freeze(["files", "channel_id", "thread_ts"])
89956
90846
  })
89957
90847
  });
90848
+ const SLACK_REACTION_NAMES = Object.freeze({
90849
+ "👀": "eyes",
90850
+ "✍": "writing_hand",
90851
+ "🤔": "thinking_face",
90852
+ "👍": "+1",
90853
+ "💔": "broken_heart",
90854
+ "🙈": "see_no_evil"
90855
+ });
90856
+ function slackReactionName(glyph) {
90857
+ return Object.prototype.hasOwnProperty.call(SLACK_REACTION_NAMES, glyph) ? SLACK_REACTION_NAMES[glyph] : null;
90858
+ }
90859
+ const reactionOperation = (pathTemplate) => Object.freeze({
90860
+ method: "POST",
90861
+ direction: "outbound",
90862
+ pathTemplate,
90863
+ credentialPlacement: "authorizationBearer",
90864
+ parameterPlacement: "jsonBody",
90865
+ allowedParams: Object.freeze(["channel", "timestamp", "name"])
90866
+ });
90867
+ const SLACK_REACTION_OPERATIONS = Object.freeze({
90868
+ reactionsAdd: reactionOperation("/api/reactions.add"),
90869
+ reactionsRemove: reactionOperation("/api/reactions.remove")
90870
+ });
90871
+ const SLACK_REACTION_MAPPING = Object.freeze({
90872
+ operation: "reactionsAdd",
90873
+ removeOperation: "reactionsRemove",
90874
+ conversationParam: "channel",
90875
+ messageIdParam: "timestamp",
90876
+ reactionParam: "name",
90877
+ value: "emoji-name"
90878
+ });
89958
90879
  const MAX_SELECTOR_DEPTH = 6;
89959
90880
  const MAX_EVENTS_PER_PAYLOAD = 100;
89960
90881
  const TELEGRAM = Object.freeze({
@@ -90055,7 +90976,8 @@ const TELEGRAM = Object.freeze({
90055
90976
  operation: "setMessageReaction",
90056
90977
  conversationParam: "chat_id",
90057
90978
  messageIdParam: "message_id",
90058
- reactionParam: "reaction"
90979
+ reactionParam: "reaction",
90980
+ value: "reaction-list"
90059
90981
  })
90060
90982
  });
90061
90983
  const SLACK$1 = Object.freeze({
@@ -90128,7 +91050,8 @@ const SLACK$1 = Object.freeze({
90128
91050
  allowedParams: Object.freeze(["channel", "ts", "text"])
90129
91051
  },
90130
91052
  // Files attached to a final answer (specs/channel-file-replies). Core-only, like the two above.
90131
- ...SLACK_FILE_UPLOAD_OPERATIONS
91053
+ ...SLACK_FILE_UPLOAD_OPERATIONS,
91054
+ ...SLACK_REACTION_OPERATIONS
90132
91055
  }),
90133
91056
  inbound: Object.freeze({
90134
91057
  eventsPath: Object.freeze(["events"]),
@@ -90153,8 +91076,8 @@ const SLACK$1 = Object.freeze({
90153
91076
  conversationParam: "channel",
90154
91077
  messageIdParam: "ts"
90155
91078
  }),
90156
- // No reaction: this provider keeps the progress bubble (specs/messenger-progress-stages R13).
90157
- reaction: null
91079
+ // The stage as a reaction on the sender's message (specs/messenger-progress-stages R15).
91080
+ reaction: SLACK_REACTION_MAPPING
90158
91081
  });
90159
91082
  const DISCORD$1 = Object.freeze({
90160
91083
  provider: "discord",
@@ -90372,7 +91295,7 @@ class ChannelApprovalPayloadError extends Error {
90372
91295
  function fail$1(detail) {
90373
91296
  throw new ChannelApprovalPayloadError(detail);
90374
91297
  }
90375
- function record$2(value2, detail) {
91298
+ function record$3(value2, detail) {
90376
91299
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) fail$1(detail);
90377
91300
  return value2;
90378
91301
  }
@@ -90409,14 +91332,14 @@ const DISCORD_STRUCTURE = ["type", "components", "style", "label", "custom_id"];
90409
91332
  function verifySlackApprovalBlocks(blocks, presentation) {
90410
91333
  const list2 = array(blocks, "blocks must be an array");
90411
91334
  if (list2.length !== 2) fail$1("blocks must be exactly a section and an actions row");
90412
- const section = record$2(list2[0], "the first block must be an object");
91335
+ const section = record$3(list2[0], "the first block must be an object");
90413
91336
  exact(section, ["type", "text"], "the section block carries unexpected fields");
90414
91337
  if (section.type !== "section") fail$1("the first block must be a section");
90415
- const sectionText = record$2(section.text, "the section text must be an object");
91338
+ const sectionText = record$3(section.text, "the section text must be an object");
90416
91339
  exact(sectionText, ["type", "text"], "the section text carries unexpected fields");
90417
91340
  if (sectionText.type !== "plain_text") fail$1("the section text must be plain text");
90418
91341
  if (sectionText.text !== presentation.text) fail$1("the section text is not the approved reply");
90419
- const actions = record$2(list2[1], "the second block must be an object");
91342
+ const actions = record$3(list2[1], "the second block must be an object");
90420
91343
  exact(actions, ["type", "elements"], "the actions block carries unexpected fields");
90421
91344
  if (actions.type !== "actions") fail$1("the second block must be an actions row");
90422
91345
  const elements = array(actions.elements, "elements must be an array");
@@ -90426,11 +91349,11 @@ function verifySlackApprovalBlocks(blocks, presentation) {
90426
91349
  { actionId: presentation.actionIds.deny, label: presentation.denyLabel }
90427
91350
  ];
90428
91351
  for (const [index, element] of elements.entries()) {
90429
- const button = record$2(element, "each element must be an object");
91352
+ const button = record$3(element, "each element must be an object");
90430
91353
  exact(button, ["type", "action_id", "text"], "a button carries unexpected fields");
90431
91354
  if (button.type !== "button") fail$1("every element must be a button");
90432
91355
  if (button.action_id !== expected[index].actionId) fail$1("a button names an unexpected action");
90433
- const label = record$2(button.text, "a button label must be an object");
91356
+ const label = record$3(button.text, "a button label must be an object");
90434
91357
  exact(label, ["type", "text"], "a button label carries unexpected fields");
90435
91358
  if (label.type !== "plain_text") fail$1("a button label must be plain text");
90436
91359
  if (label.text !== expected[index].label) fail$1("a button label is not the one Core supplied");
@@ -90441,7 +91364,7 @@ function verifySlackApprovalBlocks(blocks, presentation) {
90441
91364
  function verifyDiscordApprovalComponents(components, presentation) {
90442
91365
  const list2 = array(components, "components must be an array");
90443
91366
  if (list2.length !== 1) fail$1("exactly one action row is permitted");
90444
- const row2 = record$2(list2[0], "the action row must be an object");
91367
+ const row2 = record$3(list2[0], "the action row must be an object");
90445
91368
  exact(row2, ["type", "components"], "the action row carries unexpected fields");
90446
91369
  if (row2.type !== 1) fail$1("the first component must be an action row");
90447
91370
  const buttons = array(row2.components, "row components must be an array");
@@ -90451,7 +91374,7 @@ function verifyDiscordApprovalComponents(components, presentation) {
90451
91374
  { actionId: presentation.actionIds.deny, label: presentation.denyLabel, style: DISCORD_DANGER }
90452
91375
  ];
90453
91376
  for (const [index, element] of buttons.entries()) {
90454
- const button = record$2(element, "each component must be an object");
91377
+ const button = record$3(element, "each component must be an object");
90455
91378
  exact(button, ["type", "style", "label", "custom_id"], "a button carries unexpected fields");
90456
91379
  if (button.type !== 2) fail$1("every component must be a button");
90457
91380
  if (button.style !== expected[index].style) fail$1("a button uses an unsupported style");
@@ -90728,11 +91651,11 @@ function mapEntry(entry, contract, context, command, action) {
90728
91651
  };
90729
91652
  }
90730
91653
  function mapChannelInboundPayload(contract, payload, context) {
90731
- const entries = readPath$1(payload, contract.inbound.eventsPath);
90732
- if (!Array.isArray(entries)) return { events: [], dropped: [] };
91654
+ const entries2 = readPath$1(payload, contract.inbound.eventsPath);
91655
+ if (!Array.isArray(entries2)) return { events: [], dropped: [] };
90733
91656
  const events = [];
90734
91657
  const dropped = [];
90735
- for (const [index, entry] of entries.slice(0, MAX_EVENTS_PER_PAYLOAD).entries()) {
91658
+ for (const [index, entry] of entries2.slice(0, MAX_EVENTS_PER_PAYLOAD).entries()) {
90736
91659
  const normalized = normalizeChannelInboundEntry(contract.provider, entry);
90737
91660
  if (!normalized) {
90738
91661
  dropped.push({ index, reason: "not a command this connection can execute" });
@@ -90747,7 +91670,7 @@ function mapChannelInboundPayload(contract, payload, context) {
90747
91670
  verifiedBotRef: context.verifiedBotRef ?? null
90748
91671
  })));
90749
91672
  }
90750
- if (entries.length > MAX_EVENTS_PER_PAYLOAD) {
91673
+ if (entries2.length > MAX_EVENTS_PER_PAYLOAD) {
90751
91674
  dropped.push({ index: MAX_EVENTS_PER_PAYLOAD, reason: "payload exceeded the per-poll event limit" });
90752
91675
  }
90753
91676
  return { events: Object.freeze(events), dropped: Object.freeze(dropped) };
@@ -90945,12 +91868,12 @@ function boundedStructure(value2, key, depth) {
90945
91868
  if (Object.getPrototypeOf(source2) !== Object.prototype && Object.getPrototypeOf(source2) !== null) {
90946
91869
  reject("PARAM_NOT_STRUCTURED", `param ${key} must be a plain object`);
90947
91870
  }
90948
- const entries = Object.entries(source2);
90949
- if (entries.length > MAX_STRUCTURED_PARAM_ENTRIES) {
91871
+ const entries2 = Object.entries(source2);
91872
+ if (entries2.length > MAX_STRUCTURED_PARAM_ENTRIES) {
90950
91873
  reject("STRUCTURED_PARAM_TOO_WIDE", `param ${key} has more than ${MAX_STRUCTURED_PARAM_ENTRIES} fields`);
90951
91874
  }
90952
91875
  const result2 = {};
90953
- for (const [field, item] of entries) {
91876
+ for (const [field, item] of entries2) {
90954
91877
  if (field === "__proto__" || field === "constructor" || field === "prototype") {
90955
91878
  reject("PARAM_NOT_STRUCTURED", `param ${key} contains a reserved field name`);
90956
91879
  }
@@ -91487,20 +92410,29 @@ async function reactChannelProgress(input) {
91487
92410
  const contract = channelProviderContract(input.provider);
91488
92411
  const mapping = contract.reaction;
91489
92412
  if (!mapping || !input.emoji) return { ok: false, retryable: false };
92413
+ const value2 = (glyph) => mapping.value === "emoji-name" ? slackReactionName(glyph) : [{ type: "emoji", emoji: glyph }];
92414
+ const reaction = value2(input.emoji);
92415
+ if (reaction === null) return { ok: false, retryable: false };
92416
+ const send = (operation, reactionValue) => input.executor.execute(input.connectionId, {
92417
+ operation,
92418
+ params: {
92419
+ [mapping.conversationParam]: input.destination.conversationRef,
92420
+ [mapping.messageIdParam]: input.messageId,
92421
+ [mapping.reactionParam]: reactionValue
92422
+ },
92423
+ credentialRevision: input.connectionRevision
92424
+ });
92425
+ const previous = input.previousEmoji ? value2(input.previousEmoji) : null;
92426
+ if (mapping.removeOperation && previous !== null && input.previousEmoji !== input.emoji) {
92427
+ await send(mapping.removeOperation, previous).catch(() => void 0);
92428
+ }
91490
92429
  let result2;
91491
92430
  try {
91492
- result2 = await input.executor.execute(input.connectionId, {
91493
- operation: mapping.operation,
91494
- params: {
91495
- [mapping.conversationParam]: input.destination.conversationRef,
91496
- [mapping.messageIdParam]: input.messageId,
91497
- [mapping.reactionParam]: [{ type: "emoji", emoji: input.emoji }]
91498
- },
91499
- credentialRevision: input.connectionRevision
91500
- });
92431
+ result2 = await send(mapping.operation, reaction);
91501
92432
  } catch {
91502
92433
  return { ok: false, retryable: true };
91503
92434
  }
92435
+ if (result2.body?.error === "already_reacted") return { ok: true };
91504
92436
  return classifyProgressResult(contract, result2, input.onDiagnostic);
91505
92437
  }
91506
92438
  async function editChannelProgress(input) {
@@ -91840,9 +92772,9 @@ async function withdrawIntakeDurably(cancelled, connectionKeys, deps) {
91840
92772
  const EMPTY_WITHDRAWAL = Object.freeze({
91841
92773
  unsafeToReopen: Object.freeze([])
91842
92774
  });
91843
- async function journalCancelledIntake(entries, deps) {
92775
+ async function journalCancelledIntake(entries2, deps) {
91844
92776
  const undurable = [];
91845
- for (const entry of entries) {
92777
+ for (const entry of entries2) {
91846
92778
  try {
91847
92779
  await deps.journal.record(entry);
91848
92780
  } catch {
@@ -92036,6 +92968,7 @@ class ChannelProgressRails {
92036
92968
  }
92037
92969
  /** requestId → its rail and the session it belongs to. Both are part of every match. */
92038
92970
  rails = /* @__PURE__ */ new Map();
92971
+ receipts = /* @__PURE__ */ new Map();
92039
92972
  get size() {
92040
92973
  return this.rails.size;
92041
92974
  }
@@ -92059,6 +92992,9 @@ class ChannelProgressRails {
92059
92992
  const reactTo = source2?.destination.replyToTransportEventId ?? null;
92060
92993
  const reacting = react !== void 0 && source2 !== void 0 && reactTo !== null;
92061
92994
  let target = null;
92995
+ const receipt = reacting ? this.receipts.get(requestId) : void 0;
92996
+ if (reacting) this.receipts.delete(requestId);
92997
+ let shown = null;
92062
92998
  const rail = new ChannelProgressRail({
92063
92999
  requestId,
92064
93000
  // From acceptance, which is what the tracker's clock means and what the reader has been
@@ -92078,7 +93014,10 @@ class ChannelProgressRails {
92078
93014
  connectionRevision: target.generation,
92079
93015
  destination: target.destination
92080
93016
  };
92081
- return reacting ? react(input, id2, text2) : this.options.edit(input, id2, text2);
93017
+ if (!reacting) return this.options.edit(input, id2, text2);
93018
+ const result2 = await react(input, id2, text2, shown);
93019
+ if (result2.ok) shown = text2;
93020
+ return result2;
92082
93021
  }
92083
93022
  },
92084
93023
  ...this.options.onDiagnostic ? { onDiagnostic: this.options.onDiagnostic } : {}
@@ -92094,10 +93033,74 @@ class ChannelProgressRails {
92094
93033
  });
92095
93034
  if (reacting) {
92096
93035
  target = { generation: source2.connectionRevision, destination: source2.destination };
92097
- rail.adopt(reactTo, "");
93036
+ if (receipt) void receipt.drawn.then(() => {
93037
+ shown = receipt.shown;
93038
+ rail.adopt(reactTo, shown ?? "");
93039
+ });
93040
+ else rail.adopt(reactTo, "");
92098
93041
  }
92099
93042
  return reacting;
92100
93043
  }
93044
+ /**
93045
+ * Draws 👀 on the sender's message the moment it is admitted (R16), before the adapter's receive
93046
+ * loop and the dispatch. Display only and never awaited by the caller; `expect` takes it over,
93047
+ * `settleReceipt` closes it when no turn follows.
93048
+ */
93049
+ acknowledgeReceipt(requestId, source2) {
93050
+ const react = this.options.react;
93051
+ const messageId2 = source2.destination.replyToTransportEventId;
93052
+ if (!react || !messageId2 || !this.options.isActive() || !isCoreRequestId(requestId)) return;
93053
+ if (this.rails.has(requestId) || this.receipts.has(requestId)) return;
93054
+ if (this.receipts.size >= MAX_PROGRESS_RAILS) return;
93055
+ const receipt = {
93056
+ generation: source2.connectionRevision,
93057
+ destination: source2.destination,
93058
+ messageId: messageId2,
93059
+ shown: null,
93060
+ drawn: Promise.resolve({ ok: true }),
93061
+ settling: false,
93062
+ cancelTimer: null
93063
+ };
93064
+ this.receipts.set(requestId, receipt);
93065
+ receipt.drawn = this.drawReceipt(receipt, CHANNEL_STAGE_REACTION.accepted);
93066
+ }
93067
+ /** No rail followed this request — a command, a refusal, a question back: close its 👀 (R16). */
93068
+ settleReceipt(requestId, outcome) {
93069
+ const receipt = this.receipts.get(requestId);
93070
+ if (!receipt || receipt.settling) return;
93071
+ receipt.settling = true;
93072
+ const finish = async (attempt2) => {
93073
+ if (this.receipts.get(requestId) !== receipt) return;
93074
+ const result2 = await this.drawReceipt(receipt, CHANNEL_STAGE_REACTION[outcome]);
93075
+ if (this.receipts.get(requestId) !== receipt) return;
93076
+ if (result2.ok || !result2.retryable || attempt2 >= PROGRESS_TERMINAL_ATTEMPTS) {
93077
+ this.receipts.delete(requestId);
93078
+ return;
93079
+ }
93080
+ receipt.cancelTimer = this.options.setTimer(() => {
93081
+ receipt.cancelTimer = null;
93082
+ void finish(attempt2 + 1);
93083
+ }, PROGRESS_MIN_EDIT_INTERVAL_MS);
93084
+ };
93085
+ void receipt.drawn.then(() => finish(1));
93086
+ }
93087
+ async drawReceipt(receipt, emoji2) {
93088
+ if (!this.options.react || !this.options.isActive() || this.options.currentGeneration() !== receipt.generation) {
93089
+ return { ok: false, retryable: false };
93090
+ }
93091
+ try {
93092
+ const result2 = await this.options.react({
93093
+ provider: this.options.provider,
93094
+ connectionId: this.options.connectionId,
93095
+ connectionRevision: receipt.generation,
93096
+ destination: receipt.destination
93097
+ }, receipt.messageId, emoji2, receipt.shown);
93098
+ if (result2.ok) receipt.shown = emoji2;
93099
+ return result2;
93100
+ } catch {
93101
+ return { ok: false, retryable: true };
93102
+ }
93103
+ }
92101
93104
  /**
92102
93105
  * Points this request's rail at the acknowledgement the outbox just delivered.
92103
93106
  *
@@ -92151,6 +93154,8 @@ class ChannelProgressRails {
92151
93154
  closeAll() {
92152
93155
  for (const entry of this.rails.values()) entry.rail.dispose();
92153
93156
  this.rails.clear();
93157
+ for (const receipt of this.receipts.values()) receipt.cancelTimer?.();
93158
+ this.receipts.clear();
92154
93159
  }
92155
93160
  }
92156
93161
  const MAX_FINAL_ANSWER_BYTES = 64 * 1024;
@@ -93058,12 +94063,13 @@ class ChannelConnectionComposition {
93058
94063
  executor: this.outboundExecutor,
93059
94064
  ...this.options.onDiagnostic ? { onDiagnostic: this.options.onDiagnostic } : {}
93060
94065
  }),
93061
- // Only where the provider can react (Telegram): the others keep the bubble (R13).
94066
+ // Only where the provider can react (Telegram R13, Slack R15): the others keep the bubble.
93062
94067
  ...channelProviderContract(input.provider).reaction ? {
93063
- react: (target, messageId2, emoji2) => reactChannelProgress({
94068
+ react: (target, messageId2, emoji2, shown) => reactChannelProgress({
93064
94069
  ...target,
93065
94070
  messageId: messageId2,
93066
94071
  emoji: emoji2,
94072
+ previousEmoji: shown,
93067
94073
  executor: this.outboundExecutor,
93068
94074
  ...this.options.onDiagnostic ? { onDiagnostic: this.options.onDiagnostic } : {}
93069
94075
  })
@@ -93331,6 +94337,7 @@ async function dispatchChannelControl(input, deps) {
93331
94337
  boundAt: deps.now()
93332
94338
  }, word.kind === "help" ? channelHelpText() : word.kind === "welcome" ? channelWelcomeText() : channelUnknownCommandText(word.word));
93333
94339
  }
94340
+ parts2.progress.settleReceipt(input.requestId, "completed");
93334
94341
  parts2.reply.unbind(input.requestId);
93335
94342
  await settleIntakeEntry(parts2.queue, input.requestId, "executed", deps.settlement);
93336
94343
  deps.onSettled?.(input.requestId);
@@ -93378,10 +94385,11 @@ async function dispatchChannelControl(input, deps) {
93378
94385
  deps.onSettled?.(input.requestId);
93379
94386
  }
93380
94387
  const startedWork = result2.ok && result2.sessionId !== null;
94388
+ let reacting = false;
93381
94389
  if (answerable()) {
93382
94390
  const sessionId = (result2.ok ? result2.sessionId : null) ?? `command:${input.connectionId}`;
93383
94391
  const acknowledged = result2.ok && result2.text === CHANNEL_ACK_TEXT();
93384
- const reacting = startedWork && bound !== null && !SYNCHRONOUS_OPERATIONS.has(bound.operation) && parts2.progress.expect(input.requestId, sessionId, acknowledged ? {
94392
+ reacting = startedWork && bound !== null && !SYNCHRONOUS_OPERATIONS.has(bound.operation) && parts2.progress.expect(input.requestId, sessionId, acknowledged ? {
93385
94393
  destination: input.event.destination,
93386
94394
  connectionRevision: input.event.connectionRevision
93387
94395
  } : void 0);
@@ -93394,7 +94402,9 @@ async function dispatchChannelControl(input, deps) {
93394
94402
  boundAt: deps.now()
93395
94403
  }, result2.ok ? result2.text : ambiguous ? CHANNEL_DISPATCH_UNCONFIRMED() : REFUSAL_TEXT());
93396
94404
  }
93397
- if (!result2.ok || bound === null || (startedWork ? SYNCHRONOUS_OPERATIONS.has(bound.operation) : true)) {
94405
+ const terminal2 = !result2.ok || bound === null || !startedWork || SYNCHRONOUS_OPERATIONS.has(bound.operation);
94406
+ if (!reacting && !ambiguous && terminal2) parts2.progress.settleReceipt(input.requestId, result2.ok ? "completed" : "failed");
94407
+ if (terminal2) {
93398
94408
  const state = result2.ok ? "executed" : ambiguous ? "unknown" : "failed";
93399
94409
  await settleIntakeEntry(parts2.queue, input.requestId, state, deps.settlement);
93400
94410
  if (result2.ok) {
@@ -93659,8 +94669,8 @@ class ChannelThreadOwnership {
93659
94669
  * Dropping the rest is what keeps a revoke-and-repair from handing an old thread to the new
93660
94670
  * pairing — and a restart from handing a claimed thread to whoever writes in first.
93661
94671
  */
93662
- restore(entries, currentGeneration) {
93663
- for (const entry of entries) {
94672
+ restore(entries2, currentGeneration) {
94673
+ for (const entry of entries2) {
93664
94674
  if (entry.connectionRevision !== currentGeneration) continue;
93665
94675
  const id2 = keyOf$1(entry);
93666
94676
  if (!this.owners.has(id2)) {
@@ -93726,8 +94736,8 @@ class ChannelThreadSelection {
93726
94736
  * Entries stamped with any other revision are dropped rather than restamped: a thread that was
93727
94737
  * working in a session under a pairing the user has since revoked must not resume pointing at it.
93728
94738
  */
93729
- restore(entries, currentGeneration) {
93730
- for (const entry of entries) {
94739
+ restore(entries2, currentGeneration) {
94740
+ for (const entry of entries2) {
93731
94741
  if (entry.connectionRevision !== currentGeneration) continue;
93732
94742
  this.selected.set(keyOf(entry), entry.sessionId);
93733
94743
  }
@@ -95517,9 +96527,9 @@ function channelPollPlan(provider2) {
95517
96527
  // `timeout` is the provider's long-poll hold, and it must stay under the executor's own
95518
96528
  // deadline (60s) or every poll would look like a transport timeout.
95519
96529
  params: { [field]: 0, timeout: 25 },
95520
- advance: (entries, _body, current2) => {
95521
- if (entries.length === 0) return current2;
95522
- const last = Number(entries[entries.length - 1].transportEventId);
96530
+ advance: (entries2, _body, current2) => {
96531
+ if (entries2.length === 0) return current2;
96532
+ const last = Number(entries2[entries2.length - 1].transportEventId);
95523
96533
  const next = Number.isFinite(last) ? last + 1 : Number(current2[field] ?? 0);
95524
96534
  const held = Number(current2[field] ?? 0);
95525
96535
  return { ...current2, [field]: contract.cursor.monotonic ? Math.max(held, next) : next };
@@ -95735,8 +96745,8 @@ function createChannelConnectionLifecycle(deps) {
95735
96745
  const receive = createChannelReceiveRuntime({
95736
96746
  parts: parts2,
95737
96747
  canPersistCredentials: () => options.canPersistCredentials(),
95738
- onWork: (entries) => {
95739
- void routeAdmitted(parts2, entries);
96748
+ onWork: (entries2) => {
96749
+ void routeAdmitted(parts2, entries2);
95740
96750
  },
95741
96751
  onFailure: () => {
95742
96752
  if (composition.get(identity.connectionId) === parts2 && !hasMovedOn(identity.connectionId)) registry2.current.setState(identity.connectionId, "failed");
@@ -95836,15 +96846,21 @@ function createChannelConnectionLifecycle(deps) {
95836
96846
  message: "the pairing greeting could not be queued"
95837
96847
  }));
95838
96848
  }
95839
- async function routeAdmitted(parts2, entries) {
95840
- for (const entry of entries) {
96849
+ async function routeAdmitted(parts2, entries2) {
96850
+ for (const entry of entries2) {
95841
96851
  const claimed = await options.offerPairingProof({
95842
96852
  principal: parts2.identity.principal,
95843
96853
  provider: parts2.provider,
95844
96854
  connectionId: parts2.identity.connectionId,
95845
96855
  event: entry.event
95846
96856
  }).catch(() => false);
95847
- if (!claimed) continue;
96857
+ if (!claimed) {
96858
+ try {
96859
+ options.acknowledgeReceipt?.(parts2, entry);
96860
+ } catch {
96861
+ }
96862
+ continue;
96863
+ }
95848
96864
  parts2.queue.transition(entry.id, "executed");
95849
96865
  await parts2.journal.record(parts2.queue.get(entry.id) ?? entry).catch(() => void 0);
95850
96866
  }
@@ -95908,6 +96924,22 @@ function createChannelConnectionLifecycle(deps) {
95908
96924
  botIdentity: (connectionId2) => botIdentities.get(connectionId2) ?? null
95909
96925
  };
95910
96926
  }
96927
+ function channelReceiptEligible(event, identity, grants) {
96928
+ if (event.sender.bot || event.action || event.command || event.connectionId !== identity.connectionId) return false;
96929
+ const text2 = event.text.trim();
96930
+ if (!text2 || text2.startsWith("/")) return false;
96931
+ const sender2 = event.sender.ref;
96932
+ const granted = grants.find(identity, { connectionId: event.connectionId, senderRef: sender2, conversationRef: event.conversation.ref });
96933
+ if (granted) return granted.revision === event.connectionRevision;
96934
+ return event.invitedMention === true && grants.list(identity).some((grant) => grant.senderRef === sender2 && grant.revision === event.connectionRevision);
96935
+ }
96936
+ function acknowledgeChannelReceipt(parts2, entry, grants) {
96937
+ if (!channelReceiptEligible(entry.event, parts2.identity, grants)) return;
96938
+ parts2.progress.acknowledgeReceipt(entry.id, {
96939
+ destination: entry.event.destination,
96940
+ connectionRevision: entry.event.connectionRevision
96941
+ });
96942
+ }
95911
96943
  function createChannelRequestLedger(options) {
95912
96944
  function drop(requestId) {
95913
96945
  options.approvalBindings.delete(requestId);
@@ -96654,7 +97686,7 @@ const MAX_IDENTIFIER = 200;
96654
97686
  const MAX_SECRET = 4096;
96655
97687
  const MAX_CHANNEL_SCOPE_LIST = 200;
96656
97688
  const MAX_LIST = MAX_CHANNEL_SCOPE_LIST;
96657
- function record$1(value2, keys2) {
97689
+ function record$2(value2, keys2) {
96658
97690
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
96659
97691
  const prototype = Object.getPrototypeOf(value2);
96660
97692
  if (prototype !== Object.prototype && prototype !== null) return null;
@@ -96674,7 +97706,7 @@ function identifierList(value2) {
96674
97706
  return parsed2.every((entry) => entry !== null) ? parsed2 : null;
96675
97707
  }
96676
97708
  function parseChannelPairingScope(value2) {
96677
- const source2 = record$1(value2, ["allowedProjectIds", "allowedMachineIds", "allowedSessionRefs", "operations"]);
97709
+ const source2 = record$2(value2, ["allowedProjectIds", "allowedMachineIds", "allowedSessionRefs", "operations"]);
96678
97710
  if (!source2) return null;
96679
97711
  const projects = identifierList(source2.allowedProjectIds);
96680
97712
  const machines2 = identifierList(source2.allowedMachineIds);
@@ -96707,7 +97739,7 @@ function expectation(source2) {
96707
97739
  };
96708
97740
  }
96709
97741
  function parseChannelConnectionWriteInput(value2) {
96710
- const source2 = record$1(value2, [
97742
+ const source2 = record$2(value2, [
96711
97743
  "accountScopeKey",
96712
97744
  "epoch",
96713
97745
  "expectedConnectionRevision",
@@ -96721,7 +97753,7 @@ function parseChannelConnectionWriteInput(value2) {
96721
97753
  const provider2 = parseChannelProvider(source2.provider);
96722
97754
  const scope2 = parseChannelPairingScope(source2.scope);
96723
97755
  if (!base2 || !provider2 || !scope2) return null;
96724
- const secretsSource = record$1(source2.secrets, providerSecretFields(provider2));
97756
+ const secretsSource = record$2(source2.secrets, providerSecretFields(provider2));
96725
97757
  if (!secretsSource) return null;
96726
97758
  const secrets = {};
96727
97759
  for (const [field, raw] of Object.entries(secretsSource)) {
@@ -96730,7 +97762,7 @@ function parseChannelConnectionWriteInput(value2) {
96730
97762
  }
96731
97763
  let agentSelection;
96732
97764
  if (source2.agentSelection !== void 0) {
96733
- const raw = record$1(source2.agentSelection, ["agent", "model", "effort"]);
97765
+ const raw = record$2(source2.agentSelection, ["agent", "model", "effort"]);
96734
97766
  if (!raw || typeof raw.agent !== "string" || typeof raw.model !== "string" || raw.effort !== null && typeof raw.effort !== "string") return null;
96735
97767
  try {
96736
97768
  agentSelection = normalizeChannelAgentSelection({ agent: raw.agent, model: raw.model, effort: raw.effort });
@@ -96741,7 +97773,7 @@ function parseChannelConnectionWriteInput(value2) {
96741
97773
  return { ...base2, provider: provider2, secrets, scope: scope2, ...agentSelection ? { agentSelection } : {} };
96742
97774
  }
96743
97775
  function parseChannelBeginPairingInput(value2) {
96744
- const source2 = record$1(value2, ["accountScopeKey", "epoch", "expectedConnectionRevision", "provider", "scope"]);
97776
+ const source2 = record$2(value2, ["accountScopeKey", "epoch", "expectedConnectionRevision", "provider", "scope"]);
96745
97777
  if (!source2) return null;
96746
97778
  const base2 = expectation(source2);
96747
97779
  const provider2 = parseChannelProvider(source2.provider);
@@ -96750,7 +97782,7 @@ function parseChannelBeginPairingInput(value2) {
96750
97782
  return { ...base2, provider: provider2, scope: scope2 };
96751
97783
  }
96752
97784
  function parseChannelConfirmPairingClaimInput(value2) {
96753
- const source2 = record$1(value2, [
97785
+ const source2 = record$2(value2, [
96754
97786
  "accountScopeKey",
96755
97787
  "epoch",
96756
97788
  "expectedConnectionRevision",
@@ -96767,7 +97799,7 @@ function parseChannelConfirmPairingClaimInput(value2) {
96767
97799
  return { ...base2, provider: provider2, claimHandle, decision: source2.decision };
96768
97800
  }
96769
97801
  function parseChannelRevokeInput(value2) {
96770
- const source2 = record$1(value2, ["accountScopeKey", "epoch", "expectedConnectionRevision", "provider"]);
97802
+ const source2 = record$2(value2, ["accountScopeKey", "epoch", "expectedConnectionRevision", "provider"]);
96771
97803
  if (!source2) return null;
96772
97804
  const base2 = expectation(source2);
96773
97805
  const provider2 = parseChannelProvider(source2.provider);
@@ -97396,6 +98428,7 @@ function createChannelRequestCoordinator(deps) {
97396
98428
  canPersistCredentials: () => options.canPersistCredentials(),
97397
98429
  ...options.botClaimedElsewhere ? { botClaimedElsewhere: options.botClaimedElsewhere } : {},
97398
98430
  offerPairingProof: (input) => options.offerPairingProof(input),
98431
+ acknowledgeReceipt: (parts2, entry) => acknowledgeChannelReceipt(parts2, entry, grants),
97399
98432
  ...options.takeOwedWelcome ? { takeOwedWelcome: options.takeOwedWelcome } : {},
97400
98433
  channelAdapters,
97401
98434
  generations,
@@ -98951,9 +99984,9 @@ function parseScope(value2) {
98951
99984
  }
98952
99985
  function parseSecrets(value2) {
98953
99986
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
98954
- const entries = Object.entries(value2);
98955
- if (entries.length === 0 || !entries.every(([, secret]) => nonEmptyString(secret))) return null;
98956
- return Object.fromEntries(entries);
99987
+ const entries2 = Object.entries(value2);
99988
+ if (entries2.length === 0 || !entries2.every(([, secret]) => nonEmptyString(secret))) return null;
99989
+ return Object.fromEntries(entries2);
98957
99990
  }
98958
99991
  function parseEntry(value2) {
98959
99992
  if (!value2 || typeof value2 !== "object" || Array.isArray(value2)) return null;
@@ -100920,8 +101953,8 @@ class ExtensionStore {
100920
101953
  const pending2 = [];
100921
101954
  let totalBytes = 0;
100922
101955
  const visit = async (directory, relativeDirectory) => {
100923
- const entries = (await readdir(directory, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name));
100924
- for (const entry of entries) {
101956
+ const entries2 = (await readdir(directory, { withFileTypes: true })).sort((left, right) => left.name.localeCompare(right.name));
101957
+ for (const entry of entries2) {
100925
101958
  const absolutePath = join(directory, entry.name);
100926
101959
  const relativePath2 = relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name;
100927
101960
  if (entry.isSymbolicLink()) throw new Error(`extension assets contain a symbolic link: ${relativePath2}`);
@@ -100982,13 +102015,13 @@ class ExtensionStore {
100982
102015
  }
100983
102016
  }
100984
102017
  async extract(zip, stagingPath) {
100985
- const entries = Object.values(zip.files);
100986
- if (entries.length > (this.options.maxEntries ?? 5e3)) {
102018
+ const entries2 = Object.values(zip.files);
102019
+ if (entries2.length > (this.options.maxEntries ?? 5e3)) {
100987
102020
  throw new Error("extension archive has too many entries");
100988
102021
  }
100989
102022
  const maxUncompressedBytes = this.options.maxUncompressedBytes ?? 50 * 1024 * 1024;
100990
102023
  let extractedBytes = 0;
100991
- for (const entry of entries) {
102024
+ for (const entry of entries2) {
100992
102025
  const rawPath = archivePath(entry);
100993
102026
  assertSafeArchivePath(rawPath);
100994
102027
  assertRegularEntry(entry);
@@ -101080,33 +102113,33 @@ function createHostSessionKeyStore(input) {
101080
102113
  return cache2;
101081
102114
  }
101082
102115
  async function read2() {
101083
- const entries = /* @__PURE__ */ new Map();
102116
+ const entries2 = /* @__PURE__ */ new Map();
101084
102117
  try {
101085
102118
  const raw = JSON.parse(await readFile(join(input.dir, FILE), "utf8"));
101086
- if (typeof raw.sealed !== "string") return entries;
102119
+ if (typeof raw.sealed !== "string") return entries2;
101087
102120
  const stored = JSON.parse(input.safeStorage.decryptString(Buffer.from(raw.sealed, "base64")));
101088
- if (!Array.isArray(stored)) return entries;
102121
+ if (!Array.isArray(stored)) return entries2;
101089
102122
  for (const entry of stored) {
101090
102123
  const [id2, key] = entry;
101091
102124
  if (typeof id2 === "string" && typeof key?.dataEncryptionKey === "string" && typeof key.dek === "string") {
101092
- entries.set(id2, { dataEncryptionKey: key.dataEncryptionKey, dek: key.dek });
102125
+ entries2.set(id2, { dataEncryptionKey: key.dataEncryptionKey, dek: key.dek });
101093
102126
  }
101094
102127
  }
101095
102128
  } catch {
101096
102129
  }
101097
- return entries;
102130
+ return entries2;
101098
102131
  }
101099
102132
  return {
101100
102133
  async get(sessionId) {
101101
102134
  return (await load2()).get(sessionId) ?? null;
101102
102135
  },
101103
102136
  async put(sessionId, key) {
101104
- const entries = await load2();
101105
- entries.delete(sessionId);
101106
- entries.set(sessionId, key);
101107
- while (entries.size > limit) entries.delete(entries.keys().next().value);
102137
+ const entries2 = await load2();
102138
+ entries2.delete(sessionId);
102139
+ entries2.set(sessionId, key);
102140
+ while (entries2.size > limit) entries2.delete(entries2.keys().next().value);
101108
102141
  const write = writes.then(async () => {
101109
- const sealed2 = input.safeStorage.encryptString(JSON.stringify([...entries])).toString("base64");
102142
+ const sealed2 = input.safeStorage.encryptString(JSON.stringify([...entries2])).toString("base64");
101110
102143
  await mkdir(input.dir, { recursive: true, mode: 448 });
101111
102144
  const path2 = join(input.dir, FILE);
101112
102145
  const staging = `${path2}.${process.pid}.tmp`;
@@ -102073,91 +103106,166 @@ async function steerAgentSession(transport, sessionId, text2) {
102073
103106
  return rejectedSteer(error instanceof Error ? error.message : String(error));
102074
103107
  }
102075
103108
  }
102076
- const STREAM_HISTORY_LIMIT = 50;
102077
- const SPAWN_RPC_TIMEOUT_MS = 6e4;
102078
- function isReadableFileSync(path2) {
102079
- try {
102080
- accessSync(path2, constants$3.R_OK);
102081
- return statSync(path2).isFile();
102082
- } catch {
102083
- return false;
102084
- }
103109
+ function record$1(value2) {
103110
+ return value2 !== null && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
102085
103111
  }
102086
- function readFileOrNull(path2) {
103112
+ function readObject(path2, read2) {
102087
103113
  try {
102088
- return readFileSync(path2, "utf8");
103114
+ return record$1(JSON.parse(read2(path2) ?? "null"));
102089
103115
  } catch {
102090
103116
  return null;
102091
103117
  }
102092
103118
  }
102093
- async function recentContents(context, transport, sessionId, limit) {
102094
- const messages2 = await context.rest.recentMessages(sessionId, limit);
102095
- const contents = [];
102096
- for (const message of [...messages2].sort((left, right) => left.seq - right.seq)) {
102097
- const ciphertext = unwrapEncryptedField(message.content);
102098
- if (!ciphertext) continue;
102099
- const content = decodeContent(transport, ciphertext);
102100
- if (content) contents.push({ seq: message.seq, content });
103119
+ function localKeys(scope2, read2) {
103120
+ for (const path2 of machineSettingsPaths(scope2)) {
103121
+ const machineId = readObject(path2, read2)?.machineId;
103122
+ if (typeof machineId !== "string" || !machineId) continue;
103123
+ const entries2 = record$1(readObject(join(dirname(path2), "sessions.json"), read2)?.sessions);
103124
+ const keys2 = /* @__PURE__ */ new Map();
103125
+ for (const [id2, value2] of Object.entries(entries2 ?? {})) {
103126
+ const entry = record$1(value2);
103127
+ if (entry?.encryptionVariant !== "dataKey" || record$1(entry.metadata)?.machineId !== machineId) continue;
103128
+ const encoded = entry.encryptionKey;
103129
+ if (typeof encoded !== "string") continue;
103130
+ const key = Buffer.from(encoded, "base64");
103131
+ if (key.length === 32 && key.toString("base64") === encoded) keys2.set(id2, new Uint8Array(key));
103132
+ }
103133
+ return { machineId, keys: keys2 };
102101
103134
  }
102102
- return contents;
103135
+ return null;
102103
103136
  }
102104
- async function openStream(context, transport, sessionId) {
102105
- let snapshot = createWaitSnapshot();
102106
- const listeners2 = /* @__PURE__ */ new Set();
102107
- const publish2 = (next) => {
102108
- if (next === snapshot) return;
102109
- snapshot = next;
102110
- for (const listener of listeners2) listener(next);
102111
- };
102112
- const apply = (update) => {
102113
- const message = readNewMessage(update, sessionId);
102114
- if (message) {
102115
- const content = decodeContent(transport, message.ciphertext);
102116
- if (content) publish2(foldMessageIntoSnapshot(snapshot, content));
102117
- return;
103137
+ function createAgentSessionReader(context, transport, scope2, read2) {
103138
+ let keys2 = /* @__PURE__ */ new Map();
103139
+ let locked = /* @__PURE__ */ new Set();
103140
+ async function listSessions() {
103141
+ const rows = await context.rest.listSessions();
103142
+ const local = localKeys(scope2, read2);
103143
+ const nextKeys = /* @__PURE__ */ new Map(), nextLocked = /* @__PURE__ */ new Set();
103144
+ const legacyKeys = context.syncCredential.secret ? [decodeBase64$1(context.syncCredential.secret)] : [];
103145
+ const sessions = normalizeSessionRows(rows.filter((row2) => !row2.dataEncryptionKey), legacyKeys);
103146
+ try {
103147
+ for (const row2 of rows) {
103148
+ if (!row2.dataEncryptionKey) continue;
103149
+ nextLocked.add(row2.id);
103150
+ const key = local?.keys.get(row2.id);
103151
+ if (!key) continue;
103152
+ const metadata = row2.metadata ? await decryptDataKeyRecord(key, row2.metadata) : null;
103153
+ if (!metadata || metadata.machineId !== local?.machineId) continue;
103154
+ const state = row2.agentState ? await decryptDataKeyRecord(key, row2.agentState) : null;
103155
+ sessions.push(normalizeDecryptedSessionRow(row2, metadata, state));
103156
+ nextKeys.set(row2.id, new Uint8Array(key));
103157
+ nextLocked.delete(row2.id);
103158
+ }
103159
+ } finally {
103160
+ for (const key of local?.keys.values() ?? []) key.fill(0);
102118
103161
  }
102119
- const agentState = readSessionAgentState(update, sessionId);
102120
- if (!agentState) return;
103162
+ for (const key of keys2.values()) key.fill(0);
103163
+ keys2 = nextKeys;
103164
+ locked = nextLocked;
103165
+ return sessions.sort((a, b) => b.updatedAt - a.updatedAt);
103166
+ }
103167
+ async function decode2(sessionId, ciphertext) {
103168
+ if (locked.has(sessionId)) return null;
103169
+ const key = keys2.get(sessionId);
103170
+ if (key) return decryptDataKeyJson(key, ciphertext);
102121
103171
  try {
102122
- publish2(foldAgentStateIntoSnapshot(snapshot, transport.decodePayload(agentState)));
103172
+ return transport.decodePayload(ciphertext);
102123
103173
  } catch {
103174
+ return null;
102124
103175
  }
102125
- };
102126
- const buffered = [];
102127
- let replaying = true;
102128
- const unsubscribe = transport.onUpdate((update) => {
102129
- if (replaying) buffered.push(update);
102130
- else apply(update);
102131
- });
102132
- try {
102133
- for (const { content } of await recentContents(context, transport, sessionId, STREAM_HISTORY_LIMIT)) {
102134
- snapshot = foldMessageIntoSnapshot(snapshot, content);
103176
+ }
103177
+ async function readSequencedMessages(sessionId, limit) {
103178
+ const messages2 = await context.rest.recentMessages(sessionId, limit);
103179
+ const contents = [];
103180
+ for (const message of [...messages2].sort((a, b) => a.seq - b.seq)) {
103181
+ const ciphertext = unwrapEncryptedField(message.content);
103182
+ const content = ciphertext ? await decode2(sessionId, ciphertext) : null;
103183
+ if (content) contents.push({ seq: message.seq, content });
103184
+ }
103185
+ return contents;
103186
+ }
103187
+ async function openStream(sessionId) {
103188
+ let snapshot = createWaitSnapshot(), closed = false, replaying = true;
103189
+ let pending2 = Promise.resolve();
103190
+ const listeners2 = /* @__PURE__ */ new Set();
103191
+ const buffered = [];
103192
+ const publish2 = (next) => {
103193
+ if (closed || next === snapshot) return;
103194
+ snapshot = next;
103195
+ for (const listener of listeners2) listener(next);
103196
+ };
103197
+ const apply = async (update) => {
103198
+ if (closed) return;
103199
+ const message = readNewMessage(update, sessionId);
103200
+ if (message) {
103201
+ const content = await decode2(sessionId, message.ciphertext);
103202
+ if (content) publish2(foldMessageIntoSnapshot(snapshot, content));
103203
+ return;
103204
+ }
103205
+ const state = readSessionAgentState(update, sessionId);
103206
+ const opened = state ? await decode2(sessionId, state) : null;
103207
+ if (opened) publish2(foldAgentStateIntoSnapshot(snapshot, opened));
103208
+ };
103209
+ const unsubscribe = transport.onUpdate((update) => {
103210
+ if (replaying) buffered.push(update);
103211
+ else pending2 = pending2.then(() => apply(update)).catch(() => {
103212
+ });
103213
+ });
103214
+ try {
103215
+ for (const { content } of await readSequencedMessages(sessionId, 50)) snapshot = foldMessageIntoSnapshot(snapshot, content);
103216
+ for (const update of buffered) await apply(update);
103217
+ buffered.length = 0;
103218
+ replaying = false;
103219
+ } catch (error) {
103220
+ closed = true;
103221
+ unsubscribe();
103222
+ throw error;
102135
103223
  }
102136
- } catch (error) {
102137
- unsubscribe();
102138
- throw error;
103224
+ return {
103225
+ current: () => snapshot,
103226
+ subscribe: (listener) => {
103227
+ listeners2.add(listener);
103228
+ return () => listeners2.delete(listener);
103229
+ },
103230
+ close: () => {
103231
+ closed = true;
103232
+ listeners2.clear();
103233
+ unsubscribe();
103234
+ }
103235
+ };
102139
103236
  }
102140
- replaying = false;
102141
- for (const update of buffered) apply(update);
102142
- buffered.length = 0;
102143
103237
  return {
102144
- current: () => snapshot,
102145
- subscribe: (listener) => {
102146
- listeners2.add(listener);
102147
- return () => listeners2.delete(listener);
103238
+ listSessions,
103239
+ readSequencedMessages,
103240
+ openStream,
103241
+ assertLegacyWrite: (sessionId) => {
103242
+ if (keys2.has(sessionId) || locked.has(sessionId)) throw new Error("Local dataKey session writes require a scoped transport; this command does not support them yet.");
102148
103243
  },
102149
- close: () => {
102150
- listeners2.clear();
102151
- unsubscribe();
102152
- }
103244
+ readMessages: async (sessionId, limit) => (await readSequencedMessages(sessionId, limit)).map((entry) => entry.content)
102153
103245
  };
102154
103246
  }
103247
+ const SPAWN_RPC_TIMEOUT_MS = 6e4;
103248
+ function isReadableFileSync(path2) {
103249
+ try {
103250
+ accessSync(path2, constants$3.R_OK);
103251
+ return statSync(path2).isFile();
103252
+ } catch {
103253
+ return false;
103254
+ }
103255
+ }
103256
+ function readFileOrNull(path2) {
103257
+ try {
103258
+ return readFileSync(path2, "utf8");
103259
+ } catch {
103260
+ return null;
103261
+ }
103262
+ }
102155
103263
  function createAgentRuntime(context, transport, env = process.env) {
102156
- const keys2 = [decodeBase64$1(context.syncCredential.secret)];
102157
103264
  const machineBudget = machineSpawnBudget({ cpuCount: cpus().length, totalMemoryBytes: totalmem() });
102158
103265
  const homeDir = context.homeDir, lineageRoot = readAgentEnv(env, machineBudget)?.scope ?? context.homeDir;
102159
103266
  const credentialHome = context.credential.source === "saycode" ? null : dirname(context.credential.path);
102160
103267
  const now = () => Date.now();
103268
+ const { assertLegacyWrite, ...reader } = createAgentSessionReader(context, transport, { homeDir, env, credentialHome }, readFileOrNull);
102161
103269
  return {
102162
103270
  env,
102163
103271
  homeDir,
@@ -102167,22 +103275,23 @@ function createAgentRuntime(context, transport, env = process.env) {
102167
103275
  readFile: readFileOrNull,
102168
103276
  modelCatalogReady: () => startCliModelCatalogForContext(context),
102169
103277
  isReadableFile: isReadableFileSync,
102170
- listSessions: async () => normalizeSessionRows(await context.rest.listSessions(), keys2),
102171
- readMessages: async (sessionId, limit) => (await recentContents(context, transport, sessionId, limit)).map((entry) => entry.content),
102172
- readSequencedMessages: (sessionId, limit) => recentContents(context, transport, sessionId, limit),
102173
- openStream: (sessionId) => openStream(context, transport, sessionId),
102174
- sendPrompt: async (sessionId, text2) => createCliSessionConnection({
102175
- rest: context.rest,
102176
- transport,
102177
- credential: context.syncCredential,
102178
- sessionId,
102179
- // No model/effort override: an agent prompting a sibling must not silently
102180
- // change that session's configuration.
102181
- settings: createLiveChatSettings({})
102182
- }).prompt({ text: text2 }),
103278
+ ...reader,
103279
+ sendPrompt: async (sessionId, text2) => {
103280
+ assertLegacyWrite(sessionId);
103281
+ return createCliSessionConnection({
103282
+ rest: context.rest,
103283
+ transport,
103284
+ credential: context.syncCredential,
103285
+ sessionId,
103286
+ // No model/effort override: an agent prompting a sibling must not silently
103287
+ // change that session's configuration.
103288
+ settings: createLiveChatSettings({})
103289
+ }).prompt({ text: text2 });
103290
+ },
102183
103291
  spawnSession: async (input) => {
102184
103292
  const machineId = readMachineId({ homeDir, env, credentialHome }, readFileOrNull);
102185
103293
  if (!machineId) throw new CliError("Could not determine this machine, so no session can be spawned here.");
103294
+ const machineKey = readLocalMachineRpcKey({ homeDir, env, credentialHome }, machineId, readFileOrNull);
102186
103295
  const result2 = await transport.rpc(
102187
103296
  machineId,
102188
103297
  "spawn-happy-session",
@@ -102197,7 +103306,8 @@ function createAgentRuntime(context, transport, env = process.env) {
102197
103306
  ...inheritedSandboxPolicyEnv(env.HAPPY_PROJECT_SANDBOX_CONFIG)
102198
103307
  }
102199
103308
  },
102200
- SPAWN_RPC_TIMEOUT_MS
103309
+ SPAWN_RPC_TIMEOUT_MS,
103310
+ ...machineKey ? [machineKey] : []
102201
103311
  );
102202
103312
  if (!result2?.sessionId) throw new CliError("The machine daemon returned no session id for spawn-happy-session.");
102203
103313
  return { sessionId: result2.sessionId };
@@ -102206,8 +103316,22 @@ function createAgentRuntime(context, transport, env = process.env) {
102206
103316
  countSpawns: (agentKey, maxSpawn) => spawnBudgetUsage(homeDir, lineageRoot, agentKey, now(), maxSpawn),
102207
103317
  recordSpawn: (record2) => appendSpawnRecord(lineageRoot, record2),
102208
103318
  readLineage: () => readSpawnRecords(homeDir, lineageRoot),
102209
- steerSession: (sessionId, text2) => steerAgentSession(transport, sessionId, text2),
102210
- stopSession: (machineId, sessionId) => stopAgentSession(transport, machineId, sessionId),
103319
+ steerSession: async (sessionId, text2) => {
103320
+ try {
103321
+ assertLegacyWrite(sessionId);
103322
+ } catch (error) {
103323
+ return { accepted: false, reason: "rejected", error: String(error) };
103324
+ }
103325
+ return steerAgentSession(transport, sessionId, text2);
103326
+ },
103327
+ stopSession: async (machineId, sessionId) => {
103328
+ try {
103329
+ assertLegacyWrite(sessionId);
103330
+ } catch (error) {
103331
+ return { stopped: false, reason: "rejected", error: String(error) };
103332
+ }
103333
+ return stopAgentSession(transport, machineId, sessionId);
103334
+ },
102211
103335
  // Omitted entirely without API access: a Happy `access.key` is sync-only, so
102212
103336
  // there is no project list to match against and nothing to register with.
102213
103337
  // `--worktree` is gated on the same access for the same reason, and its absence is what