@openchambery/web 2.0.0-beta.3 → 2.0.0-beta.30
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.
- package/README.md +14 -0
- package/dist/assets/{AgentAvatar-B_JQoreg.js → AgentAvatar-O2x_1cFU.js} +1 -1
- package/dist/assets/AssistantView-pBywAsqy.js +3 -0
- package/dist/assets/{DiagramView-BNnda8qO.js → DiagramView-B984aE-R.js} +1 -1
- package/dist/assets/{FadeInOnReveal-VYQTSAkr.js → FadeInOnReveal-CMBd5BTd.js} +1 -1
- package/dist/assets/Icon-BN87CyS4.js +1 -0
- package/dist/assets/MarkdownRenderer-DHiNU8n3.js +2 -0
- package/dist/assets/{MarkdownRendererImpl-DwrHFGUp.js → MarkdownRendererImpl-xJHMGHju.js} +3 -3
- package/dist/assets/MarkstreamRendererImpl-BZflns6x.js +3 -0
- package/dist/assets/{MobileDetailNavigation-BXr6AGLT.js → MobileDetailNavigation-DUvxsvrB.js} +1 -1
- package/dist/assets/{MobileShareBridge-D7OE9OP_.js → MobileShareBridge-DWo-J3Vc.js} +3 -3
- package/dist/assets/MobileSurface-B-Y6U5xm.js +2 -0
- package/dist/assets/{MultiRunWindow-DF3iIFCd.js → MultiRunWindow-rYiVSNKK.js} +1 -1
- package/dist/assets/{OnboardingScreen-BjHmlwlw.js → OnboardingScreen-BLjZIBOs.js} +1 -1
- package/dist/assets/{OverlayScrollbar-DLtWO_Gp.js → OverlayScrollbar-CoZ4wtau.js} +1 -1
- package/dist/assets/SettingsView-pjD0LrHE.js +25 -0
- package/dist/assets/{SettingsWindow-BI5swspb.js → SettingsWindow-CFOQRwjA.js} +1 -1
- package/dist/assets/TerminalView-Dw5jaYkc.js +8 -0
- package/dist/assets/{ToolOutputDialog-BEWhBcRd.js → ToolOutputDialog-B4v-ona2.js} +1 -1
- package/dist/assets/appThemeRegistry-BqDCGocy.js +1 -0
- package/dist/assets/{assistant-attachment-cache-C0aRnFkj.js → assistant-attachment-cache-sG8YQsVs.js} +1 -1
- package/dist/assets/{button-DKuz3L8l.js → button-Ck8ipzBm.js} +1 -1
- package/dist/assets/collapsible-nU99prkD.js +1 -0
- package/dist/assets/context-menu-DMzZN1oi.js +1 -0
- package/dist/assets/{desktopHosts-CoDkOnyS.js → desktopHosts-DuftEj8B.js} +1 -1
- package/dist/assets/desktopRelayRestore-g378BdZf.js +1 -0
- package/dist/assets/{diagram-D7T8HQgj.js → diagram-CvO4P8ds.js} +1 -1
- package/dist/assets/dialog-ByGUkS3c.js +1 -0
- package/dist/assets/dropdown-menu.styles-ayLWk2gf.js +1 -0
- package/dist/assets/es-DCjl24dV.js +15 -0
- package/dist/assets/fileMentionTouchSelection-Cgkxwzqb.js +2 -0
- package/dist/assets/{fileQueries-JrpcsOCs.js → fileQueries-DuF89nFO.js} +1 -1
- package/dist/assets/fr-DfJ5j0t5.js +13 -0
- package/dist/assets/{gitApi-CM42qUed.js → gitApi-Dwqkc2nZ.js} +3 -3
- package/dist/assets/{imageSource-CIl0FL10.js → imageSource-BpUsiwFd.js} +1 -1
- package/dist/assets/{input-CzQy8UhV.js → input-CXhFmHcq.js} +1 -1
- package/dist/assets/insertionBoundaries-BhWfAOPY.js +1 -0
- package/dist/assets/ja-B_9_w2Xf.js +15 -0
- package/dist/assets/ko-DlGbo3kV.js +15 -0
- package/dist/assets/main-CGMinhgQ.js +169 -0
- package/dist/assets/main-pMpcLJha.js +2 -0
- package/dist/assets/{markdownHeightCache-Cj1I5NDw.js → markdownHeightCache-g_UsWzbA.js} +1 -1
- package/dist/assets/markdownTheme-DM5h1i18.js +1 -0
- package/dist/assets/{mcpQueries-D5mQymgM.js → mcpQueries-DfrW3VHF.js} +1 -1
- package/dist/assets/miniChat-YOt30ECc.js +2 -0
- package/dist/assets/mobile-BK1bBCYD.js +2 -0
- package/dist/assets/multirun-Dua24A69.js +3 -0
- package/dist/assets/persistence-Dnj1QOdX.js +3 -0
- package/dist/assets/pl-DjUsS8Ei.js +15 -0
- package/dist/assets/platform-X3Y3GyM5.js +6 -0
- package/dist/assets/projectMeta-DXdoPmbz.js +1 -0
- package/dist/assets/pt-BR-B8sV7qo1.js +15 -0
- package/dist/assets/{relay-image-stream-DM7atqJs.js → relay-image-stream-D2XooXKZ.js} +1 -1
- package/dist/assets/renderElectronMiniChatApp-BOuEz_oT.js +2 -0
- package/dist/assets/renderMobileApp-C8Alpv-B.js +28 -0
- package/dist/assets/runtime-fetch-CiqQd__Y.js +1 -0
- package/dist/assets/runtime-switch-BJM2k_uH.js +1 -0
- package/dist/assets/runtimeConfig-DX_NP2Zm.js +2 -0
- package/dist/assets/runtimeEndpointReset-BSQU3e5U.js +148 -0
- package/dist/assets/runtimeEndpointReset-DsuP5FPT.css +1 -0
- package/dist/assets/sessionChangeSummary-8AIBkExf.js +2 -0
- package/dist/assets/{sessionLookup-myHe5ltu.js → sessionLookup-Bdwd1Fit.js} +1 -1
- package/dist/assets/{sortable-tabs-strip-BQtmHx9r.js → sortable-tabs-strip-T-dk_L69.js} +1 -1
- package/dist/assets/src-OzbnJsph.css +2 -0
- package/dist/assets/store-Cuktref-.js +15 -0
- package/dist/assets/{terminalApi-BxcdiduX.js → terminalApi-C_4joKUG.js} +1 -1
- package/dist/assets/textarea-biAqiqnJ.js +1 -0
- package/dist/assets/{tooltip-Cx5gTbdY.js → tooltip-BKL7J5hx.js} +1 -1
- package/dist/assets/uk-Bh7HXyLp.js +15 -0
- package/dist/assets/{url-CDHnjlsm.js → url-DNRMq_0R.js} +1 -1
- package/dist/assets/{useAppFontEffects-CNeo0aZs.js → useAppFontEffects-bafOEpgK.js} +57 -52
- package/dist/assets/{useAssistantUIStore-asNG6K7n.js → useAssistantUIStore-DBwfg9nX.js} +1 -1
- package/dist/assets/useDesktopWindowControlsLayout-CkCVOkxa.js +1 -0
- package/dist/assets/{useEffectiveDirectory-B4LvR8Gt.js → useEffectiveDirectory-B7htpJfU.js} +1 -1
- package/dist/assets/{useFontPreferences-CUnQ4TVm.js → useFontPreferences-CMrbTA0r.js} +1 -1
- package/dist/assets/useMobileNavigationStore-D2tnhKYV.js +415 -0
- package/dist/assets/{useRuntimeAPIs-DwLtO0c_.js → useRuntimeAPIs-DSLXJybW.js} +1 -1
- package/dist/assets/useSessionAutoCleanup-BC5OOH3M.js +2 -0
- package/dist/assets/{useThemeSystem-BlNUWH47.js → useThemeSystem-DMzWVJaW.js} +1 -1
- package/dist/assets/useUIStore-Dfx6TKvi.js +1 -0
- package/dist/assets/{useWorkerHighlightedLines-CJZbJDW3.js → useWorkerHighlightedLines-BNQUAMEB.js} +1 -1
- package/dist/assets/{vendor-base-ui-CDjyofeD.js → vendor-base-ui-CnHfUmDN.js} +1 -1
- package/dist/assets/{vendor-cmdk-BFH0UpXX.js → vendor-cmdk-CeAGfrLo.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-accessibility-BrR3AKvM.js → vendor-dnd-kit-accessibility-BY5LhhUB.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-core-CNfdvk6H.js → vendor-dnd-kit-core-CvWxUPdb.js} +1 -1
- package/dist/assets/{vendor-dnd-kit-sortable-DQUaTeGj.js → vendor-dnd-kit-sortable-hHdP9lLb.js} +1 -1
- package/dist/assets/{vendor-framer-motion-4dogsUk3.js → vendor-framer-motion-DFy_BSVd.js} +1 -1
- package/dist/assets/{vendor-legendapp-list-BU7MdPZR.js → vendor-legendapp-list-WE32lwNk.js} +1 -1
- package/dist/assets/{vendor-markstream-react-qHC2FykU.js → vendor-markstream-react-DX55XLGO.js} +3 -3
- package/dist/assets/vendor-opencode-client-MF2GBrFx.js +12 -0
- package/dist/assets/{vendor-pierre-diffs-CUfqC003.js → vendor-pierre-diffs-DX7XTPG3.js} +1 -1
- package/dist/assets/{vendor-react-C94A0AHl.js → vendor-react-DLPElpoQ.js} +1 -1
- package/dist/assets/{vendor-react-drawio-FVlWqKVE.js → vendor-react-drawio-ECtEwvbc.js} +1 -1
- package/dist/assets/{vendor-reactuses-core-C2A3EjUi.js → vendor-reactuses-core-Dq5ocTF8.js} +1 -1
- package/dist/assets/{vendor-sonner-DNqHmUQs.js → vendor-sonner-DVzioF3d.js} +1 -1
- package/dist/assets/{vendor-stream-diffs-CwbKsmlv.js → vendor-stream-diffs-DWy3xSmp.js} +2 -2
- package/dist/assets/{vendor-tanstack-react-query-D0iFK57C.js → vendor-tanstack-react-query-D7pys_c9.js} +1 -1
- package/dist/assets/{vendor-tanstack-react-virtual-B2kV6cnT.js → vendor-tanstack-react-virtual-tlDgH6PG.js} +1 -1
- package/dist/assets/{vendor-zustand-BqLKf6ZY.js → vendor-zustand-DzRqcymP.js} +2 -2
- package/dist/assets/zh-CN-Uzia0gk4.js +15 -0
- package/dist/assets/zh-TW-CiI7DLdB.js +15 -0
- package/dist/index.html +15 -15
- package/dist/mini-chat.html +15 -15
- package/dist/mobile.html +15 -15
- package/package.json +3 -2
- package/server/index.js +46 -27
- package/server/lib/browser-provider/DOCUMENTATION.md +141 -0
- package/server/lib/browser-provider/contract.js +312 -0
- package/server/lib/browser-provider/host.js +421 -0
- package/server/lib/browser-provider/host.test.js +304 -0
- package/server/lib/browser-provider/routes.js +122 -0
- package/server/lib/browser-provider/routes.test.js +120 -0
- package/server/lib/browser-provider/selection-store.js +43 -0
- package/server/lib/browser-provider/selection-store.test.js +35 -0
- package/server/lib/event-stream/DOCUMENTATION.md +3 -1
- package/server/lib/event-stream/reasoning-projection.js +16 -1
- package/server/lib/event-stream/reasoning-projection.test.js +15 -0
- package/server/lib/git/DOCUMENTATION.md +1 -0
- package/server/lib/git/routes.js +7 -4
- package/server/lib/git/routes.test.js +34 -0
- package/server/lib/git/service.js +3 -1
- package/server/lib/llm/DOCUMENTATION.md +6 -4
- package/server/lib/llm/generate.js +8 -9
- package/server/lib/llm/generate.test.js +33 -6
- package/server/lib/message-queue/service.js +1 -1
- package/server/lib/notifications/DOCUMENTATION.md +3 -1
- package/server/lib/notifications/runtime.js +128 -31
- package/server/lib/notifications/runtime.test.js +103 -0
- package/server/lib/notifications/template-runtime.js +130 -38
- package/server/lib/notifications/template-runtime.test.js +96 -0
- package/server/lib/opencode/DOCUMENTATION.md +36 -19
- package/server/lib/opencode/agents.js +13 -22
- package/server/lib/opencode/auth-state-runtime.js +3 -3
- package/server/lib/opencode/auth-state-runtime.test.js +59 -0
- package/server/lib/opencode/auth.d.ts +13 -0
- package/server/lib/opencode/auth.js +74 -64
- package/server/lib/opencode/auth.test.js +57 -0
- package/server/lib/opencode/commands.js +1 -1
- package/server/lib/opencode/config-entities-layering.test.js +72 -0
- package/server/lib/opencode/config-entity-routes.js +16 -124
- package/server/lib/opencode/config-entity-routes.test.js +48 -11
- package/server/lib/opencode/config-files.d.ts +17 -0
- package/server/lib/opencode/config-files.js +187 -0
- package/server/lib/opencode/config-files.test.js +201 -0
- package/server/lib/opencode/core-routes.js +0 -1
- package/server/lib/opencode/core-routes.test.js +0 -15
- package/server/lib/opencode/credential-boundary.d.ts +1 -0
- package/server/lib/opencode/credential-boundary.js +11 -0
- package/server/lib/opencode/ensure-cli.d.ts +8 -0
- package/server/lib/opencode/ensure-cli.js +4 -1
- package/server/lib/opencode/env-config.js +4 -1
- package/server/lib/opencode/env-config.test.js +28 -0
- package/server/lib/opencode/env-runtime.test.js +18 -1
- package/server/lib/opencode/feature-routes-runtime.js +19 -7
- package/server/lib/opencode/feature-routes-runtime.test.js +7 -0
- package/server/lib/opencode/hmr-state-runtime.js +4 -4
- package/server/lib/opencode/lifecycle.js +1 -49
- package/server/lib/opencode/lifecycle.test.js +30 -4
- package/server/lib/opencode/location-release.d.ts +8 -0
- package/server/lib/opencode/location-release.js +32 -0
- package/server/lib/opencode/location-release.test.js +43 -0
- package/server/lib/opencode/mcp.js +1 -1
- package/server/lib/opencode/opencode2-pin.js +2 -1
- package/server/lib/opencode/owned-runtime-upgrade.test.js +5 -5
- package/server/lib/opencode/plugin-routes.test.js +4 -2
- package/server/lib/opencode/plugins.js +12 -60
- package/server/lib/opencode/plugins.test.js +5 -3
- package/server/lib/opencode/providers.js +6 -3
- package/server/lib/opencode/proxy.js +13 -1
- package/server/lib/opencode/proxy.test.js +31 -3
- package/server/lib/opencode/remote-upgrade.integration.test.js +66 -0
- package/server/lib/opencode/routes.js +40 -8
- package/server/lib/opencode/routes.test.js +51 -10
- package/server/lib/opencode/runtime-contract.js +3 -2
- package/server/lib/opencode/runtime-contract.test.js +12 -12
- package/server/lib/opencode/shared.js +6 -175
- package/server/lib/opencode/update-discovery.js +39 -0
- package/server/lib/opencode/update-discovery.test.js +39 -0
- package/server/lib/opencode/upgrade-screen.d.ts +30 -0
- package/server/lib/opencode/upgrade-screen.js +292 -0
- package/server/lib/opencode/upgrade-screen.test.js +196 -0
- package/server/lib/opencode/version-alignment.test.js +40 -0
- package/server/lib/question-auto-delegate/DOCUMENTATION.md +21 -6
- package/server/lib/question-auto-delegate/core.d.ts +5 -1
- package/server/lib/question-auto-delegate/core.js +21 -13
- package/server/lib/question-auto-delegate/form-io.d.ts +6 -0
- package/server/lib/question-auto-delegate/form-io.js +39 -0
- package/server/lib/question-auto-delegate/runtime.js +4 -24
- package/server/lib/question-auto-delegate/runtime.test.js +138 -0
- package/server/lib/quota/DOCUMENTATION.md +9 -0
- package/server/lib/quota/provider-credentials.test.js +29 -0
- package/server/lib/quota/providers/claude.js +4 -4
- package/server/lib/quota/providers/codex.js +4 -4
- package/server/lib/quota/providers/codex.test.js +1 -1
- package/server/lib/quota/providers/copilot.js +5 -5
- package/server/lib/quota/providers/google/auth.js +3 -3
- package/server/lib/quota/providers/google/index.js +2 -2
- package/server/lib/quota/providers/index.js +3 -7
- package/server/lib/quota/providers/index.test.js +2 -2
- package/server/lib/quota/providers/kimi.js +4 -4
- package/server/lib/quota/providers/minimax-shared.js +4 -4
- package/server/lib/quota/providers/nanogpt.js +4 -4
- package/server/lib/quota/providers/openai.js +4 -4
- package/server/lib/quota/providers/openrouter.js +4 -4
- package/server/lib/quota/providers/wafer.js +4 -4
- package/server/lib/quota/providers/zai.js +4 -4
- package/server/lib/quota/providers/zhipuai-coding-plan.js +6 -20
- package/server/lib/quota/routes.js +1 -1
- package/server/lib/relay/DOCUMENTATION.md +3 -1
- package/server/lib/relay/service.js +1 -0
- package/server/lib/relay/service.test.js +13 -0
- package/server/lib/scheduled-tasks/DOCUMENTATION.md +8 -1
- package/server/lib/scheduled-tasks/runtime.js +12 -4
- package/server/lib/scheduled-tasks/runtime.test.js +81 -6
- package/server/lib/session-goal/DOCUMENTATION.md +11 -0
- package/server/lib/session-goal/runtime.js +4 -4
- package/server/lib/session-goal/runtime.test.js +32 -3
- package/server/lib/session-index/DOCUMENTATION.md +7 -0
- package/server/lib/session-index/event-ingest.js +13 -0
- package/server/lib/session-index/event-ingest.test.js +7 -0
- package/server/lib/session-index/service.js +45 -0
- package/server/lib/session-index/service.test.js +47 -0
- package/server/lib/session-metadata/DOCUMENTATION.md +33 -17
- package/server/lib/session-metadata/opencode-session-record.d.ts +57 -0
- package/server/lib/session-metadata/opencode-session-record.js +316 -0
- package/server/lib/session-metadata/opencode-session-record.test.js +242 -0
- package/server/lib/session-metadata/routes.js +5 -1
- package/server/lib/session-metadata/session-archive.js +1 -1
- package/server/lib/session-metadata/session-metadata-store.d.ts +2 -0
- package/server/lib/session-metadata/session-metadata-store.js +63 -16
- package/server/lib/session-metadata/session-metadata-store.test.js +23 -0
- package/server/lib/session-metadata/session-projection.d.ts +1 -0
- package/server/lib/session-metadata/session-projection.js +49 -2
- package/server/lib/session-metadata/session-projection.test.js +35 -0
- package/server/lib/session-title/DOCUMENTATION.md +52 -14
- package/server/lib/session-title/native-runtime.test.js +59 -5
- package/server/lib/session-title/runtime.js +65 -19
- package/server/lib/session-title/session-access.js +108 -3
- package/server/lib/session-title/session-access.test.js +125 -7
- package/server/lib/tts/DOCUMENTATION.md +7 -5
- package/server/lib/tts/credential-source.test.js +23 -0
- package/server/lib/tts/routes.js +2 -2
- package/server/lib/tts/service.js +16 -37
- package/dist/assets/AssistantView-ClQAfGOT.js +0 -3
- package/dist/assets/Icon-BiEQEcih.js +0 -1
- package/dist/assets/MarkdownRenderer-CJHSsWin.js +0 -2
- package/dist/assets/MarkstreamRendererImpl-CSnbQMu3.js +0 -3
- package/dist/assets/MobileSurface-D2jUkX9l.js +0 -2
- package/dist/assets/SettingsView-BRxELjow.js +0 -25
- package/dist/assets/TerminalView-BU3x8-fr.js +0 -8
- package/dist/assets/appThemeRegistry-4MR0F7Jz.js +0 -1
- package/dist/assets/collapsible-DXbDynN4.js +0 -3
- package/dist/assets/context-menu-CqolsLCm.js +0 -1
- package/dist/assets/desktopRelayRestore-DDlVKyOE.js +0 -1
- package/dist/assets/dialog-DQYl3W_o.js +0 -1
- package/dist/assets/dropdown-menu.styles-Bzo6L3FH.js +0 -1
- package/dist/assets/es-CN3E_zXj.js +0 -15
- package/dist/assets/fileMentionTouchSelection-BZ6ovBYH.js +0 -4
- package/dist/assets/fr-9zZvQZD2.js +0 -13
- package/dist/assets/insertionBoundaries-CtkZ9720.js +0 -1
- package/dist/assets/ja-Dnjk_82N.js +0 -15
- package/dist/assets/ko-BxCEE99C.js +0 -15
- package/dist/assets/main-BILgBI3B.js +0 -169
- package/dist/assets/main-BeveiW6F.js +0 -2
- package/dist/assets/markdownTheme-CKac3wKC.js +0 -1
- package/dist/assets/miniChat-BgrzM3u7.js +0 -2
- package/dist/assets/mobile-DtYoQE8C.js +0 -2
- package/dist/assets/multirun-jy8dEI96.js +0 -4
- package/dist/assets/persistence-C9LQpiOS.js +0 -3
- package/dist/assets/pl-CNNsWnPM.js +0 -15
- package/dist/assets/platform-BQghHRfp.js +0 -6
- package/dist/assets/pt-BR-BZB2DJMp.js +0 -15
- package/dist/assets/renderElectronMiniChatApp-BGrrmeME.js +0 -2
- package/dist/assets/renderMobileApp-u3c4GtFC.js +0 -28
- package/dist/assets/runtime-fetch-zIalUnbb.js +0 -1
- package/dist/assets/runtime-switch-LvWDU-SL.js +0 -1
- package/dist/assets/runtimeConfig-BIGBRGQI.js +0 -2
- package/dist/assets/runtimeEndpointReset-D2savkuw.js +0 -147
- package/dist/assets/sessionChangeSummary-eIbpSfrO.js +0 -2
- package/dist/assets/src-CLN7Z-zx.css +0 -2
- package/dist/assets/store-DLCiZaA6.js +0 -15
- package/dist/assets/textarea-CakKvqFw.js +0 -1
- package/dist/assets/uk-rwQrxeYM.js +0 -15
- package/dist/assets/useDesktopWindowControlsLayout-DBJ56qIo.js +0 -1
- package/dist/assets/useMobileNavigationStore-ZHJPxlHt.js +0 -414
- package/dist/assets/useSessionAutoCleanup-BIVmrnYU.js +0 -2
- package/dist/assets/useUIStore-CQYKJnrg.js +0 -1
- package/dist/assets/vendor-opencode-client-BOe2qpQb.js +0 -12
- package/dist/assets/zh-CN-BvmBDnkD.js +0 -15
- package/dist/assets/zh-TW-9wcHTX0y.js +0 -15
- package/server/lib/opencode/snippets.js +0 -242
- package/server/lib/opencode/snippets.test.js +0 -97
|
@@ -457,6 +457,17 @@ describe('contact turn notifications', () => {
|
|
|
457
457
|
});
|
|
458
458
|
|
|
459
459
|
describe('notification trigger live activity end', () => {
|
|
460
|
+
it('treats session.execution.succeeded as completion, including a v2 data envelope', async () => {
|
|
461
|
+
vi.stubGlobal('fetch', vi.fn(async () => rootSessionResponse('ses_done')));
|
|
462
|
+
const { runtime, sendLiveActivityEnd, emitDesktopNotification } = createRuntime();
|
|
463
|
+
await runtime.maybeSendPushForTrigger({
|
|
464
|
+
type: 'session.execution.succeeded',
|
|
465
|
+
data: { sessionID: 'ses_done' },
|
|
466
|
+
});
|
|
467
|
+
expect(sendLiveActivityEnd).toHaveBeenCalledWith({ sessionId: 'ses_done', status: 'complete' });
|
|
468
|
+
expect(emitDesktopNotification).toHaveBeenCalled();
|
|
469
|
+
});
|
|
470
|
+
|
|
460
471
|
it('ends the live activity on top-level completion and error', async () => {
|
|
461
472
|
vi.stubGlobal('fetch', vi.fn(async () => rootSessionResponse()));
|
|
462
473
|
const { runtime, sendLiveActivityEnd } = createRuntime();
|
|
@@ -615,6 +626,97 @@ describe('notification event toggles', () => {
|
|
|
615
626
|
expect(sendPushToAllUiSessions).not.toHaveBeenCalled();
|
|
616
627
|
});
|
|
617
628
|
|
|
629
|
+
it('notifies on an OpenCode 2 question form and a permission request', async () => {
|
|
630
|
+
vi.useFakeTimers();
|
|
631
|
+
vi.stubGlobal('fetch', vi.fn(async () => jsonResponse({
|
|
632
|
+
data: { id: 'ses_ask', parentID: null, title: '发布说明', metadata: {} },
|
|
633
|
+
})));
|
|
634
|
+
const resolveNotificationTemplate = vi.fn((template, variables) => (
|
|
635
|
+
typeof template === 'string'
|
|
636
|
+
? template.replace(/\{(\w+)\}/g, (_match, key) => variables?.[key] ?? '')
|
|
637
|
+
: template
|
|
638
|
+
));
|
|
639
|
+
const { runtime, emitDesktopNotification, sendApnsToAllUiSessions } = createRuntime({
|
|
640
|
+
buildTemplateVariables: vi.fn(async () => ({ session_name: '' })),
|
|
641
|
+
resolveNotificationTemplate,
|
|
642
|
+
readSettingsFromDisk: vi.fn(async () => ({
|
|
643
|
+
...defaultSettings,
|
|
644
|
+
notificationTemplates: {
|
|
645
|
+
question: { title: 'Needs your answer', message: '{last_message}' },
|
|
646
|
+
},
|
|
647
|
+
})),
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
await runtime.maybeSendPushForTrigger({
|
|
651
|
+
type: 'form.created',
|
|
652
|
+
data: {
|
|
653
|
+
form: {
|
|
654
|
+
id: 'frm_1',
|
|
655
|
+
sessionID: 'ses_ask',
|
|
656
|
+
title: 'Questions',
|
|
657
|
+
metadata: { kind: 'question' },
|
|
658
|
+
fields: [{ key: 'q0', title: '确认入口', description: '从哪个入口触发?' }],
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
});
|
|
662
|
+
await vi.runAllTimersAsync();
|
|
663
|
+
|
|
664
|
+
expect(emitDesktopNotification).toHaveBeenCalledWith(expect.objectContaining({
|
|
665
|
+
kind: 'question',
|
|
666
|
+
body: '从哪个入口触发?',
|
|
667
|
+
}));
|
|
668
|
+
expect(sendApnsToAllUiSessions).toHaveBeenCalledWith(
|
|
669
|
+
expect.objectContaining({ type: 'question', sessionName: '发布说明' }),
|
|
670
|
+
{ requireNoSse: true },
|
|
671
|
+
);
|
|
672
|
+
|
|
673
|
+
emitDesktopNotification.mockClear();
|
|
674
|
+
sendApnsToAllUiSessions.mockClear();
|
|
675
|
+
await runtime.maybeSendPushForTrigger({
|
|
676
|
+
type: 'permission.asked',
|
|
677
|
+
data: {
|
|
678
|
+
id: 'per_1',
|
|
679
|
+
sessionID: 'ses_ask',
|
|
680
|
+
action: 'edit',
|
|
681
|
+
message: '修改 README',
|
|
682
|
+
resources: ['README.md'],
|
|
683
|
+
},
|
|
684
|
+
});
|
|
685
|
+
await vi.runAllTimersAsync();
|
|
686
|
+
|
|
687
|
+
expect(emitDesktopNotification).toHaveBeenCalledWith(expect.objectContaining({
|
|
688
|
+
kind: 'permission',
|
|
689
|
+
body: '修改 README',
|
|
690
|
+
}));
|
|
691
|
+
expect(sendApnsToAllUiSessions).toHaveBeenCalledWith(
|
|
692
|
+
expect.objectContaining({ type: 'permission', sessionName: '发布说明' }),
|
|
693
|
+
{ requireNoSse: true },
|
|
694
|
+
);
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('uses the wrapped OpenCode 2 session title when the template has none', async () => {
|
|
698
|
+
vi.stubGlobal('fetch', vi.fn(async () => jsonResponse({
|
|
699
|
+
data: {
|
|
700
|
+
id: 'ses_done',
|
|
701
|
+
parentID: null,
|
|
702
|
+
title: '发布说明',
|
|
703
|
+
metadata: {},
|
|
704
|
+
},
|
|
705
|
+
})));
|
|
706
|
+
const { runtime, sendApnsToAllUiSessions } = createRuntime({
|
|
707
|
+
buildTemplateVariables: vi.fn(async () => ({ session_name: '' })),
|
|
708
|
+
});
|
|
709
|
+
await runtime.maybeSendPushForTrigger({
|
|
710
|
+
type: 'session.execution.succeeded',
|
|
711
|
+
location: { directory: '/repo' },
|
|
712
|
+
data: { sessionID: 'ses_done' },
|
|
713
|
+
});
|
|
714
|
+
expect(sendApnsToAllUiSessions).toHaveBeenCalledWith(
|
|
715
|
+
expect.objectContaining({ type: 'ready', sessionName: '发布说明' }),
|
|
716
|
+
{ requireNoSse: true },
|
|
717
|
+
);
|
|
718
|
+
});
|
|
719
|
+
|
|
618
720
|
it('still sends goal settle push by default', async () => {
|
|
619
721
|
vi.stubGlobal('fetch', vi.fn(async () => jsonResponse({ id: 'ses_goal', title: 'Goal run' })));
|
|
620
722
|
const { runtime, sendPushToAllUiSessions } = createRuntime();
|
|
@@ -626,6 +728,7 @@ describe('notification event toggles', () => {
|
|
|
626
728
|
});
|
|
627
729
|
expect(sendPushToAllUiSessions).toHaveBeenCalledTimes(1);
|
|
628
730
|
expect(sendPushToAllUiSessions.mock.calls[0][0]).toMatchObject({ tag: 'goal-ses_goal' });
|
|
731
|
+
expect(sendPushToAllUiSessions.mock.calls[0][0].data).toMatchObject({ sessionName: 'Goal run' });
|
|
629
732
|
});
|
|
630
733
|
});
|
|
631
734
|
|
|
@@ -1,5 +1,75 @@
|
|
|
1
|
+
import { projectSessionMessageRecords } from '../session-turn-pages/session-message-projection.js';
|
|
1
2
|
import { summarizeText as summarizeSharedText } from '../text/summarization.js';
|
|
2
3
|
|
|
4
|
+
const trimmedText = (value) => (typeof value === 'string' && value.trim() ? value.trim() : '');
|
|
5
|
+
|
|
6
|
+
const isSessionRecord = (value) => {
|
|
7
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
8
|
+
return typeof value.id === 'string'
|
|
9
|
+
|| typeof value.sessionID === 'string'
|
|
10
|
+
|| typeof value.title === 'string'
|
|
11
|
+
|| typeof value.parentID === 'string'
|
|
12
|
+
|| (value.metadata && typeof value.metadata === 'object');
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* OpenCode 2 `GET /api/session/:id` returns `{ data: Session }`.
|
|
17
|
+
* Legacy responses and tests still return the session object itself.
|
|
18
|
+
* @param {unknown} payload
|
|
19
|
+
* @returns {object | null}
|
|
20
|
+
*/
|
|
21
|
+
export const readNotificationSessionRecord = (payload) => {
|
|
22
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) return null;
|
|
23
|
+
if (isSessionRecord(payload.data)) return payload.data;
|
|
24
|
+
return isSessionRecord(payload) ? payload : null;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const TITLE_EVENT_TYPES = new Set(['session.created', 'session.updated', 'session.renamed']);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Title carried by a session lifecycle event.
|
|
31
|
+
* v2 puts it on `data.title` (`session.created` / `session.renamed`).
|
|
32
|
+
* Legacy events put it on `properties.info.title`.
|
|
33
|
+
* @param {object | null | undefined} payload
|
|
34
|
+
* @returns {{ sessionId: string, title: string } | null}
|
|
35
|
+
*/
|
|
36
|
+
export const readSessionTitleFromEvent = (payload) => {
|
|
37
|
+
if (!payload || typeof payload !== 'object' || !TITLE_EVENT_TYPES.has(payload.type)) return null;
|
|
38
|
+
const bodies = [payload.properties, payload.data].filter((body) => body && typeof body === 'object' && !Array.isArray(body));
|
|
39
|
+
for (const body of bodies) {
|
|
40
|
+
const info = body.info && typeof body.info === 'object' ? body.info : null;
|
|
41
|
+
const title = trimmedText(info?.title) || trimmedText(body.title);
|
|
42
|
+
const sessionId = trimmedText(info?.id)
|
|
43
|
+
|| trimmedText(info?.sessionID)
|
|
44
|
+
|| trimmedText(body.sessionID)
|
|
45
|
+
|| trimmedText(body.id);
|
|
46
|
+
if (title && sessionId) return { sessionId, title };
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const readNotificationDirectory = (payload) => {
|
|
52
|
+
const properties = payload?.properties && typeof payload.properties === 'object' ? payload.properties : {};
|
|
53
|
+
const data = payload?.data && typeof payload.data === 'object' && !Array.isArray(payload.data) ? payload.data : {};
|
|
54
|
+
const info = properties.info && typeof properties.info === 'object' ? properties.info : {};
|
|
55
|
+
const dataInfo = data.info && typeof data.info === 'object' ? data.info : {};
|
|
56
|
+
const candidates = [
|
|
57
|
+
properties.directory,
|
|
58
|
+
info.directory,
|
|
59
|
+
info.location?.directory,
|
|
60
|
+
payload?.location?.directory,
|
|
61
|
+
data.directory,
|
|
62
|
+
data.location?.directory,
|
|
63
|
+
dataInfo.directory,
|
|
64
|
+
dataInfo.location?.directory,
|
|
65
|
+
];
|
|
66
|
+
for (const value of candidates) {
|
|
67
|
+
const directory = trimmedText(value);
|
|
68
|
+
if (directory) return directory;
|
|
69
|
+
}
|
|
70
|
+
return '';
|
|
71
|
+
};
|
|
72
|
+
|
|
3
73
|
export const createNotificationTemplateRuntime = (deps) => {
|
|
4
74
|
const {
|
|
5
75
|
readSettingsFromDisk,
|
|
@@ -133,37 +203,45 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
133
203
|
return '';
|
|
134
204
|
};
|
|
135
205
|
|
|
136
|
-
const fetchLastAssistantMessageText = async (sessionId, messageId, maxLength = NOTIFICATION_BODY_MAX_CHARS) => {
|
|
206
|
+
const fetchLastAssistantMessageText = async (sessionId, messageId, maxLength = NOTIFICATION_BODY_MAX_CHARS, directory) => {
|
|
137
207
|
if (!sessionId) return '';
|
|
138
208
|
|
|
139
209
|
try {
|
|
140
|
-
const
|
|
141
|
-
const
|
|
210
|
+
const base = buildOpenCodeUrl(`/session/${encodeURIComponent(sessionId)}/message`, '');
|
|
211
|
+
const params = new URLSearchParams({ limit: '5', order: 'desc' });
|
|
212
|
+
const scopedDirectory = trimmedText(directory);
|
|
213
|
+
if (scopedDirectory) params.set('directory', scopedDirectory);
|
|
214
|
+
const response = await fetch(`${base}?${params.toString()}`, {
|
|
142
215
|
method: 'GET',
|
|
143
216
|
headers: {
|
|
144
217
|
Accept: 'application/json',
|
|
145
|
-
...getOpenCodeAuthHeaders(),
|
|
218
|
+
...(typeof getOpenCodeAuthHeaders === 'function' ? getOpenCodeAuthHeaders() : {}),
|
|
146
219
|
},
|
|
147
220
|
signal: AbortSignal.timeout(3000),
|
|
148
221
|
});
|
|
149
222
|
|
|
150
223
|
if (!response.ok) return '';
|
|
151
224
|
|
|
152
|
-
const
|
|
153
|
-
|
|
225
|
+
const payload = await response.json().catch(() => null);
|
|
226
|
+
// Legacy lists are a chronological array. OpenCode 2 returns `{ data }`
|
|
227
|
+
// and `order=desc` puts the newest turn first.
|
|
228
|
+
const newestFirst = !Array.isArray(payload);
|
|
229
|
+
const records = Array.isArray(payload)
|
|
230
|
+
? payload
|
|
231
|
+
: (Array.isArray(payload?.data) ? payload.data : null);
|
|
232
|
+
if (!records) return '';
|
|
233
|
+
const projected = projectSessionMessageRecords(records);
|
|
234
|
+
const messages = newestFirst ? projected : [...projected].reverse();
|
|
154
235
|
|
|
155
236
|
let target = null;
|
|
156
237
|
if (messageId) {
|
|
157
238
|
target = messages.find((message) => message?.info?.id === messageId && message?.info?.role === 'assistant');
|
|
158
239
|
}
|
|
159
240
|
if (!target) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
break;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
241
|
+
target = messages.find((message) => (
|
|
242
|
+
message?.info?.role === 'assistant'
|
|
243
|
+
&& extractTextFromParts(message.parts, maxLength).length > 0
|
|
244
|
+
));
|
|
167
245
|
}
|
|
168
246
|
|
|
169
247
|
if (!target || !Array.isArray(target.parts)) return '';
|
|
@@ -175,9 +253,9 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
175
253
|
};
|
|
176
254
|
|
|
177
255
|
const cacheSessionTitle = (sessionId, title) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
256
|
+
const id = trimmedText(sessionId);
|
|
257
|
+
const name = trimmedText(title);
|
|
258
|
+
if (id && name) sessionTitleCache.set(id, name);
|
|
181
259
|
};
|
|
182
260
|
|
|
183
261
|
const getCachedSessionTitle = (sessionId) => {
|
|
@@ -185,15 +263,12 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
185
263
|
};
|
|
186
264
|
|
|
187
265
|
const maybeCacheSessionInfoFromEvent = (payload) => {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
const info = payload.properties?.info;
|
|
192
|
-
if (!info || typeof info !== 'object') return;
|
|
193
|
-
cacheSessionTitle(info.id, info.title);
|
|
266
|
+
const titled = readSessionTitleFromEvent(payload);
|
|
267
|
+
if (!titled) return;
|
|
268
|
+
cacheSessionTitle(titled.sessionId, titled.title);
|
|
194
269
|
};
|
|
195
270
|
|
|
196
|
-
const fetchSessionInfo = async (sessionId) => {
|
|
271
|
+
const fetchSessionInfo = async (sessionId, directory) => {
|
|
197
272
|
if (!sessionId) return null;
|
|
198
273
|
|
|
199
274
|
const cached = sessionInfoCache.get(sessionId);
|
|
@@ -202,20 +277,24 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
202
277
|
}
|
|
203
278
|
|
|
204
279
|
try {
|
|
205
|
-
const
|
|
280
|
+
const base = buildOpenCodeUrl(`/session/${encodeURIComponent(sessionId)}`, '');
|
|
281
|
+
const url = directory ? `${base}?directory=${encodeURIComponent(directory)}` : base;
|
|
206
282
|
const response = await fetch(url, {
|
|
207
283
|
method: 'GET',
|
|
208
|
-
headers: {
|
|
284
|
+
headers: {
|
|
285
|
+
Accept: 'application/json',
|
|
286
|
+
...(typeof getOpenCodeAuthHeaders === 'function' ? getOpenCodeAuthHeaders() : {}),
|
|
287
|
+
},
|
|
209
288
|
signal: AbortSignal.timeout(2000),
|
|
210
289
|
});
|
|
211
290
|
if (!response.ok) {
|
|
212
291
|
console.warn(`[Notification] fetchSessionInfo: ${response.status} for session ${sessionId}`);
|
|
213
292
|
return null;
|
|
214
293
|
}
|
|
215
|
-
const
|
|
216
|
-
if (
|
|
217
|
-
sessionInfoCache.set(sessionId, { data, at: Date.now() });
|
|
218
|
-
return
|
|
294
|
+
const session = readNotificationSessionRecord(await response.json().catch(() => null));
|
|
295
|
+
if (session) {
|
|
296
|
+
sessionInfoCache.set(sessionId, { data: session, at: Date.now() });
|
|
297
|
+
return session;
|
|
219
298
|
}
|
|
220
299
|
return null;
|
|
221
300
|
} catch (error) {
|
|
@@ -227,7 +306,11 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
227
306
|
const buildTemplateVariables = async (payload, sessionId) => {
|
|
228
307
|
const info = payload?.properties?.info || {};
|
|
229
308
|
|
|
230
|
-
|
|
309
|
+
const fromEvent = readSessionTitleFromEvent(payload);
|
|
310
|
+
let sessionTitle = trimmedText(payload?.properties?.sessionTitle)
|
|
311
|
+
|| trimmedText(payload?.properties?.session?.title)
|
|
312
|
+
|| trimmedText(info.sessionTitle)
|
|
313
|
+
|| (fromEvent && (!sessionId || fromEvent.sessionId === sessionId) ? fromEvent.title : '');
|
|
231
314
|
|
|
232
315
|
if (!sessionTitle && sessionId) {
|
|
233
316
|
const cached = getCachedSessionTitle(sessionId);
|
|
@@ -238,25 +321,34 @@ export const createNotificationTemplateRuntime = (deps) => {
|
|
|
238
321
|
|
|
239
322
|
let sessionInfo = null;
|
|
240
323
|
if (!sessionTitle && sessionId) {
|
|
241
|
-
sessionInfo = await fetchSessionInfo(sessionId);
|
|
242
|
-
|
|
243
|
-
|
|
324
|
+
sessionInfo = await fetchSessionInfo(sessionId, readNotificationDirectory(payload));
|
|
325
|
+
const fetchedTitle = trimmedText(sessionInfo?.title);
|
|
326
|
+
if (fetchedTitle) {
|
|
327
|
+
sessionTitle = fetchedTitle;
|
|
244
328
|
cacheSessionTitle(sessionId, sessionTitle);
|
|
245
329
|
}
|
|
246
330
|
}
|
|
247
331
|
|
|
332
|
+
const eventAgent = trimmedText(info.agent) || trimmedText(info.mode);
|
|
333
|
+
const eventModel = trimmedText(info.modelID)
|
|
334
|
+
|| trimmedText(info.model?.modelID)
|
|
335
|
+
|| trimmedText(info.model?.id);
|
|
336
|
+
if ((!eventAgent || !eventModel) && sessionId && !sessionInfo) {
|
|
337
|
+
sessionInfo = await fetchSessionInfo(sessionId, readNotificationDirectory(payload));
|
|
338
|
+
}
|
|
339
|
+
|
|
248
340
|
const agentName = (() => {
|
|
249
|
-
const mode =
|
|
250
|
-
? info.agent.trim()
|
|
251
|
-
: (typeof info.mode === 'string' ? info.mode.trim() : '');
|
|
341
|
+
const mode = eventAgent || trimmedText(sessionInfo?.agent) || trimmedText(sessionInfo?.mode);
|
|
252
342
|
if (!mode) return 'Agent';
|
|
253
343
|
return mode.split(/[-_\s]+/).filter(Boolean)
|
|
254
344
|
.map((token) => token.charAt(0).toUpperCase() + token.slice(1)).join(' ');
|
|
255
345
|
})();
|
|
256
346
|
|
|
257
347
|
const modelName = (() => {
|
|
258
|
-
const raw =
|
|
259
|
-
|
|
348
|
+
const raw = eventModel
|
|
349
|
+
|| trimmedText(sessionInfo?.model?.modelID)
|
|
350
|
+
|| trimmedText(sessionInfo?.model?.id)
|
|
351
|
+
|| trimmedText(sessionInfo?.modelID);
|
|
260
352
|
if (!raw) return 'Assistant';
|
|
261
353
|
return raw.split(/[-_]+/).filter(Boolean)
|
|
262
354
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
@@ -82,3 +82,99 @@ describe('notification template message extraction', () => {
|
|
|
82
82
|
await expect(runtime.fetchLastAssistantMessageText('session-1', 'msg-1')).resolves.toBe('final answer');
|
|
83
83
|
});
|
|
84
84
|
});
|
|
85
|
+
|
|
86
|
+
describe('notification session titles', () => {
|
|
87
|
+
afterEach(() => {
|
|
88
|
+
globalThis.fetch = originalFetch;
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('reads the OpenCode 2 session title from the data envelope', async () => {
|
|
92
|
+
const runtime = createNotificationTemplateRuntime({
|
|
93
|
+
readSettingsFromDisk: async () => ({}),
|
|
94
|
+
buildOpenCodeUrl: (path) => `http://opencode.test${path}`,
|
|
95
|
+
getOpenCodeAuthHeaders: () => ({ authorization: 'Basic test' }),
|
|
96
|
+
resolveGitBinaryForSpawn: () => 'git',
|
|
97
|
+
});
|
|
98
|
+
globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({
|
|
99
|
+
data: { id: 'ses_1', title: '发布说明' },
|
|
100
|
+
})));
|
|
101
|
+
|
|
102
|
+
const variables = await runtime.buildTemplateVariables({
|
|
103
|
+
type: 'message.updated',
|
|
104
|
+
location: { directory: '/repo' },
|
|
105
|
+
properties: { info: { sessionID: 'ses_1', role: 'assistant', finish: 'stop' } },
|
|
106
|
+
}, 'ses_1');
|
|
107
|
+
|
|
108
|
+
expect(variables.session_name).toBe('发布说明');
|
|
109
|
+
expect(globalThis.fetch).toHaveBeenCalledWith(
|
|
110
|
+
'http://opencode.test/session/ses_1?directory=%2Frepo',
|
|
111
|
+
expect.objectContaining({
|
|
112
|
+
headers: expect.objectContaining({ authorization: 'Basic test' }),
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('keeps a title learned from session.renamed when the session fetch fails', async () => {
|
|
118
|
+
const runtime = createRuntime();
|
|
119
|
+
globalThis.fetch = vi.fn(async () => new Response(null, { status: 401 }));
|
|
120
|
+
runtime.maybeCacheSessionInfoFromEvent({
|
|
121
|
+
type: 'session.renamed',
|
|
122
|
+
data: { sessionID: 'ses_1', title: '修通知标题' },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const variables = await runtime.buildTemplateVariables({
|
|
126
|
+
type: 'session.execution.succeeded',
|
|
127
|
+
data: { sessionID: 'ses_1' },
|
|
128
|
+
}, 'ses_1');
|
|
129
|
+
|
|
130
|
+
expect(variables.session_name).toBe('修通知标题');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('reads the latest assistant text from an OpenCode 2 message page', async () => {
|
|
134
|
+
const runtime = createRuntime();
|
|
135
|
+
globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({
|
|
136
|
+
data: [
|
|
137
|
+
{
|
|
138
|
+
id: 'msg_new',
|
|
139
|
+
sessionID: 'ses_1',
|
|
140
|
+
type: 'assistant',
|
|
141
|
+
agent: 'build',
|
|
142
|
+
model: { id: 'gpt-5', providerID: 'openai' },
|
|
143
|
+
content: [
|
|
144
|
+
{ type: 'reasoning', text: 'private' },
|
|
145
|
+
{ type: 'text', text: '发布说明已写好' },
|
|
146
|
+
],
|
|
147
|
+
time: { created: 2 },
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'msg_old',
|
|
151
|
+
sessionID: 'ses_1',
|
|
152
|
+
type: 'assistant',
|
|
153
|
+
content: [{ type: 'text', text: 'older' }],
|
|
154
|
+
finish: 'stop',
|
|
155
|
+
time: { created: 1 },
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
})));
|
|
159
|
+
|
|
160
|
+
await expect(runtime.fetchLastAssistantMessageText('ses_1')).resolves.toBe('发布说明已写好');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('fills agent and model names from the OpenCode 2 session record', async () => {
|
|
164
|
+
const runtime = createRuntime();
|
|
165
|
+
globalThis.fetch = vi.fn(async () => new Response(JSON.stringify({
|
|
166
|
+
data: { id: 'ses_1', title: '发布说明', agent: 'build', model: { id: 'gpt-5', providerID: 'openai' } },
|
|
167
|
+
})));
|
|
168
|
+
|
|
169
|
+
const variables = await runtime.buildTemplateVariables({
|
|
170
|
+
type: 'session.execution.succeeded',
|
|
171
|
+
data: { sessionID: 'ses_1' },
|
|
172
|
+
}, 'ses_1');
|
|
173
|
+
|
|
174
|
+
expect(variables).toMatchObject({
|
|
175
|
+
session_name: '发布说明',
|
|
176
|
+
agent_name: 'Build',
|
|
177
|
+
model_name: 'Gpt 5',
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|