pmx-canvas 0.8.2 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +601 -22
- package/CHANGELOG.md +59 -0
- package/Readme.md +131 -532
- package/dist/canvas/global.css +2724 -476
- package/dist/canvas/index.js +168 -107
- package/dist/canvas/mcp-app.html +2899 -596
- package/dist/json-render/index.js +20 -20
- package/dist/types/client/canvas/BarHint.d.ts +12 -2
- package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
- package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
- package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
- package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
- package/dist/types/client/canvas/CommandBar.d.ts +3 -4
- package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
- package/dist/types/client/canvas/ContextChip.d.ts +3 -0
- package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
- package/dist/types/client/canvas/EmptyState.d.ts +4 -4
- package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
- package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
- package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
- package/dist/types/client/canvas/HomeView.d.ts +9 -7
- package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
- package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
- package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
- package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
- package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
- package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
- package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
- package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
- package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
- package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
- package/dist/types/client/canvas/canvas-area.d.ts +1 -1
- package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
- package/dist/types/client/canvas/graph-model.d.ts +37 -0
- package/dist/types/client/canvas/memory-layout.d.ts +23 -0
- package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
- package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
- package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
- package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
- package/dist/types/client/icons.d.ts +0 -10
- package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
- package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
- package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
- package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
- package/dist/types/client/state/boards-store.d.ts +81 -9
- package/dist/types/client/state/canvas-store.d.ts +14 -1
- package/dist/types/client/state/context-chip-store.d.ts +22 -0
- package/dist/types/client/state/context-status-store.d.ts +35 -0
- package/dist/types/client/state/human-store.d.ts +2 -1
- package/dist/types/client/state/intent-bridge.d.ts +11 -8
- package/dist/types/client/state/knowledge-store.d.ts +22 -0
- package/dist/types/client/state/near-pin-store.d.ts +44 -1
- package/dist/types/client/state/presence-store.d.ts +4 -4
- package/dist/types/client/state/presentation.d.ts +28 -0
- package/dist/types/client/state/relation-store.d.ts +10 -0
- package/dist/types/client/state/session-store.d.ts +41 -2
- package/dist/types/client/state/workspace-settings.d.ts +7 -0
- package/dist/types/client/types.d.ts +13 -1
- package/dist/types/client/utils/keyboard.d.ts +2 -0
- package/dist/types/client/utils/time-ago.d.ts +4 -0
- package/dist/types/mcp/context-uri-template.d.ts +3 -1
- package/dist/types/server/agent-presence.d.ts +10 -3
- package/dist/types/server/ax-context.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +30 -7
- package/dist/types/server/canvas-operations.d.ts +24 -2
- package/dist/types/server/canvas-provenance.d.ts +22 -2
- package/dist/types/server/canvas-serialization.d.ts +2 -0
- package/dist/types/server/canvas-state.d.ts +93 -15
- package/dist/types/server/canvas-validation.d.ts +1 -1
- package/dist/types/server/context-brief.d.ts +94 -6
- package/dist/types/server/context-reads.d.ts +33 -7
- package/dist/types/server/edit-summary.d.ts +20 -0
- package/dist/types/server/index.d.ts +71 -10
- package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
- package/dist/types/server/lifecycle-status.d.ts +10 -0
- package/dist/types/server/memory-graph.d.ts +9 -0
- package/dist/types/server/mutation-history.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
- package/dist/types/server/operations/ops/boards.d.ts +2 -1
- package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
- package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
- package/dist/types/server/operations/ops/nodes.d.ts +5 -0
- package/dist/types/server/operations/ops/query.d.ts +23 -0
- package/dist/types/server/operations/registry.d.ts +1 -0
- package/dist/types/server/placement.d.ts +7 -0
- package/dist/types/server/scope-fence.d.ts +2 -1
- package/dist/types/server/spatial-analysis.d.ts +6 -3
- package/dist/types/server/tour-control.d.ts +32 -0
- package/dist/types/server/wiki-links.d.ts +14 -0
- package/dist/types/server/workbench-auth.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +2 -0
- package/dist/types/shared/agent-presence.d.ts +15 -12
- package/dist/types/shared/auto-arrange.d.ts +1 -0
- package/dist/types/shared/boards.d.ts +4 -4
- package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
- package/dist/types/shared/knowledge-presets.d.ts +6 -0
- package/dist/types/shared/lifecycle-status.d.ts +24 -0
- package/dist/types/shared/memory-graph.d.ts +53 -0
- package/dist/types/shared/relations.d.ts +68 -0
- package/dist/types/shared/semantic-attention.d.ts +2 -0
- package/dist/types/shared/tour.d.ts +38 -0
- package/dist/types/shared/workspace-settings.d.ts +11 -0
- package/docs/RELEASE.md +21 -3
- package/docs/adr-001-bun-only-runtime.md +7 -1
- package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
- package/docs/api-reference.md +109 -0
- package/docs/ax-host-adapter-contract.md +1 -1
- package/docs/ax-state-contract.md +5 -4
- package/docs/cli.md +153 -15
- package/docs/demos/pmx-canvas-demo.mp4 +0 -0
- package/docs/design/AgentContext.dc.html +3 -3
- package/docs/design/BoardPins.dc.html +37 -22
- package/docs/design/Categories.dc.html +208 -0
- package/docs/design/CategoryEditor.dc.html +33 -0
- package/docs/design/Chrome.dc.html +52 -9
- package/docs/design/ContextChip.dc.html +224 -0
- package/docs/design/ContextChip600.dc.html +136 -0
- package/docs/design/EmptyState.dc.html +45 -0
- package/docs/design/Flows.dc.html +23 -1
- package/docs/design/HeaderMarks.dc.html +183 -0
- package/docs/design/Home.dc.html +19 -1
- package/docs/design/HomeGraph.dc.html +86 -202
- package/docs/design/HomeMap.dc.html +23 -1
- package/docs/design/HomeTeam.dc.html +32 -14
- package/docs/design/Lifecycle.dc.html +22 -0
- package/docs/design/Lifecycle600.dc.html +18 -0
- package/docs/design/LifecycleVariants.dc.html +157 -0
- package/docs/design/LinkAuthorship.dc.html +139 -0
- package/docs/design/LinksChanged.dc.html +203 -0
- package/docs/design/LinksOptions.dc.html +137 -0
- package/docs/design/LinksOptions600.dc.html +134 -0
- package/docs/design/NearPin.dc.html +9 -10
- package/docs/design/Pane600.dc.html +4 -3
- package/docs/design/Relations.dc.html +3 -2
- package/docs/design/Restore.dc.html +152 -0
- package/docs/design/SeeChange.dc.html +267 -0
- package/docs/design/Uses.dc.html +12 -6
- package/docs/design/canvas.json +146 -19
- package/docs/design.md +449 -16
- package/docs/environment.md +0 -1
- package/docs/evals/curation-effect.md +128 -0
- package/docs/http-api.md +459 -37
- package/docs/mcp.md +146 -18
- package/docs/node-types.md +58 -3
- package/docs/plans/design-gaps-2026-10-08.md +53 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
- package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
- package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
- package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
- package/docs/product-context-vision-2026-09.md +17 -1
- package/docs/product-vision-2026-09.md +1082 -43
- package/docs/product-vision-review-2026-09.md +13 -1
- package/docs/screenshots/workbench-live-dark.png +0 -0
- package/docs/screenshots/workbench-live-light.png +0 -0
- package/docs/sdk.md +62 -2
- package/package.json +1 -1
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +1 -1
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +601 -22
- package/skills/pmx-canvas/SKILL.md +216 -42
- package/skills/pmx-canvas/evals/evals.json +224 -0
- package/skills/pmx-canvas/references/full-reference.md +120 -21
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
- package/skills/pmx-canvas/references/importing-context.md +132 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
- package/skills/pmx-canvas-testing/SKILL.md +20 -0
- package/src/cli/agent.ts +2 -0
- package/src/cli/commands/ax.ts +12 -0
- package/src/cli/commands/boards.ts +88 -11
- package/src/cli/commands/edges.ts +28 -0
- package/src/cli/commands/groups.ts +7 -0
- package/src/cli/commands/nodes.ts +1 -0
- package/src/cli/commands/query.ts +34 -2
- package/src/cli/commands/record.ts +199 -48
- package/src/cli/index.ts +6 -1
- package/src/client/App.tsx +177 -70
- package/src/client/canvas/AttentionHistory.tsx +0 -2
- package/src/client/canvas/BarHint.tsx +40 -2
- package/src/client/canvas/BoardCopyDialog.tsx +1 -1
- package/src/client/canvas/BoardFolderDialog.tsx +7 -7
- package/src/client/canvas/BoardLinkDialog.tsx +138 -33
- package/src/client/canvas/BoardPinButton.tsx +33 -0
- package/src/client/canvas/BoardSwitcher.tsx +26 -40
- package/src/client/canvas/CanvasNode.tsx +360 -237
- package/src/client/canvas/CanvasViewport.tsx +45 -7
- package/src/client/canvas/CommandBar.tsx +29 -23
- package/src/client/canvas/CommandPalette.tsx +5 -12
- package/src/client/canvas/ConnectionBanner.tsx +3 -3
- package/src/client/canvas/ContextChip.tsx +296 -0
- package/src/client/canvas/ContextMenu.tsx +63 -5
- package/src/client/canvas/CrossBoardReferences.tsx +60 -0
- package/src/client/canvas/EdgeLayer.tsx +339 -76
- package/src/client/canvas/EmptyState.tsx +33 -18
- package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
- package/src/client/canvas/ExternalWriters.tsx +57 -42
- package/src/client/canvas/HomeGraphView.tsx +942 -0
- package/src/client/canvas/HomeMapView.tsx +641 -0
- package/src/client/canvas/HomeReviewView.tsx +167 -0
- package/src/client/canvas/HomeView.tsx +687 -160
- package/src/client/canvas/IntentLayer.tsx +19 -12
- package/src/client/canvas/KnowledgeDialog.tsx +488 -0
- package/src/client/canvas/LifecycleChrome.tsx +133 -0
- package/src/client/canvas/LifecycleFields.tsx +91 -0
- package/src/client/canvas/LifecyclePanel.tsx +543 -0
- package/src/client/canvas/MemoryInspector.tsx +104 -0
- package/src/client/canvas/Minimap.tsx +3 -3
- package/src/client/canvas/NearPinLayer.tsx +187 -0
- package/src/client/canvas/NodeContextMark.tsx +175 -97
- package/src/client/canvas/Presentation.tsx +23 -57
- package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
- package/src/client/canvas/RelationsPanel.tsx +82 -0
- package/src/client/canvas/SelectionBar.tsx +3 -6
- package/src/client/canvas/SessionPanel.tsx +90 -50
- package/src/client/canvas/SessionReceipt.tsx +311 -39
- package/src/client/canvas/ShortcutOverlay.tsx +1 -1
- package/src/client/canvas/SnapshotPanel.tsx +27 -36
- package/src/client/canvas/ToolRail.tsx +367 -192
- package/src/client/canvas/TopBar.tsx +354 -124
- package/src/client/canvas/canvas-area.ts +10 -3
- package/src/client/canvas/graph-geometry.ts +195 -0
- package/src/client/canvas/graph-model.ts +254 -0
- package/src/client/canvas/memory-layout.ts +106 -0
- package/src/client/canvas/use-chrome-height.ts +20 -0
- package/src/client/canvas/use-focus-trap.ts +31 -5
- package/src/client/canvas/use-memory-graph.ts +58 -0
- package/src/client/canvas/use-node-drag.ts +2 -1
- package/src/client/canvas/use-pan-zoom.ts +64 -7
- package/src/client/icons.tsx +0 -63
- package/src/client/mcp-app/style.css +0 -6
- package/src/client/nodes/AxStepControls.tsx +74 -50
- package/src/client/nodes/BoardNode.tsx +5 -1
- package/src/client/nodes/ContextNode.tsx +18 -11
- package/src/client/nodes/ExtAppFrame.tsx +6 -3
- package/src/client/nodes/FileNode.tsx +46 -42
- package/src/client/nodes/HtmlNode.tsx +32 -0
- package/src/client/nodes/ImageNode.tsx +1 -1
- package/src/client/nodes/InlineFormatBar.tsx +6 -9
- package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
- package/src/client/nodes/MarkdownNode.tsx +377 -109
- package/src/client/nodes/McpAppNode.tsx +50 -20
- package/src/client/nodes/MdFormatBar.tsx +21 -18
- package/src/client/nodes/PromptNode.tsx +1 -1
- package/src/client/nodes/ResponseNode.tsx +1 -1
- package/src/client/nodes/viewer-runtime-state.ts +54 -22
- package/src/client/state/attention-bridge.ts +64 -15
- package/src/client/state/boards-store.ts +193 -39
- package/src/client/state/canvas-store.ts +99 -12
- package/src/client/state/context-chip-store.ts +70 -0
- package/src/client/state/context-status-store.ts +53 -0
- package/src/client/state/human-store.ts +13 -7
- package/src/client/state/intent-bridge.ts +51 -30
- package/src/client/state/knowledge-store.ts +43 -0
- package/src/client/state/near-pin-store.ts +115 -6
- package/src/client/state/presence-store.ts +7 -10
- package/src/client/state/presentation.ts +91 -0
- package/src/client/state/relation-store.ts +81 -0
- package/src/client/state/session-store.ts +144 -18
- package/src/client/state/sse-bridge.ts +235 -32
- package/src/client/state/workspace-settings.ts +11 -0
- package/src/client/theme/global.css +2724 -476
- package/src/client/types.ts +12 -0
- package/src/client/utils/keyboard.ts +10 -0
- package/src/client/utils/time-ago.ts +14 -0
- package/src/json-render/renderer/index.tsx +31 -0
- package/src/mcp/canvas-app.ts +12 -2
- package/src/mcp/context-uri-template.ts +7 -4
- package/src/mcp/server.ts +63 -41
- package/src/mcp/workbench-app.ts +39 -6
- package/src/server/agent-presence.ts +128 -28
- package/src/server/ax-context.ts +27 -13
- package/src/server/board-export.ts +12 -1
- package/src/server/canvas-db.ts +331 -42
- package/src/server/canvas-operations.ts +195 -83
- package/src/server/canvas-provenance.ts +53 -22
- package/src/server/canvas-serialization.ts +2 -0
- package/src/server/canvas-state.ts +950 -122
- package/src/server/canvas-validation.ts +9 -2
- package/src/server/context-brief.ts +345 -38
- package/src/server/context-reads.ts +178 -30
- package/src/server/demo-state.json +355 -24
- package/src/server/edit-summary.ts +74 -0
- package/src/server/html-surface.ts +8 -1
- package/src/server/index.ts +181 -11
- package/src/server/knowledge-lifecycle.ts +304 -0
- package/src/server/lifecycle-status.ts +232 -0
- package/src/server/memory-graph.ts +166 -0
- package/src/server/mutation-history.ts +18 -6
- package/src/server/operations/composites.ts +32 -11
- package/src/server/operations/http.ts +7 -2
- package/src/server/operations/index.ts +4 -0
- package/src/server/operations/mcp.ts +26 -1
- package/src/server/operations/ops/ax-presence.ts +1 -1
- package/src/server/operations/ops/ax-read.ts +57 -6
- package/src/server/operations/ops/ax-timeline.ts +28 -2
- package/src/server/operations/ops/backup.ts +5 -3
- package/src/server/operations/ops/batch.ts +35 -4
- package/src/server/operations/ops/boards.ts +188 -47
- package/src/server/operations/ops/canvas-wire.ts +31 -1
- package/src/server/operations/ops/edges.ts +36 -2
- package/src/server/operations/ops/groups.ts +6 -1
- package/src/server/operations/ops/lifecycle.ts +262 -0
- package/src/server/operations/ops/memory-graph.ts +45 -0
- package/src/server/operations/ops/nodes.ts +107 -24
- package/src/server/operations/ops/query.ts +281 -68
- package/src/server/operations/ops/snapshots.ts +73 -1
- package/src/server/operations/ops/validate.ts +5 -1
- package/src/server/operations/ops/viewport.ts +97 -3
- package/src/server/operations/registry.ts +74 -25
- package/src/server/placement.ts +29 -0
- package/src/server/scope-fence.ts +78 -6
- package/src/server/server.ts +115 -16
- package/src/server/spatial-analysis.ts +48 -23
- package/src/server/tour-control.ts +82 -0
- package/src/server/wiki-links.ts +137 -0
- package/src/server/workbench-auth.ts +11 -0
- package/src/shared/agent-context.ts +3 -0
- package/src/shared/agent-presence.ts +15 -15
- package/src/shared/auto-arrange.ts +2 -0
- package/src/shared/boards.ts +4 -4
- package/src/shared/knowledge-lifecycle.ts +22 -0
- package/src/shared/knowledge-presets.ts +80 -0
- package/src/shared/lifecycle-status.ts +26 -0
- package/src/shared/memory-graph.ts +74 -0
- package/src/shared/relations.ts +98 -0
- package/src/shared/semantic-attention.ts +15 -1
- package/src/shared/tour.ts +23 -5
- package/src/shared/workspace-settings.ts +13 -0
- package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
- package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
- package/dist/types/client/canvas/kind-colors.d.ts +0 -3
- package/dist/types/server/agent-context.d.ts +0 -22
- package/src/client/canvas/ContextPinBar.tsx +0 -25
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { findOpenCanvasPosition } from '../utils/placement.js';
|
|
2
|
-
import {
|
|
2
|
+
import { refreshKnowledge } from './knowledge-store';
|
|
3
|
+
import { applyWorkspaceSettings } from './workspace-settings';
|
|
4
|
+
import { cameraSchema, tourStopSchema } from '../../shared/tour.js';
|
|
5
|
+
import { ownsCamera, playStop, presentationError, presenting, presentStop, recordingCamera } from './presentation';
|
|
3
6
|
import { isHostedWorkbench, workbenchFetch } from './workbench-transport';
|
|
4
7
|
import { normalizeExtAppToolResult } from '../utils/ext-app-tool-result.js';
|
|
5
8
|
import type { CanvasAnnotation, CanvasEdge, CanvasNodeState, NodeActor } from '../types';
|
|
@@ -11,8 +14,12 @@ import {
|
|
|
11
14
|
axSurfaceState,
|
|
12
15
|
bringToFront,
|
|
13
16
|
cancelViewportAnimation,
|
|
17
|
+
commitViewport,
|
|
14
18
|
canvasTheme,
|
|
15
19
|
connectionStatus,
|
|
20
|
+
contextPinsRevision,
|
|
21
|
+
contextPinsPendingWrites,
|
|
22
|
+
refreshContextPins,
|
|
16
23
|
replaceContextPinsFromServer,
|
|
17
24
|
edges,
|
|
18
25
|
focusNode,
|
|
@@ -30,8 +37,18 @@ import {
|
|
|
30
37
|
reconnectDelay,
|
|
31
38
|
resetCanvasInteractionState,
|
|
32
39
|
} from './canvas-store';
|
|
33
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
activeBoardId,
|
|
42
|
+
applyBoards,
|
|
43
|
+
boardGeneration,
|
|
44
|
+
catalogEpoch,
|
|
45
|
+
connectBoardCatalog,
|
|
46
|
+
loadBoards,
|
|
47
|
+
memoryGraphRevision,
|
|
48
|
+
setBoardSwitchHandler,
|
|
49
|
+
} from './boards-store';
|
|
34
50
|
import { applyContextStatus, refreshContextStatus } from './context-status-store';
|
|
51
|
+
import { refreshContextChip } from './context-chip-store';
|
|
35
52
|
import {
|
|
36
53
|
fetchAgentPresence,
|
|
37
54
|
fetchAxSurfaceState,
|
|
@@ -41,9 +58,9 @@ import {
|
|
|
41
58
|
tabAgentKey,
|
|
42
59
|
} from './intent-bridge';
|
|
43
60
|
import { agentActivity, applyPresenceSnapshot, sessionActive } from './presence-store';
|
|
44
|
-
import { applyHumanSnapshot, startHumanPresence } from './human-store';
|
|
61
|
+
import { applyHumanSnapshot, humanClientId, startHumanPresence, stopHumanPresence } from './human-store';
|
|
45
62
|
import type { HumanPresenceSnapshot } from '../../shared/human-presence.js';
|
|
46
|
-
import { applySessionReceipt, dismissSessionReceipt, refreshTimeline } from './session-store';
|
|
63
|
+
import { activityLens, applySessionReceipt, dismissSessionReceipt, refreshTimeline } from './session-store';
|
|
47
64
|
import { initSessionThemeOverride, themeOverrideActive } from './theme-override';
|
|
48
65
|
import { DEFAULT_POSITIONS, makeNodeState } from './node-factory';
|
|
49
66
|
import { invalidateTokenCache } from '../theme/tokens';
|
|
@@ -64,14 +81,19 @@ let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
64
81
|
// "opens" (headers pass through) but no event ever arrives. Fallback: if the
|
|
65
82
|
// stream produces no event at all within the watchdog window, switch to
|
|
66
83
|
// polling GET /api/workbench/poll, which returns buffered events (or a full
|
|
67
|
-
// connect snapshot) as a
|
|
84
|
+
// connect snapshot) as a completed response as soon as an event arrives. The same
|
|
68
85
|
// EVENT_HANDLERS map serves both transports. `?transport=poll` forces polling
|
|
69
86
|
// from the start; `?transport=sse` disables the fallback.
|
|
70
87
|
const SSE_FIRST_EVENT_WATCHDOG_MS = 3000;
|
|
71
88
|
const POLL_INTERVAL_MS = 2000;
|
|
89
|
+
const POLL_WAIT_MS = 1000;
|
|
90
|
+
const POLL_LIVE_INTERVAL_MS = 80;
|
|
72
91
|
const POLL_ERROR_INTERVAL_MS = 5000;
|
|
92
|
+
// Bound headers AND body, including host transports that ignore AbortSignal.
|
|
93
|
+
const POLL_REQUEST_TIMEOUT_MS = 10000;
|
|
73
94
|
let pollingMode = false;
|
|
74
95
|
let pollTimer: ReturnType<typeof setTimeout> | null = null;
|
|
96
|
+
let pollAbort: AbortController | null = null;
|
|
75
97
|
let pollSeq: number | null = null;
|
|
76
98
|
let pollGeneration = 0;
|
|
77
99
|
let sseFirstEventWatchdog: ReturnType<typeof setTimeout> | null = null;
|
|
@@ -369,6 +391,7 @@ function parseCanvasNode(raw: Record<string, unknown>): CanvasNodeState | null {
|
|
|
369
391
|
collapsed: raw.collapsed === true,
|
|
370
392
|
pinned: raw.pinned === true,
|
|
371
393
|
contentRevision: typeof raw.contentRevision === 'number' ? raw.contentRevision : 0,
|
|
394
|
+
linksRevision: typeof raw.linksRevision === 'number' ? raw.linksRevision : 0,
|
|
372
395
|
...(isActor(raw.createdBy) ? { createdBy: raw.createdBy } : {}),
|
|
373
396
|
...(isActor(raw.lastEditedBy) ? { lastEditedBy: raw.lastEditedBy } : {}),
|
|
374
397
|
data,
|
|
@@ -391,10 +414,18 @@ function parseCanvasEdge(raw: Record<string, unknown>): CanvasEdge | null {
|
|
|
391
414
|
id: raw.id,
|
|
392
415
|
from: raw.from,
|
|
393
416
|
to: raw.to,
|
|
417
|
+
...(typeof raw.toBoardId === 'string' ? { toBoardId: raw.toBoardId } : {}),
|
|
394
418
|
type: raw.type,
|
|
419
|
+
...(typeof raw.kind === 'string' ? { kind: raw.kind } : {}),
|
|
420
|
+
...(typeof raw.reason === 'string' ? { reason: raw.reason } : {}),
|
|
395
421
|
...(typeof raw.label === 'string' ? { label: raw.label } : {}),
|
|
396
422
|
...(raw.style === 'solid' || raw.style === 'dashed' || raw.style === 'dotted' ? { style: raw.style } : {}),
|
|
397
423
|
...(raw.animated === true ? { animated: true } : {}),
|
|
424
|
+
...(typeof raw.revision === 'number' ? { revision: raw.revision } : {}),
|
|
425
|
+
...(isActor(raw.changedBy) ? { changedBy: raw.changedBy } : {}),
|
|
426
|
+
...(typeof raw.changedAt === 'string' ? { changedAt: raw.changedAt } : {}),
|
|
427
|
+
...(isActor(raw.createdBy) ? { createdBy: raw.createdBy } : {}),
|
|
428
|
+
...(typeof raw.createdAt === 'string' ? { createdAt: raw.createdAt } : {}),
|
|
398
429
|
};
|
|
399
430
|
}
|
|
400
431
|
|
|
@@ -471,10 +502,9 @@ function handleAgentSessionEnded(data: Record<string, unknown>): void {
|
|
|
471
502
|
applySessionReceipt(data);
|
|
472
503
|
}
|
|
473
504
|
|
|
474
|
-
let stopHumanPresence: (() => void) | null = null;
|
|
475
505
|
let boardResyncGeneration = 0;
|
|
476
506
|
let layoutRevision = 0;
|
|
477
|
-
let
|
|
507
|
+
let pendingBoardLayout: Record<string, unknown> | null = null;
|
|
478
508
|
|
|
479
509
|
function handleHumanPresence(data: Record<string, unknown>): void {
|
|
480
510
|
applyHumanSnapshot(data as Partial<HumanPresenceSnapshot>);
|
|
@@ -485,32 +515,61 @@ function handleHumanPresence(data: Record<string, unknown>): void {
|
|
|
485
515
|
* a different board now, so take its layout like a fresh connection would —
|
|
486
516
|
* camera included — and drop what belonged to the old board.
|
|
487
517
|
*/
|
|
488
|
-
function resyncForBoardSwitch(): void {
|
|
518
|
+
function resyncForBoardSwitch(initial: boolean): void {
|
|
519
|
+
const pending = pendingBoardLayout;
|
|
520
|
+
pendingBoardLayout = null;
|
|
521
|
+
if (initial) {
|
|
522
|
+
// Untagged initial fetches may already have supplied the layout. Tagged
|
|
523
|
+
// SSE waits here for the first catalog to identify its owner.
|
|
524
|
+
if (pending?.boardId === activeBoardId.value) handleCanvasLayoutUpdate(pending);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const boardId = activeBoardId.value;
|
|
489
528
|
const generation = ++boardResyncGeneration;
|
|
490
529
|
savedLayout = null;
|
|
491
530
|
hasInitialServerLayout.value = false;
|
|
492
531
|
resetIntents();
|
|
493
532
|
resetCanvasInteractionState();
|
|
494
533
|
resetAttentionBridge();
|
|
495
|
-
|
|
534
|
+
applyServerCanvasLayout({ nodes: [], edges: [] });
|
|
535
|
+
if (pending?.boardId === activeBoardId.value) handleCanvasLayoutUpdate(pending);
|
|
536
|
+
// Keep the change available when visiting another board, so its Open board
|
|
537
|
+
// action can bring the person back to Undo. Home still clears board chrome.
|
|
538
|
+
if (activeBoardId.value === null) dismissSessionReceipt();
|
|
539
|
+
if (activityLens.value?.kind === 'receipt') activityLens.value = null;
|
|
496
540
|
applyContextStatus({});
|
|
497
541
|
axSurfaceState.value = null;
|
|
498
542
|
void refreshAxSurface();
|
|
499
543
|
const layoutAtRequest = layoutRevision;
|
|
500
|
-
const pinsAtRequest =
|
|
501
|
-
void fetchCanvasState().then((layout) => {
|
|
544
|
+
const pinsAtRequest = contextPinsRevision;
|
|
545
|
+
void fetchCanvasState(boardId).then((layout) => {
|
|
502
546
|
if (generation === boardResyncGeneration && layoutAtRequest === layoutRevision)
|
|
503
|
-
handleCanvasLayoutUpdate({ layout });
|
|
547
|
+
handleCanvasLayoutUpdate({ layout, boardId });
|
|
504
548
|
});
|
|
505
549
|
void requestJson<{ nodeIds?: unknown }>('fetchPinnedContext', '/api/canvas/pinned-context', {}).then((data) => {
|
|
506
|
-
if (generation === boardResyncGeneration && pinsAtRequest ===
|
|
550
|
+
if (generation === boardResyncGeneration && pinsAtRequest === contextPinsRevision)
|
|
507
551
|
handleContextPinsChanged({ nodeIds: data.nodeIds });
|
|
508
552
|
});
|
|
509
553
|
}
|
|
510
554
|
setBoardSwitchHandler(resyncForBoardSwitch);
|
|
511
555
|
|
|
512
556
|
function handleConnected(data: Record<string, unknown>): void {
|
|
557
|
+
memoryGraphRevision.value++;
|
|
558
|
+
// A current connection authorizes catalogs from this server run. Old HTTP
|
|
559
|
+
// continuations and buffered layouts cannot cross the snapshot boundary.
|
|
560
|
+
boardResyncGeneration++;
|
|
561
|
+
pendingBoardLayout = null;
|
|
562
|
+
// The following snapshot is a fresh baseline, including lower revisions after
|
|
563
|
+
// a restart/restore. Keep same-board editors and their text selections mounted.
|
|
564
|
+
hasInitialServerLayout.value = false;
|
|
565
|
+
if (typeof data.catalogEpoch === 'string') connectBoardCatalog(data.catalogEpoch);
|
|
566
|
+
// A same-bundle daemon restart keeps drafts alive but rotates its human capability.
|
|
567
|
+
// Install it before connect-time requests or presence heartbeats can write.
|
|
568
|
+
if (!isHostedWorkbench() && typeof data.workbenchToken === 'string') {
|
|
569
|
+
(window as Window & { __PMX_WORKBENCH_TOKEN?: string }).__PMX_WORKBENCH_TOKEN = data.workbenchToken;
|
|
570
|
+
}
|
|
513
571
|
sessionId.value = (data.sessionId as string) || '';
|
|
572
|
+
applyWorkspaceSettings(data.workspaceSettings);
|
|
514
573
|
connectionStatus.value = 'connected';
|
|
515
574
|
// Hosted writes capture the active board. Load its identity before reporting
|
|
516
575
|
// the viewport, rather than sending a spurious Home precondition on boot.
|
|
@@ -522,11 +581,12 @@ function handleConnected(data: Record<string, unknown>): void {
|
|
|
522
581
|
// keep it live from here on.
|
|
523
582
|
void fetchAgentPresence().then(applyPresenceSnapshot);
|
|
524
583
|
void refreshContextStatus();
|
|
584
|
+
refreshContextChip();
|
|
525
585
|
// Human collaborators (phase 8): announce this tab and read who else is here.
|
|
526
586
|
// An `?agent=<key>` tab is an AGENT's view — it must not heartbeat a human
|
|
527
587
|
// guest cursor (its writes already book as the agent).
|
|
528
|
-
stopHumanPresence
|
|
529
|
-
if (!tabAgentKey && !isHostedWorkbench())
|
|
588
|
+
stopHumanPresence();
|
|
589
|
+
if (!tabAgentKey && !isHostedWorkbench()) startHumanPresence();
|
|
530
590
|
void requestJson<Partial<HumanPresenceSnapshot> | null>(
|
|
531
591
|
'fetchHumanPresence',
|
|
532
592
|
'/api/canvas/human-presence',
|
|
@@ -942,6 +1002,9 @@ function handleCanvasResponseComplete(data: Record<string, unknown>): void {
|
|
|
942
1002
|
// ── Agent canvas tool events ──────────────────────────────────
|
|
943
1003
|
|
|
944
1004
|
function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
|
|
1005
|
+
// Even an inactive board can affect library links. This invalidates a read;
|
|
1006
|
+
// the ownership guards below still control applying the canvas snapshot.
|
|
1007
|
+
memoryGraphRevision.value++;
|
|
945
1008
|
const layout = data.layout as
|
|
946
1009
|
| {
|
|
947
1010
|
nodes?: Array<Record<string, unknown>>;
|
|
@@ -951,6 +1014,22 @@ function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
|
|
|
951
1014
|
}
|
|
952
1015
|
| undefined;
|
|
953
1016
|
if (!layout?.nodes) return;
|
|
1017
|
+
// Board IDs repeat on A → B → A and after restores. Only the accepted
|
|
1018
|
+
// activation may establish its camera/content baseline.
|
|
1019
|
+
if (typeof data.boardGeneration === 'number') {
|
|
1020
|
+
if (data.catalogEpoch !== catalogEpoch || (boardGeneration !== null && data.boardGeneration < boardGeneration))
|
|
1021
|
+
return;
|
|
1022
|
+
if (boardGeneration === null || data.boardGeneration > boardGeneration) {
|
|
1023
|
+
pendingBoardLayout = data;
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// The server sends a switched board's layout before boards-changed. Do not
|
|
1028
|
+
// mount it under the previous owner, even when card IDs happen to match.
|
|
1029
|
+
if (data.boardId !== undefined && data.boardId !== activeBoardId.value) {
|
|
1030
|
+
pendingBoardLayout = data;
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
954
1033
|
layoutRevision += 1;
|
|
955
1034
|
const shouldApplyViewport = !hasInitialServerLayout.value;
|
|
956
1035
|
hasInitialServerLayout.value = true;
|
|
@@ -986,6 +1065,8 @@ function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
|
|
|
986
1065
|
);
|
|
987
1066
|
|
|
988
1067
|
syncAttentionFromSse({ event: 'canvas-layout-update', data });
|
|
1068
|
+
// Content changes move the brief's size and can clip it (debounced, so a burst fetches once).
|
|
1069
|
+
refreshContextChip(1_000);
|
|
989
1070
|
}
|
|
990
1071
|
|
|
991
1072
|
function reconnectDelayMs(attempt: number): number {
|
|
@@ -1007,7 +1088,8 @@ function handleCanvasFocusNode(data: Record<string, unknown>): void {
|
|
|
1007
1088
|
}
|
|
1008
1089
|
|
|
1009
1090
|
function handleCanvasViewportUpdate(data: Record<string, unknown>): void {
|
|
1010
|
-
|
|
1091
|
+
// A delayed echo of this tab's earlier commit must not cancel its newer gesture.
|
|
1092
|
+
if (ownsCamera() || data.clientId === humanClientId) return;
|
|
1011
1093
|
const viewport = data.viewport as Record<string, unknown> | undefined;
|
|
1012
1094
|
if (!viewport) return;
|
|
1013
1095
|
const x = typeof viewport.x === 'number' ? viewport.x : 0;
|
|
@@ -1017,6 +1099,40 @@ function handleCanvasViewportUpdate(data: Record<string, unknown>): void {
|
|
|
1017
1099
|
replaceViewport({ x, y, scale });
|
|
1018
1100
|
}
|
|
1019
1101
|
|
|
1102
|
+
// Tour steps and camera moves animate locally; a deterministic capture owns
|
|
1103
|
+
// its camera outright and ignores both.
|
|
1104
|
+
function handleCanvasTourStep(data: Record<string, unknown>): void {
|
|
1105
|
+
if (recordingCamera.value) return;
|
|
1106
|
+
// A human stepping in one tab moves tabs already presenting; only a
|
|
1107
|
+
// `present` step (the agent's default) starts presenting elsewhere.
|
|
1108
|
+
if (!presenting.value && data.present !== true) return;
|
|
1109
|
+
const stop = tourStopSchema.safeParse(data.stop);
|
|
1110
|
+
if (stop.success) presentStop(stop.data);
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
function handleCanvasTourExit(): void {
|
|
1114
|
+
if (recordingCamera.value) return;
|
|
1115
|
+
presenting.value = false;
|
|
1116
|
+
presentationError.value = '';
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function handleCanvasCameraMove(data: Record<string, unknown>): void {
|
|
1120
|
+
if (recordingCamera.value) return;
|
|
1121
|
+
const stop = tourStopSchema.safeParse(data.stop);
|
|
1122
|
+
if (!stop.success) return;
|
|
1123
|
+
try {
|
|
1124
|
+
// The presentation camera is local; otherwise the end camera becomes the
|
|
1125
|
+
// board viewport — unrecorded, so a camera move is never an undo step.
|
|
1126
|
+
playStop(stop.data, presenting.value ? undefined : (end) => commitViewport(end, { recordHistory: false }));
|
|
1127
|
+
} catch {
|
|
1128
|
+
// Target not in this tab's layout yet: take the server's resolved camera.
|
|
1129
|
+
const fallback = cameraSchema.safeParse(data.viewport);
|
|
1130
|
+
if (!fallback.success) return;
|
|
1131
|
+
cancelViewportAnimation();
|
|
1132
|
+
replaceViewport(fallback.data);
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1020
1136
|
function handleContextUsage(data: Record<string, unknown>): void {
|
|
1021
1137
|
const id = 'context-main';
|
|
1022
1138
|
const existing = nodes.value.get(id);
|
|
@@ -1042,15 +1158,17 @@ function handleThemeChanged(data: Record<string, unknown>): void {
|
|
|
1042
1158
|
}
|
|
1043
1159
|
|
|
1044
1160
|
function handleContextPinsChanged(data: Record<string, unknown>): void {
|
|
1045
|
-
|
|
1161
|
+
memoryGraphRevision.value++;
|
|
1046
1162
|
const nodeIds = Array.isArray(data.nodeIds) ? data.nodeIds.filter((id): id is string => typeof id === 'string') : [];
|
|
1047
|
-
replaceContextPinsFromServer(nodeIds);
|
|
1163
|
+
if (contextPinsPendingWrites === 0) replaceContextPinsFromServer(nodeIds);
|
|
1048
1164
|
syncAttentionFromSse({ event: 'context-pins-changed', data });
|
|
1049
1165
|
void refreshContextStatus();
|
|
1166
|
+
refreshContextChip();
|
|
1050
1167
|
}
|
|
1051
1168
|
|
|
1052
1169
|
function handleContextStatusChanged(): void {
|
|
1053
1170
|
void refreshContextStatus();
|
|
1171
|
+
refreshContextChip();
|
|
1054
1172
|
}
|
|
1055
1173
|
|
|
1056
1174
|
// AX state changes arrive as per-primitive deltas; rather than reduce them, treat
|
|
@@ -1128,13 +1246,22 @@ export const EVENT_HANDLERS: Record<string, (data: Record<string, unknown>) => v
|
|
|
1128
1246
|
'ext-app-result': handleExtAppResult,
|
|
1129
1247
|
'context-pins-changed': handleContextPinsChanged,
|
|
1130
1248
|
'context-status-changed': handleContextStatusChanged,
|
|
1131
|
-
'
|
|
1249
|
+
'knowledge-changed': () => void refreshKnowledge(),
|
|
1250
|
+
'lifecycle-changed': () => void refreshKnowledge(),
|
|
1251
|
+
'boards-changed': (data) => {
|
|
1252
|
+
applyBoards(data);
|
|
1253
|
+
refreshContextChip();
|
|
1254
|
+
},
|
|
1132
1255
|
'canvas-layout-update': handleCanvasLayoutUpdate,
|
|
1133
1256
|
'canvas-focus-node': handleCanvasFocusNode,
|
|
1134
1257
|
'canvas-viewport-update': handleCanvasViewportUpdate,
|
|
1258
|
+
'canvas-tour-step': handleCanvasTourStep,
|
|
1259
|
+
'canvas-tour-exit': handleCanvasTourExit,
|
|
1260
|
+
'canvas-camera-move': handleCanvasCameraMove,
|
|
1135
1261
|
'context-usage': handleContextUsage,
|
|
1136
1262
|
'trace-state': handleTraceState,
|
|
1137
1263
|
'theme-changed': handleThemeChanged,
|
|
1264
|
+
'settings-changed': (data) => applyWorkspaceSettings(data.workspaceSettings),
|
|
1138
1265
|
'canvas-prompt-created': handleCanvasPromptCreated,
|
|
1139
1266
|
'canvas-prompt-status': handleCanvasPromptStatus,
|
|
1140
1267
|
'canvas-response-start': handleCanvasResponseStart,
|
|
@@ -1174,6 +1301,12 @@ function startPollingTransport(): () => void {
|
|
|
1174
1301
|
pollingMode = true;
|
|
1175
1302
|
pollGeneration += 1;
|
|
1176
1303
|
const generation = pollGeneration;
|
|
1304
|
+
pollAbort?.abort();
|
|
1305
|
+
const controller = new AbortController();
|
|
1306
|
+
pollAbort = controller;
|
|
1307
|
+
// MCP-app polls are host tool calls, not direct HTTP requests. Keep their
|
|
1308
|
+
// existing cadence; HTTP clients can park one request until an event arrives.
|
|
1309
|
+
const longPolling = !isHostedWorkbench();
|
|
1177
1310
|
connectionStatus.value = 'connecting';
|
|
1178
1311
|
// Server-run identity from the last poll response. The event seq counter
|
|
1179
1312
|
// restarts at 0 on every server boot, so a cursor from a previous run can
|
|
@@ -1183,22 +1316,39 @@ function startPollingTransport(): () => void {
|
|
|
1183
1316
|
|
|
1184
1317
|
const pollOnce = async (): Promise<void> => {
|
|
1185
1318
|
if (generation !== pollGeneration) return;
|
|
1319
|
+
const requestController = new AbortController();
|
|
1320
|
+
let deadline: ReturnType<typeof setTimeout> | undefined;
|
|
1186
1321
|
try {
|
|
1187
1322
|
const params = new URLSearchParams();
|
|
1188
|
-
if (pollSeq !== null)
|
|
1323
|
+
if (pollSeq !== null) {
|
|
1324
|
+
params.set('since', String(pollSeq));
|
|
1325
|
+
if (longPolling) params.set('waitMs', String(POLL_WAIT_MS));
|
|
1326
|
+
}
|
|
1189
1327
|
const sid = sessionId.value;
|
|
1190
1328
|
if (sid) params.set('session', sid);
|
|
1191
1329
|
const query = params.toString();
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1330
|
+
const pinsAtRequest = contextPinsRevision;
|
|
1331
|
+
const body = await Promise.race([
|
|
1332
|
+
(async () => {
|
|
1333
|
+
const response = await workbenchFetch(`/api/workbench/poll${query ? `?${query}` : ''}`, {
|
|
1334
|
+
signal: AbortSignal.any([controller.signal, requestController.signal]),
|
|
1335
|
+
});
|
|
1336
|
+
if (!response.ok) throw new Error(`poll failed with HTTP ${response.status}`);
|
|
1337
|
+
return (await response.json()) as {
|
|
1338
|
+
ok: boolean;
|
|
1339
|
+
seq: number;
|
|
1340
|
+
snapshot: boolean;
|
|
1341
|
+
sessionId?: string;
|
|
1342
|
+
events: Array<{ event: string; payload: unknown }>;
|
|
1343
|
+
};
|
|
1344
|
+
})(),
|
|
1345
|
+
new Promise<never>((_, reject) => {
|
|
1346
|
+
deadline = setTimeout(() => {
|
|
1347
|
+
requestController.abort();
|
|
1348
|
+
reject(new Error('poll request timed out'));
|
|
1349
|
+
}, POLL_REQUEST_TIMEOUT_MS);
|
|
1350
|
+
}),
|
|
1351
|
+
]);
|
|
1202
1352
|
if (generation !== pollGeneration) return;
|
|
1203
1353
|
if (!body.ok) throw new Error('poll returned ok: false');
|
|
1204
1354
|
const priorSession = lastPollSessionId;
|
|
@@ -1222,13 +1372,55 @@ function startPollingTransport(): () => void {
|
|
|
1222
1372
|
resetIntents();
|
|
1223
1373
|
responseToThreadMap.clear();
|
|
1224
1374
|
}
|
|
1225
|
-
|
|
1375
|
+
// A poll may have captured pins before a newer human click or write acknowledgement.
|
|
1376
|
+
// Skip only those pin frames, then read the canonical set so peer changes and server
|
|
1377
|
+
// normalization still arrive even though the event cursor advances past the old batch.
|
|
1378
|
+
const acceptPins = pinsAtRequest === contextPinsRevision && contextPinsPendingWrites === 0;
|
|
1379
|
+
let skippedPins = false;
|
|
1380
|
+
// Full layouts in a buffered poll can predate an HTTP refresh. Replaying
|
|
1381
|
+
// an obsolete empty layout would detach a live editor before its card
|
|
1382
|
+
// reappears. Keep the last snapshot across bookkeeping frames only:
|
|
1383
|
+
// commands (focus, tours, deltas, etc.) still need their preceding layout.
|
|
1384
|
+
const supersededLayouts = new Set<(typeof body.events)[number]>();
|
|
1385
|
+
if (!body.snapshot && hasInitialServerLayout.value) {
|
|
1386
|
+
let previousLayout: (typeof body.events)[number] | undefined;
|
|
1387
|
+
for (const entry of body.events) {
|
|
1388
|
+
const data = entry.payload as Record<string, unknown>;
|
|
1389
|
+
const sameActivation =
|
|
1390
|
+
data.catalogEpoch === catalogEpoch &&
|
|
1391
|
+
data.boardGeneration === boardGeneration &&
|
|
1392
|
+
(entry.event === 'boards-changed' ? data.activeBoardId : data.boardId) === activeBoardId.value;
|
|
1393
|
+
if (entry.event === 'canvas-layout-update' && sameActivation) {
|
|
1394
|
+
if (previousLayout) supersededLayouts.add(previousLayout);
|
|
1395
|
+
previousLayout = entry;
|
|
1396
|
+
} else if (
|
|
1397
|
+
!(entry.event === 'boards-changed' && sameActivation) &&
|
|
1398
|
+
!['agent-presence', 'human-presence', 'ax-intent', 'ax-intent-clear'].includes(entry.event)
|
|
1399
|
+
) {
|
|
1400
|
+
previousLayout = undefined;
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
for (const entry of body.events) {
|
|
1405
|
+
if (supersededLayouts.has(entry)) continue;
|
|
1406
|
+
if (entry.event === 'context-pins-changed' && !acceptPins) {
|
|
1407
|
+
skippedPins = true;
|
|
1408
|
+
continue;
|
|
1409
|
+
}
|
|
1410
|
+
dispatchWorkbenchEvent(entry.event, entry.payload);
|
|
1411
|
+
}
|
|
1412
|
+
if (skippedPins) {
|
|
1413
|
+
void refreshContextPins();
|
|
1414
|
+
void refreshContextStatus();
|
|
1415
|
+
refreshContextChip();
|
|
1416
|
+
}
|
|
1226
1417
|
dispatchedOnce = true;
|
|
1227
1418
|
pollSeq = body.seq;
|
|
1228
1419
|
reconnectAttempt.value = 0;
|
|
1229
1420
|
reconnectDelay.value = 0;
|
|
1230
1421
|
connectionStatus.value = 'connected';
|
|
1231
|
-
|
|
1422
|
+
// Bound busy-stream request rate without imposing a two-second cursor gap.
|
|
1423
|
+
pollTimer = setTimeout(() => void pollOnce(), longPolling ? POLL_LIVE_INTERVAL_MS : POLL_INTERVAL_MS);
|
|
1232
1424
|
} catch (err) {
|
|
1233
1425
|
if (generation !== pollGeneration) return;
|
|
1234
1426
|
console.warn('[sse-bridge] poll transport error:', err);
|
|
@@ -1236,14 +1428,19 @@ function startPollingTransport(): () => void {
|
|
|
1236
1428
|
reconnectAttempt.value += 1;
|
|
1237
1429
|
reconnectDelay.value = POLL_ERROR_INTERVAL_MS;
|
|
1238
1430
|
pollTimer = setTimeout(() => void pollOnce(), POLL_ERROR_INTERVAL_MS);
|
|
1431
|
+
} finally {
|
|
1432
|
+
clearTimeout(deadline);
|
|
1239
1433
|
}
|
|
1240
1434
|
};
|
|
1241
1435
|
void pollOnce();
|
|
1242
1436
|
|
|
1243
1437
|
return () => {
|
|
1438
|
+
stopHumanPresence();
|
|
1244
1439
|
pollGeneration += 1;
|
|
1245
1440
|
pollingMode = false;
|
|
1246
1441
|
pollSeq = null;
|
|
1442
|
+
controller.abort();
|
|
1443
|
+
if (pollAbort === controller) pollAbort = null;
|
|
1247
1444
|
if (pollTimer) {
|
|
1248
1445
|
clearTimeout(pollTimer);
|
|
1249
1446
|
pollTimer = null;
|
|
@@ -1252,6 +1449,8 @@ function startPollingTransport(): () => void {
|
|
|
1252
1449
|
}
|
|
1253
1450
|
|
|
1254
1451
|
export function connectSSE(): () => void {
|
|
1452
|
+
boardResyncGeneration++;
|
|
1453
|
+
pendingBoardLayout = null;
|
|
1255
1454
|
// Host-default theming: a ?theme= param applies before the first server
|
|
1256
1455
|
// frame so an embedding host panel never flashes the server-global theme.
|
|
1257
1456
|
initSessionThemeOverride(applyCanvasTheme);
|
|
@@ -1302,6 +1501,7 @@ export function connectSSE(): () => void {
|
|
|
1302
1501
|
|
|
1303
1502
|
for (const [event] of Object.entries(EVENT_HANDLERS)) {
|
|
1304
1503
|
source.addEventListener(event, (e) => {
|
|
1504
|
+
if (eventSource !== source) return;
|
|
1305
1505
|
gotFirstEvent = true;
|
|
1306
1506
|
try {
|
|
1307
1507
|
dispatchWorkbenchEvent(event, JSON.parse((e as MessageEvent).data));
|
|
@@ -1336,6 +1536,7 @@ export function connectSSE(): () => void {
|
|
|
1336
1536
|
};
|
|
1337
1537
|
|
|
1338
1538
|
return () => {
|
|
1539
|
+
stopHumanPresence();
|
|
1339
1540
|
if (reconnectTimer) {
|
|
1340
1541
|
clearTimeout(reconnectTimer);
|
|
1341
1542
|
reconnectTimer = null;
|
|
@@ -1348,6 +1549,8 @@ export function connectSSE(): () => void {
|
|
|
1348
1549
|
pollGeneration += 1;
|
|
1349
1550
|
pollingMode = false;
|
|
1350
1551
|
pollSeq = null;
|
|
1552
|
+
pollAbort?.abort();
|
|
1553
|
+
pollAbort = null;
|
|
1351
1554
|
if (pollTimer) {
|
|
1352
1555
|
clearTimeout(pollTimer);
|
|
1353
1556
|
pollTimer = null;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { computed, signal } from '@preact/signals';
|
|
2
|
+
import { workspaceSettingsSchema, type WorkspaceSettings } from '../../shared/workspace-settings.js';
|
|
3
|
+
|
|
4
|
+
export const workspaceSettings = signal<WorkspaceSettings | null>(null);
|
|
5
|
+
export const agentSessionsEnabled = computed(() => workspaceSettings.value?.agentSessions ?? false);
|
|
6
|
+
export const knowledgeLifecycleEnabled = computed(() => workspaceSettings.value?.knowledgeLifecycle ?? false);
|
|
7
|
+
|
|
8
|
+
export function applyWorkspaceSettings(value: unknown): void {
|
|
9
|
+
const parsed = workspaceSettingsSchema.safeParse(value);
|
|
10
|
+
if (parsed.success) workspaceSettings.value = parsed.data;
|
|
11
|
+
}
|