pmx-canvas 0.4.7 → 0.5.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/extension.mjs +154 -5
- package/.github/extensions/pmx-canvas/steering-delivery.mjs +79 -0
- package/CHANGELOG.md +156 -0
- package/Readme.md +72 -23
- package/dist/canvas/global.css +3069 -796
- package/dist/canvas/index.js +104 -104
- package/dist/json-render/index.css +1 -1
- package/dist/types/cli/agent.d.ts +1 -0
- package/dist/types/cli/commands/pump.d.ts +9 -0
- package/dist/types/client/App.d.ts +1 -1
- package/dist/types/client/canvas/AgentPresenceLayer.d.ts +11 -0
- package/dist/types/client/canvas/BarHint.d.ts +22 -0
- package/dist/types/client/canvas/CanvasNode.d.ts +5 -0
- package/dist/types/client/canvas/CanvasViewport.d.ts +4 -4
- package/dist/types/client/canvas/CommandBar.d.ts +10 -0
- package/dist/types/client/canvas/ConnectionBanner.d.ts +13 -0
- package/dist/types/client/canvas/ContextMenu.d.ts +6 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +2 -1
- package/dist/types/client/canvas/EmptyState.d.ts +11 -0
- package/dist/types/client/canvas/ExternalWriters.d.ts +5 -0
- package/dist/types/client/canvas/HumanPresenceLayer.d.ts +8 -0
- package/dist/types/client/canvas/Minimap.d.ts +12 -1
- package/dist/types/client/canvas/ScopeFenceLayer.d.ts +8 -0
- package/dist/types/client/canvas/SelectionBar.d.ts +6 -0
- package/dist/types/client/canvas/SessionPanel.d.ts +1 -0
- package/dist/types/client/canvas/SessionReceipt.d.ts +17 -0
- package/dist/types/client/canvas/TextPrompt.d.ts +25 -0
- package/dist/types/client/canvas/ToolRail.d.ts +21 -0
- package/dist/types/client/canvas/TopBar.d.ts +7 -0
- package/dist/types/client/canvas/canvas-area.d.ts +25 -0
- package/dist/types/client/canvas/create-in-view.d.ts +18 -0
- package/dist/types/client/canvas/import-files.d.ts +8 -0
- package/dist/types/client/canvas/kind-colors.d.ts +3 -0
- package/dist/types/client/canvas/use-focus-trap.d.ts +14 -0
- package/dist/types/client/canvas/use-now.d.ts +6 -0
- package/dist/types/client/icons.d.ts +11 -4
- package/dist/types/client/nodes/ExtAppFrame.d.ts +32 -0
- package/dist/types/client/nodes/GroupNode.d.ts +6 -1
- package/dist/types/client/nodes/MarkdownNode.d.ts +2 -0
- package/dist/types/client/nodes/inline-editor-commands.d.ts +3 -4
- package/dist/types/client/state/canvas-store.d.ts +49 -5
- package/dist/types/client/state/human-store.d.ts +26 -0
- package/dist/types/client/state/iframe-mode.d.ts +1 -0
- package/dist/types/client/state/intent-bridge.d.ts +37 -5
- package/dist/types/client/state/node-factory.d.ts +0 -1
- package/dist/types/client/state/presence-store.d.ts +78 -0
- package/dist/types/client/state/session-store.d.ts +176 -0
- package/dist/types/client/types.d.ts +2 -1
- package/dist/types/client/utils/platform.d.ts +2 -0
- package/dist/types/server/agent-presence.d.ts +192 -0
- package/dist/types/server/ax-gate-ttl.d.ts +6 -0
- package/dist/types/server/ax-interaction.d.ts +4 -10
- package/dist/types/server/ax-state-manager.d.ts +21 -2
- package/dist/types/server/ax-state.d.ts +49 -3
- package/dist/types/server/ax-wait.d.ts +13 -0
- package/dist/types/server/canvas-db.d.ts +23 -1
- package/dist/types/server/canvas-operations.d.ts +1 -0
- package/dist/types/server/canvas-state.d.ts +27 -13
- package/dist/types/server/canvas-validation.d.ts +0 -14
- package/dist/types/server/html-surface.d.ts +1 -1
- package/dist/types/server/human-presence.d.ts +40 -0
- package/dist/types/server/index.d.ts +29 -3
- package/dist/types/server/intent-registry.d.ts +7 -0
- package/dist/types/server/mutation-history.d.ts +15 -2
- package/dist/types/server/operations/invoker.d.ts +13 -2
- package/dist/types/server/operations/ops/ax-presence.d.ts +2 -0
- package/dist/types/server/operations/ops/human-presence.d.ts +3 -0
- package/dist/types/server/operations/registry.d.ts +11 -0
- package/dist/types/server/operations/types.d.ts +1 -1
- package/dist/types/server/scope-fence.d.ts +62 -0
- package/dist/types/server/server.d.ts +0 -12
- package/dist/types/shared/agent-presence.d.ts +130 -0
- package/dist/types/shared/approval-gates.d.ts +19 -0
- package/dist/types/shared/auto-arrange.d.ts +0 -1
- package/dist/types/shared/ax-kinds.d.ts +11 -0
- package/dist/types/shared/human-presence.d.ts +31 -0
- package/dist/types/shared/iframe-probe.d.ts +8 -0
- package/dist/types/shared/scope-fence.d.ts +21 -0
- package/docs/RELEASE.md +54 -0
- package/docs/ax-host-adapter-contract.md +30 -14
- package/docs/cli.md +37 -1
- package/docs/environment.md +3 -0
- package/docs/http-api.md +222 -5
- package/docs/mcp.md +6 -5
- package/docs/node-types.md +3 -1
- package/docs/plans/plan-009-tech-debt-backlog.md +13 -0
- package/docs/screenshot.png +0 -0
- package/docs/screenshots/multi-agent-live.png +0 -0
- package/docs/screenshots/workbench-live-dark.png +0 -0
- package/docs/screenshots/workbench-live-light.png +0 -0
- package/docs/sdk.md +1 -1
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +109 -18
- package/skills/pmx-canvas/references/codex-app-adapter.md +28 -0
- package/skills/pmx-canvas/references/full-reference.md +55 -16
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +41 -24
- package/skills/pmx-canvas-orchestration/SKILL.md +56 -2
- package/skills/pmx-canvas-testing/SKILL.md +57 -0
- package/src/cli/agent.ts +1 -0
- package/src/cli/commands/copilot.ts +15 -8
- package/src/cli/commands/nodes.ts +1 -19
- package/src/cli/commands/pump.ts +252 -0
- package/src/cli/index.ts +1 -5
- package/src/cli/shared.ts +1 -1
- package/src/client/App.tsx +209 -585
- package/src/client/canvas/AgentPresenceLayer.tsx +74 -0
- package/src/client/canvas/AttentionHistory.tsx +0 -10
- package/src/client/canvas/BarHint.tsx +49 -0
- package/src/client/canvas/CanvasNode.tsx +488 -136
- package/src/client/canvas/CanvasViewport.tsx +190 -94
- package/src/client/canvas/CommandBar.tsx +140 -0
- package/src/client/canvas/CommandPalette.tsx +133 -58
- package/src/client/canvas/ConnectionBanner.tsx +65 -0
- package/src/client/canvas/ContextMenu.tsx +88 -27
- package/src/client/canvas/ContextPinBar.tsx +2 -2
- package/src/client/canvas/EdgeLayer.tsx +121 -38
- package/src/client/canvas/EmptyState.tsx +93 -0
- package/src/client/canvas/ExpandedNodeOverlay.tsx +64 -118
- package/src/client/canvas/ExternalWriters.tsx +308 -0
- package/src/client/canvas/FocusFieldLayer.tsx +12 -1
- package/src/client/canvas/HumanPresenceLayer.tsx +45 -0
- package/src/client/canvas/IntentLayer.tsx +1 -1
- package/src/client/canvas/Minimap.tsx +117 -190
- package/src/client/canvas/ScopeFenceLayer.tsx +38 -0
- package/src/client/canvas/SelectionBar.tsx +175 -23
- package/src/client/canvas/SessionPanel.tsx +481 -0
- package/src/client/canvas/SessionReceipt.tsx +141 -0
- package/src/client/canvas/ShortcutOverlay.tsx +21 -6
- package/src/client/canvas/SnapshotPanel.tsx +166 -80
- package/src/client/canvas/TextPrompt.tsx +128 -0
- package/src/client/canvas/ToolRail.tsx +447 -0
- package/src/client/canvas/TopBar.tsx +308 -0
- package/src/client/canvas/auto-fit.ts +1 -5
- package/src/client/canvas/canvas-area.ts +41 -0
- package/src/client/canvas/create-in-view.ts +48 -0
- package/src/client/canvas/import-files.ts +64 -0
- package/src/client/canvas/kind-colors.ts +22 -0
- package/src/client/canvas/snap-guides.ts +1 -1
- package/src/client/canvas/use-focus-trap.ts +54 -0
- package/src/client/canvas/use-now.ts +17 -0
- package/src/client/canvas/use-pan-zoom.ts +13 -2
- package/src/client/icons.tsx +57 -18
- package/src/client/nodes/ExtAppFrame.tsx +79 -2
- package/src/client/nodes/GroupNode.tsx +9 -28
- package/src/client/nodes/InlineFormatBar.tsx +4 -3
- package/src/client/nodes/InlineMarkdownEditor.tsx +27 -0
- package/src/client/nodes/MarkdownNode.tsx +59 -24
- package/src/client/nodes/MdFormatBar.tsx +2 -4
- package/src/client/nodes/inline-editor-commands.ts +17 -6
- package/src/client/nodes/use-surface-frame.ts +3 -1
- package/src/client/state/attention-bridge.ts +3 -0
- package/src/client/state/canvas-store.ts +273 -84
- package/src/client/state/human-store.ts +175 -0
- package/src/client/state/iframe-mode.ts +33 -9
- package/src/client/state/intent-bridge.ts +75 -13
- package/src/client/state/node-factory.ts +2 -8
- package/src/client/state/presence-store.ts +168 -0
- package/src/client/state/session-store.ts +419 -0
- package/src/client/state/sse-bridge.ts +93 -30
- package/src/client/theme/global.css +3069 -796
- package/src/client/types.ts +3 -1
- package/src/client/utils/platform.ts +6 -1
- package/src/mcp/canvas-access.ts +2 -2
- package/src/server/agent-presence.ts +755 -0
- package/src/server/ax-context.ts +45 -0
- package/src/server/ax-gate-ttl.ts +61 -0
- package/src/server/ax-interaction.ts +29 -1
- package/src/server/ax-state-manager.ts +78 -6
- package/src/server/ax-state.ts +82 -15
- package/src/server/ax-wait.ts +33 -0
- package/src/server/canvas-db.ts +237 -61
- package/src/server/canvas-operations.ts +17 -8
- package/src/server/canvas-state.ts +131 -58
- package/src/server/canvas-validation.ts +3 -53
- package/src/server/demo-state.json +2 -70
- package/src/server/html-surface.ts +11 -2
- package/src/server/human-presence.ts +160 -0
- package/src/server/index.ts +107 -4
- package/src/server/intent-registry.ts +13 -0
- package/src/server/mutation-history.ts +47 -2
- package/src/server/operations/composites.ts +6 -3
- package/src/server/operations/http.ts +10 -1
- package/src/server/operations/index.ts +4 -0
- package/src/server/operations/invoker.ts +33 -6
- package/src/server/operations/ops/ax-presence.ts +62 -0
- package/src/server/operations/ops/ax-read.ts +8 -0
- package/src/server/operations/ops/ax-state.ts +106 -1
- package/src/server/operations/ops/ax-timeline.ts +54 -5
- package/src/server/operations/ops/ax-work.ts +46 -0
- package/src/server/operations/ops/batch.ts +3 -1
- package/src/server/operations/ops/edges.ts +88 -2
- package/src/server/operations/ops/groups.ts +4 -3
- package/src/server/operations/ops/human-presence.ts +35 -0
- package/src/server/operations/ops/json-render.ts +11 -1
- package/src/server/operations/ops/nodes.ts +65 -16
- package/src/server/operations/ops/query.ts +1 -0
- package/src/server/operations/ops/snapshots.ts +16 -0
- package/src/server/operations/registry.ts +196 -1
- package/src/server/operations/types.ts +1 -1
- package/src/server/scope-fence.ts +207 -0
- package/src/server/server.ts +177 -79
- package/src/server/trace-manager.ts +0 -2
- package/src/server/web-artifacts.ts +0 -1
- package/src/shared/agent-presence.ts +162 -0
- package/src/shared/approval-gates.ts +35 -0
- package/src/shared/auto-arrange.ts +121 -13
- package/src/shared/ax-kinds.ts +23 -0
- package/src/shared/human-presence.ts +30 -0
- package/src/shared/iframe-probe.ts +8 -0
- package/src/shared/scope-fence.ts +31 -0
- package/dist/types/client/canvas/DockedNode.d.ts +0 -4
- package/src/client/canvas/DockedNode.tsx +0 -165
|
@@ -196,6 +196,7 @@ export declare function addCanvasEdge(input: {
|
|
|
196
196
|
style?: CanvasEdge['style'];
|
|
197
197
|
animated?: boolean;
|
|
198
198
|
}): CanvasEdge;
|
|
199
|
+
export declare function updateCanvasEdge(id: string, patch: Partial<Pick<CanvasEdge, 'type' | 'label' | 'style' | 'animated'>>): CanvasEdge;
|
|
199
200
|
export declare function removeCanvasEdge(id: string): {
|
|
200
201
|
removed: boolean;
|
|
201
202
|
};
|
|
@@ -65,7 +65,6 @@ export interface CanvasNodeState {
|
|
|
65
65
|
zIndex: number;
|
|
66
66
|
collapsed: boolean;
|
|
67
67
|
pinned: boolean;
|
|
68
|
-
dockPosition: 'left' | 'right' | null;
|
|
69
68
|
data: Record<string, unknown>;
|
|
70
69
|
}
|
|
71
70
|
export interface ViewportState {
|
|
@@ -120,11 +119,10 @@ export interface CanvasNodeUpdate {
|
|
|
120
119
|
height: number;
|
|
121
120
|
};
|
|
122
121
|
collapsed?: boolean;
|
|
123
|
-
dockPosition?: 'left' | 'right' | null;
|
|
124
122
|
}
|
|
125
123
|
export type CanvasChangeType = 'pins' | 'nodes' | 'ax' | 'ax-timeline';
|
|
126
124
|
export interface MutationRecordInfo {
|
|
127
|
-
operationType: 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'addWorkItem' | 'updateWorkItem' | 'requestApproval' | 'resolveApproval' | 'addReviewAnnotation' | 'updateReviewAnnotation' | 'requestElicitation' | 'respondElicitation' | 'requestMode' | 'resolveModeRequest' | 'setPolicy' | 'arrange' | 'batch' | 'groupNodes' | '
|
|
125
|
+
operationType: 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'updateEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'addWorkItem' | 'updateWorkItem' | 'requestApproval' | 'resolveApproval' | 'addReviewAnnotation' | 'updateReviewAnnotation' | 'requestElicitation' | 'respondElicitation' | 'requestMode' | 'resolveModeRequest' | 'setPolicy' | 'arrange' | 'batch' | 'groupNodes' | 'releaseGroupChildren' | 'viewport';
|
|
128
126
|
description: string;
|
|
129
127
|
forward: () => void;
|
|
130
128
|
inverse: () => void;
|
|
@@ -202,13 +200,6 @@ declare class CanvasStateManager {
|
|
|
202
200
|
private emptyPersistedState;
|
|
203
201
|
/** Load canvas state from SQLite. Call once on server startup. */
|
|
204
202
|
loadFromDisk(options?: LoadFromDiskOptions): boolean;
|
|
205
|
-
/**
|
|
206
|
-
* Whether this workspace's canvas DB already holds saved state. Used to gate
|
|
207
|
-
* brand-new-workspace seeding (e.g. the default docked status/context widgets)
|
|
208
|
-
* so we never add nodes to a canvas that already has content. Reflects the
|
|
209
|
-
* pre-run persisted flag until the next save.
|
|
210
|
-
*/
|
|
211
|
-
hasPersistedState(): boolean;
|
|
212
203
|
/** Debounced save — coalesces rapid mutations into a single write. */
|
|
213
204
|
private scheduleSave;
|
|
214
205
|
flushToDisk(): void;
|
|
@@ -247,6 +238,7 @@ declare class CanvasStateManager {
|
|
|
247
238
|
annotations: CanvasAnnotation[];
|
|
248
239
|
} | null;
|
|
249
240
|
/** Delete a snapshot. */
|
|
241
|
+
renameSnapshot(id: string, name: string): boolean;
|
|
250
242
|
deleteSnapshot(id: string): boolean;
|
|
251
243
|
/** Remove all snapshots from the DB. Used by test teardown. */
|
|
252
244
|
clearAllSnapshots(): void;
|
|
@@ -259,6 +251,7 @@ declare class CanvasStateManager {
|
|
|
259
251
|
getNode(id: string): CanvasNodeState | undefined;
|
|
260
252
|
getNodeForPersistence(id: string): CanvasNodeState | undefined;
|
|
261
253
|
addEdge(edge: CanvasEdge): boolean;
|
|
254
|
+
updateEdge(id: string, patch: Partial<Pick<CanvasEdge, 'type' | 'label' | 'style' | 'animated'>>): CanvasEdge | null;
|
|
262
255
|
removeEdge(id: string): boolean;
|
|
263
256
|
getEdges(): CanvasEdge[];
|
|
264
257
|
getEdgesForNode(nodeId: string): CanvasEdge[];
|
|
@@ -323,13 +316,19 @@ declare class CanvasStateManager {
|
|
|
323
316
|
detail?: string | null;
|
|
324
317
|
action?: string | null;
|
|
325
318
|
nodeIds?: string[];
|
|
319
|
+
ttlMs?: number;
|
|
326
320
|
}, options?: {
|
|
327
321
|
source?: PmxAxSource;
|
|
328
322
|
}): PmxAxApprovalGate;
|
|
329
|
-
resolveApproval(id: string, decision: 'approved' | 'rejected', options?: {
|
|
323
|
+
resolveApproval(id: string, decision: 'approved' | 'rejected' | 'held', options?: {
|
|
330
324
|
resolution?: string;
|
|
331
325
|
source?: PmxAxSource;
|
|
332
326
|
}): PmxAxApprovalGate | null;
|
|
327
|
+
/** Reopen a resolved (typically auto-held) gate with a fresh TTL. */
|
|
328
|
+
reopenApproval(id: string, options?: {
|
|
329
|
+
ttlMs?: number;
|
|
330
|
+
source?: PmxAxSource;
|
|
331
|
+
}): PmxAxApprovalGate | null;
|
|
333
332
|
getReviewAnnotations(): PmxAxReviewAnnotation[];
|
|
334
333
|
addReviewAnnotation(input: {
|
|
335
334
|
body: string;
|
|
@@ -388,6 +387,10 @@ declare class CanvasStateManager {
|
|
|
388
387
|
setPolicy(patch: {
|
|
389
388
|
tools?: Partial<PmxAxPolicy['tools']>;
|
|
390
389
|
prompt?: Partial<PmxAxPolicy['prompt']>;
|
|
390
|
+
scope?: {
|
|
391
|
+
nodeIds: string[];
|
|
392
|
+
padding?: number;
|
|
393
|
+
} | null;
|
|
391
394
|
}, options?: {
|
|
392
395
|
source?: PmxAxSource;
|
|
393
396
|
}): PmxAxPolicy;
|
|
@@ -419,7 +422,7 @@ declare class CanvasStateManager {
|
|
|
419
422
|
agentId?: string | null;
|
|
420
423
|
target?: string | null;
|
|
421
424
|
}): PmxAxSteeringMessage;
|
|
422
|
-
markSteeringDelivered(id: string): boolean;
|
|
425
|
+
markSteeringDelivered(id: string, consumer?: string | null): boolean;
|
|
423
426
|
ingestActivity(input: {
|
|
424
427
|
kind: PmxAxActivityKind;
|
|
425
428
|
title: string;
|
|
@@ -477,8 +480,19 @@ declare class CanvasStateManager {
|
|
|
477
480
|
clearContextPins(): void;
|
|
478
481
|
/** Move child nodes into a group. Sets data.parentGroup on children and data.children on the group. */
|
|
479
482
|
groupNodes(groupId: string, childIds: string[], options?: GroupNodesOptions): boolean;
|
|
480
|
-
/**
|
|
483
|
+
/**
|
|
484
|
+
* Ungroup = dissolve: the children become independent nodes (members of the
|
|
485
|
+
* enclosing group when nested) and the frame is removed, in one undo step.
|
|
486
|
+
* Delegates to `removeNode` so the human's Ungroup and the agent's
|
|
487
|
+
* `group.remove` are the same operation. False when `groupId` is not a group.
|
|
488
|
+
*/
|
|
481
489
|
ungroupNodes(groupId: string): boolean;
|
|
490
|
+
/**
|
|
491
|
+
* Internal membership step: release every child but keep the frame (the
|
|
492
|
+
* first half of "set this group's children to …"). Not an ungroup — a
|
|
493
|
+
* human or agent ungroup dissolves the frame (`ungroupNodes`).
|
|
494
|
+
*/
|
|
495
|
+
releaseGroupChildren(groupId: string): boolean;
|
|
482
496
|
clear(): void;
|
|
483
497
|
}
|
|
484
498
|
export declare const canvasState: CanvasStateManager;
|
|
@@ -21,12 +21,6 @@ export interface CanvasSizeWarning {
|
|
|
21
21
|
minWidth: number;
|
|
22
22
|
minHeight: number;
|
|
23
23
|
}
|
|
24
|
-
export interface CanvasHiddenEdgeEndpoint {
|
|
25
|
-
edgeId: string;
|
|
26
|
-
nodeId: string;
|
|
27
|
-
nodeTitle: string | null;
|
|
28
|
-
dockPosition: 'left' | 'right';
|
|
29
|
-
}
|
|
30
24
|
export interface CanvasValidationResult {
|
|
31
25
|
ok: boolean;
|
|
32
26
|
collisions: CanvasValidationPair[];
|
|
@@ -37,13 +31,6 @@ export interface CanvasValidationResult {
|
|
|
37
31
|
from: string;
|
|
38
32
|
to: string;
|
|
39
33
|
}>;
|
|
40
|
-
/**
|
|
41
|
-
* Edges whose endpoint node is DOCKED — it renders in the HUD column, not on
|
|
42
|
-
* the canvas, so the edge visually terminates in empty space even though both
|
|
43
|
-
* endpoint IDs resolve (0.4.6 orb feedback #1). Advisory: reported for
|
|
44
|
-
* diagnosis, but does NOT fail `ok` (see the note at the return site).
|
|
45
|
-
*/
|
|
46
|
-
hiddenEdgeEndpoints: CanvasHiddenEdgeEndpoint[];
|
|
47
34
|
/** Nodes below their type's readable minimum (advisory — does not fail `ok`). */
|
|
48
35
|
sizeWarnings: CanvasSizeWarning[];
|
|
49
36
|
summary: {
|
|
@@ -53,7 +40,6 @@ export interface CanvasValidationResult {
|
|
|
53
40
|
containments: number;
|
|
54
41
|
containmentViolations: number;
|
|
55
42
|
missingEdgeEndpoints: number;
|
|
56
|
-
hiddenEdgeEndpoints: number;
|
|
57
43
|
sizeWarnings: number;
|
|
58
44
|
};
|
|
59
45
|
}
|
|
@@ -60,7 +60,7 @@ export declare function buildContentHeightReporter(frameToken: string): string;
|
|
|
60
60
|
* of the main SPA bundle). Escaping is mandatory: the source is arbitrary node
|
|
61
61
|
* data and must not be parsed as markup.
|
|
62
62
|
*/
|
|
63
|
-
export declare function buildMermaidSurfaceHtml(source: string): string;
|
|
63
|
+
export declare function buildMermaidSurfaceHtml(source: string, inlineEntry?: string | null): string;
|
|
64
64
|
export interface HtmlSurfaceOptions {
|
|
65
65
|
theme: SurfaceTheme;
|
|
66
66
|
/**
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { type HumanPresence, type HumanPresenceSnapshot } from '../shared/human-presence.js';
|
|
3
|
+
/**
|
|
4
|
+
* Registry of open workbench tabs (rail-chrome-v2 phase 8). Fed by the
|
|
5
|
+
* browser's own `human.presence.set` heartbeats; broadcast as one coalesced
|
|
6
|
+
* `human-presence` SSE frame on every change including expiry, so clients
|
|
7
|
+
* never run their own expiry timers. `lockedNodes()` is the edit lock the
|
|
8
|
+
* operation registry consults for agent writes (user wins).
|
|
9
|
+
*/
|
|
10
|
+
export declare const HUMAN_PRESENCE_SHAPE: {
|
|
11
|
+
clientId: z.ZodString;
|
|
12
|
+
name: z.ZodOptional<z.ZodString>;
|
|
13
|
+
cursor: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
14
|
+
x: z.ZodNumber;
|
|
15
|
+
y: z.ZodNumber;
|
|
16
|
+
}, z.core.$strip>>>;
|
|
17
|
+
grabbingNodeId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
/** Explicit leave (tab closing): drop the presence now. */
|
|
19
|
+
left: z.ZodOptional<z.ZodBoolean>;
|
|
20
|
+
};
|
|
21
|
+
type Emitter = (event: string, payload: Record<string, unknown>) => void;
|
|
22
|
+
export declare class HumanPresenceRegistry {
|
|
23
|
+
private readonly humans;
|
|
24
|
+
private emit;
|
|
25
|
+
private sweepTimer;
|
|
26
|
+
private emitTimer;
|
|
27
|
+
setEmitter(emitter: Emitter | null): void;
|
|
28
|
+
set(raw: unknown, now?: number): HumanPresence | null;
|
|
29
|
+
snapshot(now?: number): HumanPresenceSnapshot;
|
|
30
|
+
/** Nodes a human is holding right now → that human's name. The edit lock. */
|
|
31
|
+
lockedNodes(now?: number): Map<string, string>;
|
|
32
|
+
reset(): void;
|
|
33
|
+
private publicView;
|
|
34
|
+
private sweep;
|
|
35
|
+
private scheduleEmit;
|
|
36
|
+
private ensureSweeper;
|
|
37
|
+
private maybeStopSweeper;
|
|
38
|
+
}
|
|
39
|
+
export declare const humanPresence: HumanPresenceRegistry;
|
|
40
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { AgentPhase, AgentPresence, AgentPresenceSnapshot } from '../shared/agent-presence.js';
|
|
2
3
|
import { canvasState } from './canvas-state.js';
|
|
3
4
|
import type { CanvasAnnotation, CanvasNodeState, CanvasEdge, CanvasLayout } from './canvas-state.js';
|
|
4
5
|
import { type AxInteractionInput, type AxInteractionPublicResult } from './ax-interaction.js';
|
|
@@ -32,6 +33,7 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
32
33
|
port?: number;
|
|
33
34
|
});
|
|
34
35
|
private runIntentCommit;
|
|
36
|
+
private runIntentCommitInner;
|
|
35
37
|
start(options?: {
|
|
36
38
|
open?: boolean;
|
|
37
39
|
automationWebView?: boolean | CanvasAutomationWebViewOptions;
|
|
@@ -134,7 +136,7 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
134
136
|
childLayout?: 'grid' | 'column' | 'flow';
|
|
135
137
|
intentId?: string;
|
|
136
138
|
}): boolean;
|
|
137
|
-
/**
|
|
139
|
+
/** Dissolve a group: children released (into the enclosing group when nested), frame removed — one undo step. */
|
|
138
140
|
ungroupNodes(groupId: string, options?: {
|
|
139
141
|
intentId?: string;
|
|
140
142
|
}): boolean;
|
|
@@ -168,7 +170,7 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
168
170
|
agentId?: string | null;
|
|
169
171
|
target?: string | null;
|
|
170
172
|
}): PmxAxSteeringMessage;
|
|
171
|
-
markSteeringDelivered(id: string): boolean;
|
|
173
|
+
markSteeringDelivered(id: string, consumer?: string | null): boolean;
|
|
172
174
|
/**
|
|
173
175
|
* Ghost Cursor of Intent — announce a spatial move before making it. The ghost
|
|
174
176
|
* is ephemeral presence (auto-expiring, never snapshotted); the registry emits
|
|
@@ -227,6 +229,11 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
227
229
|
resolution?: string;
|
|
228
230
|
source?: PmxAxSource;
|
|
229
231
|
}): PmxAxApprovalGate | null;
|
|
232
|
+
/** Reopen a resolved (typically auto-held) approval gate with a fresh TTL. */
|
|
233
|
+
reopenApproval(id: string, options?: {
|
|
234
|
+
ttlMs?: number;
|
|
235
|
+
source?: PmxAxSource;
|
|
236
|
+
}): PmxAxApprovalGate | null;
|
|
230
237
|
addEvidence(input: {
|
|
231
238
|
kind: PmxAxEvidenceKind;
|
|
232
239
|
title: string;
|
|
@@ -262,6 +269,26 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
262
269
|
reportHostCapability(input: unknown, options?: {
|
|
263
270
|
source?: PmxAxSource;
|
|
264
271
|
}): PmxAxHostCapability;
|
|
272
|
+
/** Who is writing to this board right now, their phase, and the context budget. */
|
|
273
|
+
getAgentPresence(): AgentPresenceSnapshot;
|
|
274
|
+
/**
|
|
275
|
+
* Update this SDK caller's presence (rail-chrome-v2 phase 2): phase, detail,
|
|
276
|
+
* focus node, cursor, and `attached` (true starts a session, false ends it).
|
|
277
|
+
*/
|
|
278
|
+
setAgentPresence(input: {
|
|
279
|
+
agentId?: string | null;
|
|
280
|
+
label?: string;
|
|
281
|
+
phase?: AgentPhase;
|
|
282
|
+
detail?: string | null;
|
|
283
|
+
focusNodeId?: string | null;
|
|
284
|
+
cursor?: {
|
|
285
|
+
x: number;
|
|
286
|
+
y: number;
|
|
287
|
+
} | null;
|
|
288
|
+
attached?: boolean;
|
|
289
|
+
}, options?: {
|
|
290
|
+
source?: PmxAxSource;
|
|
291
|
+
}): AgentPresence;
|
|
265
292
|
listElicitations(): PmxAxElicitation[];
|
|
266
293
|
requestElicitation(input: {
|
|
267
294
|
prompt: string;
|
|
@@ -388,7 +415,6 @@ export declare class PmxCanvas extends EventEmitter {
|
|
|
388
415
|
height: number;
|
|
389
416
|
};
|
|
390
417
|
collapsed?: boolean;
|
|
391
|
-
dockPosition?: 'left' | 'right' | null;
|
|
392
418
|
}>): {
|
|
393
419
|
applied: number;
|
|
394
420
|
skipped: number;
|
|
@@ -27,6 +27,13 @@ export declare class IntentRegistry {
|
|
|
27
27
|
* synchronous: once this method has accepted the intent, a later veto cannot
|
|
28
28
|
* race in between the check and the mutation.
|
|
29
29
|
*/
|
|
30
|
+
/**
|
|
31
|
+
* How a linked commit would fare right now. 'absent' = the ghost expired
|
|
32
|
+
* quietly (TTL sweep) with no veto — the mutation should proceed UNLINKED
|
|
33
|
+
* instead of failing: a slow spawn is not a human "no", and inside a batch
|
|
34
|
+
* the 409 poisoned every sibling op (orchestration review #5).
|
|
35
|
+
*/
|
|
36
|
+
commitDisposition(id: string): 'live' | 'vetoed' | 'committing' | 'absent';
|
|
30
37
|
beginCommit(id: string, allowedKinds: readonly PmxAxIntentKind[]): PmxAxIntent;
|
|
31
38
|
completeCommit(id: string, settledNodeId?: string): void;
|
|
32
39
|
abortCommit(id: string): void;
|
|
@@ -12,12 +12,18 @@
|
|
|
12
12
|
* - _replaying flag prevents undo/redo from recording new entries
|
|
13
13
|
*/
|
|
14
14
|
import type { CanvasNodeState, CanvasEdge } from './canvas-state.js';
|
|
15
|
-
export type MutationOp = 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'arrange' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'addWorkItem' | 'updateWorkItem' | 'requestApproval' | 'resolveApproval' | 'addReviewAnnotation' | 'updateReviewAnnotation' | 'requestElicitation' | 'respondElicitation' | 'requestMode' | 'resolveModeRequest' | 'setPolicy' | 'batch' | 'viewport' | 'groupNodes' | '
|
|
15
|
+
export type MutationOp = 'addNode' | 'updateNode' | 'removeNode' | 'addEdge' | 'updateEdge' | 'removeEdge' | 'addAnnotation' | 'removeAnnotation' | 'clear' | 'arrange' | 'restoreSnapshot' | 'setPins' | 'setAxFocus' | 'addWorkItem' | 'updateWorkItem' | 'requestApproval' | 'resolveApproval' | 'addReviewAnnotation' | 'updateReviewAnnotation' | 'requestElicitation' | 'respondElicitation' | 'requestMode' | 'resolveModeRequest' | 'setPolicy' | 'batch' | 'viewport' | 'groupNodes' | 'releaseGroupChildren';
|
|
16
|
+
/** Who made a mutation: the human in the workbench, or an agent through any transport. */
|
|
17
|
+
export type MutationActor = 'human' | 'agent';
|
|
16
18
|
export interface MutationEntry {
|
|
17
19
|
id: string;
|
|
18
20
|
timestamp: string;
|
|
19
21
|
description: string;
|
|
20
22
|
operationType: MutationOp;
|
|
23
|
+
actor: MutationActor;
|
|
24
|
+
/** Writer key (agentId, else transport source) for agent entries — lets a
|
|
25
|
+
* client prove "the top edit is MINE" before undoing on a multi-agent board. */
|
|
26
|
+
writer?: string;
|
|
21
27
|
forward: () => void;
|
|
22
28
|
inverse: () => void;
|
|
23
29
|
}
|
|
@@ -25,6 +31,8 @@ export interface MutationSummary {
|
|
|
25
31
|
id: string;
|
|
26
32
|
timestamp: string;
|
|
27
33
|
description: string;
|
|
34
|
+
actor: MutationActor;
|
|
35
|
+
writer?: string;
|
|
28
36
|
operationType: MutationOp;
|
|
29
37
|
isCurrent: boolean;
|
|
30
38
|
isUndone: boolean;
|
|
@@ -71,7 +79,9 @@ declare class MutationHistory {
|
|
|
71
79
|
* Record a new mutation. Truncates any redo-able future, then appends.
|
|
72
80
|
* If called while replaying (undo/redo), the call is silently ignored.
|
|
73
81
|
*/
|
|
74
|
-
record(entry: Omit<MutationEntry, 'id' | 'timestamp'>
|
|
82
|
+
record(entry: Omit<MutationEntry, 'id' | 'timestamp' | 'actor'> & {
|
|
83
|
+
actor?: MutationActor;
|
|
84
|
+
}): void;
|
|
75
85
|
/** Undo the last applied mutation. Returns the entry that was undone, or null. */
|
|
76
86
|
undo(): MutationEntry | null;
|
|
77
87
|
/** Redo the next undone mutation. Returns the entry that was redone, or null. */
|
|
@@ -80,6 +90,8 @@ declare class MutationHistory {
|
|
|
80
90
|
canRedo(): boolean;
|
|
81
91
|
/** Get all entries with current/undone status for display. */
|
|
82
92
|
getSummaries(): MutationSummary[];
|
|
93
|
+
/** The entry Ctrl+Z would undo next (top of the shared stack), or null. */
|
|
94
|
+
top(): MutationSummary | null;
|
|
83
95
|
/** Human-readable timeline for the canvas://history resource. */
|
|
84
96
|
toHumanReadable(): string;
|
|
85
97
|
/** Number of recorded entries. */
|
|
@@ -102,4 +114,5 @@ export declare function diffLayouts(snapshotName: string, snapshotLayout: {
|
|
|
102
114
|
*/
|
|
103
115
|
export declare function formatDiff(diff: SnapshotDiffResult): string;
|
|
104
116
|
export declare const mutationHistory: MutationHistory;
|
|
117
|
+
export declare function setMutationActor(actor: MutationActor | null, writer?: string | null): void;
|
|
105
118
|
export {};
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
export interface OperationInvoker {
|
|
2
2
|
invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* The agent-presence label for a transport: `PMX_CANVAS_AGENT_SOURCE` lets a
|
|
6
|
+
* host name its agent ('codex', 'claude-code', …) so writes, attach and cursor
|
|
7
|
+
* all key on one identity; otherwise the transport's own label.
|
|
8
|
+
*/
|
|
9
|
+
export declare function agentSourceLabel(fallback: string): string;
|
|
4
10
|
/** Runs operations in-process against the shared canvasState singleton. */
|
|
5
11
|
export declare class LocalOperationInvoker implements OperationInvoker {
|
|
12
|
+
private readonly source;
|
|
13
|
+
/** `source` labels this caller's agent presence ('mcp', 'sdk', …). */
|
|
14
|
+
constructor(source?: string);
|
|
6
15
|
invoke(name: string, input: Record<string, unknown>): Promise<unknown>;
|
|
7
16
|
}
|
|
8
17
|
/** Builds the HTTP request from the op's route template (`:id` from input, GET flags to query). */
|
|
9
18
|
export declare class HttpOperationInvoker implements OperationInvoker {
|
|
10
19
|
private readonly baseUrl;
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
private readonly source;
|
|
21
|
+
/** `source` labels this caller's agent presence on the server ('cli', 'mcp', …). */
|
|
22
|
+
constructor(baseUrl: string, source?: string);
|
|
23
|
+
invoke(name: string, rawInput: Record<string, unknown>): Promise<unknown>;
|
|
13
24
|
}
|
|
@@ -18,6 +18,17 @@ export interface ExecuteOperationMeta {
|
|
|
18
18
|
* its inner dispatches (batch churn is exempt, matching the skill contract).
|
|
19
19
|
*/
|
|
20
20
|
suppressAutoGhost?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Who is calling — the presence writer label ('mcp', 'sdk', 'api', …).
|
|
23
|
+
* Defaults to 'api'. Workbench calls (suppressAutoGhost) never touch presence.
|
|
24
|
+
*/
|
|
25
|
+
source?: string;
|
|
26
|
+
/**
|
|
27
|
+
* The human's own browser issued this call. Distinct from suppressAutoGhost
|
|
28
|
+
* (which batch also sets for its inner dispatches): the scope fence applies
|
|
29
|
+
* to agents only, and batch inner writes are always agent-originated.
|
|
30
|
+
*/
|
|
31
|
+
fromWorkbench?: boolean;
|
|
21
32
|
}
|
|
22
33
|
export declare function executeOperation(name: string, rawInput: unknown, meta?: ExecuteOperationMeta): Promise<unknown>;
|
|
23
34
|
export {};
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import type { ZodRawShape } from 'zod';
|
|
13
13
|
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
14
|
-
export type OperationErrorStatus = 400 | 404 | 409;
|
|
14
|
+
export type OperationErrorStatus = 400 | 403 | 404 | 409;
|
|
15
15
|
/** Operation failure that maps to an HTTP status + `{ ok:false, error }` body and MCP `isError`. */
|
|
16
16
|
export declare class OperationError extends Error {
|
|
17
17
|
readonly status: OperationErrorStatus;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope fence enforcement (rail-chrome-v2 phase 4, design item 4).
|
|
3
|
+
*
|
|
4
|
+
* When the AX policy carries a scope fence, an attached agent may only WRITE
|
|
5
|
+
* inside it: existing-node writes must target fenced nodes, new nodes must
|
|
6
|
+
* land inside the fence's bounding box (fenced nodes + padding), and
|
|
7
|
+
* board-wide writes (arrange, clear, restore) are refused. Reads are never
|
|
8
|
+
* fenced, and neither are the human's own workbench writes — the fence is the
|
|
9
|
+
* human's tool, not a cage for the human.
|
|
10
|
+
*
|
|
11
|
+
* One core check (`checkFenceTarget`) serves two describers: the operation
|
|
12
|
+
* registry (`checkScopeFence`, every HTTP/MCP/CLI write) and the sync SDK,
|
|
13
|
+
* whose methods bypass the registry and describe their own targets.
|
|
14
|
+
*
|
|
15
|
+
* Safe default: a mutating op this module does not know how to scope is
|
|
16
|
+
* refused while a fence is set, rather than silently allowed. Likewise a
|
|
17
|
+
* write this module cannot resolve (an edge given by search, a missing id)
|
|
18
|
+
* is refused — fail closed, never a bypass.
|
|
19
|
+
*
|
|
20
|
+
* Trust note: the workbench marker is a self-reported header. Under the
|
|
21
|
+
* local single-workspace model that is by design (any local process may
|
|
22
|
+
* write; the safety model is human veto plus this fence), so the fence holds
|
|
23
|
+
* cooperating agents to the region the human granted — it is not an
|
|
24
|
+
* authentication boundary.
|
|
25
|
+
*/
|
|
26
|
+
import { type FenceRect } from '../shared/scope-fence.js';
|
|
27
|
+
import type { Operation } from './operations/types.js';
|
|
28
|
+
/** Bounding box of the fenced nodes plus padding; null when none of them exist. */
|
|
29
|
+
export declare function scopeFenceRect(fence: {
|
|
30
|
+
nodeIds: string[];
|
|
31
|
+
padding: number;
|
|
32
|
+
}): FenceRect | null;
|
|
33
|
+
/** What a write touches, in fence terms. */
|
|
34
|
+
export interface FenceTarget {
|
|
35
|
+
/** Existing nodes the write targets — every one must be fenced. */
|
|
36
|
+
nodeIds?: string[];
|
|
37
|
+
/** World points a write introduces (a new node, annotation points) — all must be inside the box. */
|
|
38
|
+
points?: Array<{
|
|
39
|
+
x: number;
|
|
40
|
+
y: number;
|
|
41
|
+
}>;
|
|
42
|
+
/** A new node with no position: refused (it cannot be placed inside the fence). */
|
|
43
|
+
unplacedCreate?: boolean;
|
|
44
|
+
/** Rewrites the whole board — never allowed under a fence. */
|
|
45
|
+
boardWide?: boolean;
|
|
46
|
+
/** The op is not fence-aware — refused while a fence is set. */
|
|
47
|
+
unknown?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Returns a human-readable refusal, or null when the target is inside the fence. */
|
|
50
|
+
export declare function checkFenceTarget(target: FenceTarget, opName: string, writerKey?: string): string | null;
|
|
51
|
+
/** Describe what a registry op would write, in fence terms. */
|
|
52
|
+
export declare function describeOpTarget(op: Operation, rawInput: unknown): FenceTarget;
|
|
53
|
+
/** Registry entry point: refusal text or null. Only call for mutating, agent-originated ops. */
|
|
54
|
+
export declare function checkScopeFence(op: Operation, rawInput: unknown, writerKey?: string): string | null;
|
|
55
|
+
/**
|
|
56
|
+
* The GLOBAL fence belongs to the human: an agent must not clear, widen, or
|
|
57
|
+
* replace it through `ax.policy.set`. Per-agent territories (`agentScopes`)
|
|
58
|
+
* are the orchestration surface — an agent MAY set territories for OTHER
|
|
59
|
+
* writers (fencing its workers), but never its own: granting yourself a wider
|
|
60
|
+
* territory (or deleting it) is escalation.
|
|
61
|
+
*/
|
|
62
|
+
export declare function checkScopeOwnership(rawInput: unknown, writerKey?: string): string | null;
|
|
@@ -93,18 +93,6 @@ export declare function setPrimaryWorkbenchCanvasPromptHandler(handler: PrimaryW
|
|
|
93
93
|
export declare function isCanvasBundlePath(distPath: string, bundleDir: string): boolean;
|
|
94
94
|
export declare function buildMacBrowserOpenScript(appName: string, url: string): string;
|
|
95
95
|
export declare function openUrlInExternalBrowser(url: string): boolean;
|
|
96
|
-
/**
|
|
97
|
-
* Seed the docked status (left) + context (right) widgets so a freshly opened
|
|
98
|
-
* canvas shows them by default — the same nodes the agent-event path creates on
|
|
99
|
-
* demand (`status-main`, `context-main`), just present from the start.
|
|
100
|
-
*
|
|
101
|
-
* First-run only: we bail if the workspace canvas already has persisted state,
|
|
102
|
-
* so we never add them to a board with content, and — because first-run state is
|
|
103
|
-
* persisted on save — deleting or undocking them later is respected (they are
|
|
104
|
-
* not re-seeded). Create-if-missing keeps it idempotent if the agent path
|
|
105
|
-
* already made one. Returns true if anything was seeded.
|
|
106
|
-
*/
|
|
107
|
-
export declare function ensureDefaultDockedNodes(): boolean;
|
|
108
96
|
export declare function emitPrimaryWorkbenchEvent(event: string, payload?: PrimaryWorkbenchEventPayload): void;
|
|
109
97
|
export declare function consumePrimaryWorkbenchIntents(limit?: number): PrimaryWorkbenchIntent[];
|
|
110
98
|
export declare function getPrimaryWorkbenchUrl(workspaceRoot?: string): string | null;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent presence — the contract behind every agent surface in the rail chrome
|
|
3
|
+
* (rail-chrome-v2 phase 2). Shared by server and client so the two cannot
|
|
4
|
+
* drift. See design/rail-chrome-v2/PLAN.md, "Phase 2 contract".
|
|
5
|
+
*
|
|
6
|
+
* Presence is DERIVED from feeds that already exist (AX activity ingest,
|
|
7
|
+
* agent-originated mutations) plus an explicit set for adapters with richer
|
|
8
|
+
* hooks. Nothing here is a second source of truth for work items, gates, or
|
|
9
|
+
* steering — those stay in the AX state.
|
|
10
|
+
*/
|
|
11
|
+
export type AgentPhase = 'idle' | 'thinking' | 'tooling' | 'waiting-approval';
|
|
12
|
+
export declare const AGENT_PHASES: readonly AgentPhase[];
|
|
13
|
+
export interface AgentPresence {
|
|
14
|
+
/** Writer key: `agentId` when the host supplies one, else the source label. */
|
|
15
|
+
sessionId: string;
|
|
16
|
+
/** Host label (copilot, codex, mcp, api, cli, …). */
|
|
17
|
+
source: string;
|
|
18
|
+
agentId: string | null;
|
|
19
|
+
/** Display name. */
|
|
20
|
+
label: string;
|
|
21
|
+
phase: AgentPhase;
|
|
22
|
+
/** Tool or operation name while `tooling`. */
|
|
23
|
+
detail: string | null;
|
|
24
|
+
focusNodeId: string | null;
|
|
25
|
+
/** World coordinates. */
|
|
26
|
+
cursor: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
} | null;
|
|
30
|
+
/** A session is attached: `session-start` (or explicit attach) seen and no `session-end`. */
|
|
31
|
+
attached: boolean;
|
|
32
|
+
/** Fleet membership: the orchestrating writer's key this worker belongs to (chrome rolls workers up under it). */
|
|
33
|
+
parentAgentId?: string | null;
|
|
34
|
+
/** True when steering addressed to this writer can actually reach it: it is
|
|
35
|
+
* an attached session, or its consumer key has claimed deliveries before. */
|
|
36
|
+
steerable?: boolean;
|
|
37
|
+
/** Steers sitting unclaimed in this writer's delivery queue (pump-health:
|
|
38
|
+
* a growing count means the writer is not polling). Only set for steerable rows. */
|
|
39
|
+
pendingSteers?: number;
|
|
40
|
+
/** When this writer's consumer key last claimed its delivery queue — the
|
|
41
|
+
* proof-of-polling behind `steerable`. Absent for writers that never claimed
|
|
42
|
+
* (steerable only via an attached adapter loop). */
|
|
43
|
+
lastClaimAt?: string;
|
|
44
|
+
/** Agent writes observed for this writer — feeds the external-steering indicator. */
|
|
45
|
+
opCount: number;
|
|
46
|
+
/**
|
|
47
|
+
* The agent's REAL context window, when its host reports it (tokens used /
|
|
48
|
+
* window size). Null when no adapter reports — the top-bar meter then shows
|
|
49
|
+
* the pinned-context estimate against the configured budget instead, and
|
|
50
|
+
* says so.
|
|
51
|
+
*/
|
|
52
|
+
contextUsage: {
|
|
53
|
+
used: number;
|
|
54
|
+
total: number;
|
|
55
|
+
} | null;
|
|
56
|
+
lastSeenAt: string;
|
|
57
|
+
}
|
|
58
|
+
export interface ContextBudget {
|
|
59
|
+
/** Estimated tokens of the pinned-context payload. */
|
|
60
|
+
used: number;
|
|
61
|
+
total: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* One agent write, as the External Steering activity feed lists it. Derived
|
|
65
|
+
* from the same presence touch every agent-originated operation already
|
|
66
|
+
* makes — not a second log.
|
|
67
|
+
*/
|
|
68
|
+
export interface AgentActivityEntry {
|
|
69
|
+
id: string;
|
|
70
|
+
at: string;
|
|
71
|
+
/** Writer key after attribution (the session's when the write was folded into it). */
|
|
72
|
+
sessionId: string;
|
|
73
|
+
label: string;
|
|
74
|
+
/** Registry operation name (node.add, edge.remove, ax.work.create, …). */
|
|
75
|
+
op: string;
|
|
76
|
+
/** Human sentence: "Created markdown “Release plan”". */
|
|
77
|
+
summary: string;
|
|
78
|
+
nodeId: string | null;
|
|
79
|
+
}
|
|
80
|
+
export interface AgentPresenceSnapshot {
|
|
81
|
+
presences: AgentPresence[];
|
|
82
|
+
budget: ContextBudget;
|
|
83
|
+
/** True when any presence is attached — the master gate for agent chrome. */
|
|
84
|
+
sessionActive: boolean;
|
|
85
|
+
/** Most recent agent writes, newest first (bounded by MAX_ACTIVITY_ENTRIES). */
|
|
86
|
+
activity: AgentActivityEntry[];
|
|
87
|
+
}
|
|
88
|
+
/** Activity entries kept for the feed — oldest fall off. */
|
|
89
|
+
export declare const MAX_ACTIVITY_ENTRIES = 50;
|
|
90
|
+
/** Unattached writers fade this long after their last write. */
|
|
91
|
+
export declare const PRESENCE_ACTIVITY_TTL_MS: number;
|
|
92
|
+
/** Attached sessions expire after this much quiet without a `session-end`. */
|
|
93
|
+
export declare const PRESENCE_ATTACHED_IDLE_TTL_MS: number;
|
|
94
|
+
/** `tooling` decays to `idle` after this much quiet. */
|
|
95
|
+
export declare const PRESENCE_TOOLING_SETTLE_MS = 4000;
|
|
96
|
+
/** Explicit `thinking` without any fresh touch settles to idle — hosts set it
|
|
97
|
+
* at turn start and often never send the turn-end idle, leaving three
|
|
98
|
+
* "Thinking" chips on a silent board. */
|
|
99
|
+
export declare const PRESENCE_THINKING_SETTLE_MS: number;
|
|
100
|
+
export declare const MAX_PRESENCES = 16;
|
|
101
|
+
/** Default `budget.total` when `PMX_CANVAS_CONTEXT_BUDGET_TOKENS` is unset. */
|
|
102
|
+
export declare const CONTEXT_BUDGET_DEFAULT_TOKENS = 32000;
|
|
103
|
+
/**
|
|
104
|
+
* Source labels that name a TRANSPORT, not an agent. A write arriving under
|
|
105
|
+
* one of these while exactly one session is attached is the attached agent's
|
|
106
|
+
* own work reaching the board through that transport (Copilot/Codex/Claude
|
|
107
|
+
* Code all write via MCP or HTTP while their host holds the session).
|
|
108
|
+
*/
|
|
109
|
+
export declare const TRANSPORT_SOURCES: readonly string[];
|
|
110
|
+
/**
|
|
111
|
+
* The label a human-started session (*Start agent session* in the browser)
|
|
112
|
+
* carries until an agent writes into it — the session is a placeholder for
|
|
113
|
+
* whichever agent comes next, so the first identified writer names it.
|
|
114
|
+
*/
|
|
115
|
+
export declare const HUMAN_STARTED_SESSION_LABEL = "Agent session";
|
|
116
|
+
/** The one selector every agent surface reads. */
|
|
117
|
+
export declare function isSessionActive(presences: readonly AgentPresence[]): boolean;
|
|
118
|
+
/** Writers that are live but not attached — the External Steering mode. */
|
|
119
|
+
export declare function externalWriters(presences: readonly AgentPresence[]): AgentPresence[];
|
|
120
|
+
/** Rough token estimate for a JSON payload (chars / 4) — good enough for a meter. */
|
|
121
|
+
export declare function estimateTokens(text: string): number;
|
|
122
|
+
/** Chip label per phase — shared by the top-bar chip and the on-canvas cursor chip. */
|
|
123
|
+
export declare function agentPhaseLabel(presence: Pick<AgentPresence, 'phase' | 'detail'>): string;
|
|
124
|
+
/**
|
|
125
|
+
* Stable per-agent identity hue (0-359) from the writer key. Phase colors
|
|
126
|
+
* kept telling the human WHAT an agent is doing while erasing WHO — two
|
|
127
|
+
* thinking agents rendered identically. Identity rides the glyph/border;
|
|
128
|
+
* phase stays on dots and labels.
|
|
129
|
+
*/
|
|
130
|
+
export declare function agentIdentityHue(key: string): number;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unattended approval policy (rail-chrome-v2 phase 4, design item 3).
|
|
3
|
+
*
|
|
4
|
+
* A pending approval gate carries a TTL. If the human does not answer in time
|
|
5
|
+
* the gate resolves to `held` — the safe default: the action does NOT proceed,
|
|
6
|
+
* a `policy` entry lands in the timeline, and the gate can be reopened from the
|
|
7
|
+
* session panel. Shared so the server's sweeper and the client's countdown read
|
|
8
|
+
* one clock.
|
|
9
|
+
*/
|
|
10
|
+
export declare const DEFAULT_GATE_TTL_MS: number;
|
|
11
|
+
export declare const MIN_GATE_TTL_MS = 1000;
|
|
12
|
+
export declare const MAX_GATE_TTL_MS: number;
|
|
13
|
+
export declare function clampGateTtlMs(value: unknown, fallback?: number): number;
|
|
14
|
+
/** Milliseconds until a gate auto-holds (0 when expired, null when it has no TTL). */
|
|
15
|
+
export declare function gateRemainingMs(gate: {
|
|
16
|
+
expiresAt: string | null;
|
|
17
|
+
}, now?: number): number | null;
|
|
18
|
+
/** `M:SS` for the gate countdown ("auto-holds in 4:31"). */
|
|
19
|
+
export declare function formatCountdown(ms: number): string;
|