pmx-canvas 0.8.2 → 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 +49 -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/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/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 +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 +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 +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/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 +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
|
@@ -19,9 +19,6 @@ import {
|
|
|
19
19
|
type AgentPhase,
|
|
20
20
|
type AgentPresence,
|
|
21
21
|
type AgentPresenceSnapshot,
|
|
22
|
-
CONTEXT_BUDGET_DEFAULT_TOKENS,
|
|
23
|
-
type ContextBudget,
|
|
24
|
-
estimateTokens,
|
|
25
22
|
HUMAN_STARTED_SESSION_LABEL,
|
|
26
23
|
isSessionActive,
|
|
27
24
|
MAX_ACTIVITY_ENTRIES,
|
|
@@ -30,11 +27,11 @@ import {
|
|
|
30
27
|
PRESENCE_ATTACHED_IDLE_TTL_MS,
|
|
31
28
|
PRESENCE_THINKING_SETTLE_MS,
|
|
32
29
|
PRESENCE_TOOLING_SETTLE_MS,
|
|
30
|
+
type SessionTouches,
|
|
33
31
|
TRANSPORT_SOURCES,
|
|
34
32
|
} from '../shared/agent-presence.js';
|
|
35
|
-
import { serializeNodeForAgentContext } from '../shared/agent-context.js';
|
|
36
33
|
import type { PmxAxActivityKind } from './ax-state.js';
|
|
37
|
-
import {
|
|
34
|
+
import { canvasState } from './canvas-state.js';
|
|
38
35
|
import { OperationError } from './operations/types.js';
|
|
39
36
|
|
|
40
37
|
type PresenceEmitter = (event: string, payload: Record<string, unknown>) => void;
|
|
@@ -65,8 +62,12 @@ export interface PresenceTouch {
|
|
|
65
62
|
}
|
|
66
63
|
|
|
67
64
|
/** Title a write's summary can name, or a type-based fallback. */
|
|
68
|
-
function nodeTitle(nodeId: string | null | undefined): string | null {
|
|
69
|
-
const node = nodeId
|
|
65
|
+
function nodeTitle(nodeId: string | null | undefined, boardId?: string): string | null {
|
|
66
|
+
const node = nodeId
|
|
67
|
+
? boardId && boardId !== canvasState.activeBoardId
|
|
68
|
+
? canvasState.readBoard(boardId, false)?.state.nodes.find((entry) => entry.id === nodeId)
|
|
69
|
+
: canvasState.getNode(nodeId)
|
|
70
|
+
: undefined;
|
|
70
71
|
if (!node) return null;
|
|
71
72
|
const title = typeof node.data.title === 'string' ? node.data.title.trim() : '';
|
|
72
73
|
return title || `${node.type} node`;
|
|
@@ -109,11 +110,16 @@ export function describeWrite(
|
|
|
109
110
|
return { summary: `Updated ${quote(title, 'a node')}`, nodeId };
|
|
110
111
|
case 'node.remove':
|
|
111
112
|
return { summary: 'Removed a node', nodeId };
|
|
112
|
-
case 'edge.add':
|
|
113
|
+
case 'edge.add': {
|
|
114
|
+
const from = str(res.from) || str(input.from);
|
|
115
|
+
const to = str(res.to) || str(input.to);
|
|
116
|
+
const toBoardId = str(res.toBoardId) || str(input.toBoardId);
|
|
117
|
+
const foreignTarget = toBoardId && toBoardId !== canvasState.activeBoardId;
|
|
113
118
|
return {
|
|
114
|
-
summary: `Connected ${quote(nodeTitle(
|
|
115
|
-
nodeId:
|
|
119
|
+
summary: `Connected ${quote(nodeTitle(from), 'a node')} → ${quote(nodeTitle(to, toBoardId), foreignTarget ? `a node on board ${toBoardId}` : 'a node')}`,
|
|
120
|
+
nodeId: foreignTarget ? (canvasState.getNode(from) ? from : null) : to || null,
|
|
116
121
|
};
|
|
122
|
+
}
|
|
117
123
|
case 'edge.remove':
|
|
118
124
|
return { summary: 'Removed an edge', nodeId: null };
|
|
119
125
|
case 'group.create':
|
|
@@ -207,26 +213,74 @@ interface StoredPresence extends AgentPresence {
|
|
|
207
213
|
startSnapshotId: string | null;
|
|
208
214
|
/** When this session attached (ISO); the receipt counts reads and pins from here. */
|
|
209
215
|
startedAt: string | null;
|
|
216
|
+
/**
|
|
217
|
+
* Each board as the session found it — its cards and content revision —
|
|
218
|
+
* captured at attach and when a board opens mid-session (a session started
|
|
219
|
+
* on Home gets one when its first write opens a board). The live lens
|
|
220
|
+
* counts from here.
|
|
221
|
+
*/
|
|
222
|
+
startBoards: Map<string, { nodeIds: Set<string>; revision: number }>;
|
|
223
|
+
/** The last computed touches and what they were computed from. */
|
|
224
|
+
touchesCache: { key: string; value: SessionTouches | undefined } | null;
|
|
210
225
|
}
|
|
211
226
|
|
|
212
|
-
|
|
213
|
-
|
|
227
|
+
type Baselines = StoredPresence['startBoards'];
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Record the open board as this session finds it, unless it already has. A
|
|
231
|
+
* board created after the session started began empty: a write on Home opens
|
|
232
|
+
* a new board while saving, after its first cards exist, so those count as
|
|
233
|
+
* the session's.
|
|
234
|
+
*/
|
|
235
|
+
function captureBaseline(baselines: Baselines, since: string | null): void {
|
|
236
|
+
const boardId = canvasState.activeBoardId;
|
|
237
|
+
if (!boardId || baselines.has(boardId)) return;
|
|
238
|
+
const createdAt = canvasState.listBoards().find((board) => board.id === boardId)?.createdAt;
|
|
239
|
+
const bornInSession = since !== null && createdAt !== undefined && createdAt >= since;
|
|
240
|
+
baselines.set(
|
|
241
|
+
boardId,
|
|
242
|
+
bornInSession
|
|
243
|
+
? { nodeIds: new Set(), revision: 0 }
|
|
244
|
+
: {
|
|
245
|
+
nodeIds: new Set(canvasState.nodeStamps().map((node) => node.id)),
|
|
246
|
+
revision: canvasState.getContentRevision().revision,
|
|
247
|
+
},
|
|
248
|
+
);
|
|
214
249
|
}
|
|
215
250
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
251
|
+
/** The cards an attached session read, created, edited and pinned on the open board since it found it. */
|
|
252
|
+
function sessionTouches(stored: StoredPresence, readVersion: number): SessionTouches | undefined {
|
|
253
|
+
const boardId = canvasState.activeBoardId;
|
|
254
|
+
if (!stored.attached || !stored.startedAt || !boardId) return undefined;
|
|
255
|
+
// A board can open without a change event (a write on Home): record it now.
|
|
256
|
+
captureBaseline(stored.startBoards, stored.startedAt);
|
|
257
|
+
const start = stored.startBoards.get(boardId);
|
|
258
|
+
if (!start) return undefined;
|
|
259
|
+
const since = stored.startedAt;
|
|
260
|
+
const pins = Object.entries(canvasState.getContextPinMeta());
|
|
261
|
+
const key = `${boardId}|${canvasState.getContentRevision().revision}|${readVersion}|${pins.map(([id, meta]) => `${id}@${meta.pinnedAt}`).join(',')}`;
|
|
262
|
+
if (stored.touchesCache?.key === key) return stored.touchesCache.value;
|
|
263
|
+
const stamps = canvasState.nodeStamps();
|
|
264
|
+
const value: SessionTouches = {
|
|
265
|
+
startedAt: since,
|
|
266
|
+
read: canvasState.getReadNodeIdsSince(boardId, since),
|
|
267
|
+
created: stamps.filter((node) => !start.nodeIds.has(node.id) && node.createdBy?.actor === 'agent').map((n) => n.id),
|
|
268
|
+
edited: stamps
|
|
269
|
+
.filter(
|
|
270
|
+
(node) =>
|
|
271
|
+
start.nodeIds.has(node.id) &&
|
|
272
|
+
node.lastEditedBy?.actor === 'agent' &&
|
|
273
|
+
(node.contentRevision ?? 0) > start.revision,
|
|
274
|
+
)
|
|
275
|
+
.map((node) => node.id),
|
|
276
|
+
pinned: pins.filter(([, meta]) => meta.pinnedBy.actor === 'agent' && meta.pinnedAt >= since).map(([id]) => id),
|
|
277
|
+
};
|
|
278
|
+
stored.touchesCache = { key, value };
|
|
279
|
+
return value;
|
|
219
280
|
}
|
|
220
281
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const nodes = Array.from(canvasState.contextPinnedNodeIds)
|
|
224
|
-
.map((id) => canvasState.getNode(id))
|
|
225
|
-
.filter((node): node is CanvasNodeState => node !== undefined);
|
|
226
|
-
const payload = nodes.map((node) =>
|
|
227
|
-
serializeNodeForAgentContext(node, { defaultTextLength: 700, webpageTextLength: 1600, includePosition: true }),
|
|
228
|
-
);
|
|
229
|
-
return { used: nodes.length === 0 ? 0 : estimateTokens(JSON.stringify(payload)), total: contextBudgetTotal() };
|
|
282
|
+
function presenceKey(source: string, agentId: string | null | undefined): string {
|
|
283
|
+
return agentId && agentId.trim() ? agentId.trim() : source;
|
|
230
284
|
}
|
|
231
285
|
|
|
232
286
|
/** Returns the id of the pre-session snapshot the server took, if any. */
|
|
@@ -400,6 +454,8 @@ export class AgentPresenceRegistry {
|
|
|
400
454
|
toolingUntilMs: null,
|
|
401
455
|
startSnapshotId: null,
|
|
402
456
|
startedAt: null,
|
|
457
|
+
startBoards: new Map(),
|
|
458
|
+
touchesCache: null,
|
|
403
459
|
};
|
|
404
460
|
const wasAttached = stored.attached;
|
|
405
461
|
stored.lastSeenMs = now;
|
|
@@ -457,6 +513,7 @@ export class AgentPresenceRegistry {
|
|
|
457
513
|
// placeholder that already adopted work is a real session and stays.
|
|
458
514
|
let inheritedSnapshotId: string | null = null;
|
|
459
515
|
let inheritedStartedAt: string | null = null;
|
|
516
|
+
let inheritedStartBoards: Baselines | null = null;
|
|
460
517
|
if (stored.source !== 'browser') {
|
|
461
518
|
const placeholder = [...this.presences.values()].find(
|
|
462
519
|
(presence) =>
|
|
@@ -473,10 +530,13 @@ export class AgentPresenceRegistry {
|
|
|
473
530
|
}
|
|
474
531
|
inheritedSnapshotId = placeholder.startSnapshotId;
|
|
475
532
|
inheritedStartedAt = placeholder.startedAt;
|
|
533
|
+
inheritedStartBoards = placeholder.startBoards;
|
|
476
534
|
}
|
|
477
535
|
}
|
|
478
536
|
stored.startSnapshotId = inheritedSnapshotId ?? this.onSessionStart(this.publicView(stored, now));
|
|
479
537
|
stored.startedAt = inheritedStartedAt ?? new Date(now).toISOString();
|
|
538
|
+
stored.startBoards = inheritedStartBoards ?? new Map();
|
|
539
|
+
captureBaseline(stored.startBoards, stored.startedAt);
|
|
480
540
|
}
|
|
481
541
|
if (input.attached === false) {
|
|
482
542
|
if (wasAttached)
|
|
@@ -612,6 +672,23 @@ export class AgentPresenceRegistry {
|
|
|
612
672
|
if (this.presences.size > 0) this.scheduleEmit();
|
|
613
673
|
}
|
|
614
674
|
|
|
675
|
+
/** Bumped when an agent read is recorded: the live lens's read list changed. */
|
|
676
|
+
private readVersion = 0;
|
|
677
|
+
|
|
678
|
+
/** A read landed (server.ts's read listener): re-count and re-emit. */
|
|
679
|
+
noteRead(): void {
|
|
680
|
+
this.readVersion += 1;
|
|
681
|
+
this.refresh();
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** A board opened: attached sessions record it as they find it, before any write lands. */
|
|
685
|
+
boardOpened(): void {
|
|
686
|
+
for (const stored of this.presences.values()) {
|
|
687
|
+
if (stored.attached) captureBaseline(stored.startBoards, stored.startedAt);
|
|
688
|
+
}
|
|
689
|
+
this.refresh();
|
|
690
|
+
}
|
|
691
|
+
|
|
615
692
|
/** Remove a writer (session-end). */
|
|
616
693
|
detach(sessionId: string): boolean {
|
|
617
694
|
const stored = this.presences.get(sessionId);
|
|
@@ -628,15 +705,22 @@ export class AgentPresenceRegistry {
|
|
|
628
705
|
|
|
629
706
|
snapshot(now = Date.now()): AgentPresenceSnapshot {
|
|
630
707
|
this.sweep(now, { emit: false });
|
|
631
|
-
const presences = [...this.presences.values()].map((stored) => this.publicView(stored, now));
|
|
708
|
+
const presences = [...this.presences.values()].map((stored) => this.publicView(stored, now, true));
|
|
632
709
|
return {
|
|
633
710
|
presences,
|
|
634
|
-
budget: estimateContextBudget(),
|
|
635
711
|
sessionActive: isSessionActive(presences),
|
|
636
712
|
activity: this.activity.map((entry) => ({ ...entry })),
|
|
637
713
|
};
|
|
638
714
|
}
|
|
639
715
|
|
|
716
|
+
/** Latest attached session: a newly attached reviewer must check fast context again. */
|
|
717
|
+
reviewSessionStartedAt(): number | null {
|
|
718
|
+
const starts = [...this.presences.values()]
|
|
719
|
+
.filter((presence) => presence.attached && presence.startedAt)
|
|
720
|
+
.map((presence) => Date.parse(presence.startedAt!));
|
|
721
|
+
return starts.length ? Math.max(...starts) : null;
|
|
722
|
+
}
|
|
723
|
+
|
|
640
724
|
/** Test / shutdown hook. */
|
|
641
725
|
reset(): void {
|
|
642
726
|
this.steeringConsumers.clear();
|
|
@@ -650,15 +734,22 @@ export class AgentPresenceRegistry {
|
|
|
650
734
|
this.maybeStopSweeper();
|
|
651
735
|
}
|
|
652
736
|
|
|
653
|
-
|
|
737
|
+
/** `withTouches`: only emitted snapshots carry the session's touches (they read the DB). */
|
|
738
|
+
private publicView(stored: StoredPresence, now: number, withTouches = false): AgentPresence {
|
|
654
739
|
const {
|
|
655
740
|
lastSeenMs: _lastSeenMs,
|
|
656
741
|
phaseSetMs: _phaseSetMs,
|
|
657
742
|
toolingUntilMs,
|
|
658
743
|
startSnapshotId: _startSnapshotId,
|
|
744
|
+
startBoards: _startBoards,
|
|
745
|
+
touchesCache: _touchesCache,
|
|
659
746
|
lastActivityNodeId,
|
|
660
747
|
...presence
|
|
661
748
|
} = stored;
|
|
749
|
+
if (withTouches) {
|
|
750
|
+
const session = sessionTouches(stored, this.readVersion);
|
|
751
|
+
if (session) presence.session = session;
|
|
752
|
+
}
|
|
662
753
|
let phase = presence.phase;
|
|
663
754
|
let detail = presence.detail;
|
|
664
755
|
if (toolingUntilMs !== null && toolingUntilMs > now) {
|
|
@@ -801,6 +892,15 @@ export const agentPresence = new AgentPresenceRegistry();
|
|
|
801
892
|
// and context pins. Re-emit on every AX / pin change — whatever transport made
|
|
802
893
|
// it — so no caller has to remember to refresh, and the SDK paths that bypass
|
|
803
894
|
// the operation registry are covered too.
|
|
895
|
+
// The live lens also follows card content (a person's edit takes a card out of
|
|
896
|
+
// the agent's session) and the open board — but not moves, so a drag stays quiet.
|
|
897
|
+
let lastBoardId = canvasState.activeBoardId;
|
|
898
|
+
let lastRevision = canvasState.getContentRevision().revision;
|
|
804
899
|
canvasState.onChange((type) => {
|
|
805
|
-
|
|
900
|
+
const boardId = canvasState.activeBoardId;
|
|
901
|
+
const revision = canvasState.getContentRevision().revision;
|
|
902
|
+
if (boardId !== lastBoardId) agentPresence.boardOpened();
|
|
903
|
+
else if (type === 'ax' || type === 'pins' || revision !== lastRevision) agentPresence.refresh();
|
|
904
|
+
lastBoardId = boardId;
|
|
905
|
+
lastRevision = revision;
|
|
806
906
|
});
|
package/src/server/ax-context.ts
CHANGED
|
@@ -15,6 +15,8 @@ import {
|
|
|
15
15
|
} from './ax-state.js';
|
|
16
16
|
import type { CanvasLayout } from './canvas-state.js';
|
|
17
17
|
import { canvasState, type CanvasNodeState } from './canvas-state.js';
|
|
18
|
+
import { lifecycleQualifications } from './lifecycle-status.js';
|
|
19
|
+
import { memoryKey } from '../shared/memory-graph.js';
|
|
18
20
|
|
|
19
21
|
/**
|
|
20
22
|
* Compact, surface-safe view of the canvas-bound AX state, injected into (and
|
|
@@ -54,18 +56,29 @@ export function buildCanvasAxSurfaceSnapshot(): PmxAxSurfaceSnapshot {
|
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
function serializeNodes(nodes: CanvasNodeState[]) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
59
|
+
function serializeNodes(nodes: CanvasNodeState[], qualifications: ReturnType<typeof lifecycleQualifications>) {
|
|
60
|
+
const qualification = (node: CanvasNodeState) =>
|
|
61
|
+
canvasState.activeBoardId
|
|
62
|
+
? qualifications[memoryKey({ boardId: canvasState.activeBoardId, cardId: node.id })]
|
|
63
|
+
: undefined;
|
|
64
|
+
return nodes
|
|
65
|
+
.filter((node) => !qualification(node)?.retired)
|
|
66
|
+
.map((node) => ({
|
|
67
|
+
...serializeNodeForAgentContext(node, {
|
|
68
|
+
defaultTextLength: 700,
|
|
69
|
+
webpageTextLength: 1600,
|
|
70
|
+
includePosition: true,
|
|
71
|
+
}),
|
|
72
|
+
qualification: qualification(node),
|
|
73
|
+
}));
|
|
65
74
|
}
|
|
66
75
|
|
|
67
|
-
export function buildCanvasAxPinnedContext(): PmxAxPinnedContext {
|
|
68
|
-
const nodeIds = Array.from(canvasState.contextPinnedNodeIds)
|
|
76
|
+
export function buildCanvasAxPinnedContext(qualifications = lifecycleQualifications()): PmxAxPinnedContext {
|
|
77
|
+
const nodeIds = Array.from(canvasState.contextPinnedNodeIds).filter(
|
|
78
|
+
(id) =>
|
|
79
|
+
!canvasState.activeBoardId ||
|
|
80
|
+
!qualifications[memoryKey({ boardId: canvasState.activeBoardId, cardId: id })]?.retired,
|
|
81
|
+
);
|
|
69
82
|
const nodes = nodeIds
|
|
70
83
|
.map((id) => canvasState.getNode(id))
|
|
71
84
|
.filter((node): node is CanvasNodeState => node !== undefined);
|
|
@@ -73,7 +86,7 @@ export function buildCanvasAxPinnedContext(): PmxAxPinnedContext {
|
|
|
73
86
|
preamble: nodes.length > 0 ? buildAgentContextPreamble(nodes) : '',
|
|
74
87
|
nodeIds,
|
|
75
88
|
count: nodeIds.length,
|
|
76
|
-
nodes: serializeNodes(nodes),
|
|
89
|
+
nodes: serializeNodes(nodes, qualifications),
|
|
77
90
|
};
|
|
78
91
|
}
|
|
79
92
|
|
|
@@ -122,6 +135,7 @@ function buildHumanAnnotations(layout: CanvasLayout): PmxAxHumanAnnotation[] {
|
|
|
122
135
|
export function buildCanvasAxContext(consumer?: string): PmxAxContext & { boardId: string | null } {
|
|
123
136
|
const layout = canvasState.getLayout();
|
|
124
137
|
const ax = canvasState.getAxState();
|
|
138
|
+
const qualifications = lifecycleQualifications();
|
|
125
139
|
const focusNodes = ax.focus.nodeIds
|
|
126
140
|
.map((id) => canvasState.getNode(id))
|
|
127
141
|
.filter((node): node is CanvasNodeState => node !== undefined);
|
|
@@ -138,9 +152,9 @@ export function buildCanvasAxContext(consumer?: string): PmxAxContext & { boardI
|
|
|
138
152
|
omittedPending: Math.max(0, totalPending - pendingSteering.length),
|
|
139
153
|
pendingActivity: buildPendingAxActivity(ax, consumer),
|
|
140
154
|
},
|
|
141
|
-
pinned: buildCanvasAxPinnedContext(),
|
|
155
|
+
pinned: buildCanvasAxPinnedContext(qualifications),
|
|
142
156
|
focus: ax.focus,
|
|
143
|
-
focusNodes: serializeNodes(focusNodes),
|
|
157
|
+
focusNodes: serializeNodes(focusNodes, qualifications),
|
|
144
158
|
workItems: ax.workItems,
|
|
145
159
|
approvalGates: ax.approvalGates,
|
|
146
160
|
reviewAnnotations: ax.reviewAnnotations,
|
|
@@ -489,6 +489,7 @@ export async function buildBoardExport(
|
|
|
489
489
|
const edges = snapshotEdges.map((edge: CanvasEdge) => ({
|
|
490
490
|
from: edge.from,
|
|
491
491
|
to: edge.to,
|
|
492
|
+
...(edge.toBoardId !== undefined ? { toBoardId: edge.toBoardId } : {}),
|
|
492
493
|
label: edge.label ?? '',
|
|
493
494
|
style: edge.style ?? 'solid',
|
|
494
495
|
}));
|
|
@@ -537,7 +538,10 @@ function exportPage(input: {
|
|
|
537
538
|
title: string;
|
|
538
539
|
scheme: 'dark' | 'light';
|
|
539
540
|
exportedAt: string;
|
|
540
|
-
board: {
|
|
541
|
+
board: {
|
|
542
|
+
cards: ExportCard[];
|
|
543
|
+
edges: Array<{ from: string; to: string; toBoardId?: string; label: string; style: string }>;
|
|
544
|
+
};
|
|
541
545
|
assets: Record<string, string>;
|
|
542
546
|
}): string {
|
|
543
547
|
const date = new Date(input.exportedAt).toLocaleString('en-GB', { dateStyle: 'medium', timeStyle: 'short' });
|
|
@@ -670,10 +674,17 @@ for (const card of ordered) {
|
|
|
670
674
|
el.appendChild(body(card));
|
|
671
675
|
title.addEventListener('click', () => expand(card));
|
|
672
676
|
}
|
|
677
|
+
for (const edge of board.edges.filter((edge) => edge.from === card.id && edge.toBoardId !== undefined)) {
|
|
678
|
+
const connection = document.createElement('div');
|
|
679
|
+
connection.className = 'card-title';
|
|
680
|
+
connection.textContent = 'External connection → ' + edge.toBoardId + '/' + edge.to + (edge.label ? ' · ' + edge.label : '');
|
|
681
|
+
el.appendChild(connection);
|
|
682
|
+
}
|
|
673
683
|
world.appendChild(el);
|
|
674
684
|
}
|
|
675
685
|
const NS = 'http://www.w3.org/2000/svg';
|
|
676
686
|
for (const edge of board.edges) {
|
|
687
|
+
if (edge.toBoardId !== undefined) continue;
|
|
677
688
|
const a = byId.get(edge.from), b = byId.get(edge.to);
|
|
678
689
|
if (!a || !b) continue;
|
|
679
690
|
const x1 = a.x + a.w / 2, y1 = a.y + a.h / 2, x2 = b.x + b.w / 2, y2 = b.y + b.h / 2;
|