@nelsonlongxiang/dsh-open-a2a-net 0.6.41 → 0.6.43

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 (72) hide show
  1. package/README.md +2 -1
  2. package/README_zh.md +2 -1
  3. package/assets/nexusDist/assets/index-CaA76j0F.js +9 -0
  4. package/assets/nexusDist/index.html +1 -1
  5. package/assets/stageDist/assets/index-Bj7S2pA1.css +1 -1
  6. package/assets/stageDist/assets/index-C-28k_o5.js +974 -974
  7. package/assets/stageDist/index.html +12 -12
  8. package/cli/a2a.mjs +24 -4
  9. package/lib/canvas-store.js +249 -17
  10. package/lib/client.js +62 -72
  11. package/lib/client.js.map +1 -1
  12. package/lib/config.js +1 -0
  13. package/lib/faces/admin.js +103 -63
  14. package/lib/faces/wire.js +3 -1
  15. package/lib/index.js +116 -22
  16. package/lib/origin-claim.js +4 -0
  17. package/lib/skill/SKILL.md +117 -117
  18. package/lib/skill/references/collaboration.md +428 -428
  19. package/lib/skill/references/discovery.md +78 -78
  20. package/lib/skill/references/intake.md +107 -107
  21. package/lib/skill/references/membership.md +73 -73
  22. package/lib/skill/references/pitfalls.md +51 -51
  23. package/lib/skill/references/router.md +96 -96
  24. package/lib/skill/references/routing.md +65 -65
  25. package/lib/types/canvas-store.d.ts +48 -1
  26. package/lib/types/config.d.ts +2 -0
  27. package/lib/types/faces/admin.d.ts +2 -0
  28. package/lib/types/origin-claim.d.ts +2 -0
  29. package/nexus-stage/package.json +21 -21
  30. package/nexus-stage/scripts/mockhost.py +261 -261
  31. package/nexus-stage/src/canvas-ops.ts +191 -191
  32. package/nexus-stage/src/canvas-wire.ts +164 -164
  33. package/nexus-stage/src/cli-home-picker.ts +42 -42
  34. package/nexus-stage/src/fault.ts +56 -56
  35. package/nexus-stage/src/federation-overlay.ts +103 -103
  36. package/nexus-stage/src/federation.ts +241 -241
  37. package/nexus-stage/src/layout-doc.ts +145 -145
  38. package/nexus-stage/src/main.ts +520 -513
  39. package/nexus-stage/src/planning-card-text.ts +73 -73
  40. package/nexus-stage/src/planning-card.ts +63 -63
  41. package/nexus-stage/src/planning-edges.ts +26 -26
  42. package/nexus-stage/src/planning-fit.ts +45 -45
  43. package/nexus-stage/src/planning-frame-hue.ts +13 -13
  44. package/nexus-stage/src/planning-legend.ts +39 -39
  45. package/nexus-stage/src/planning-member-handles.ts +61 -61
  46. package/nexus-stage/src/planning-menus.ts +422 -422
  47. package/nexus-stage/src/planning-mirror.ts +75 -75
  48. package/nexus-stage/src/planning-nodelist.ts +294 -294
  49. package/nexus-stage/src/planning-overlap.ts +159 -159
  50. package/nexus-stage/src/planning-reachability.ts +28 -28
  51. package/nexus-stage/src/planning-relayout.ts +121 -121
  52. package/nexus-stage/src/planning-roster.ts +15 -15
  53. package/nexus-stage/src/planning-search.ts +168 -168
  54. package/nexus-stage/src/planning-types.ts +115 -115
  55. package/nexus-stage/src/planning-undo.ts +68 -68
  56. package/nexus-stage/src/planning-view.ts +1499 -1499
  57. package/nexus-stage/src/planning.css +340 -340
  58. package/nexus-stage/src/seat-name-editor.ts +31 -31
  59. package/nexus-stage/src/session-drag.ts +64 -64
  60. package/nexus-stage/src/viewport.ts +106 -106
  61. package/nexus-stage/src/wb-chat.ts +684 -684
  62. package/nexus-stage/src/wb-directory.ts +237 -237
  63. package/nexus-stage/src/wb-ledger.ts +279 -279
  64. package/nexus-stage/src/wb-names.ts +43 -43
  65. package/nexus-stage/src/wb-rail.ts +147 -147
  66. package/nexus-stage/src/workbench.css +388 -388
  67. package/nexus-stage/src/workbench.ts +1294 -1272
  68. package/nexus-stage/src/world.ts +509 -509
  69. package/nexus-stage/tsconfig.json +24 -24
  70. package/nexus-stage/vite.config.ts +15 -15
  71. package/package.json +1 -1
  72. package/assets/nexusDist/assets/index-NkNSeIx6.js +0 -9
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["css","Button","IconGlobeOutlineRegular","_Fragment"],"sources":["../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs","client/A2aControl.js","client/locales.js","client/index.js"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from \"react/jsx-runtime\";\n/**\n * A2A network control: a footer action opening this host's recent network\n * activity feed. The feed is the panel's whole content — in-flight routes,\n * owed receipts, and the routing activity ring. Session/team management\n * lives in the planning view (design: docs/design/panel-activity-feed.md):\n * the stage links row above the feed is the explicit navigation to it.\n * Probes the host's state route at registration time (the seat renders only\n * on a host that serves it). While open, the feed polls so it visibly\n * follows each turn; the closed trigger's unread badge rides a slow poll.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport { Button, IconGlobeOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives';\nimport css from './A2aControl.module.css';\n/** Fetch the host's network state; undefined means the route is absent. */\nasync function fetchState() {\n const response = await fetch('/__dsh_a2a/state', { cache: 'no-store' });\n if (!response.ok)\n return undefined;\n const body = await response.json();\n if (body.nodes !== true || !Array.isArray(body.sessions))\n return undefined;\n return {\n sessions: body.sessions,\n activity: Array.isArray(body.activity) ? body.activity : [],\n inFlight: Array.isArray(body.inFlight) ? body.inFlight : [],\n tasks: Array.isArray(body.tasks) ? body.tasks : [],\n ...(typeof body.tasksStaleCount === 'number' ? { tasksStaleCount: body.tasksStaleCount } : {}),\n ...(typeof body.version === 'string' ? { version: body.version } : {}),\n };\n}\n/** Compact relative time for an activity row. */\nfunction relativeTime(ts) {\n const seconds = Math.max(0, Math.round((Date.now() - ts) / 1000));\n if (seconds < 60)\n return `${String(seconds)}s`;\n const minutes = Math.round(seconds / 60);\n if (minutes < 60)\n return `${String(minutes)}m`;\n return `${String(Math.round(minutes / 60))}h`;\n}\n/** One feed row: direction glyph (aria-labelled), team, peer host, age, fail mark. */\nfunction FeedRow({ dir, team, peer, ts, ok, target, openSession, t }) {\n const fresh = Date.now() - ts < 5_000;\n return (_jsxs(\"div\", { className: clsx(css.activityRow, fresh && css.fresh), \"data-ok\": ok === undefined ? undefined : String(ok), \"data-jump\": target !== undefined, title: target !== undefined ? t('a2a.jump') : undefined, onClick: () => { if (target !== undefined)\n openSession(target.id); }, role: target !== undefined ? 'button' : undefined, children: [_jsx(\"span\", { className: clsx(css.activityDir, dir), \"aria-label\": dir === 'fwd' ? 'forwarded' : dir === 'in' ? 'inbound' : 'outbound', children: dir === 'fwd' ? '⇢' : dir === 'in' ? '←' : '→' }), _jsx(\"span\", { className: css.activityTeam, title: team, children: team }), _jsx(\"span\", { className: css.activityPeer, title: peer, children: peer === '' || peer === 'local' ? '' : peer.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '') }), ok === false ? _jsx(\"span\", { className: css.failMark, \"aria-label\": \"failed\", children: \"\\u2715\" }) : null, _jsx(\"span\", { className: css.activityTime, children: relativeTime(ts) })] }));\n}\n/**\n * Render the network toggle and its activity feed.\n * @param props - composed slot props.\n * @returns the control element tree.\n */\nexport function A2aControl({ wide, t, openSession }) {\n const [open, setOpen] = useState(false);\n const [state, setState] = useState({ sessions: [], activity: [], inFlight: [], tasks: [] });\n const [seenActivity, setSeenActivity] = useState(0);\n const stopped = useRef(false);\n useEffect(() => () => { stopped.current = true; }, []);\n const wrap = useRef(null);\n // The popover anchors to the trigger's viewport rect: the sidebar column\n // clips its descendants (the collapse slide), so an in-column absolute\n // popover is cut at the column edge whenever the column is narrower than\n // the popover; fixed positioning escapes the clip at rest (the ui-settings\n // panel precedent). The left edge clamps so a narrow viewport never hides\n // the popover's right side.\n const [anchor, setAnchor] = useState(null);\n // The unread badge counts inbound activity entries that arrived while the\n // panel was closed; opening the panel clears it.\n const unread = Math.max(0, state.activity.filter(entry => entry.dir === 'in').length - seenActivity);\n const toggleOpen = () => {\n if (open) {\n setOpen(false);\n return;\n }\n const rect = wrap.current?.getBoundingClientRect();\n if (rect !== undefined) {\n setAnchor({\n left: Math.max(8, Math.min(rect.left, window.innerWidth - 336)),\n bottom: window.innerHeight - rect.top + 8,\n });\n }\n setSeenActivity(state.activity.filter(entry => entry.dir === 'in').length);\n setOpen(true);\n };\n const refresh = useCallback(() => {\n void fetchState().then((next) => {\n if (!stopped.current && next !== undefined)\n setState(next);\n }).catch(() => { });\n }, []);\n useEffect(() => {\n if (open)\n refresh();\n }, [open, refresh]);\n useEffect(() => {\n // The unread badge lives on the closed trigger, so the state must move\n // even while the panel is closed — a slow poll keeps it fresh; the open\n // panel's faster poll below takes over while visible.\n const slow = setInterval(refresh, 10_000);\n refresh();\n return () => { clearInterval(slow); };\n }, [refresh]);\n useEffect(() => {\n if (!open)\n return;\n // While the feed is open, it polls so the ring visibly follows turns.\n const poll = setInterval(refresh, 2_000);\n return () => { clearInterval(poll); };\n }, [open, refresh]);\n useEffect(() => {\n if (!open)\n return;\n const onPointerDown = (event) => {\n if (event.target.closest(`.${css.wrap}`) === null)\n setOpen(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => { document.removeEventListener('pointerdown', onPointerDown); };\n }, [open]);\n // 0.6.10: the receive face of click-to-reach (0.6.7 BroadcastChannel,\n // 0.6.8 ?dsh-a2a-open deep link) is removed by ruling — 禁止任何跳转,\n // 全部使用响应式布局. The workbench's 快派 form is the only cross-surface\n // reach primitive and it stays on the workbench page; this panel's\n // FeedRow openSession remains (it switches a conversation in place inside\n // the GUI — no navigation).\n const { sessions, activity, inFlight, tasks } = state;\n return (_jsxs(\"div\", { ref: wrap, className: clsx(css.wrap, wide ? css.wide : css.rail), children: [_jsx(Button, { variant: \"ghost\", size: \"sm\", title: t('a2a.label'), \"aria-label\": t('a2a.label'), \"aria-expanded\": open, onClick: () => { toggleOpen(); }, children: _jsxs(\"span\", { className: css.trigger, children: [_jsx(IconGlobeOutlineRegular, {}), wide ? _jsx(\"span\", { children: t('a2a.label') }) : null, unread > 0 ? _jsx(\"span\", { className: css.unreadBadge, \"aria-label\": String(unread), children: unread > 9 ? '9+' : String(unread) }) : null] }) }), open\n ? (_jsxs(\"div\", { className: css.popover, role: \"dialog\", \"aria-label\": t('a2a.title'), style: anchor === null ? undefined : { left: `${String(anchor.left)}px`, bottom: `${String(anchor.bottom)}px` }, children: [_jsxs(\"div\", { className: css.title, children: [t('a2a.title'), state.version !== undefined ? _jsxs(\"span\", { className: css.versionTag, title: state.version, children: [\"v\", state.version] }) : null] }), _jsxs(\"div\", { className: css.stageRow, children: [_jsx(\"span\", { className: css.stageTitle, children: t('a2a.stageTitle') }), _jsx(\"span\", { className: css.stageLinks, children: _jsx(\"a\", { className: css.stageLink, href: \"/__dsh_a2a_nexus/\", target: \"_blank\", rel: \"noreferrer\", title: t('a2a.stageWorkbench'), children: t('a2a.stageWorkbench') }) })] }), _jsxs(\"div\", { \"aria-live\": \"polite\", children: [inFlight.length > 0 && (_jsx(\"div\", { className: css.inFlightList, \"aria-label\": t('a2a.inFlight'), children: inFlight.map((route) => (_jsxs(\"div\", { className: clsx(css.inFlightRow, Date.now() - route.startedAt > 120_000 && css.inFlightStale), title: Date.now() - route.startedAt > 120_000 ? t('a2a.inFlightStale') : undefined, children: [_jsx(\"span\", { className: css.inFlightPulse, \"aria-hidden\": true }), _jsx(FeedRow, { dir: \"out\", team: route.team, peer: route.peer, ts: route.startedAt, openSession: openSession, t: t })] }, `${route.team}-${String(route.startedAt)}`))) })), (() => {\n // C-ruling: render only settleable debt; the stale tail is a\n // count line, never a row-per-noise listing.\n const settleableTasks = tasks.filter(task => task.settleable !== false);\n const staleCount = state.tasksStaleCount ?? (tasks.length - settleableTasks.length);\n if (settleableTasks.length === 0 && staleCount === 0)\n return null;\n return (_jsxs(_Fragment, { children: [settleableTasks.length > 0 && (_jsxs(_Fragment, { children: [_jsx(\"div\", { className: css.sectionTitle, children: t('a2a.tasks') }), _jsx(\"div\", { className: css.inFlightList, \"aria-label\": t('a2a.tasks'), children: settleableTasks.map((task) => (_jsx(\"div\", { className: css.inFlightRow, title: t('a2a.tasksNote'), children: _jsx(FeedRow, { dir: \"out\", team: task.team, peer: task.peer, ts: task.startedAt, openSession: openSession, t: t }) }, task.taskId))) })] })), staleCount > 0 && (_jsxs(\"div\", { className: css.staleNote, title: t('a2a.tasksNote'), children: [t('a2a.tasksStale'), \" \\u00B7 \", String(staleCount)] }))] }));\n })(), _jsx(\"div\", { className: css.sectionTitle, children: t('a2a.activity') }), activity.length === 0\n ? _jsx(\"div\", { className: css.empty, children: t('a2a.activityEmpty') })\n : (_jsx(\"div\", { className: css.activityList, children: [...activity].reverse().slice(0, 10).map((entry, index) => {\n // A same-host session team resolves to its session row;\n // clicking the activity line jumps there (the linkage\n // into an ongoing collaboration).\n const target = sessions.find(row => row.team === entry.team);\n return (_jsx(FeedRow, { dir: entry.dir, team: entry.team, peer: entry.peer, ts: entry.ts, ok: entry.ok, ...(target !== undefined ? { target } : {}), openSession: openSession, t: t }, `${String(entry.ts)}-${String(index)}`));\n }) }))] })] }))\n : null] }));\n}\n","/** Locale dictionaries for the A2A network sidebar control (namespace `a2aNet`). */\n/** Simplified Chinese dictionary. */\nexport const zh = {\n 'a2a.label': 'A2A 网络',\n 'a2a.title': 'A2A 网络动态',\n 'a2a.activity': '最近动态',\n 'a2a.activityEmpty': '暂无网络动态——加入网络即可开始协作',\n 'a2a.inFlight': '进行中的路由',\n 'a2a.inFlightStale': '等待回执超时(对端处理慢或回执丢失);180 秒后自动解除并按已送达处理',\n 'a2a.tasks': '欠回执的异步任务',\n 'a2a.tasksNote': '异步欠账分三段:未决(等回执)→ 死信(超期自动标注,目标恢复后仍可补回执结算)→ 归档(结算留痕不蒸发)',\n 'a2a.tasksStale': '对话式已送达未答(非欠账,24h 自动死信)',\n 'a2a.jump': '点击跳转到该会话',\n 'a2a.stageTitle': 'A2A 工作台(目录 · 台账 · 活动 · 规划)',\n 'a2a.stageWorkbench': '打开工作台',\n};\n/** English dictionary. */\nexport const en = {\n 'a2a.label': 'A2A network',\n 'a2a.title': 'A2A network activity',\n 'a2a.activity': 'Recent activity',\n 'a2a.activityEmpty': 'No network activity yet — join sessions to the network to start collaborating',\n 'a2a.inFlight': 'Routes in flight',\n 'a2a.inFlightStale': 'Reply wait past 120s (slow or lost receipt); auto-releases as delivered at 180s',\n 'a2a.tasks': 'Owed receipts',\n 'a2a.tasksNote': 'Async debts in three tiers: pending (awaiting receipt) → dead-lettered (auto-flagged past the stale TTL; a revived target can still settle late) → archived (settled records kept for audit)',\n 'a2a.tasksStale': 'Delivered conversationally, no formal receipt (not debt; auto-dead-letters at the TTL)',\n 'a2a.jump': 'Click to open this session',\n 'a2a.stageTitle': 'A2A workbench (directory · ledger · activity · plan)',\n 'a2a.stageWorkbench': 'Open workbench',\n};\n","import { A2aControl } from \"./A2aControl.js\";\nimport { en, zh } from \"./locales.js\";\n/** Dictionary namespace owned by this plugin (network control copy). */\nconst NS = 'a2aNet';\n/** Services required by the browser half: the slot system, the sessions\n * flow used to wake cold rows, and the locale dictionaries. */\nexport const inject = ['slots', 'sessions', 'locale'];\n/** Registers the network control's dictionaries and its sidebar seat.\n * @param ctx - Client root context.\n */\nexport function apply(ctx) {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'a2a-net: dictionaries');\n // The network control renders only on a host that serves the session-node\n // state route (a2a host half + session nodes composed); the probe keeps\n // the seat quiet everywhere else.\n void fetch('/__dsh_a2a/state', { cache: 'no-store' })\n .then(response => (response.ok ? response.json() : undefined))\n .then((info) => {\n if (info?.nodes !== true)\n return;\n ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({\n name: 'sidebar.footer.action',\n id: 'a2a',\n order: 10,\n locale: NS,\n // Wake = the standard session-open flow: the host materializes the\n // session's agent and the a2a host half's persisted intent remounts\n // the node, so the cold row flips live on the next poll.\n inject: () => ({\n openSession: (id) => { ctx.sessions.open(id); },\n }),\n }, A2aControl));\n })\n .catch(() => { });\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["css","Button","IconGlobeOutlineRegular","_Fragment"],"sources":["../node_modules/clsx/dist/clsx.mjs","client/A2aControl.js","client/locales.js","client/index.js"],"sourcesContent":["function r(e){var t,f,n=\"\";if(\"string\"==typeof e||\"number\"==typeof e)n+=e;else if(\"object\"==typeof e)if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(f=r(e[t]))&&(n&&(n+=\" \"),n+=f)}else for(f in e)e[f]&&(n&&(n+=\" \"),n+=f);return n}export function clsx(){for(var e,t,f=0,n=\"\",o=arguments.length;f<o;f++)(e=arguments[f])&&(t=r(e))&&(n&&(n+=\" \"),n+=t);return n}export default clsx;","import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from \"react/jsx-runtime\";\n/**\n * A2A network control: a footer action opening this host's recent network\n * activity feed. The feed is the panel's whole content — in-flight routes,\n * owed receipts, and the routing activity ring. Session/team management\n * lives in the planning view (design: docs/design/panel-activity-feed.md):\n * the stage links row above the feed is the explicit navigation to it.\n * Probes the host's state route at registration time (the seat renders only\n * on a host that serves it). While open, the feed polls so it visibly\n * follows each turn; the closed trigger's unread badge rides a slow poll.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport { Button, IconGlobeOutlineRegular } from '@deepseek-ai/dsh-client-ui-primitives';\nimport css from './A2aControl.module.css';\n/** Fetch the host's network state; undefined means the route is absent. */\nasync function fetchState() {\n const response = await fetch('/__dsh_a2a/state', { cache: 'no-store' });\n if (!response.ok)\n return undefined;\n const body = await response.json();\n if (body.nodes !== true || !Array.isArray(body.sessions))\n return undefined;\n return {\n sessions: body.sessions,\n activity: Array.isArray(body.activity) ? body.activity : [],\n inFlight: Array.isArray(body.inFlight) ? body.inFlight : [],\n tasks: Array.isArray(body.tasks) ? body.tasks : [],\n ...(typeof body.tasksStaleCount === 'number' ? { tasksStaleCount: body.tasksStaleCount } : {}),\n ...(typeof body.version === 'string' ? { version: body.version } : {}),\n };\n}\n/** Compact relative time for an activity row. */\nfunction relativeTime(ts) {\n const seconds = Math.max(0, Math.round((Date.now() - ts) / 1000));\n if (seconds < 60)\n return `${String(seconds)}s`;\n const minutes = Math.round(seconds / 60);\n if (minutes < 60)\n return `${String(minutes)}m`;\n return `${String(Math.round(minutes / 60))}h`;\n}\n/** One feed row: direction glyph (aria-labelled), team, peer host, age, fail mark. */\nfunction FeedRow({ dir, team, peer, ts, ok, target, openSession, t }) {\n const fresh = Date.now() - ts < 5_000;\n return (_jsxs(\"div\", { className: clsx(css.activityRow, fresh && css.fresh), \"data-ok\": ok === undefined ? undefined : String(ok), \"data-jump\": target !== undefined, title: target !== undefined ? t('a2a.jump') : undefined, onClick: () => { if (target !== undefined)\n openSession(target.id); }, role: target !== undefined ? 'button' : undefined, children: [_jsx(\"span\", { className: clsx(css.activityDir, dir), \"aria-label\": dir === 'fwd' ? 'forwarded' : dir === 'in' ? 'inbound' : 'outbound', children: dir === 'fwd' ? '⇢' : dir === 'in' ? '←' : '→' }), _jsx(\"span\", { className: css.activityTeam, title: team, children: team }), _jsx(\"span\", { className: css.activityPeer, title: peer, children: peer === '' || peer === 'local' ? '' : peer.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '') }), ok === false ? _jsx(\"span\", { className: css.failMark, \"aria-label\": \"failed\", children: \"\\u2715\" }) : null, _jsx(\"span\", { className: css.activityTime, children: relativeTime(ts) })] }));\n}\n/**\n * Render the network toggle and its activity feed.\n * @param props - composed slot props.\n * @returns the control element tree.\n */\nexport function A2aControl({ wide, t, openSession }) {\n const [open, setOpen] = useState(false);\n const [state, setState] = useState({ sessions: [], activity: [], inFlight: [], tasks: [] });\n const [seenActivity, setSeenActivity] = useState(0);\n const stopped = useRef(false);\n useEffect(() => () => { stopped.current = true; }, []);\n const wrap = useRef(null);\n // The popover anchors to the trigger's viewport rect: the sidebar column\n // clips its descendants (the collapse slide), so an in-column absolute\n // popover is cut at the column edge whenever the column is narrower than\n // the popover; fixed positioning escapes the clip at rest (the ui-settings\n // panel precedent). The left edge clamps so a narrow viewport never hides\n // the popover's right side.\n const [anchor, setAnchor] = useState(null);\n // The unread badge counts inbound activity entries that arrived while the\n // panel was closed; opening the panel clears it.\n const unread = Math.max(0, state.activity.filter(entry => entry.dir === 'in').length - seenActivity);\n const toggleOpen = () => {\n if (open) {\n setOpen(false);\n return;\n }\n const rect = wrap.current?.getBoundingClientRect();\n if (rect !== undefined) {\n setAnchor({\n left: Math.max(8, Math.min(rect.left, window.innerWidth - 336)),\n bottom: window.innerHeight - rect.top + 8,\n });\n }\n setSeenActivity(state.activity.filter(entry => entry.dir === 'in').length);\n setOpen(true);\n };\n const refresh = useCallback(() => {\n void fetchState().then((next) => {\n if (!stopped.current && next !== undefined)\n setState(next);\n }).catch(() => { });\n }, []);\n useEffect(() => {\n if (open)\n refresh();\n }, [open, refresh]);\n useEffect(() => {\n // The unread badge lives on the closed trigger, so the state must move\n // even while the panel is closed — a slow poll keeps it fresh; the open\n // panel's faster poll below takes over while visible.\n const slow = setInterval(refresh, 10_000);\n refresh();\n return () => { clearInterval(slow); };\n }, [refresh]);\n useEffect(() => {\n if (!open)\n return;\n // While the feed is open, it polls so the ring visibly follows turns.\n const poll = setInterval(refresh, 2_000);\n return () => { clearInterval(poll); };\n }, [open, refresh]);\n useEffect(() => {\n if (!open)\n return;\n const onPointerDown = (event) => {\n if (event.target.closest(`.${css.wrap}`) === null)\n setOpen(false);\n };\n document.addEventListener('pointerdown', onPointerDown);\n return () => { document.removeEventListener('pointerdown', onPointerDown); };\n }, [open]);\n // 0.6.10: the receive face of click-to-reach (0.6.7 BroadcastChannel,\n // 0.6.8 ?dsh-a2a-open deep link) is removed by ruling — 禁止任何跳转,\n // 全部使用响应式布局. The workbench's 快派 form is the only cross-surface\n // reach primitive and it stays on the workbench page; this panel's\n // FeedRow openSession remains (it switches a conversation in place inside\n // the GUI — no navigation).\n const { sessions, activity, inFlight, tasks } = state;\n return (_jsxs(\"div\", { ref: wrap, className: clsx(css.wrap, wide ? css.wide : css.rail), children: [_jsx(Button, { variant: \"ghost\", size: \"sm\", title: t('a2a.label'), \"aria-label\": t('a2a.label'), \"aria-expanded\": open, onClick: () => { toggleOpen(); }, children: _jsxs(\"span\", { className: css.trigger, children: [_jsx(IconGlobeOutlineRegular, {}), wide ? _jsx(\"span\", { children: t('a2a.label') }) : null, unread > 0 ? _jsx(\"span\", { className: css.unreadBadge, \"aria-label\": String(unread), children: unread > 9 ? '9+' : String(unread) }) : null] }) }), open\n ? (_jsxs(\"div\", { className: css.popover, role: \"dialog\", \"aria-label\": t('a2a.title'), style: anchor === null ? undefined : { left: `${String(anchor.left)}px`, bottom: `${String(anchor.bottom)}px` }, children: [_jsxs(\"div\", { className: css.title, children: [t('a2a.title'), state.version !== undefined ? _jsxs(\"span\", { className: css.versionTag, title: state.version, children: [\"v\", state.version] }) : null] }), _jsxs(\"div\", { className: css.stageRow, children: [_jsx(\"span\", { className: css.stageTitle, children: t('a2a.stageTitle') }), _jsx(\"span\", { className: css.stageLinks, children: _jsx(\"a\", { className: css.stageLink, href: \"/__dsh_a2a_nexus/\", target: \"_blank\", rel: \"noreferrer\", title: t('a2a.stageWorkbench'), children: t('a2a.stageWorkbench') }) })] }), _jsxs(\"div\", { \"aria-live\": \"polite\", children: [inFlight.length > 0 && (_jsx(\"div\", { className: css.inFlightList, \"aria-label\": t('a2a.inFlight'), children: inFlight.map((route) => (_jsxs(\"div\", { className: clsx(css.inFlightRow, Date.now() - route.startedAt > 120_000 && css.inFlightStale), title: Date.now() - route.startedAt > 120_000 ? t('a2a.inFlightStale') : undefined, children: [_jsx(\"span\", { className: css.inFlightPulse, \"aria-hidden\": true }), _jsx(FeedRow, { dir: \"out\", team: route.team, peer: route.peer, ts: route.startedAt, openSession: openSession, t: t })] }, `${route.team}-${String(route.startedAt)}`))) })), (() => {\n // C-ruling: render only settleable debt; the stale tail is a\n // count line, never a row-per-noise listing.\n const settleableTasks = tasks.filter(task => task.settleable !== false);\n const staleCount = state.tasksStaleCount ?? (tasks.length - settleableTasks.length);\n if (settleableTasks.length === 0 && staleCount === 0)\n return null;\n return (_jsxs(_Fragment, { children: [settleableTasks.length > 0 && (_jsxs(_Fragment, { children: [_jsx(\"div\", { className: css.sectionTitle, children: t('a2a.tasks') }), _jsx(\"div\", { className: css.inFlightList, \"aria-label\": t('a2a.tasks'), children: settleableTasks.map((task) => (_jsx(\"div\", { className: css.inFlightRow, title: t('a2a.tasksNote'), children: _jsx(FeedRow, { dir: \"out\", team: task.team, peer: task.peer, ts: task.startedAt, openSession: openSession, t: t }) }, task.taskId))) })] })), staleCount > 0 && (_jsxs(\"div\", { className: css.staleNote, title: t('a2a.tasksNote'), children: [t('a2a.tasksStale'), \" \\u00B7 \", String(staleCount)] }))] }));\n })(), _jsx(\"div\", { className: css.sectionTitle, children: t('a2a.activity') }), activity.length === 0\n ? _jsx(\"div\", { className: css.empty, children: t('a2a.activityEmpty') })\n : (_jsx(\"div\", { className: css.activityList, children: [...activity].reverse().slice(0, 10).map((entry, index) => {\n // A same-host session team resolves to its session row;\n // clicking the activity line jumps there (the linkage\n // into an ongoing collaboration).\n const target = sessions.find(row => row.team === entry.team);\n return (_jsx(FeedRow, { dir: entry.dir, team: entry.team, peer: entry.peer, ts: entry.ts, ok: entry.ok, ...(target !== undefined ? { target } : {}), openSession: openSession, t: t }, `${String(entry.ts)}-${String(index)}`));\n }) }))] })] }))\n : null] }));\n}\n","/** Locale dictionaries for the A2A network sidebar control (namespace `a2aNet`). */\n/** Simplified Chinese dictionary. */\nexport const zh = {\n 'a2a.label': 'A2A 网络',\n 'a2a.title': 'A2A 网络动态',\n 'a2a.activity': '最近动态',\n 'a2a.activityEmpty': '暂无网络动态——加入网络即可开始协作',\n 'a2a.inFlight': '进行中的路由',\n 'a2a.inFlightStale': '等待回执超时(对端处理慢或回执丢失);180 秒后自动解除并按已送达处理',\n 'a2a.tasks': '欠回执的异步任务',\n 'a2a.tasksNote': '异步欠账分三段:未决(等回执)→ 死信(超期自动标注,目标恢复后仍可补回执结算)→ 归档(结算留痕不蒸发)',\n 'a2a.tasksStale': '对话式已送达未答(非欠账,24h 自动死信)',\n 'a2a.jump': '点击跳转到该会话',\n 'a2a.stageTitle': 'A2A 工作台(目录 · 台账 · 活动 · 规划)',\n 'a2a.stageWorkbench': '打开工作台',\n};\n/** English dictionary. */\nexport const en = {\n 'a2a.label': 'A2A network',\n 'a2a.title': 'A2A network activity',\n 'a2a.activity': 'Recent activity',\n 'a2a.activityEmpty': 'No network activity yet — join sessions to the network to start collaborating',\n 'a2a.inFlight': 'Routes in flight',\n 'a2a.inFlightStale': 'Reply wait past 120s (slow or lost receipt); auto-releases as delivered at 180s',\n 'a2a.tasks': 'Owed receipts',\n 'a2a.tasksNote': 'Async debts in three tiers: pending (awaiting receipt) → dead-lettered (auto-flagged past the stale TTL; a revived target can still settle late) → archived (settled records kept for audit)',\n 'a2a.tasksStale': 'Delivered conversationally, no formal receipt (not debt; auto-dead-letters at the TTL)',\n 'a2a.jump': 'Click to open this session',\n 'a2a.stageTitle': 'A2A workbench (directory · ledger · activity · plan)',\n 'a2a.stageWorkbench': 'Open workbench',\n};\n","import { A2aControl } from \"./A2aControl.js\";\nimport { en, zh } from \"./locales.js\";\n/** Dictionary namespace owned by this plugin (network control copy). */\nconst NS = 'a2aNet';\n/** Services required by the browser half: the slot system, the sessions\n * flow used to wake cold rows, and the locale dictionaries. */\nexport const inject = ['slots', 'sessions', 'locale'];\n/** Registers the network control's dictionaries and its sidebar seat.\n * @param ctx - Client root context.\n */\nexport function apply(ctx) {\n ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'a2a-net: dictionaries');\n // The network control renders only on a host that serves the session-node\n // state route (a2a host half + session nodes composed); the probe keeps\n // the seat quiet everywhere else.\n void fetch('/__dsh_a2a/state', { cache: 'no-store' })\n .then(response => (response.ok ? response.json() : undefined))\n .then((info) => {\n if (info?.nodes !== true)\n return;\n ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({\n name: 'sidebar.footer.action',\n id: 'a2a',\n order: 10,\n locale: NS,\n // Wake = the standard session-open flow: the host materializes the\n // session's agent and the a2a host half's persisted intent remounts\n // the node, so the cold row flips live on the next poll.\n inject: () => ({\n openSession: (id) => { ctx.sessions.open(id); },\n }),\n }, A2aControl));\n })\n .catch(() => { });\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
package/lib/config.js CHANGED
@@ -100,6 +100,7 @@ export const Config = s.object({
100
100
  * audience-bound form only. See docs/protocol/delivery-origin-auth.md §2.1.
101
101
  */
102
102
  originClaimUnboundWindow: s.boolean().default(true),
103
+ canvasLegacyBareMembers: s.boolean().default(true),
103
104
  /** Extra OriginClaim audience origins (port-mapped/proxied hosts). */
104
105
  originClaimAudiences: s.array(s.string()).default([]),
105
106
  linkKeys: s.dict(s.string()).default({}),
@@ -18,7 +18,7 @@
18
18
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
19
19
  import { join } from 'node:path';
20
20
  import { fileURLToPath } from 'node:url';
21
- import { canvasNameReserved as reservedCanvasName, isRemoteMemberRef, validRemoteMemberRef } from "../canvas-store.js";
21
+ import { canvasNameReserved as reservedCanvasName, isRemoteMemberRef, validRemoteMemberRef, legacyBareMember } from "../canvas-store.js";
22
22
  import { qualifiedCanvasId } from "../canvas-id.js";
23
23
  import { refreshPeerCard, registerCanvasNotify } from "./canvas-notify.js";
24
24
  import { CLI_HANDLE_RE } from "../cli-deposit.js";
@@ -133,6 +133,9 @@ export function registerAdminFaces(webServer, d) {
133
133
  sessions,
134
134
  groups: groupStore.list(),
135
135
  canvas: {
136
+ ...d.canvasMigrationState(),
137
+ // Raw retained slots are an inspection view, never the effective team member list.
138
+ retainedMembers: canvasStore.list().flatMap(name => canvasStore.rawMembersOf(name).filter(id => !canvasStore.membersOf(name).includes(id)).map(id => ({ team: name, member: id, suspended: canvasStore.isSuspended(name, id), records: canvasStore.suspensionOf(name, id) }))),
136
139
  teams: canvasStore.list().map(name => ({
137
140
  name,
138
141
  team: canvasTeamOf(name),
@@ -148,16 +151,17 @@ export function registerAdminFaces(webServer, d) {
148
151
  const rows = [];
149
152
  const seenLocal = new Set();
150
153
  for (const id of canvasStore.membersOf(name)) {
154
+ const legacy = legacyBareMember(id);
151
155
  if (isRemoteMemberRef(id)) {
152
156
  const localId = localSessionOfHandle(id);
153
157
  if (localId !== undefined) {
154
158
  if (seenLocal.has(localId))
155
159
  continue;
156
160
  seenLocal.add(localId);
157
- rows.push({ id: localId, team: `${config.team}/${id8(localId)}`, joined: sessionNodes.has(localId) || joinedSessions.has(localId), live: sessionNodes.has(localId) });
161
+ rows.push({ ...(legacy ? { provenance: 'legacy-inferred' } : {}), id: localId, team: `${config.team}/${id8(localId)}`, joined: sessionNodes.has(localId) || joinedSessions.has(localId), live: sessionNodes.has(localId) });
158
162
  continue;
159
163
  }
160
- rows.push({ id, team: id, remote: true, ...(CLI_HANDLE_RE.test(id) ? { cliHome: canvasStore.cliHomeOf(id) ?? null, cliHeld: cliDeposits.directoryRows().some(row => row.team === id), ...(canvasStore.wasCliHomeCleared(id) ? { cliHomeCleared: true } : {}) } : {}) });
164
+ rows.push({ ...(legacy ? { provenance: 'unknown' } : {}), id, team: id, remote: true, ...(CLI_HANDLE_RE.test(id) ? { cliHome: canvasStore.cliHomeOf(id) ?? null, cliHeld: cliDeposits.directoryRows().some(row => row.team === id), ...(canvasStore.wasCliHomeCleared(id) ? { cliHomeCleared: true } : {}) } : {}) });
161
165
  continue;
162
166
  }
163
167
  if (seenLocal.has(id))
@@ -169,7 +173,8 @@ export function registerAdminFaces(webServer, d) {
169
173
  })(),
170
174
  })),
171
175
  },
172
- host: lanIp === '' ? {} : { lanIp },
176
+ // #332: home-controlled aliases, never inferred from peer cards or a file reference.
177
+ host: { ...(lanIp !== '' ? { lanIp } : {}), originClaimAudiences: [...config.originClaimAudiences ?? []] },
173
178
  // gh#20 W-C (host rail): each peer row also carries its display
174
179
  // name and version from the warm card (absent until a fetch),
175
180
  // and the last-contact timestamp for the health lamp. W-A's
@@ -445,7 +450,16 @@ export function registerAdminFaces(webServer, d) {
445
450
  readJsonBody(req, res, (body) => {
446
451
  const id = typeof body.id === 'string' ? body.id : '';
447
452
  // gh#131: read while the session is still joined — its own handle reads as it only then.
448
- const ownHandle = id !== '' ? ownCanvasHandleOf(id) : undefined;
453
+ try {
454
+ if (id !== '')
455
+ d.revokeCanvasMember(id, 'leave');
456
+ }
457
+ catch (error) {
458
+ const payload = JSON.stringify({ ok: false, error: String(error) });
459
+ res.writeHead(503, { 'Content-Type': 'application/json' });
460
+ res.end(payload);
461
+ return;
462
+ }
449
463
  if (id !== '')
450
464
  sessionNodes.delete(id);
451
465
  joinedSessions.remove(id);
@@ -453,9 +467,6 @@ export function registerAdminFaces(webServer, d) {
453
467
  // without join consent would be a routing backdoor. Team
454
468
  // roster declarations die the same way (S2 consistency) — and so
455
469
  // does a membership a fleet edit stored by the session's handle.
456
- canvasStore.dropMember(id);
457
- if (ownHandle !== undefined)
458
- canvasStore.dropMember(ownHandle);
459
470
  teamMemberships.dropSession(id);
460
471
  const payload = JSON.stringify({ id });
461
472
  res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) });
@@ -545,6 +556,12 @@ export function registerAdminFaces(webServer, d) {
545
556
  body,
546
557
  signal: AbortSignal.timeout(15_000),
547
558
  });
559
+ // #320: a JSON body cannot turn a failed/redirected HTTP operation into success.
560
+ if (!reply.ok || reply.status < 200 || reply.status >= 300) {
561
+ void reply.body?.cancel().catch(() => { });
562
+ send(JSON.stringify({ ok: false, status: reply.status, error: `peer ${peerUrl} answered HTTP ${String(reply.status)} — fleet canvas operation failed` }));
563
+ return;
564
+ }
548
565
  const raw = await reply.text();
549
566
  try {
550
567
  const out = JSON.parse(raw);
@@ -738,72 +755,95 @@ export function registerAdminFaces(webServer, d) {
738
755
  readJsonBody(req, res, async (body) => {
739
756
  const action = body.action;
740
757
  let payload;
741
- if (action === 'cli-home-options') {
742
- const name = typeof body.name === 'string' ? body.name : '';
743
- const id = typeof body.id === 'string' ? body.id : '';
744
- if (!CLI_HANDLE_RE.test(id) || !canvasStore.membersOf(name).includes(id))
745
- payload = JSON.stringify({ ok: false, error: 'an existing canvas CLI member is required' });
746
- else {
747
- try {
748
- payload = JSON.stringify({ ok: true, ...await d.cliHomeOptions(id) });
749
- }
750
- catch {
751
- payload = JSON.stringify({ ok: false, error: 'fresh home card lookup failed' });
758
+ try {
759
+ if (action === 'restore-suspended' || action === 'delete-suspended') {
760
+ const request = body;
761
+ const name = typeof request.name === 'string' ? request.name : '', id = typeof request.id === 'string' ? request.id : '';
762
+ const out = canvasStore.regulateSuspended(action === 'restore-suspended' ? 'restore' : 'delete', name, id, request.revision, request.expected);
763
+ logger.info('canvas suspended owner operation %s %s %s: %s', action, name, id, out.ok);
764
+ payload = JSON.stringify({ ...out, ...canvasStore.state() });
765
+ }
766
+ else if (action === 'cli-home-options') {
767
+ const name = typeof body.name === 'string' ? body.name : '';
768
+ const id = typeof body.id === 'string' ? body.id : '';
769
+ if (!CLI_HANDLE_RE.test(id) || !canvasStore.membersOf(name).includes(id))
770
+ payload = JSON.stringify({ ok: false, error: 'an existing canvas CLI member is required' });
771
+ else {
772
+ try {
773
+ payload = JSON.stringify({ ok: true, ...await d.cliHomeOptions(id) });
774
+ }
775
+ catch {
776
+ payload = JSON.stringify({ ok: false, error: 'fresh home card lookup failed' });
777
+ }
752
778
  }
753
779
  }
754
- }
755
- else if (action === 'create') {
756
- const name = typeof body.name === 'string' ? body.name : '';
757
- const stored = canvasNameReserved(name) ? undefined : canvasStore.create(name);
758
- payload = JSON.stringify(stored === undefined ? { ok: false, error: 'invalid, roster-reserved name or team cap reached' } : { ok: true, name: stored, teams: canvasStore.list() });
759
- }
760
- else if (action === 'remove') {
761
- const name = typeof body.name === 'string' ? body.name : '';
762
- payload = JSON.stringify({ ok: canvasStore.remove(name), teams: canvasStore.list() });
763
- }
764
- else if (action === 'add-member') {
765
- const name = typeof body.name === 'string' ? body.name : '';
766
- const id = typeof body.id === 'string' ? body.id : '';
767
- // Dual member shape: a local session id must be a joined node;
768
- // a remote ref is a zone-qualified team handle whose shape this
769
- // gate checks and whose existence the UI picker (directory
770
- // rows) vouches for. Humans compose both via the workbench.
771
- const memberValid = isRemoteMemberRef(id)
772
- ? validRemoteMemberRef(id)
773
- : id !== '' && (sessionNodes.has(id) || joinedSessions.has(id));
774
- if (!canvasStore.hasTeam(name) && canvasNameReserved(name)) {
775
- payload = JSON.stringify({ ok: false, error: 'roster reserves this canvas name' });
780
+ else if (action === 'create') {
781
+ const name = typeof body.name === 'string' ? body.name : '';
782
+ const stored = canvasNameReserved(name) ? undefined : canvasStore.create(name);
783
+ payload = JSON.stringify(stored === undefined ? { ok: false, error: 'invalid, roster-reserved name or team cap reached' } : { ok: true, name: stored, teams: canvasStore.list() });
776
784
  }
777
- else if (name === '' || !memberValid) {
778
- payload = JSON.stringify({ ok: false, error: 'name and a member (joined session id, or a zone-qualified remote team handle like zone/a1b2c3d4) are required' });
785
+ else if (action === 'remove') {
786
+ const name = typeof body.name === 'string' ? body.name : '';
787
+ payload = JSON.stringify({ ok: canvasStore.remove(name), teams: canvasStore.list() });
779
788
  }
780
- else {
781
- const pin = await d.cliHomeForAdd(id, body.home).catch(() => ({ ok: false, error: 'linked home card lookup failed' }));
782
- if (!pin.ok)
783
- payload = JSON.stringify({ ok: false, error: pin.error });
789
+ else if (action === 'add-member') {
790
+ const name = typeof body.name === 'string' ? body.name : '';
791
+ const id = typeof body.id === 'string' ? body.id : '';
792
+ // Dual member shape: a local session id must be a joined node;
793
+ // a remote ref is a zone-qualified team handle whose shape this
794
+ // gate checks and whose existence the UI picker (directory
795
+ // rows) vouches for. Humans compose both via the workbench.
796
+ const memberValid = isRemoteMemberRef(id)
797
+ ? validRemoteMemberRef(id)
798
+ : id !== '' && (sessionNodes.has(id) || joinedSessions.has(id));
799
+ if (!canvasStore.hasTeam(name) && canvasNameReserved(name)) {
800
+ payload = JSON.stringify({ ok: false, error: 'roster reserves this canvas name' });
801
+ }
802
+ else if (name === '' || !memberValid) {
803
+ payload = JSON.stringify({ ok: false, error: 'name and a member (joined session id, or a zone-qualified remote team handle like zone/a1b2c3d4) are required' });
804
+ }
784
805
  else {
785
- canvasStore.create(name);
786
- const explicitHome = body.home !== undefined;
787
- const home = explicitHome ? pin.home : canvasStore.cliHomeOf(id) ?? pin.home;
788
- payload = JSON.stringify({ ok: canvasStore.addMember(name, id, home, { replaceHome: explicitHome }), teams: canvasStore.list(), members: canvasStore.membersOf(name) });
806
+ const pin = await d.cliHomeForAdd(id, body.home).catch(() => ({ ok: false, error: 'linked home card lookup failed' }));
807
+ if (!pin.ok)
808
+ payload = JSON.stringify({ ok: false, error: pin.error });
809
+ else {
810
+ canvasStore.create(name);
811
+ const explicitHome = body.home !== undefined;
812
+ const home = explicitHome ? pin.home : canvasStore.cliHomeOf(id) ?? pin.home;
813
+ payload = JSON.stringify({ ok: canvasStore.addMember(name, id, home, { replaceHome: explicitHome }), teams: canvasStore.list(), members: canvasStore.membersOf(name) });
814
+ }
789
815
  }
790
816
  }
791
- }
792
- else if (action === 'remove-member') {
793
- const name = typeof body.name === 'string' ? body.name : '';
794
- const id = typeof body.id === 'string' ? body.id : '';
795
- if (name === '' || id === '') {
796
- payload = JSON.stringify({ ok: false, error: 'name and id are required' });
817
+ else if (action === 'remove-member') {
818
+ const name = typeof body.name === 'string' ? body.name : '';
819
+ const id = typeof body.id === 'string' ? body.id : '';
820
+ if (name === '' || id === '') {
821
+ payload = JSON.stringify({ ok: false, error: 'name and id are required' });
822
+ }
823
+ else {
824
+ // gh#131: removing a local session removes its handle-stored slot too.
825
+ let removed = false;
826
+ try {
827
+ removed = isRemoteMemberRef(id) ? (!canvasStore.isSuspended(name, id) && canvasStore.removeMember(name, id)) : d.revokeCanvasMember(id, 'remove', name);
828
+ }
829
+ catch (error) {
830
+ payload = JSON.stringify({ ok: false, error: String(error) });
831
+ res.writeHead(503, { 'Content-Type': 'application/json' });
832
+ res.end(payload);
833
+ return;
834
+ }
835
+ payload = JSON.stringify({ ok: removed, teams: canvasStore.list(), members: canvasStore.membersOf(name) });
836
+ }
797
837
  }
798
838
  else {
799
- // gh#131: removing a local session removes its handle-stored slot too.
800
- const ownHandle = isRemoteMemberRef(id) ? undefined : ownCanvasHandleOf(id);
801
- const removed = [id, ...(ownHandle !== undefined ? [ownHandle] : [])].map(each => canvasStore.removeMember(name, each)).some(Boolean);
802
- payload = JSON.stringify({ ok: removed, teams: canvasStore.list(), members: canvasStore.membersOf(name) });
839
+ payload = JSON.stringify({ ok: false, error: 'unknown action' });
803
840
  }
804
841
  }
805
- else {
806
- payload = JSON.stringify({ ok: false, error: 'unknown action' });
842
+ catch (error) {
843
+ const failure = JSON.stringify({ ok: false, error: String(error) });
844
+ res.writeHead(503, { 'Content-Type': 'application/json' });
845
+ res.end(failure);
846
+ return;
807
847
  }
808
848
  res.writeHead(200, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) });
809
849
  res.end(payload);
package/lib/faces/wire.js CHANGED
@@ -182,7 +182,9 @@ export function registerWireFaces(webServer, d) {
182
182
  const name = typeof body.name === 'string' ? body.name : '';
183
183
  const rawId = typeof body.id === 'string' ? body.id : '';
184
184
  // gh#131: a peer names our sessions by handle — the session it names, for removal.
185
- const id = (isRemoteMemberRef(rawId) ? localSessionOfHandle(rawId) : undefined) ?? rawId;
185
+ // A bare handle may still infer for routing, never for deleting another stored slot.
186
+ const provenOwn = rawId.endsWith('@' + d.ownCanvasFingerprint);
187
+ const id = (provenOwn ? localSessionOfHandle(rawId) : undefined) ?? rawId;
186
188
  let payload;
187
189
  if (op === 'create') {
188
190
  const stored = canvasNameReserved(name) ? undefined : canvasStore.create(name);