@openchambery/web 1.19.7-beta.8 → 1.19.7
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/dist/assets/AgentAvatar-CX0a1rKE.js +1 -0
- package/dist/assets/AssistantView-nVF0kdFh.js +2 -0
- package/dist/assets/DiagramView-B2UtQ8mw.js +1 -0
- package/dist/assets/FadeInOnReveal-CK4asqVR.js +1 -0
- package/dist/assets/Icon-BYM02QyU.js +1 -0
- package/dist/assets/MarkdownLoadingSkeleton-DWs4Jg7b.js +3 -0
- package/dist/assets/MarkdownRenderer-DT_GN1Ex.js +2 -0
- package/dist/assets/MarkdownRendererImpl-Cslh4XN_.js +7 -0
- package/dist/assets/MarkstreamRendererImpl-CsNwy1T5.css +1 -0
- package/dist/assets/MarkstreamRendererImpl-Ymm12x6J.js +1 -0
- package/dist/assets/MobileDetailNavigation-DD29ENgU.js +1 -0
- package/dist/assets/MobileShareBridge-CrTe_QND.js +4 -0
- package/dist/assets/MobileSurface-DjxchpUC.js +2 -0
- package/dist/assets/{MultiRunWindow-D_jo8cqz.js → MultiRunWindow-jG5dCEMH.js} +1 -1
- package/dist/assets/OnboardingScreen-C3-wzeSX.js +1 -0
- package/dist/assets/OverlayScrollbar-KUpvYHdv.js +2 -0
- package/dist/assets/SettingsView-UKlEewdq.js +25 -0
- package/dist/assets/{SettingsWindow-CzMoz2fi.js → SettingsWindow-DO1aAplT.js} +1 -1
- package/dist/assets/TerminalView-BG1UPLsz.js +8 -0
- package/dist/assets/ToolOutputDialog-CWr5eVTK.js +10 -0
- package/dist/assets/appThemeRegistry-BI5drVpC.js +1 -0
- package/dist/assets/{button-z0GOBtWi.js → button-C0cth9tl.js} +1 -1
- package/dist/assets/clipboard-DJ4nQEuY.js +1 -0
- package/dist/assets/{collapsible-D9zJRpeB.js → collapsible-DFT9cE80.js} +1 -1
- package/dist/assets/context-menu-CAp74NfV.js +1 -0
- package/dist/assets/desktopHosts-CWxex1c3.js +1 -0
- package/dist/assets/desktopRelayRestore-9EbBYQ9s.js +1 -0
- package/dist/assets/{diagram-CoEqmReY.js → diagram-BryzMuta.js} +1 -1
- package/dist/assets/{dialog-x2hirWBd.js → dialog-CgbV5ljg.js} +1 -1
- package/dist/assets/dropdown-menu.styles-Bzo6L3FH.js +1 -0
- package/dist/assets/{es-C8Sqjsia.js → es-BHqnSjoZ.js} +1 -1
- package/dist/assets/fileQueries-BY-9OWzp.js +1 -0
- package/dist/assets/fileReferenceActions-BJJtX_S5.js +1 -0
- package/dist/assets/{fr-p-Zwm_j2.js → fr-BfrlJY9i.js} +1 -1
- package/dist/assets/{gitApi-BE0vA4zm.js → gitApi-D9hFlE3T.js} +3 -3
- package/dist/assets/{input-BG3VWTIF.js → input-DFLpqjGR.js} +1 -1
- package/dist/assets/insertionBoundaries-D2L7D0Oe.js +4 -0
- package/dist/assets/{ja-B6REu1qP.js → ja-DP-7qt-x.js} +1 -1
- package/dist/assets/{ko-CJBNH9IZ.js → ko-E2B5vGQP.js} +1 -1
- package/dist/assets/main-BRedOs_V.js +169 -0
- package/dist/assets/main-DlXpJ0Vw.js +2 -0
- package/dist/assets/markdown-worker-BuveaH3O.js +1 -0
- package/dist/assets/markdownTheme-CzZm3riB.js +2 -0
- package/dist/assets/miniChat-CBLbFpfy.js +2 -0
- package/dist/assets/mobile-CoZsWdWk.js +2 -0
- package/dist/assets/multirun-BF-I0r2_.js +4 -0
- package/dist/assets/outsideFileGrants-DnplRxr-.js +1 -0
- package/dist/assets/path-utils-jGozTg9d.js +1 -0
- package/dist/assets/persistence-DdUac14I.js +3 -0
- package/dist/assets/{pl-B2cBI_7K.js → pl-r8X5gRM5.js} +1 -1
- package/dist/assets/platform-B4KKLnZ5.js +2 -0
- package/dist/assets/projectMeta-2t4L5_4a.js +3 -0
- package/dist/assets/{pt-BR-CrCc9hmt.js → pt-BR-DcpdXCf5.js} +1 -1
- package/dist/assets/renderElectronMiniChatApp-fl7O3iHy.js +2 -0
- package/dist/assets/renderMobileApp-CKYDwf4U.js +28 -0
- package/dist/assets/runtimeConfig-B4eb4ZiU.js +2 -0
- package/dist/assets/runtimeEndpointReset-BHuTYyzc.js +147 -0
- package/dist/assets/sessionChangeSummary-DOq8EN_H.js +1 -0
- package/dist/assets/sessionLookup-DNc0ZeR3.js +1 -0
- package/dist/assets/sortable-tabs-strip-C8_9kHqu.js +1 -0
- package/dist/assets/src-DasvfkEg.css +2 -0
- package/dist/assets/{store-CL3WP48T.js → store-BDFdnk6l.js} +2 -2
- package/dist/assets/terminalApi-947l7Ra3.js +1 -0
- package/dist/assets/textarea-Cgo_nWUU.js +1 -0
- package/dist/assets/toolHelpers-DEvVP9U0.js +2 -0
- package/dist/assets/{tooltip-CtH61mQh.js → tooltip-BifLJ8o0.js} +1 -1
- package/dist/assets/{uk-C8YKAz0x.js → uk-Di0vl9V-.js} +1 -1
- package/dist/assets/url-Pk6tQ1EZ.js +1 -0
- package/dist/assets/{ChatContainer-D3onaUkL.js → useAppFontEffects-Dle8xs5h.js} +44 -39
- package/dist/assets/useAssistantUIStore-me6s63b2.js +1 -0
- package/dist/assets/useDesktopWindowControlsLayout-_QAwKcQn.js +1 -0
- package/dist/assets/useEffectiveDirectory-BSyuAgz6.js +1 -0
- package/dist/assets/useFontPreferences-BCOBlaAd.js +1 -0
- package/dist/assets/useMobileNavigationStore-f8xHJk7C.js +413 -0
- package/dist/assets/useSessionAutoCleanup-BHiXaW0J.js +1 -0
- package/dist/assets/useUIStore-CuaA8-Hp.js +1 -0
- package/dist/assets/useWorkerHighlightedLines-DuvXTSm_.js +7 -0
- package/dist/assets/utils-BiK3tBGM.js +1 -0
- package/dist/assets/{vendor-markstream-react-CwvbEg9R.js → vendor-markstream-react-C3K8YH0d.js} +4 -4
- package/dist/assets/{zh-CN-CCYwUnmB.js → zh-CN-coFCfeQE.js} +1 -1
- package/dist/assets/{zh-TW-D8Y6py08.js → zh-TW-oDRZ1-yb.js} +1 -1
- package/dist/index.html +9 -11
- package/dist/mini-chat.html +9 -11
- package/dist/mobile.html +15 -17
- package/package.json +2 -1
- package/server/index.js +2 -0
- package/server/lib/assistant-delivery-parts.js +3 -1
- package/server/lib/assistant-delivery-parts.test.js +11 -0
- package/server/lib/assistants/DOCUMENTATION.md +76 -3
- package/server/lib/assistants/assign.js +225 -0
- package/server/lib/assistants/assign.test.js +118 -0
- package/server/lib/assistants/bubbles.js +45 -0
- package/server/lib/assistants/bubbles.test.js +36 -0
- package/server/lib/assistants/cards.js +148 -0
- package/server/lib/assistants/cards.test.js +116 -0
- package/server/lib/assistants/contact-store.js +284 -0
- package/server/lib/assistants/contact-store.test.js +131 -0
- package/server/lib/assistants/contact-tools.js +761 -0
- package/server/lib/assistants/contact-tools.test.js +328 -0
- package/server/lib/assistants/contracts.js +20 -1
- package/server/lib/assistants/harness.js +551 -0
- package/server/lib/assistants/harness.test.js +607 -0
- package/server/lib/assistants/routes.js +44 -4
- package/server/lib/assistants/service.js +930 -17
- package/server/lib/assistants/service.test.js +1163 -45
- package/server/lib/event-stream/DOCUMENTATION.md +12 -1
- package/server/lib/event-stream/directory-ws-bridge.js +11 -2
- package/server/lib/event-stream/global-ws-bridge.js +19 -3
- package/server/lib/event-stream/protocol.js +11 -1
- package/server/lib/event-stream/reasoning-projection.js +425 -0
- package/server/lib/event-stream/reasoning-projection.test.js +300 -0
- package/server/lib/event-stream/runtime.js +10 -0
- package/server/lib/event-stream/runtime.test.js +53 -0
- package/server/lib/llm/DOCUMENTATION.md +84 -0
- package/server/lib/llm/catalog.js +100 -0
- package/server/lib/llm/catalog.test.js +78 -0
- package/server/lib/llm/completions.js +151 -0
- package/server/lib/llm/completions.test.js +127 -0
- package/server/lib/llm/generate.js +570 -0
- package/server/lib/llm/generate.test.js +459 -0
- package/server/lib/llm/routes.js +47 -0
- package/server/lib/llm/temp-directory.js +37 -0
- package/server/lib/notifications/APNS.md +19 -13
- package/server/lib/notifications/DOCUMENTATION.md +4 -3
- package/server/lib/notifications/runtime.js +153 -38
- package/server/lib/notifications/runtime.test.js +122 -0
- package/server/lib/opencode/DOCUMENTATION.md +2 -1
- package/server/lib/opencode/feature-routes-runtime.js +30 -0
- package/server/lib/opencode/feature-routes-runtime.test.js +20 -0
- package/server/lib/opencode/proxy.js +103 -9
- package/server/lib/opencode/proxy.test.js +177 -1
- package/server/lib/opencode/settings-runtime.js +2 -2
- package/server/lib/relay/reasoning-projection.e2e.test.ts +529 -0
- package/server/lib/session-goal/DOCUMENTATION.md +8 -1
- package/server/lib/session-goal/runtime.js +20 -0
- package/server/lib/session-index/DOCUMENTATION.md +3 -1
- package/server/lib/session-index/service.js +10 -4
- package/server/lib/session-index/service.test.js +37 -0
- package/server/lib/session-title/runtime.js +1 -0
- package/server/lib/session-title/runtime.test.js +11 -0
- package/server/lib/session-turn-pages/DOCUMENTATION.md +2 -1
- package/server/lib/session-turn-pages/routes.js +22 -3
- package/server/lib/session-turn-pages/routes.test.js +43 -0
- package/server/lib/transcript-cache/DOCUMENTATION.md +2 -2
- package/server/lib/transcript-cache/routes.js +9 -2
- package/server/lib/transcript-cache/routes.test.js +32 -0
- package/dist/assets/AgentAvatar-Di8F4QJh.js +0 -1
- package/dist/assets/AssistantView-DYdXsGDh.js +0 -1
- package/dist/assets/ContextPanel-Cj6RVUAe.js +0 -285
- package/dist/assets/DiagramView-BoRTG67Z.js +0 -1
- package/dist/assets/FadeInOnReveal-CV2s6YN8.js +0 -1
- package/dist/assets/Icon-CLpn1mIz.js +0 -1
- package/dist/assets/MarkdownRendererImpl-DwUKY9k4.js +0 -7
- package/dist/assets/MarkstreamRendererImpl-BRvprELo.js +0 -1
- package/dist/assets/MarkstreamRendererImpl-Ca7RATbs.css +0 -1
- package/dist/assets/MobileDetailNavigation-C0Z8beRL.js +0 -1
- package/dist/assets/MobileShareBridge-DTvmm9vz.js +0 -4
- package/dist/assets/MobileSurface-CfrI-5uf.js +0 -2
- package/dist/assets/OnboardingScreen-D6rtE7rj.js +0 -1
- package/dist/assets/OverlayScrollbar-DlNZho3q.js +0 -2
- package/dist/assets/SettingsView-BOT158gD.js +0 -25
- package/dist/assets/TerminalView-sf_LfP6Z.js +0 -8
- package/dist/assets/ToolOutputDialog-BI5vwuaT.js +0 -10
- package/dist/assets/appThemeRegistry-G_zwgqAH.js +0 -2
- package/dist/assets/clipboard-D35XSwID.js +0 -1
- package/dist/assets/desktop-CfLlmafK.js +0 -1
- package/dist/assets/desktopHosts-C1Ns3DUu.js +0 -1
- package/dist/assets/desktopRelayRestore-8pvI_dSc.js +0 -1
- package/dist/assets/fileQueries-DGHZqZHc.js +0 -1
- package/dist/assets/insertionBoundaries-CJksyugt.js +0 -4
- package/dist/assets/main-BTeHOAU_.js +0 -29
- package/dist/assets/main-BUBx-y1E.js +0 -2
- package/dist/assets/markdown-worker-y3ewaqAK.js +0 -4
- package/dist/assets/markdownTheme-SPe4jOwX.js +0 -2
- package/dist/assets/miniChat-Btnf42TH.js +0 -2
- package/dist/assets/mobile-CAjwV6ly.js +0 -2
- package/dist/assets/multirun-Rpz1_-fz.js +0 -4
- package/dist/assets/persistence-w-AiX2i4.js +0 -3
- package/dist/assets/platform-4eCmsdol.js +0 -2
- package/dist/assets/projectMeta-CvUuy-j5.js +0 -3
- package/dist/assets/radio-D_a28OlM.js +0 -1
- package/dist/assets/renderElectronMiniChatApp-DCzBQk2w.js +0 -2
- package/dist/assets/renderMobileApp-U5sclQRp.js +0 -28
- package/dist/assets/runtime-switch-BkpTTbEi.js +0 -1
- package/dist/assets/runtimeConfig-6inIl99b.js +0 -2
- package/dist/assets/runtimeEndpointReset-B_rUFDT5.js +0 -4
- package/dist/assets/shikiHighlight-CMpSAuTl.js +0 -1
- package/dist/assets/sortable-tabs-strip-CNDgyX0B.js +0 -1
- package/dist/assets/src-DZt1nWf5.css +0 -2
- package/dist/assets/terminalApi-CGtOtc8I.js +0 -1
- package/dist/assets/url-KeihbE0d.js +0 -1
- package/dist/assets/useAppFontEffects-3xHRVJcf.js +0 -7
- package/dist/assets/useAssistantUIStore-I0VWrB5V.js +0 -1
- package/dist/assets/useDesktopWindowControlsLayout-QJlddPRn.js +0 -1
- package/dist/assets/useEffectiveDirectory-Bcj1wP95.js +0 -1
- package/dist/assets/useFontPreferences-BLZ3y6mN.js +0 -1
- package/dist/assets/useMobileNavigationStore-CD0GRjaV.js +0 -412
- package/dist/assets/useSessionAutoCleanup-CN_6ABvx.js +0 -1
- package/dist/assets/useUIStore-CNLmCoPU.js +0 -1
- package/dist/assets/useWorkerHighlightedLines-DX9znDoW.js +0 -7
- package/dist/assets/utils-CTL16t2f.js +0 -1
|
@@ -11,6 +11,15 @@ import {
|
|
|
11
11
|
extractDirectoryFromRequest,
|
|
12
12
|
isDirectoryTurnAdmissionPath,
|
|
13
13
|
} from './instance-recovery-runtime.js';
|
|
14
|
+
import {
|
|
15
|
+
createReasoningOutboundFilter,
|
|
16
|
+
createSseBlockSplitter,
|
|
17
|
+
filterSseBlock,
|
|
18
|
+
projectMessagesPayloadForReasoning,
|
|
19
|
+
readIncludeReasoningFromUrl,
|
|
20
|
+
readIncludeReasoningQuery,
|
|
21
|
+
stripIncludeReasoningParam,
|
|
22
|
+
} from '../event-stream/reasoning-projection.js';
|
|
14
23
|
|
|
15
24
|
export const createDirectoryQueryCanonicalizer = ({ realpath, ...cacheOptions } = {}) => {
|
|
16
25
|
const realpathCache = createRealpathCache({ fallbackOnError: true, realpath, ...cacheOptions });
|
|
@@ -379,6 +388,14 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
379
388
|
let connectTimedOut = false;
|
|
380
389
|
let writeQueue = Promise.resolve(true);
|
|
381
390
|
const sseBoundary = createSseBoundaryTracker();
|
|
391
|
+
const requestUrl = typeof req.originalUrl === 'string' && req.originalUrl.length > 0
|
|
392
|
+
? req.originalUrl
|
|
393
|
+
: (typeof req.url === 'string' ? req.url : '');
|
|
394
|
+
// OpenChamber projection control — never forwarded to OpenCode.
|
|
395
|
+
const includeReasoning = readIncludeReasoningQuery(req.query)
|
|
396
|
+
&& readIncludeReasoningFromUrl(requestUrl);
|
|
397
|
+
const reasoningFilter = includeReasoning ? null : createReasoningOutboundFilter();
|
|
398
|
+
const sseSplitter = includeReasoning ? null : createSseBlockSplitter();
|
|
382
399
|
|
|
383
400
|
req.on('close', closeUpstream);
|
|
384
401
|
|
|
@@ -400,10 +417,9 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
400
417
|
};
|
|
401
418
|
|
|
402
419
|
try {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
const upstreamPath = requestUrl.startsWith('/api') ? requestUrl.slice(4) || '/' : requestUrl;
|
|
420
|
+
// Strip includeReasoning before building the upstream OpenCode path.
|
|
421
|
+
const strippedUrl = stripIncludeReasoningParam(requestUrl);
|
|
422
|
+
const upstreamPath = strippedUrl.startsWith('/api') ? strippedUrl.slice(4) || '/' : strippedUrl;
|
|
407
423
|
const headers = normalizeForwardedDirectoryHeaders(
|
|
408
424
|
collectForwardProxyHeaders(req.headers, getOpenCodeAuthHeaders())
|
|
409
425
|
);
|
|
@@ -495,10 +511,40 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
495
511
|
}
|
|
496
512
|
if (value && value.length > 0) {
|
|
497
513
|
resetStallTimer();
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
514
|
+
if (includeReasoning) {
|
|
515
|
+
// Enabled path: byte-identical passthrough (existing behavior).
|
|
516
|
+
sseBoundary.observe(value);
|
|
517
|
+
const canContinue = await enqueueSseWrite(value);
|
|
518
|
+
if (!canContinue) {
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
// Disabled path: parse SSE blocks, drop reasoning, re-serialize.
|
|
523
|
+
// Preserves id/event lines and GlobalEvent wrap when unchanged drop-only.
|
|
524
|
+
const blocks = sseSplitter.push(value);
|
|
525
|
+
let canContinue = true;
|
|
526
|
+
for (const block of blocks) {
|
|
527
|
+
const filtered = filterSseBlock(block, reasoningFilter);
|
|
528
|
+
if (!filtered) {
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
sseBoundary.observe(filtered);
|
|
532
|
+
canContinue = await enqueueSseWrite(filtered);
|
|
533
|
+
if (!canContinue) break;
|
|
534
|
+
}
|
|
535
|
+
if (!canContinue) {
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (!includeReasoning && sseSplitter) {
|
|
543
|
+
for (const block of sseSplitter.finish()) {
|
|
544
|
+
const filtered = filterSseBlock(block, reasoningFilter);
|
|
545
|
+
if (filtered) {
|
|
546
|
+
sseBoundary.observe(filtered);
|
|
547
|
+
await enqueueSseWrite(filtered);
|
|
502
548
|
}
|
|
503
549
|
}
|
|
504
550
|
}
|
|
@@ -524,6 +570,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
524
570
|
} finally {
|
|
525
571
|
clearConnectTimer();
|
|
526
572
|
clearStallTimer();
|
|
573
|
+
reasoningFilter?.dispose?.();
|
|
527
574
|
if (heartbeatTimer) {
|
|
528
575
|
clearTimeout(heartbeatTimer);
|
|
529
576
|
heartbeatTimer = null;
|
|
@@ -584,7 +631,7 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
584
631
|
|
|
585
632
|
const forwardSanitizedSessionListRequest = async (req, res, next, logLabel) => {
|
|
586
633
|
try {
|
|
587
|
-
const upstreamPath = await getRequestUpstreamPath(req);
|
|
634
|
+
const upstreamPath = stripIncludeReasoningParam(await getRequestUpstreamPath(req));
|
|
588
635
|
const result = await fetchSessionListPayload(upstreamPath, { req });
|
|
589
636
|
|
|
590
637
|
res.status(result.upstream.status);
|
|
@@ -617,6 +664,47 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
617
664
|
}
|
|
618
665
|
};
|
|
619
666
|
|
|
667
|
+
/**
|
|
668
|
+
* Official session.messages list (`GET /api/session/:id/message`).
|
|
669
|
+
* When includeReasoning=false, strip reasoning parts from the JSON body.
|
|
670
|
+
* includeReasoning is never forwarded upstream.
|
|
671
|
+
*/
|
|
672
|
+
const forwardSessionMessagesListRequest = async (req, res, next) => {
|
|
673
|
+
try {
|
|
674
|
+
const includeReasoning = readIncludeReasoningQuery(req.query);
|
|
675
|
+
const upstreamPath = stripIncludeReasoningParam(await getRequestUpstreamPath(req));
|
|
676
|
+
const result = await fetchSessionListPayload(upstreamPath, { req });
|
|
677
|
+
|
|
678
|
+
res.status(result.upstream.status);
|
|
679
|
+
applyForwardProxyResponseHeaders(result.upstream.headers, res);
|
|
680
|
+
|
|
681
|
+
if (!result.isJson) {
|
|
682
|
+
res.setHeader('content-type', result.contentType);
|
|
683
|
+
res.end(result.bodyText);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
if (result.parseError || result.payload == null) {
|
|
688
|
+
res.setHeader('content-type', result.contentType);
|
|
689
|
+
res.end(result.bodyText);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
res.setHeader('content-type', result.contentType);
|
|
694
|
+
res.json(projectMessagesPayloadForReasoning(result.payload, includeReasoning));
|
|
695
|
+
} catch (error) {
|
|
696
|
+
if (isAbortError(error)) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
console.error('[proxy] OpenCode session.messages proxy error:', error?.message ?? error);
|
|
700
|
+
if (!res.headersSent) {
|
|
701
|
+
res.status(503).json({ error: 'OpenCode service unavailable' });
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
next(error);
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
|
|
620
708
|
// Ensure API prefix is detected before proxying
|
|
621
709
|
app.use('/api', (_req, _res, next) => {
|
|
622
710
|
ensureOpenCodeApiPrefix();
|
|
@@ -776,6 +864,12 @@ export const registerOpenCodeProxy = (app, deps) => {
|
|
|
776
864
|
return forwardSanitizedSessionListRequest(req, res, next, 'session.list');
|
|
777
865
|
});
|
|
778
866
|
|
|
867
|
+
// Official session.messages list (not exact message — that is owned by
|
|
868
|
+
// session-turn-pages and registered before this proxy).
|
|
869
|
+
app.get('/api/session/:sessionID/message', (req, res, next) => {
|
|
870
|
+
return forwardSessionMessagesListRequest(req, res, next);
|
|
871
|
+
});
|
|
872
|
+
|
|
779
873
|
app.get('/api/global/event', forwardSseRequest);
|
|
780
874
|
app.get('/api/event', forwardSseRequest);
|
|
781
875
|
|
|
@@ -1,12 +1,69 @@
|
|
|
1
|
-
import
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import request from 'supertest';
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
2
4
|
|
|
3
5
|
import {
|
|
4
6
|
createDirectoryQueryCanonicalizer,
|
|
5
7
|
isInteractiveSessionRequest,
|
|
6
8
|
normalizeForwardedDirectoryHeaders,
|
|
9
|
+
registerOpenCodeProxy,
|
|
7
10
|
resolveSessionTurnAdmissionRequest,
|
|
8
11
|
} from './proxy.js';
|
|
9
12
|
|
|
13
|
+
const originalFetch = globalThis.fetch;
|
|
14
|
+
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
globalThis.fetch = originalFetch;
|
|
17
|
+
vi.restoreAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const readyRuntime = () => ({
|
|
21
|
+
isOpenCodeReady: true,
|
|
22
|
+
openCodeNotReadySince: 0,
|
|
23
|
+
isRestartingOpenCode: false,
|
|
24
|
+
openCodePort: 4096,
|
|
25
|
+
openCodeBaseUrl: 'http://127.0.0.1:4096',
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const mountProxy = () => {
|
|
29
|
+
const app = express();
|
|
30
|
+
// Fresh app each time — registerOpenCodeProxy guards on app setting.
|
|
31
|
+
registerOpenCodeProxy(app, {
|
|
32
|
+
fs: { promises: { realpath: async (v) => v } },
|
|
33
|
+
os: {},
|
|
34
|
+
path: {},
|
|
35
|
+
OPEN_CODE_READY_GRACE_MS: 1000,
|
|
36
|
+
LONG_REQUEST_TIMEOUT_MS: 60_000,
|
|
37
|
+
SSE_UPSTREAM_CONNECT_TIMEOUT_MS: 5_000,
|
|
38
|
+
SSE_UPSTREAM_STALL_TIMEOUT_MS: 5_000,
|
|
39
|
+
getRuntime: readyRuntime,
|
|
40
|
+
getOpenCodeAuthHeaders: () => ({}),
|
|
41
|
+
buildOpenCodeUrl: (path) => `http://127.0.0.1:4096${path}`,
|
|
42
|
+
ensureOpenCodeApiPrefix: () => {},
|
|
43
|
+
});
|
|
44
|
+
return app;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const sseBody = (blocks) => {
|
|
48
|
+
const encoder = new TextEncoder();
|
|
49
|
+
const text = blocks.join('');
|
|
50
|
+
let sent = false;
|
|
51
|
+
return {
|
|
52
|
+
getReader() {
|
|
53
|
+
return {
|
|
54
|
+
async read() {
|
|
55
|
+
if (sent) return { done: true, value: undefined };
|
|
56
|
+
sent = true;
|
|
57
|
+
return { done: false, value: encoder.encode(text) };
|
|
58
|
+
},
|
|
59
|
+
async cancel() {},
|
|
60
|
+
releaseLock() {},
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
async cancel() {},
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
10
67
|
describe('createDirectoryQueryCanonicalizer', () => {
|
|
11
68
|
it('canonicalizes directory query params and preserves other params', async () => {
|
|
12
69
|
const canonicalize = createDirectoryQueryCanonicalizer({
|
|
@@ -124,3 +181,122 @@ describe('resolveSessionTurnAdmissionRequest', () => {
|
|
|
124
181
|
expect(resolveSessionTurnAdmissionRequest({ method: 'POST', url: '/api/session/ses_1/message' })).toBeNull();
|
|
125
182
|
});
|
|
126
183
|
});
|
|
184
|
+
|
|
185
|
+
describe('registerOpenCodeProxy reasoning projection routes', () => {
|
|
186
|
+
it('strips includeReasoning from SSE upstream and drops reasoning events when false', async () => {
|
|
187
|
+
const upstreamUrls = [];
|
|
188
|
+
globalThis.fetch = vi.fn(async (url) => {
|
|
189
|
+
upstreamUrls.push(String(url));
|
|
190
|
+
return {
|
|
191
|
+
ok: true,
|
|
192
|
+
status: 200,
|
|
193
|
+
headers: new Headers({ 'content-type': 'text/event-stream' }),
|
|
194
|
+
body: sseBody([
|
|
195
|
+
'id: 1\ndata: {"type":"message.part.updated","properties":{"part":{"id":"t","messageID":"m","type":"text","text":"hi"}}}\n\n',
|
|
196
|
+
'id: 2\ndata: {"type":"session.next.reasoning.delta.1","properties":{"delta":"secret"}}\n\n',
|
|
197
|
+
'id: 3\ndata: {"type":"message.part.delta","properties":{"messageID":"m","partID":"t","field":"text","delta":"!"}}\n\n',
|
|
198
|
+
]),
|
|
199
|
+
};
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const app = mountProxy();
|
|
203
|
+
const res = await request(app)
|
|
204
|
+
.get('/api/event?directory=%2Ftmp&includeReasoning=false')
|
|
205
|
+
.buffer(true)
|
|
206
|
+
.parse((response, callback) => {
|
|
207
|
+
const chunks = [];
|
|
208
|
+
response.on('data', (c) => chunks.push(Buffer.from(c)));
|
|
209
|
+
response.on('end', () => callback(null, Buffer.concat(chunks).toString('utf8')));
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
expect(res.status).toBe(200);
|
|
213
|
+
expect(upstreamUrls).toHaveLength(1);
|
|
214
|
+
expect(upstreamUrls[0]).not.toContain('includeReasoning');
|
|
215
|
+
expect(upstreamUrls[0]).toContain('directory=');
|
|
216
|
+
expect(res.body).toContain('"type":"message.part.updated"');
|
|
217
|
+
expect(res.body).toContain('"type":"message.part.delta"');
|
|
218
|
+
expect(res.body).not.toContain('reasoning');
|
|
219
|
+
expect(res.body).not.toContain('secret');
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('byte-passthrough SSE when includeReasoning is omitted', async () => {
|
|
223
|
+
const raw =
|
|
224
|
+
'id: 1\ndata: {"type":"session.next.reasoning.delta","properties":{"delta":"keep"}}\n\n';
|
|
225
|
+
globalThis.fetch = vi.fn(async () => ({
|
|
226
|
+
ok: true,
|
|
227
|
+
status: 200,
|
|
228
|
+
headers: new Headers({ 'content-type': 'text/event-stream' }),
|
|
229
|
+
body: sseBody([raw]),
|
|
230
|
+
}));
|
|
231
|
+
|
|
232
|
+
const app = mountProxy();
|
|
233
|
+
const res = await request(app)
|
|
234
|
+
.get('/api/global/event')
|
|
235
|
+
.buffer(true)
|
|
236
|
+
.parse((response, callback) => {
|
|
237
|
+
const chunks = [];
|
|
238
|
+
response.on('data', (c) => chunks.push(Buffer.from(c)));
|
|
239
|
+
response.on('end', () => callback(null, Buffer.concat(chunks).toString('utf8')));
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
expect(res.status).toBe(200);
|
|
243
|
+
expect(res.body).toContain('session.next.reasoning.delta');
|
|
244
|
+
expect(res.body).toContain('keep');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('filters session.messages list JSON and strips includeReasoning upstream', async () => {
|
|
248
|
+
const upstreamUrls = [];
|
|
249
|
+
globalThis.fetch = vi.fn(async (url) => {
|
|
250
|
+
upstreamUrls.push(String(url));
|
|
251
|
+
return {
|
|
252
|
+
ok: true,
|
|
253
|
+
status: 200,
|
|
254
|
+
headers: new Headers({ 'content-type': 'application/json' }),
|
|
255
|
+
async text() {
|
|
256
|
+
return JSON.stringify([
|
|
257
|
+
{
|
|
258
|
+
info: { id: 'msg_1', tokens: { reasoning: 12 } },
|
|
259
|
+
parts: [
|
|
260
|
+
{ id: 'r', type: 'reasoning', text: 'hidden' },
|
|
261
|
+
{ id: 't', type: 'text', text: 'visible' },
|
|
262
|
+
],
|
|
263
|
+
},
|
|
264
|
+
]);
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const app = mountProxy();
|
|
270
|
+
const res = await request(app)
|
|
271
|
+
.get('/api/session/ses_1/message?limit=30&includeReasoning=false&directory=%2Frepo');
|
|
272
|
+
|
|
273
|
+
expect(res.status).toBe(200);
|
|
274
|
+
expect(upstreamUrls[0]).not.toContain('includeReasoning');
|
|
275
|
+
expect(upstreamUrls[0]).toContain('/session/ses_1/message');
|
|
276
|
+
expect(res.body[0].parts).toEqual([{ id: 't', type: 'text', text: 'visible' }]);
|
|
277
|
+
expect(res.body[0].info.tokens.reasoning).toBe(12);
|
|
278
|
+
expect(JSON.stringify(res.body)).not.toContain('hidden');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('keeps reasoning parts on session.messages list when includeReasoning is omitted', async () => {
|
|
282
|
+
globalThis.fetch = vi.fn(async () => ({
|
|
283
|
+
ok: true,
|
|
284
|
+
status: 200,
|
|
285
|
+
headers: new Headers({ 'content-type': 'application/json' }),
|
|
286
|
+
async text() {
|
|
287
|
+
return JSON.stringify([
|
|
288
|
+
{
|
|
289
|
+
info: { id: 'msg_1' },
|
|
290
|
+
parts: [{ id: 'r', type: 'reasoning', text: 'keep-me' }],
|
|
291
|
+
},
|
|
292
|
+
]);
|
|
293
|
+
},
|
|
294
|
+
}));
|
|
295
|
+
|
|
296
|
+
const app = mountProxy();
|
|
297
|
+
const res = await request(app).get('/api/session/ses_1/message');
|
|
298
|
+
expect(res.status).toBe(200);
|
|
299
|
+
expect(res.body[0].parts[0].type).toBe('reasoning');
|
|
300
|
+
expect(res.body[0].parts[0].text).toBe('keep-me');
|
|
301
|
+
});
|
|
302
|
+
});
|
|
@@ -666,8 +666,8 @@ export const createSettingsRuntime = (deps) => {
|
|
|
666
666
|
return { settings, changed: false };
|
|
667
667
|
}
|
|
668
668
|
|
|
669
|
-
const defaultLight = '
|
|
670
|
-
const defaultDark = '
|
|
669
|
+
const defaultLight = 'carbonfox-light';
|
|
670
|
+
const defaultDark = 'vitesse-dark-dark';
|
|
671
671
|
|
|
672
672
|
let nextLightThemeId = hasLight ? settings.lightThemeId : undefined;
|
|
673
673
|
let nextDarkThemeId = hasDark ? settings.darkThemeId : undefined;
|