pmx-canvas 0.8.0 → 0.9.0
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 +66 -0
- package/Readme.md +131 -532
- package/dist/canvas/global.css +2759 -510
- package/dist/canvas/index.js +168 -107
- package/dist/canvas/mcp-app.html +2936 -632
- package/dist/json-render/index.css +81 -1
- 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/HtmlNode.d.ts +1 -0
- 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/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 +55 -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 +454 -18
- package/docs/environment.md +0 -1
- package/docs/evals/curation-effect.md +128 -0
- package/docs/http-api.md +465 -38
- package/docs/mcp.md +150 -19
- 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 +234 -43
- package/skills/pmx-canvas/evals/evals.json +62 -0
- package/skills/pmx-canvas/references/ax-html-control-surface.md +6 -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 +87 -11
- package/src/cli/commands/edges.ts +28 -0
- package/src/cli/commands/groups.ts +7 -0
- package/src/cli/commands/nodes.ts +4 -2
- package/src/cli/commands/query.ts +34 -2
- package/src/cli/commands/record.ts +199 -48
- package/src/cli/index.ts +7 -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 +533 -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 +0 -1
- package/src/client/nodes/FileNode.tsx +46 -42
- package/src/client/nodes/HtmlNode.tsx +44 -2
- 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/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 +206 -29
- package/src/client/state/workspace-settings.ts +11 -0
- package/src/client/theme/global.css +2759 -510
- 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.css +6 -1
- package/src/json-render/renderer/index.tsx +31 -1
- 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 +196 -84
- 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 +179 -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 +203 -13
- 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 +32 -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/json-render.ts +5 -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 +77 -26
- 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 } 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
|
|
|
@@ -474,7 +505,7 @@ function handleAgentSessionEnded(data: Record<string, unknown>): void {
|
|
|
474
505
|
let stopHumanPresence: (() => void) | null = null;
|
|
475
506
|
let boardResyncGeneration = 0;
|
|
476
507
|
let layoutRevision = 0;
|
|
477
|
-
let
|
|
508
|
+
let pendingBoardLayout: Record<string, unknown> | null = null;
|
|
478
509
|
|
|
479
510
|
function handleHumanPresence(data: Record<string, unknown>): void {
|
|
480
511
|
applyHumanSnapshot(data as Partial<HumanPresenceSnapshot>);
|
|
@@ -485,32 +516,61 @@ function handleHumanPresence(data: Record<string, unknown>): void {
|
|
|
485
516
|
* a different board now, so take its layout like a fresh connection would —
|
|
486
517
|
* camera included — and drop what belonged to the old board.
|
|
487
518
|
*/
|
|
488
|
-
function resyncForBoardSwitch(): void {
|
|
519
|
+
function resyncForBoardSwitch(initial: boolean): void {
|
|
520
|
+
const pending = pendingBoardLayout;
|
|
521
|
+
pendingBoardLayout = null;
|
|
522
|
+
if (initial) {
|
|
523
|
+
// Untagged initial fetches may already have supplied the layout. Tagged
|
|
524
|
+
// SSE waits here for the first catalog to identify its owner.
|
|
525
|
+
if (pending?.boardId === activeBoardId.value) handleCanvasLayoutUpdate(pending);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const boardId = activeBoardId.value;
|
|
489
529
|
const generation = ++boardResyncGeneration;
|
|
490
530
|
savedLayout = null;
|
|
491
531
|
hasInitialServerLayout.value = false;
|
|
492
532
|
resetIntents();
|
|
493
533
|
resetCanvasInteractionState();
|
|
494
534
|
resetAttentionBridge();
|
|
495
|
-
|
|
535
|
+
applyServerCanvasLayout({ nodes: [], edges: [] });
|
|
536
|
+
if (pending?.boardId === activeBoardId.value) handleCanvasLayoutUpdate(pending);
|
|
537
|
+
// Keep the change available when visiting another board, so its Open board
|
|
538
|
+
// action can bring the person back to Undo. Home still clears board chrome.
|
|
539
|
+
if (activeBoardId.value === null) dismissSessionReceipt();
|
|
540
|
+
if (activityLens.value?.kind === 'receipt') activityLens.value = null;
|
|
496
541
|
applyContextStatus({});
|
|
497
542
|
axSurfaceState.value = null;
|
|
498
543
|
void refreshAxSurface();
|
|
499
544
|
const layoutAtRequest = layoutRevision;
|
|
500
|
-
const pinsAtRequest =
|
|
501
|
-
void fetchCanvasState().then((layout) => {
|
|
545
|
+
const pinsAtRequest = contextPinsRevision;
|
|
546
|
+
void fetchCanvasState(boardId).then((layout) => {
|
|
502
547
|
if (generation === boardResyncGeneration && layoutAtRequest === layoutRevision)
|
|
503
|
-
handleCanvasLayoutUpdate({ layout });
|
|
548
|
+
handleCanvasLayoutUpdate({ layout, boardId });
|
|
504
549
|
});
|
|
505
550
|
void requestJson<{ nodeIds?: unknown }>('fetchPinnedContext', '/api/canvas/pinned-context', {}).then((data) => {
|
|
506
|
-
if (generation === boardResyncGeneration && pinsAtRequest ===
|
|
551
|
+
if (generation === boardResyncGeneration && pinsAtRequest === contextPinsRevision)
|
|
507
552
|
handleContextPinsChanged({ nodeIds: data.nodeIds });
|
|
508
553
|
});
|
|
509
554
|
}
|
|
510
555
|
setBoardSwitchHandler(resyncForBoardSwitch);
|
|
511
556
|
|
|
512
557
|
function handleConnected(data: Record<string, unknown>): void {
|
|
558
|
+
memoryGraphRevision.value++;
|
|
559
|
+
// A current connection authorizes catalogs from this server run. Old HTTP
|
|
560
|
+
// continuations and buffered layouts cannot cross the snapshot boundary.
|
|
561
|
+
boardResyncGeneration++;
|
|
562
|
+
pendingBoardLayout = null;
|
|
563
|
+
// The following snapshot is a fresh baseline, including lower revisions after
|
|
564
|
+
// a restart/restore. Keep same-board editors and their text selections mounted.
|
|
565
|
+
hasInitialServerLayout.value = false;
|
|
566
|
+
if (typeof data.catalogEpoch === 'string') connectBoardCatalog(data.catalogEpoch);
|
|
567
|
+
// A same-bundle daemon restart keeps drafts alive but rotates its human capability.
|
|
568
|
+
// Install it before connect-time requests or presence heartbeats can write.
|
|
569
|
+
if (!isHostedWorkbench() && typeof data.workbenchToken === 'string') {
|
|
570
|
+
(window as Window & { __PMX_WORKBENCH_TOKEN?: string }).__PMX_WORKBENCH_TOKEN = data.workbenchToken;
|
|
571
|
+
}
|
|
513
572
|
sessionId.value = (data.sessionId as string) || '';
|
|
573
|
+
applyWorkspaceSettings(data.workspaceSettings);
|
|
514
574
|
connectionStatus.value = 'connected';
|
|
515
575
|
// Hosted writes capture the active board. Load its identity before reporting
|
|
516
576
|
// the viewport, rather than sending a spurious Home precondition on boot.
|
|
@@ -522,6 +582,7 @@ function handleConnected(data: Record<string, unknown>): void {
|
|
|
522
582
|
// keep it live from here on.
|
|
523
583
|
void fetchAgentPresence().then(applyPresenceSnapshot);
|
|
524
584
|
void refreshContextStatus();
|
|
585
|
+
refreshContextChip();
|
|
525
586
|
// Human collaborators (phase 8): announce this tab and read who else is here.
|
|
526
587
|
// An `?agent=<key>` tab is an AGENT's view — it must not heartbeat a human
|
|
527
588
|
// guest cursor (its writes already book as the agent).
|
|
@@ -942,6 +1003,9 @@ function handleCanvasResponseComplete(data: Record<string, unknown>): void {
|
|
|
942
1003
|
// ── Agent canvas tool events ──────────────────────────────────
|
|
943
1004
|
|
|
944
1005
|
function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
|
|
1006
|
+
// Even an inactive board can affect library links. This invalidates a read;
|
|
1007
|
+
// the ownership guards below still control applying the canvas snapshot.
|
|
1008
|
+
memoryGraphRevision.value++;
|
|
945
1009
|
const layout = data.layout as
|
|
946
1010
|
| {
|
|
947
1011
|
nodes?: Array<Record<string, unknown>>;
|
|
@@ -951,6 +1015,22 @@ function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
|
|
|
951
1015
|
}
|
|
952
1016
|
| undefined;
|
|
953
1017
|
if (!layout?.nodes) return;
|
|
1018
|
+
// Board IDs repeat on A → B → A and after restores. Only the accepted
|
|
1019
|
+
// activation may establish its camera/content baseline.
|
|
1020
|
+
if (typeof data.boardGeneration === 'number') {
|
|
1021
|
+
if (data.catalogEpoch !== catalogEpoch || (boardGeneration !== null && data.boardGeneration < boardGeneration))
|
|
1022
|
+
return;
|
|
1023
|
+
if (boardGeneration === null || data.boardGeneration > boardGeneration) {
|
|
1024
|
+
pendingBoardLayout = data;
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
// The server sends a switched board's layout before boards-changed. Do not
|
|
1029
|
+
// mount it under the previous owner, even when card IDs happen to match.
|
|
1030
|
+
if (data.boardId !== undefined && data.boardId !== activeBoardId.value) {
|
|
1031
|
+
pendingBoardLayout = data;
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
954
1034
|
layoutRevision += 1;
|
|
955
1035
|
const shouldApplyViewport = !hasInitialServerLayout.value;
|
|
956
1036
|
hasInitialServerLayout.value = true;
|
|
@@ -986,6 +1066,8 @@ function handleCanvasLayoutUpdate(data: Record<string, unknown>): void {
|
|
|
986
1066
|
);
|
|
987
1067
|
|
|
988
1068
|
syncAttentionFromSse({ event: 'canvas-layout-update', data });
|
|
1069
|
+
// Content changes move the brief's size and can clip it (debounced, so a burst fetches once).
|
|
1070
|
+
refreshContextChip(1_000);
|
|
989
1071
|
}
|
|
990
1072
|
|
|
991
1073
|
function reconnectDelayMs(attempt: number): number {
|
|
@@ -1007,7 +1089,8 @@ function handleCanvasFocusNode(data: Record<string, unknown>): void {
|
|
|
1007
1089
|
}
|
|
1008
1090
|
|
|
1009
1091
|
function handleCanvasViewportUpdate(data: Record<string, unknown>): void {
|
|
1010
|
-
|
|
1092
|
+
// A delayed echo of this tab's earlier commit must not cancel its newer gesture.
|
|
1093
|
+
if (ownsCamera() || data.clientId === humanClientId) return;
|
|
1011
1094
|
const viewport = data.viewport as Record<string, unknown> | undefined;
|
|
1012
1095
|
if (!viewport) return;
|
|
1013
1096
|
const x = typeof viewport.x === 'number' ? viewport.x : 0;
|
|
@@ -1017,6 +1100,40 @@ function handleCanvasViewportUpdate(data: Record<string, unknown>): void {
|
|
|
1017
1100
|
replaceViewport({ x, y, scale });
|
|
1018
1101
|
}
|
|
1019
1102
|
|
|
1103
|
+
// Tour steps and camera moves animate locally; a deterministic capture owns
|
|
1104
|
+
// its camera outright and ignores both.
|
|
1105
|
+
function handleCanvasTourStep(data: Record<string, unknown>): void {
|
|
1106
|
+
if (recordingCamera.value) return;
|
|
1107
|
+
// A human stepping in one tab moves tabs already presenting; only a
|
|
1108
|
+
// `present` step (the agent's default) starts presenting elsewhere.
|
|
1109
|
+
if (!presenting.value && data.present !== true) return;
|
|
1110
|
+
const stop = tourStopSchema.safeParse(data.stop);
|
|
1111
|
+
if (stop.success) presentStop(stop.data);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
function handleCanvasTourExit(): void {
|
|
1115
|
+
if (recordingCamera.value) return;
|
|
1116
|
+
presenting.value = false;
|
|
1117
|
+
presentationError.value = '';
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function handleCanvasCameraMove(data: Record<string, unknown>): void {
|
|
1121
|
+
if (recordingCamera.value) return;
|
|
1122
|
+
const stop = tourStopSchema.safeParse(data.stop);
|
|
1123
|
+
if (!stop.success) return;
|
|
1124
|
+
try {
|
|
1125
|
+
// The presentation camera is local; otherwise the end camera becomes the
|
|
1126
|
+
// board viewport — unrecorded, so a camera move is never an undo step.
|
|
1127
|
+
playStop(stop.data, presenting.value ? undefined : (end) => commitViewport(end, { recordHistory: false }));
|
|
1128
|
+
} catch {
|
|
1129
|
+
// Target not in this tab's layout yet: take the server's resolved camera.
|
|
1130
|
+
const fallback = cameraSchema.safeParse(data.viewport);
|
|
1131
|
+
if (!fallback.success) return;
|
|
1132
|
+
cancelViewportAnimation();
|
|
1133
|
+
replaceViewport(fallback.data);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1020
1137
|
function handleContextUsage(data: Record<string, unknown>): void {
|
|
1021
1138
|
const id = 'context-main';
|
|
1022
1139
|
const existing = nodes.value.get(id);
|
|
@@ -1042,15 +1159,17 @@ function handleThemeChanged(data: Record<string, unknown>): void {
|
|
|
1042
1159
|
}
|
|
1043
1160
|
|
|
1044
1161
|
function handleContextPinsChanged(data: Record<string, unknown>): void {
|
|
1045
|
-
|
|
1162
|
+
memoryGraphRevision.value++;
|
|
1046
1163
|
const nodeIds = Array.isArray(data.nodeIds) ? data.nodeIds.filter((id): id is string => typeof id === 'string') : [];
|
|
1047
|
-
replaceContextPinsFromServer(nodeIds);
|
|
1164
|
+
if (contextPinsPendingWrites === 0) replaceContextPinsFromServer(nodeIds);
|
|
1048
1165
|
syncAttentionFromSse({ event: 'context-pins-changed', data });
|
|
1049
1166
|
void refreshContextStatus();
|
|
1167
|
+
refreshContextChip();
|
|
1050
1168
|
}
|
|
1051
1169
|
|
|
1052
1170
|
function handleContextStatusChanged(): void {
|
|
1053
1171
|
void refreshContextStatus();
|
|
1172
|
+
refreshContextChip();
|
|
1054
1173
|
}
|
|
1055
1174
|
|
|
1056
1175
|
// AX state changes arrive as per-primitive deltas; rather than reduce them, treat
|
|
@@ -1128,13 +1247,22 @@ export const EVENT_HANDLERS: Record<string, (data: Record<string, unknown>) => v
|
|
|
1128
1247
|
'ext-app-result': handleExtAppResult,
|
|
1129
1248
|
'context-pins-changed': handleContextPinsChanged,
|
|
1130
1249
|
'context-status-changed': handleContextStatusChanged,
|
|
1131
|
-
'
|
|
1250
|
+
'knowledge-changed': () => void refreshKnowledge(),
|
|
1251
|
+
'lifecycle-changed': () => void refreshKnowledge(),
|
|
1252
|
+
'boards-changed': (data) => {
|
|
1253
|
+
applyBoards(data);
|
|
1254
|
+
refreshContextChip();
|
|
1255
|
+
},
|
|
1132
1256
|
'canvas-layout-update': handleCanvasLayoutUpdate,
|
|
1133
1257
|
'canvas-focus-node': handleCanvasFocusNode,
|
|
1134
1258
|
'canvas-viewport-update': handleCanvasViewportUpdate,
|
|
1259
|
+
'canvas-tour-step': handleCanvasTourStep,
|
|
1260
|
+
'canvas-tour-exit': handleCanvasTourExit,
|
|
1261
|
+
'canvas-camera-move': handleCanvasCameraMove,
|
|
1135
1262
|
'context-usage': handleContextUsage,
|
|
1136
1263
|
'trace-state': handleTraceState,
|
|
1137
1264
|
'theme-changed': handleThemeChanged,
|
|
1265
|
+
'settings-changed': (data) => applyWorkspaceSettings(data.workspaceSettings),
|
|
1138
1266
|
'canvas-prompt-created': handleCanvasPromptCreated,
|
|
1139
1267
|
'canvas-prompt-status': handleCanvasPromptStatus,
|
|
1140
1268
|
'canvas-response-start': handleCanvasResponseStart,
|
|
@@ -1174,6 +1302,12 @@ function startPollingTransport(): () => void {
|
|
|
1174
1302
|
pollingMode = true;
|
|
1175
1303
|
pollGeneration += 1;
|
|
1176
1304
|
const generation = pollGeneration;
|
|
1305
|
+
pollAbort?.abort();
|
|
1306
|
+
const controller = new AbortController();
|
|
1307
|
+
pollAbort = controller;
|
|
1308
|
+
// MCP-app polls are host tool calls, not direct HTTP requests. Keep their
|
|
1309
|
+
// existing cadence; HTTP clients can park one request until an event arrives.
|
|
1310
|
+
const longPolling = !isHostedWorkbench();
|
|
1177
1311
|
connectionStatus.value = 'connecting';
|
|
1178
1312
|
// Server-run identity from the last poll response. The event seq counter
|
|
1179
1313
|
// restarts at 0 on every server boot, so a cursor from a previous run can
|
|
@@ -1183,22 +1317,39 @@ function startPollingTransport(): () => void {
|
|
|
1183
1317
|
|
|
1184
1318
|
const pollOnce = async (): Promise<void> => {
|
|
1185
1319
|
if (generation !== pollGeneration) return;
|
|
1320
|
+
const requestController = new AbortController();
|
|
1321
|
+
let deadline: ReturnType<typeof setTimeout> | undefined;
|
|
1186
1322
|
try {
|
|
1187
1323
|
const params = new URLSearchParams();
|
|
1188
|
-
if (pollSeq !== null)
|
|
1324
|
+
if (pollSeq !== null) {
|
|
1325
|
+
params.set('since', String(pollSeq));
|
|
1326
|
+
if (longPolling) params.set('waitMs', String(POLL_WAIT_MS));
|
|
1327
|
+
}
|
|
1189
1328
|
const sid = sessionId.value;
|
|
1190
1329
|
if (sid) params.set('session', sid);
|
|
1191
1330
|
const query = params.toString();
|
|
1192
|
-
const
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1331
|
+
const pinsAtRequest = contextPinsRevision;
|
|
1332
|
+
const body = await Promise.race([
|
|
1333
|
+
(async () => {
|
|
1334
|
+
const response = await workbenchFetch(`/api/workbench/poll${query ? `?${query}` : ''}`, {
|
|
1335
|
+
signal: AbortSignal.any([controller.signal, requestController.signal]),
|
|
1336
|
+
});
|
|
1337
|
+
if (!response.ok) throw new Error(`poll failed with HTTP ${response.status}`);
|
|
1338
|
+
return (await response.json()) as {
|
|
1339
|
+
ok: boolean;
|
|
1340
|
+
seq: number;
|
|
1341
|
+
snapshot: boolean;
|
|
1342
|
+
sessionId?: string;
|
|
1343
|
+
events: Array<{ event: string; payload: unknown }>;
|
|
1344
|
+
};
|
|
1345
|
+
})(),
|
|
1346
|
+
new Promise<never>((_, reject) => {
|
|
1347
|
+
deadline = setTimeout(() => {
|
|
1348
|
+
requestController.abort();
|
|
1349
|
+
reject(new Error('poll request timed out'));
|
|
1350
|
+
}, POLL_REQUEST_TIMEOUT_MS);
|
|
1351
|
+
}),
|
|
1352
|
+
]);
|
|
1202
1353
|
if (generation !== pollGeneration) return;
|
|
1203
1354
|
if (!body.ok) throw new Error('poll returned ok: false');
|
|
1204
1355
|
const priorSession = lastPollSessionId;
|
|
@@ -1222,13 +1373,30 @@ function startPollingTransport(): () => void {
|
|
|
1222
1373
|
resetIntents();
|
|
1223
1374
|
responseToThreadMap.clear();
|
|
1224
1375
|
}
|
|
1225
|
-
|
|
1376
|
+
// A poll may have captured pins before a newer human click or write acknowledgement.
|
|
1377
|
+
// Skip only those pin frames, then read the canonical set so peer changes and server
|
|
1378
|
+
// normalization still arrive even though the event cursor advances past the old batch.
|
|
1379
|
+
const acceptPins = pinsAtRequest === contextPinsRevision && contextPinsPendingWrites === 0;
|
|
1380
|
+
let skippedPins = false;
|
|
1381
|
+
for (const entry of body.events) {
|
|
1382
|
+
if (entry.event === 'context-pins-changed' && !acceptPins) {
|
|
1383
|
+
skippedPins = true;
|
|
1384
|
+
continue;
|
|
1385
|
+
}
|
|
1386
|
+
dispatchWorkbenchEvent(entry.event, entry.payload);
|
|
1387
|
+
}
|
|
1388
|
+
if (skippedPins) {
|
|
1389
|
+
void refreshContextPins();
|
|
1390
|
+
void refreshContextStatus();
|
|
1391
|
+
refreshContextChip();
|
|
1392
|
+
}
|
|
1226
1393
|
dispatchedOnce = true;
|
|
1227
1394
|
pollSeq = body.seq;
|
|
1228
1395
|
reconnectAttempt.value = 0;
|
|
1229
1396
|
reconnectDelay.value = 0;
|
|
1230
1397
|
connectionStatus.value = 'connected';
|
|
1231
|
-
|
|
1398
|
+
// Bound busy-stream request rate without imposing a two-second cursor gap.
|
|
1399
|
+
pollTimer = setTimeout(() => void pollOnce(), longPolling ? POLL_LIVE_INTERVAL_MS : POLL_INTERVAL_MS);
|
|
1232
1400
|
} catch (err) {
|
|
1233
1401
|
if (generation !== pollGeneration) return;
|
|
1234
1402
|
console.warn('[sse-bridge] poll transport error:', err);
|
|
@@ -1236,6 +1404,8 @@ function startPollingTransport(): () => void {
|
|
|
1236
1404
|
reconnectAttempt.value += 1;
|
|
1237
1405
|
reconnectDelay.value = POLL_ERROR_INTERVAL_MS;
|
|
1238
1406
|
pollTimer = setTimeout(() => void pollOnce(), POLL_ERROR_INTERVAL_MS);
|
|
1407
|
+
} finally {
|
|
1408
|
+
clearTimeout(deadline);
|
|
1239
1409
|
}
|
|
1240
1410
|
};
|
|
1241
1411
|
void pollOnce();
|
|
@@ -1244,6 +1414,8 @@ function startPollingTransport(): () => void {
|
|
|
1244
1414
|
pollGeneration += 1;
|
|
1245
1415
|
pollingMode = false;
|
|
1246
1416
|
pollSeq = null;
|
|
1417
|
+
controller.abort();
|
|
1418
|
+
if (pollAbort === controller) pollAbort = null;
|
|
1247
1419
|
if (pollTimer) {
|
|
1248
1420
|
clearTimeout(pollTimer);
|
|
1249
1421
|
pollTimer = null;
|
|
@@ -1252,6 +1424,8 @@ function startPollingTransport(): () => void {
|
|
|
1252
1424
|
}
|
|
1253
1425
|
|
|
1254
1426
|
export function connectSSE(): () => void {
|
|
1427
|
+
boardResyncGeneration++;
|
|
1428
|
+
pendingBoardLayout = null;
|
|
1255
1429
|
// Host-default theming: a ?theme= param applies before the first server
|
|
1256
1430
|
// frame so an embedding host panel never flashes the server-global theme.
|
|
1257
1431
|
initSessionThemeOverride(applyCanvasTheme);
|
|
@@ -1302,6 +1476,7 @@ export function connectSSE(): () => void {
|
|
|
1302
1476
|
|
|
1303
1477
|
for (const [event] of Object.entries(EVENT_HANDLERS)) {
|
|
1304
1478
|
source.addEventListener(event, (e) => {
|
|
1479
|
+
if (eventSource !== source) return;
|
|
1305
1480
|
gotFirstEvent = true;
|
|
1306
1481
|
try {
|
|
1307
1482
|
dispatchWorkbenchEvent(event, JSON.parse((e as MessageEvent).data));
|
|
@@ -1348,6 +1523,8 @@ export function connectSSE(): () => void {
|
|
|
1348
1523
|
pollGeneration += 1;
|
|
1349
1524
|
pollingMode = false;
|
|
1350
1525
|
pollSeq = null;
|
|
1526
|
+
pollAbort?.abort();
|
|
1527
|
+
pollAbort = null;
|
|
1351
1528
|
if (pollTimer) {
|
|
1352
1529
|
clearTimeout(pollTimer);
|
|
1353
1530
|
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
|
+
}
|