The `subagentchat` aside open failed silently: the host's `parseSubagentChatAddress`
(the tab type's `canOpen`) rejects a MISSING mode (`get("mode")` → `null`), so a mode
we could not stand behind — omitted to dodge the controller's 401 — made `claim` refuse
the address (`sidebarRight: tab type "subagentchat" refuses ...`), and the right column
never revealed.
Fix: the codec now normalizes an unknown/undefined mode to an EXPLICIT `mode=unknown`
instead of dropping it. That value passes both gates:
- client: `parseSubagentChatAddress` accepts `unknown` → `canOpen` true → claim accepts;
- server: `validateAddress` skips the compare when `mode === "unknown"` → no 401.
A real (one-shot/continuable) mode is still emitted when the host catalog is `ready` and
has the child; only an honest `unknown` is sent when it is not.
Drops the TEMP `diag.append` probe route + the client before/after diag fetches (their
mission is done) and rewrites the two spec cases to assert the explicit `mode=unknown`.
Verified with the host's real `parseSubagentChatAddress` pulled verbatim from the app
bundle: the old no-mode address → canOpen false (reproduces the refuse), the new
`mode=unknown` address → canOpen true. typecheck / lint / 29 subagent+sidechat specs green.
84 lines
3.3 KiB
TypeScript
84 lines
3.3 KiB
TypeScript
/**
|
|
* The `subagentchat` resource address must stay byte-compatible with the host's
|
|
* own codec: `@deepseek-ai/dsh-client-ui-subagent` parses what we emit, so a
|
|
* different query order or an unescaped child id would silently open the wrong
|
|
* page (or none).
|
|
*/
|
|
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
SUBAGENT_CHAT_ADDRESS,
|
|
SUBAGENT_CHAT_KIND,
|
|
subagentChatAddress,
|
|
} from '../src/client/native/subagent-chat.ts'
|
|
|
|
describe('subagentChatAddress', () => {
|
|
it("emits the canonical form with parent and mode", () => {
|
|
expect(subagentChatAddress({
|
|
parentSessionId: 'parent-1',
|
|
childSessionId: 'child-2',
|
|
mode: 'one-shot',
|
|
})).toBe('dsh-resource://subagentchat/session/child-2?parent=parent-1&mode=one-shot')
|
|
})
|
|
|
|
it('keeps a continuable mode verbatim', () => {
|
|
expect(subagentChatAddress({
|
|
parentSessionId: 'p',
|
|
childSessionId: 'c',
|
|
mode: 'continuable',
|
|
})).toContain('mode=continuable')
|
|
})
|
|
|
|
it('percent-encodes a child id so the address stays one path segment', () => {
|
|
const address = subagentChatAddress({
|
|
parentSessionId: 'p',
|
|
childSessionId: 'child/with:odd chars',
|
|
mode: 'one-shot',
|
|
})
|
|
expect(address).toContain('/child%2Fwith%3Aodd%20chars?')
|
|
})
|
|
|
|
it('never leaks the parent into the path (it is a routing query only)', () => {
|
|
const address = subagentChatAddress({
|
|
parentSessionId: 'p secret',
|
|
childSessionId: 'c',
|
|
mode: 'one-shot',
|
|
})
|
|
expect(address.split('?')[0]).toBe(`${SUBAGENT_CHAT_ADDRESS}c`)
|
|
})
|
|
|
|
it('names the page kind the native Sidebar roots these pages under', () => {
|
|
expect(SUBAGENT_CHAT_KIND).toBe('subagentchat')
|
|
})
|
|
|
|
it('normalizes an unknown mode to an EXPLICIT mode=unknown (never omitted)', () => {
|
|
// `parseSubagentChatAddress` (the native tab type's `canOpen`) rejects a
|
|
// missing mode (`null`), so an unrecognized/unknown mode must be EMITTED as
|
|
// the literal `mode=unknown` — which both the client parser accepts and the
|
|
// controller skips (`mode !== "unknown"` is false). Omitting it used to make
|
|
// `claim` refuse the address.
|
|
const address = subagentChatAddress({
|
|
parentSessionId: 'p',
|
|
childSessionId: 'c',
|
|
mode: 'unknown' as never,
|
|
})
|
|
expect(address).toBe('dsh-resource://subagentchat/session/c?parent=p&mode=unknown')
|
|
})
|
|
|
|
it('still emits the mode when it is one of the two durable values', () => {
|
|
expect(subagentChatAddress({ parentSessionId: 'p', childSessionId: 'c', mode: 'one-shot' }))
|
|
.toContain('mode=one-shot')
|
|
expect(subagentChatAddress({ parentSessionId: 'p', childSessionId: 'c', mode: 'continuable' }))
|
|
.toContain('mode=continuable')
|
|
})
|
|
|
|
it('normalizes an unknown (undefined) mode to an explicit mode=unknown', () => {
|
|
// The degraded path: the host catalog never hydrated, so the caller has no
|
|
// durable mode — it passes `mode: undefined`. The codec must NOT omit it
|
|
// (a missing mode is `null` and the host's `canOpen` refuses the address);
|
|
// it emits an explicit `mode=unknown`, which the controller skips
|
|
// (`mode !== "unknown"` is false) so the open never 401s.
|
|
const address = subagentChatAddress({ parentSessionId: 'p', childSessionId: 'c', mode: undefined })
|
|
expect(address).toBe('dsh-resource://subagentchat/session/c?parent=p&mode=unknown')
|
|
})
|
|
})
|