@nelsonlongxiang/dsh-open-a2a-net 0.5.14 → 0.6.24

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 (86) hide show
  1. package/README.md +243 -121
  2. package/README_zh.md +144 -84
  3. package/assets/nexusDist/assets/index-034h1cNc.js +3 -0
  4. package/assets/nexusDist/assets/index-PG9EieGX.css +1 -0
  5. package/assets/nexusDist/index.html +26 -0
  6. package/assets/stageDist/assets/index-Bj7S2pA1.css +1 -0
  7. package/assets/stageDist/assets/index-C-28k_o5.js +974 -0
  8. package/assets/stageDist/assets/interiors-CQGBjffT.png +0 -0
  9. package/assets/stageDist/assets/office-tileset-CHQT-uYL.png +0 -0
  10. package/assets/stageDist/index.html +13 -0
  11. package/lib/a2a-client.js +190 -13
  12. package/lib/canvas-store.js +218 -0
  13. package/lib/card.js +25 -0
  14. package/lib/client/A2aControl.js +45 -137
  15. package/lib/client/locales.js +16 -40
  16. package/lib/client.js +164 -467
  17. package/lib/client.js.map +1 -1
  18. package/lib/group-store.js +6 -0
  19. package/lib/idempotency-store.js +291 -0
  20. package/lib/index.js +4217 -259
  21. package/lib/joined-store.js +11 -0
  22. package/lib/layout-store.js +155 -0
  23. package/lib/peer-store.js +98 -13
  24. package/lib/receipt-ladder.js +35 -0
  25. package/lib/receipt.js +102 -0
  26. package/lib/self-suppress.js +62 -0
  27. package/lib/session-transcript.js +150 -0
  28. package/lib/skill/SKILL.md +77 -0
  29. package/lib/skill/references/discovery.md +38 -0
  30. package/lib/skill/references/membership.md +50 -0
  31. package/lib/skill/references/pitfalls.md +42 -0
  32. package/lib/skill/references/routing.md +43 -0
  33. package/lib/skill-registration.js +52 -0
  34. package/lib/stage-mount.js +32 -0
  35. package/lib/task-ledger.js +540 -0
  36. package/lib/team-store.js +122 -0
  37. package/lib/teams-bridge.js +18 -0
  38. package/lib/transport-caps.js +31 -0
  39. package/lib/types/a2a-client.d.ts +72 -1
  40. package/lib/types/canvas-store.d.ts +102 -0
  41. package/lib/types/client/A2aControl.d.ts +18 -28
  42. package/lib/types/client/locales.d.ts +1 -1
  43. package/lib/types/group-store.d.ts +6 -0
  44. package/lib/types/idempotency-store.d.ts +164 -0
  45. package/lib/types/index.d.ts +210 -6
  46. package/lib/types/joined-store.d.ts +9 -0
  47. package/lib/types/layout-store.d.ts +63 -0
  48. package/lib/types/peer-store.d.ts +45 -2
  49. package/lib/types/receipt-ladder.d.ts +25 -0
  50. package/lib/types/receipt.d.ts +51 -0
  51. package/lib/types/self-suppress.d.ts +41 -0
  52. package/lib/types/session-transcript.d.ts +37 -0
  53. package/lib/types/skill-registration.d.ts +3 -0
  54. package/lib/types/stage-mount.d.ts +29 -0
  55. package/lib/types/task-ledger.d.ts +308 -0
  56. package/lib/types/team-store.d.ts +55 -0
  57. package/lib/types/teams-bridge.d.ts +112 -0
  58. package/lib/types/transport-caps.d.ts +29 -0
  59. package/lib/types/types.d.ts +43 -0
  60. package/nexus-stage/package-lock.json +2262 -0
  61. package/nexus-stage/package.json +21 -0
  62. package/nexus-stage/scripts/__pycache__/mockhost.cpython-312.pyc +0 -0
  63. package/nexus-stage/scripts/mockhost.py +253 -0
  64. package/nexus-stage/src/canvas-ops.ts +175 -0
  65. package/nexus-stage/src/canvas-wire.ts +156 -0
  66. package/nexus-stage/src/edges.ts +72 -0
  67. package/nexus-stage/src/fault.ts +56 -0
  68. package/nexus-stage/src/federation.ts +234 -0
  69. package/nexus-stage/src/layout-doc.ts +143 -0
  70. package/nexus-stage/src/layout-wire.ts +158 -0
  71. package/nexus-stage/src/main.ts +361 -0
  72. package/nexus-stage/src/planning-menus.ts +352 -0
  73. package/nexus-stage/src/planning-nodelist.ts +294 -0
  74. package/nexus-stage/src/planning-types.ts +111 -0
  75. package/nexus-stage/src/planning-view.ts +1313 -0
  76. package/nexus-stage/src/planning.css +280 -0
  77. package/nexus-stage/src/seat.ts +35 -0
  78. package/nexus-stage/src/stage-name.ts +13 -0
  79. package/nexus-stage/src/tokens.ts +32 -0
  80. package/nexus-stage/src/viewport.ts +83 -0
  81. package/nexus-stage/src/workbench.css +267 -0
  82. package/nexus-stage/src/workbench.ts +1530 -0
  83. package/nexus-stage/src/world.ts +509 -0
  84. package/nexus-stage/tsconfig.json +25 -0
  85. package/nexus-stage/vite.config.ts +16 -0
  86. package/package.json +83 -22
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["css","Button","IconGlobeOutline14","_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 network panel.\n * Probes the host's session-node state route at registration time (the seat\n * renders only on a host that serves it). The panel has three sections:\n * session nodes (live rows show title, recent-activity excerpt, and team with\n * a join/leave toggle and open the session on row click — the wake action on\n * cold rows opens the session through the standard flow), the peer fleet\n * (tracked URLs with quality scores), and the recent routing activity ring\n * (inbound and outbound outcomes). While open, the panel polls so listings\n * visibly follow each turn.\n */\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport clsx from 'clsx';\nimport { Button, IconGlobeOutline14 } 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 groups: Array.isArray(body.groups) ? body.groups : [],\n peers: Array.isArray(body.peers) ? body.peers : [],\n activity: Array.isArray(body.activity) ? body.activity : [],\n inFlight: Array.isArray(body.inFlight) ? body.inFlight : [],\n remote: Array.isArray(body.remote) ? body.remote : [],\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/**\n * One session row: the facts block opens the session, the toggle joins or\n * leaves, and the tag button opens the group picker (assign / create /\n * clear).\n */\nfunction SessionRow({ row, t, listById, openSession, busy, onToggle, pickerFor, setPickerFor, groups, newGroupName, setNewGroupName, onAssign }) {\n const cold = row.live === false;\n const name = cold ? (listById[row.id]?.displayTitle ?? row.label) : (row.name ?? row.label);\n const description = cold ? t('a2a.cold') : (row.description ?? '');\n const pickerOpen = pickerFor === row.id;\n return (_jsxs(\"div\", { className: clsx(css.row, row.joined && css.joined), children: [_jsxs(\"button\", { type: \"button\", className: css.facts, title: cold ? t('a2a.wake') : name, onClick: () => { openSession(row.id); }, children: [_jsxs(\"span\", { className: css.nameRow, children: [_jsx(\"span\", { className: clsx(css.stateDot, cold ? 'cold' : 'live', row.joined && 'joined'), \"aria-hidden\": true }), _jsx(\"span\", { className: css.name, children: name }), row.group !== undefined ? _jsx(\"span\", { className: css.groupTag, children: row.group }) : null] }), _jsx(\"span\", { className: css.description, children: description }), _jsx(\"span\", { className: css.team, children: row.team })] }), _jsx(\"button\", { type: \"button\", className: css.groupButton, title: t('a2a.group'), \"aria-label\": t('a2a.group'), onClick: () => { setPickerFor(pickerOpen ? null : row.id); }, children: \"#\" }), cold\n ? (_jsx(Button, { variant: \"outline\", size: \"sm\", disabled: busy === row.id, onClick: () => { onToggle(row); }, children: t('a2a.leave') }))\n : (_jsx(Button, { variant: row.joined ? 'outline' : 'primary', size: \"sm\", disabled: busy === row.id, onClick: () => { onToggle(row); }, children: row.joined ? t('a2a.leave') : t('a2a.join') })), pickerOpen && (_jsxs(\"div\", { className: css.groupPicker, children: [_jsx(\"button\", { type: \"button\", className: css.groupOption, onClick: () => { onAssign(row.id, ''); }, children: t('a2a.groupClear') }), groups.map((group) => (_jsx(\"button\", { type: \"button\", className: clsx(css.groupOption, row.group === group && css.groupOptionActive), onClick: () => { onAssign(row.id, group); }, children: group }, group))), _jsxs(\"div\", { className: css.groupNew, children: [_jsx(\"input\", { className: css.groupInput, value: newGroupName, placeholder: t('a2a.groupNew'), maxLength: 40, onChange: (event) => { setNewGroupName(event.target.value); }, onKeyDown: (event) => {\n if (event.key === 'Enter' && newGroupName.trim() !== '')\n onAssign(row.id, newGroupName.trim());\n } }), _jsx(Button, { variant: \"primary\", size: \"sm\", disabled: newGroupName.trim() === '', onClick: () => { if (newGroupName.trim() !== '')\n onAssign(row.id, newGroupName.trim()); }, children: t('a2a.groupNewGo') })] })] }))] }));\n}\n/**\n * Render the network toggle and its network panel.\n * @param props - composed slot props.\n * @returns the control element tree.\n */\nexport function A2aControl({ wide, t, useSessions, openSession }) {\n const [open, setOpen] = useState(false);\n const [state, setState] = useState({ sessions: [], groups: [], peers: [], activity: [], inFlight: [], remote: [] });\n const [busy, setBusy] = useState(null);\n const [seenActivity, setSeenActivity] = useState(0);\n // Panel-local search: filters session rows by name/team/description.\n const [search, setSearch] = useState('');\n // Group UI: the row whose group picker is open, and collapsed groups.\n const [pickerFor, setPickerFor] = useState(null);\n const [newGroupName, setNewGroupName] = useState('');\n const [collapsed, setCollapsed] = useState(new Set());\n const stopped = useRef(false);\n useEffect(() => () => { stopped.current = true; }, []);\n const wrap = useRef(null);\n // Cold rows carry no host-side facts; the client's own session list holds\n // the durable title for exactly those sessions.\n const listById = useSessions(state2 => state2.byId);\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 panel is open, titles, activity excerpts, and the routing\n // ring keep moving; poll so the listing visibly follows.\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 /** Join or leave one session, then refresh the listing. */\n const toggle = (row) => {\n setBusy(row.id);\n void fetch(`/__dsh_a2a/${row.joined ? 'leave' : 'join'}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: row.id }),\n })\n .then(() => { refresh(); })\n .catch(() => { })\n .finally(() => { if (!stopped.current)\n setBusy(null); });\n };\n /** Assign one session to a group ('' clears), then refresh. */\n const assign = (id, name) => {\n void fetch('/__dsh_a2a/groups', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ action: 'assign', id, name }),\n })\n .then(() => { setPickerFor(null); setNewGroupName(''); refresh(); })\n .catch(() => { });\n };\n const { sessions, groups, peers, activity, inFlight, remote } = state;\n // Search filters by name, team, or description (case-insensitive).\n const needle = search.trim().toLowerCase();\n const matches = (row) => {\n if (needle === '')\n return true;\n const name = row.live === false ? (listById[row.id]?.displayTitle ?? row.label) : (row.name ?? row.label);\n return name.toLowerCase().includes(needle)\n || row.team.toLowerCase().includes(needle)\n || (row.description ?? '').toLowerCase().includes(needle);\n };\n const visible = sessions.filter(matches);\n const ungrouped = visible.filter(row => row.group === undefined);\n const grouped = groups\n .map(name => ({ name, rows: visible.filter(row => row.group === name) }))\n .filter(group => group.rows.length > 0);\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(IconGlobeOutline14, {}), 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] }), _jsx(\"input\", { className: css.searchInput, type: \"search\", value: search, placeholder: t('a2a.search'), \"aria-label\": t('a2a.search'), onChange: (event) => { setSearch(event.target.value); } }), sessions.length === 0\n ? _jsx(\"div\", { className: css.empty, children: t('a2a.empty') })\n : visible.length === 0\n ? _jsx(\"div\", { className: css.empty, children: t('a2a.searchEmpty') })\n : (_jsxs(_Fragment, { children: [ungrouped.length > 0 && (_jsxs(\"div\", { className: css.sessionGroup, children: [_jsxs(\"button\", { type: \"button\", className: css.groupHead, onClick: () => {\n setCollapsed((current) => {\n const next = new Set(current);\n if (next.has(''))\n next.delete('');\n else\n next.add('');\n return next;\n });\n }, children: [_jsx(\"span\", { className: clsx(css.groupChevron, collapsed.has('') && css.groupChevronClosed), \"aria-hidden\": true, children: \"\\u25BE\" }), t('a2a.groupDefault'), \" \\u00B7 \", String(ungrouped.length)] }), !collapsed.has('') && ungrouped.map((row) => (_jsx(SessionRow, { row: row, t: t, listById: listById, openSession: openSession, busy: busy, onToggle: toggle, pickerFor: pickerFor, setPickerFor: setPickerFor, groups: groups, newGroupName: newGroupName, setNewGroupName: setNewGroupName, onAssign: assign }, row.id)))] })), grouped.map(({ name, rows }) => {\n const isCollapsed = collapsed.has(name);\n return (_jsxs(\"div\", { className: css.sessionGroup, children: [_jsxs(\"button\", { type: \"button\", className: css.groupHead, onClick: () => {\n setCollapsed((current) => {\n const next = new Set(current);\n if (next.has(name))\n next.delete(name);\n else\n next.add(name);\n return next;\n });\n }, children: [_jsx(\"span\", { className: clsx(css.groupChevron, isCollapsed && css.groupChevronClosed), \"aria-hidden\": true, children: \"\\u25BE\" }), name, \" \\u00B7 \", String(rows.length)] }), !isCollapsed && rows.map((row) => (_jsx(SessionRow, { row: row, t: t, listById: listById, openSession: openSession, busy: busy, onToggle: toggle, pickerFor: pickerFor, setPickerFor: setPickerFor, groups: groups, newGroupName: newGroupName, setNewGroupName: setNewGroupName, onAssign: assign }, row.id)))] }, name));\n })] })), _jsx(\"div\", { className: css.note, children: t('a2a.note') }), (needle === '' ? remote : remote.filter(row => row.team.toLowerCase().includes(needle) || row.name.toLowerCase().includes(needle) || (row.origin ?? '').toLowerCase().includes(needle) || (row.workspace ?? '').toLowerCase().includes(needle))).length > 0 && (_jsxs(_Fragment, { children: [_jsx(\"div\", { className: css.sectionTitle, children: t('a2a.remote') }), Object.entries((needle === '' ? remote : remote.filter(row => row.team.toLowerCase().includes(needle) || row.name.toLowerCase().includes(needle) || (row.origin ?? '').toLowerCase().includes(needle) || (row.workspace ?? '').toLowerCase().includes(needle)))\n .reduce((byOrigin, row) => {\n const key = row.origin ?? '';\n byOrigin[key] = [...(byOrigin[key] ?? []), row];\n return byOrigin;\n }, {})).map(([origin, rows]) => (_jsxs(\"div\", { className: css.sessionGroup, children: [_jsxs(\"button\", { type: \"button\", className: css.groupHead, onClick: () => {\n setCollapsed((current) => {\n const next = new Set(current);\n if (next.has('remote:' + origin))\n next.delete('remote:' + origin);\n else\n next.add('remote:' + origin);\n return next;\n });\n }, children: [_jsx(\"span\", { className: clsx(css.groupChevron, collapsed.has('remote:' + origin) && css.groupChevronClosed), \"aria-hidden\": true, children: \"\\u25BE\" }), origin === '' ? t('a2a.remoteUnknownOrigin') : origin, \" \\u00B7 \", String(rows.length)] }), !collapsed.has('remote:' + origin) && rows.map((row) => (_jsx(\"div\", { className: css.row, title: row.workspace ?? undefined, children: _jsxs(\"div\", { className: css.facts, children: [_jsxs(\"span\", { className: css.nameRow, children: [_jsx(\"span\", { className: css.stateDot, \"data-peer\": true, \"aria-hidden\": true }), _jsx(\"span\", { className: css.name, children: row.name === '' ? row.team : row.name }), row.workspace !== undefined ? _jsx(\"span\", { className: css.groupTag, children: row.workspace }) : null] }), _jsx(\"span\", { className: css.team, children: row.team })] }) }, row.team)))] }, origin)))] })), _jsx(\"div\", { className: css.sectionTitle, children: t('a2a.peers') }), peers.length === 0\n ? _jsx(\"div\", { className: css.empty, children: t('a2a.peersEmpty') })\n : (_jsx(\"div\", { className: css.peerGrid, children: peers.map((peer) => (_jsxs(\"span\", { className: css.peerChip, title: peer.url, children: [_jsx(\"span\", { className: css.peerHost, children: peer.url.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '') }), typeof peer.score === 'number' ? _jsx(\"span\", { className: css.peerScore, children: String(Math.round(peer.score)) }) : null] }, peer.url))) })), _jsx(\"div\", { className: css.sectionTitle, children: t('a2a.activity') }), 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(\"span\", { className: clsx(css.activityDir, 'out'), children: '→' }), _jsx(\"span\", { className: css.activityTeam, title: route.team, children: route.team }), _jsx(\"span\", { className: css.activityPeer, children: route.peer === 'local' ? '' : route.peer.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '') }), _jsx(\"span\", { className: css.activityTime, children: relativeTime(route.startedAt) })] }, `${route.team}-${String(route.startedAt)}`))) })), 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 const fresh = Date.now() - entry.ts < 5_000;\n return (_jsxs(\"div\", { className: clsx(css.activityRow, fresh && css.fresh), \"data-ok\": entry.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, entry.dir), children: entry.dir === 'in' ? '←' : '→' }), _jsx(\"span\", { className: css.activityTeam, title: entry.team, children: entry.team }), _jsx(\"span\", { className: css.activityPeer, title: entry.peer, children: entry.peer === '' ? '' : entry.peer.replace(/^https?:\\/\\//, '').replace(/\\/.*$/, '') }), _jsx(\"span\", { className: css.activityTime, children: relativeTime(entry.ts) })] }, `${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.empty': '没有可加入的会话',\n 'a2a.join': '加入',\n 'a2a.leave': '退出',\n 'a2a.cold': '未加载(重启后待唤醒)',\n 'a2a.wake': '唤醒',\n 'a2a.note': '只有加入的会话才会被网络中的其他节点发现;点击会话行可跳转。',\n 'a2a.remote': '远程节点(按 host 分组)',\n 'a2a.remoteUnknownOrigin': '未知来源',\n 'a2a.peers': '对等节点',\n 'a2a.peersEmpty': '尚无已知对等节点(配 peers 种子入网)',\n 'a2a.activity': '路由活动',\n 'a2a.activityEmpty': '暂无路由记录',\n 'a2a.inFlight': '进行中的路由',\n 'a2a.inFlightStale': '等待回执超时(对端处理慢或回执丢失);180 秒后自动解除并按已送达处理',\n 'a2a.jump': '点击跳转到该会话',\n 'a2a.search': '搜索会话(名称 / team / 摘要)',\n 'a2a.searchEmpty': '没有匹配的会话',\n 'a2a.group': '设置分组',\n 'a2a.groupDefault': '未分组',\n 'a2a.groupClear': '清除分组',\n 'a2a.groupNew': '新建分组…',\n 'a2a.groupNewGo': '创建并归入',\n};\n/** English dictionary. */\nexport const en = {\n 'a2a.label': 'A2A network',\n 'a2a.title': 'A2A network panel',\n 'a2a.empty': 'No joinable sessions',\n 'a2a.join': 'Join',\n 'a2a.leave': 'Leave',\n 'a2a.cold': 'not loaded (waiting to wake after a restart)',\n 'a2a.wake': 'Wake',\n 'a2a.note': 'Only joined sessions are discoverable by other nodes; click a row to open the session.',\n 'a2a.remote': 'Remote teams (by host)',\n 'a2a.remoteUnknownOrigin': 'Unknown origin',\n 'a2a.peers': 'Peers',\n 'a2a.peersEmpty': 'No known peers yet (set peers seeds to join the mesh)',\n 'a2a.activity': 'Routing activity',\n 'a2a.activityEmpty': 'No routes yet',\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.jump': 'Click to open this session',\n 'a2a.search': 'Search sessions (name / team / excerpt)',\n 'a2a.searchEmpty': 'No sessions match',\n 'a2a.group': 'Set group',\n 'a2a.groupDefault': 'Ungrouped',\n 'a2a.groupClear': 'Clear group',\n 'a2a.groupNew': 'New group…',\n 'a2a.groupNewGo': 'Create & assign',\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/.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 === 'in' ? 'inbound' : 'outbound', children: 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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"}
@@ -3,6 +3,12 @@
3
3
  * session listing, stored under the a2a home so they survive restarts and
4
4
  * follow the node (not the browser). Plain JSON, one writer (the control
5
5
  * routes), read fresh by the state route.
6
+ *
7
+ * @deprecated 0.6.6 — the unified team model supersedes manual grouping:
8
+ * canvas teams are the human-assigned grouping face and remote rows already
9
+ * cluster by publishing host (`origin`). The store stays readable so old
10
+ * panels keep working, but it is not developed further; use canvas teams
11
+ * for anything new.
6
12
  * @module @nelsonlongxiang/dsh-open-a2a-net/group-store
7
13
  */
8
14
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
@@ -0,0 +1,291 @@
1
+ /**
2
+ * Server-side idempotency key uniqueness (work-order P3 / B3 ruling).
3
+ *
4
+ * Caller-born task ids are single-use by contract; until now the server only
5
+ * deduped its own ledger rows. This store closes the execution-side gap: the
6
+ * FIRST claim of a key inside the TTL window executes, a same-key replay of
7
+ * the identical payload answers `replay` (the prior attempt owns whatever
8
+ * outcome it produced — never re-steer, never double-bill), and a same-key
9
+ * DIFFERENT payload is a hard `conflict`. All three verdicts are plain data;
10
+ * the endpoint maps them to 200/409(-32003)/409(-32002).
11
+ *
12
+ * W7 slice 2: the claim row now also RETAINS the prior attempt's settled
13
+ * outcome (when a settlement hook recorded one), and {@link query} answers
14
+ * the read-only `/a2a/query` surface — the retrieval half of the S1
15
+ * recovery row. The fingerprint that guards a claim is the same value that
16
+ * authorizes a query: presenting {@link peerPayloadFingerprint} of the
17
+ * original submit means holding the original payload.
18
+ *
19
+ * Persistence at `<dsh-home>/a2a/idempotency.json`; TTL prunes lazily on
20
+ * every entry point (no timers — this host restarts too often), capacity
21
+ * evicts oldest-inserted first, and every failure degrades to memory-only.
22
+ * Never throws: orchestrator settlement paths fire here repeatedly under
23
+ * conditions nobody can predict.
24
+ * @module @nelsonlongxiang/dsh-open-a2a-net/idempotency-store
25
+ */
26
+ import { createHash } from 'node:crypto';
27
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
28
+ import { dirname } from 'node:path';
29
+ /** How long one claimed key blocks re-execution. */
30
+ export const IDEMPOTENCY_TTL_MS = 24 * 60 * 60_000;
31
+ /** Capacity before oldest-inserted eviction (a window, not an archive). */
32
+ export const IDEMPOTENCY_CAP = 256;
33
+ /** Wire code: same key came back with a DIFFERENT payload inside the window. */
34
+ export const WIRE_ERROR_IDEMPOTENCY_CONFLICT = -32002;
35
+ /** Wire code: exact replay — refused so the prior run stays authoritative. */
36
+ export const WIRE_ERROR_REPLAY_REJECTED = -32003;
37
+ /** Cap on one stored outcome text; truncation is flagged, never silent. */
38
+ export const OUTCOME_TEXT_CAP = 65_536;
39
+ /**
40
+ * The payload fingerprint of one A2A submit — the shared implementation the
41
+ * gate claims with, the query endpoint re-verifies with, and the caller-side
42
+ * bridge face recomputes from the exact submit fields. ONE implementation by
43
+ * contract (W7 slice 2): any drift across those three roles would turn
44
+ * honest outcome queries into phantom payload-mismatch verdicts. The field
45
+ * order below IS the wire format; do not reorder.
46
+ */
47
+ export function peerPayloadFingerprint(input) {
48
+ return createHash('sha256')
49
+ .update(JSON.stringify({ caller: input.caller, message: input.message, noWait: input.noWait, team: input.team }))
50
+ .digest('hex');
51
+ }
52
+ /** Cap the stored text; truncation is flagged, never silent. */
53
+ function capOutcome(outcome) {
54
+ const text = outcome.reply ?? outcome.error;
55
+ if (text === undefined || text.length <= OUTCOME_TEXT_CAP)
56
+ return outcome;
57
+ return outcome.reply !== undefined
58
+ ? { ...outcome, reply: outcome.reply.slice(0, OUTCOME_TEXT_CAP), truncated: true }
59
+ : { ...outcome, error: outcome.error.slice(0, OUTCOME_TEXT_CAP), truncated: true };
60
+ }
61
+ /** A corrupt persisted outcome degrades to no-outcome (pending), never
62
+ * blocks restore. A status without its text degrades too: a query answering
63
+ * `completed` with an empty reply would be a lie, not a degraded truth. */
64
+ function restoreOutcome(raw) {
65
+ if (typeof raw !== 'object' || raw === null)
66
+ return undefined;
67
+ const candidate = raw;
68
+ if (candidate.status !== 'completed' && candidate.status !== 'failed')
69
+ return undefined;
70
+ if (candidate.status === 'completed' && typeof candidate.reply !== 'string')
71
+ return undefined;
72
+ if (candidate.status === 'failed' && typeof candidate.error !== 'string')
73
+ return undefined;
74
+ return {
75
+ status: candidate.status,
76
+ ...(typeof candidate.reply === 'string' ? { reply: candidate.reply } : {}),
77
+ ...(typeof candidate.error === 'string' ? { error: candidate.error } : {}),
78
+ ...(candidate.truncated === true ? { truncated: true } : {}),
79
+ };
80
+ }
81
+ export class IdempotencyStore {
82
+ path;
83
+ rows = new Map();
84
+ ttlMs;
85
+ cap;
86
+ now;
87
+ claimsFresh = 0;
88
+ replays = 0;
89
+ conflicts = 0;
90
+ /**
91
+ * @param path - persistence file; empty string keeps the store memory-only.
92
+ * @param options - TTL/capacity/clock overrides for tests.
93
+ */
94
+ constructor(path, options) {
95
+ this.path = path;
96
+ this.ttlMs = options?.ttlMs ?? IDEMPOTENCY_TTL_MS;
97
+ this.cap = options?.cap ?? IDEMPOTENCY_CAP;
98
+ this.now = options?.now ?? (() => Date.now());
99
+ this.restore();
100
+ }
101
+ /**
102
+ * Claim or inspect one key against a payload fingerprint.
103
+ * @returns `'fresh'` when the key may execute now (and is recorded),
104
+ * `'replay'` when the identical fingerprint was already claimed inside the
105
+ * window, `'conflict'` when the key exists with a different fingerprint.
106
+ */
107
+ claim(taskId, fingerprint) {
108
+ if (taskId === '')
109
+ return 'fresh';
110
+ this.prune(this.now());
111
+ const existing = this.rows.get(taskId);
112
+ if (existing !== undefined) {
113
+ if (existing.fingerprint !== fingerprint) {
114
+ // 0.5.36 observability: conflicts are the "caller bug" verdict —
115
+ // counted and persisted so the UNIQUE-ization traffic decision has
116
+ // cross-restart evidence instead of a per-boot blip.
117
+ this.conflicts += 1;
118
+ this.persist();
119
+ return 'conflict';
120
+ }
121
+ // Fixed window from the first claim: a replay does NOT refresh `at`,
122
+ // so only genuinely aged keys re-open (the comment previously claimed
123
+ // a sliding refresh the code never performed — W7 slice-2 §2.3 kept
124
+ // the behavior and fixed the prose).
125
+ this.replays += 1;
126
+ this.persist();
127
+ return 'replay';
128
+ }
129
+ if (this.rows.size >= this.cap)
130
+ this.evictOldest();
131
+ this.claimsFresh += 1;
132
+ this.rows.set(taskId, { fingerprint, at: this.now() });
133
+ this.persist();
134
+ return 'fresh';
135
+ }
136
+ /**
137
+ * Read-only aggregate for the observability surfaces (state route /
138
+ * a2a_status, 0.5.36): window occupancy, outcome split, and the
139
+ * cumulative claim-verdict counters. Never throws; the only write it may
140
+ * perform is the shared lazy TTL prune.
141
+ */
142
+ stats() {
143
+ this.prune(this.now());
144
+ let pending = 0;
145
+ let settled = 0;
146
+ for (const row of this.rows.values()) {
147
+ if (row.outcome === undefined)
148
+ pending += 1;
149
+ else
150
+ settled += 1;
151
+ }
152
+ return {
153
+ window: this.rows.size,
154
+ cap: this.cap,
155
+ pending,
156
+ settled,
157
+ claimsFresh: this.claimsFresh,
158
+ replays: this.replays,
159
+ conflicts: this.conflicts,
160
+ };
161
+ }
162
+ /**
163
+ * Attach the settled outcome to an existing claim (W7 slice 2). First
164
+ * write wins: the sync / detached-bridge / receipt hooks are mutually
165
+ * exclusive by wait semantics today, so a second write would only ever
166
+ * mean a future hook bug — ignore it instead of flip-flopping the record.
167
+ * Unknown or already-settled ids answer `false`. Never throws.
168
+ * @returns whether the outcome was recorded.
169
+ */
170
+ recordOutcome(taskId, outcome, settledAt = this.now()) {
171
+ if (taskId === '')
172
+ return false;
173
+ this.prune(this.now());
174
+ const existing = this.rows.get(taskId);
175
+ if (existing === undefined || existing.outcome !== undefined)
176
+ return false;
177
+ this.rows.set(taskId, {
178
+ fingerprint: existing.fingerprint,
179
+ at: existing.at,
180
+ settledAt,
181
+ outcome: capOutcome(outcome),
182
+ });
183
+ this.persist();
184
+ return true;
185
+ }
186
+ /**
187
+ * Read-only outcome lookup for the `/a2a/query` surface (W7 slice 2).
188
+ * The caller must present the task id AND the original payload's
189
+ * fingerprint — the fingerprint match is the authorization, and a
190
+ * mismatch is a plain negative answer, never the frozen -32002 conflict
191
+ * vocabulary (queries do not produce conflicts). Empty or unknown ids
192
+ * answer unknown-task. Never claims, never throws; the only write it may
193
+ * perform is the shared lazy TTL prune.
194
+ */
195
+ query(taskId, fingerprint) {
196
+ if (taskId === '')
197
+ return { found: false, reason: 'unknown-task' };
198
+ this.prune(this.now());
199
+ const existing = this.rows.get(taskId);
200
+ if (existing === undefined)
201
+ return { found: false, reason: 'unknown-task' };
202
+ if (existing.fingerprint !== fingerprint)
203
+ return { found: false, reason: 'payload-mismatch' };
204
+ if (existing.outcome === undefined || existing.settledAt === undefined)
205
+ return { found: true, status: 'pending' };
206
+ const truncation = existing.outcome.truncated === true ? { truncated: true } : {};
207
+ return existing.outcome.status === 'completed'
208
+ ? { found: true, status: 'completed', reply: existing.outcome.reply ?? '', settledAt: existing.settledAt, ...truncation }
209
+ : { found: true, status: 'failed', error: existing.outcome.error ?? '', settledAt: existing.settledAt, ...truncation };
210
+ }
211
+ /** Whether one key currently sits inside the window (diagnostics). */
212
+ has(taskId) {
213
+ this.prune(this.now());
214
+ return this.rows.has(taskId);
215
+ }
216
+ /** Drop expired keys; runs on every entry point, never timer-driven. */
217
+ prune(now) {
218
+ let changed = false;
219
+ for (const [key, entry] of this.rows) {
220
+ if (now - entry.at > this.ttlMs) {
221
+ this.rows.delete(key);
222
+ changed = true;
223
+ }
224
+ }
225
+ if (changed)
226
+ this.persist();
227
+ }
228
+ /** Capacity guard: Map iteration order = insertion order, so first key dies. */
229
+ evictOldest() {
230
+ const oldest = this.rows.keys().next();
231
+ if (oldest.done !== true) {
232
+ this.rows.delete(oldest.value);
233
+ }
234
+ }
235
+ restore() {
236
+ if (this.path === '' || !existsSync(this.path))
237
+ return;
238
+ try {
239
+ const snapshot = JSON.parse(readFileSync(this.path, 'utf8'));
240
+ const entries = snapshot?.entries;
241
+ if (!Array.isArray(entries))
242
+ return;
243
+ for (const raw of entries) {
244
+ const entry = raw;
245
+ if (typeof entry?.taskId !== 'string' || entry.taskId === '')
246
+ continue;
247
+ if (typeof entry.fingerprint !== 'string' || typeof entry.at !== 'number' || !Number.isFinite(entry.at))
248
+ continue;
249
+ const outcome = restoreOutcome(entry.outcome);
250
+ const settledAt = typeof entry.settledAt === 'number' && Number.isFinite(entry.settledAt) ? entry.settledAt : undefined;
251
+ this.rows.set(entry.taskId, {
252
+ fingerprint: entry.fingerprint,
253
+ at: entry.at,
254
+ ...(outcome !== undefined && settledAt !== undefined ? { outcome, settledAt } : {}),
255
+ });
256
+ }
257
+ // Counters restore FIRST — before any prune(): a prune that evicts an
258
+ // expired entry calls persist(), and persisting while the in-memory
259
+ // counters are still zero would wipe the snapshot's cumulative
260
+ // evidence to disk (R1 B-1: the TTL-silent-restart sequence — exactly
261
+ // the low-traffic scenario this slice exists for). Zero-fill for
262
+ // pre-stats snapshots: an old file means "no recorded traffic", never
263
+ // fabricated numbers; malformed values (negative/fractional) degrade
264
+ // to zero rather than climbing from a poisoned base.
265
+ const stats = snapshot?.stats;
266
+ const count = (value) => typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : 0;
267
+ this.claimsFresh = count(stats?.claimsFresh);
268
+ this.replays = count(stats?.replays);
269
+ this.conflicts = count(stats?.conflicts);
270
+ this.prune(this.now());
271
+ }
272
+ catch {
273
+ // A corrupt window file must never block routing: start fresh instead.
274
+ }
275
+ }
276
+ persist() {
277
+ if (this.path === '')
278
+ return;
279
+ try {
280
+ mkdirSync(dirname(this.path), { recursive: true });
281
+ const entries = [...this.rows].map(([taskId, entry]) => ({ taskId, ...entry }));
282
+ writeFileSync(this.path, JSON.stringify({
283
+ entries,
284
+ stats: { claimsFresh: this.claimsFresh, replays: this.replays, conflicts: this.conflicts },
285
+ }), { mode: 0o600 });
286
+ }
287
+ catch {
288
+ // Unwritable home: degrade to memory-only, routing continues.
289
+ }
290
+ }
291
+ }