pmx-canvas 0.1.34 → 0.1.36
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/CHANGELOG.md +65 -0
- package/Readme.md +14 -2
- package/dist/canvas/index.js +82 -41
- package/dist/types/client/nodes/ExtAppFrame.d.ts +2 -0
- package/dist/types/mcp/canvas-access.d.ts +20 -1
- package/dist/types/server/ax-context.d.ts +1 -1
- package/dist/types/server/ax-state.d.ts +28 -0
- package/dist/types/server/ax-wait.d.ts +23 -0
- package/dist/types/server/canvas-state.d.ts +55 -3
- package/dist/types/server/html-surface.d.ts +7 -0
- package/dist/types/server/index.d.ts +60 -2
- package/docs/ax-host-adapter-contract.md +65 -0
- package/docs/http-api.md +34 -2
- package/docs/mcp.md +5 -1
- package/docs/screenshot.png +0 -0
- package/package.json +1 -1
- package/skills/pmx-canvas/SKILL.md +50 -8
- package/skills/pmx-canvas/references/codex-app-adapter.md +15 -1
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +31 -0
- package/src/client/nodes/ExtAppFrame.tsx +73 -5
- package/src/client/nodes/HtmlNode.tsx +12 -3
- package/src/client/nodes/McpAppNode.tsx +12 -3
- package/src/json-render/renderer/index.tsx +3 -0
- package/src/mcp/canvas-access.ts +74 -5
- package/src/mcp/server.ts +94 -53
- package/src/server/ax-context.ts +7 -1
- package/src/server/ax-state.ts +87 -0
- package/src/server/ax-wait.ts +56 -0
- package/src/server/canvas-state.ts +131 -3
- package/src/server/html-surface.ts +49 -11
- package/src/server/index.ts +82 -2
- package/src/server/server.ts +223 -11
package/src/server/server.ts
CHANGED
|
@@ -78,9 +78,11 @@ import {
|
|
|
78
78
|
import { buildCodeGraphSummary, formatCodeGraph } from './code-graph.js';
|
|
79
79
|
import { buildAgentContextPreamble, serializeNodeForAgentContext } from './agent-context.js';
|
|
80
80
|
import { buildCanvasAxContext, buildCanvasAxSurfaceSnapshot } from './ax-context.js';
|
|
81
|
-
import { applyAxInteraction, resolveNodeAxCapabilities } from './ax-interaction.js';
|
|
82
|
-
import { isAxEventKind, isAxEvidenceKind } from './ax-state.js';
|
|
81
|
+
import { applyAxInteraction, resolveNodeAxCapabilities, normalizeNodeAxCapabilities } from './ax-interaction.js';
|
|
82
|
+
import { isAxEventKind, isAxEvidenceKind, isAxActivityKind } from './ax-state.js';
|
|
83
|
+
import { waitForAxResolution, AX_WAIT_MAX_MS } from './ax-wait.js';
|
|
83
84
|
import type {
|
|
85
|
+
PmxAxEvidenceKind,
|
|
84
86
|
PmxAxPolicy,
|
|
85
87
|
PmxAxReviewAnchorType,
|
|
86
88
|
PmxAxReviewKind,
|
|
@@ -88,6 +90,7 @@ import type {
|
|
|
88
90
|
PmxAxReviewSeverity,
|
|
89
91
|
PmxAxReviewStatus,
|
|
90
92
|
PmxAxSource,
|
|
93
|
+
PmxAxWorkItemStatus,
|
|
91
94
|
} from './ax-state.js';
|
|
92
95
|
import { normalizeCanvasTheme, type CanvasTheme } from './canvas-db.js';
|
|
93
96
|
import { validateLocalImageFile } from './image-source.js';
|
|
@@ -1072,6 +1075,34 @@ async function readJson(req: Request): Promise<Record<string, unknown>> {
|
|
|
1072
1075
|
}
|
|
1073
1076
|
}
|
|
1074
1077
|
|
|
1078
|
+
/**
|
|
1079
|
+
* Like {@link readJson}, but PRESERVES a top-level JSON array. For endpoints that
|
|
1080
|
+
* accept either an object or a bare array (e.g. `/api/canvas/batch`, whose CLI
|
|
1081
|
+
* help and handler both document a bare `[...]` form). readJson coerces arrays to
|
|
1082
|
+
* `{}` so object-shaped handlers never crash on `body.field`; this variant keeps
|
|
1083
|
+
* the array so the handler's array branch can run. Empty/whitespace/malformed
|
|
1084
|
+
* bodies still resolve to `{}`.
|
|
1085
|
+
*/
|
|
1086
|
+
async function readJsonObjectOrArray(req: Request): Promise<Record<string, unknown> | unknown[]> {
|
|
1087
|
+
let text = '';
|
|
1088
|
+
try {
|
|
1089
|
+
text = await req.text();
|
|
1090
|
+
} catch (error) {
|
|
1091
|
+
logWorkbenchWarning('readJson', error);
|
|
1092
|
+
return {};
|
|
1093
|
+
}
|
|
1094
|
+
if (!text.trim()) return {};
|
|
1095
|
+
try {
|
|
1096
|
+
const value = JSON.parse(text) as unknown;
|
|
1097
|
+
if (Array.isArray(value)) return value;
|
|
1098
|
+
if (!value || typeof value !== 'object') return {};
|
|
1099
|
+
return value as Record<string, unknown>;
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
logWorkbenchWarning('readJson', error);
|
|
1102
|
+
return {};
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1075
1106
|
function htmlEscape(value: string): string {
|
|
1076
1107
|
return value
|
|
1077
1108
|
.replaceAll('&', '&')
|
|
@@ -1730,7 +1761,15 @@ async function createCanvasWebpageNode(body: Record<string, unknown>): Promise<R
|
|
|
1730
1761
|
async function handleCanvasAddNode(req: Request): Promise<Response> {
|
|
1731
1762
|
const body = await readJson(req);
|
|
1732
1763
|
const queryType = new URL(req.url).searchParams.get('type');
|
|
1733
|
-
|
|
1764
|
+
// Report #50: require a resolvable type rather than silently defaulting to a
|
|
1765
|
+
// markdown node — an empty / type-less body created a phantom node before.
|
|
1766
|
+
const type = typeof body.type === 'string' ? body.type : (queryType || '');
|
|
1767
|
+
if (!type) {
|
|
1768
|
+
return responseJson({
|
|
1769
|
+
ok: false,
|
|
1770
|
+
error: `node creation requires a 'type' — pass it in the JSON body ({ "type": "markdown", ... }) or as a ?type= query param. Valid types: ${[...VALID_NODE_TYPES].join(', ')} (json-render / graph / web-artifact have dedicated endpoints).`,
|
|
1771
|
+
}, 400);
|
|
1772
|
+
}
|
|
1734
1773
|
|
|
1735
1774
|
if (!VALID_NODE_TYPES.has(type)) {
|
|
1736
1775
|
if (type === 'json-render') {
|
|
@@ -1796,8 +1835,11 @@ async function handleCanvasAddNode(req: Request): Promise<Response> {
|
|
|
1796
1835
|
const content = type === 'image' && typeof body.path === 'string' && typeof body.content !== 'string'
|
|
1797
1836
|
? body.path
|
|
1798
1837
|
: body.content;
|
|
1799
|
-
// For html nodes, accept top-level `html`
|
|
1800
|
-
// can POST { type: 'html', title, html } without nesting
|
|
1838
|
+
// For html nodes, accept top-level `html` AND `axCapabilities` and merge into data
|
|
1839
|
+
// so callers can POST { type: 'html', title, html, axCapabilities } without nesting
|
|
1840
|
+
// under `data` (report #53 — transport parity with MCP canvas_add_html_node). A
|
|
1841
|
+
// top-level value overrides the same key under `data` (mirrors the `html` precedence).
|
|
1842
|
+
const topAxCapabilities = type === 'html' ? normalizeNodeAxCapabilities(body.axCapabilities) : null;
|
|
1801
1843
|
const htmlMergedData = type === 'html'
|
|
1802
1844
|
? {
|
|
1803
1845
|
...(extraData ?? {}),
|
|
@@ -1809,6 +1851,7 @@ async function handleCanvasAddNode(req: Request): Promise<Response> {
|
|
|
1809
1851
|
...(Array.isArray(body.slideTitles) ? { slideTitles: body.slideTitles } : {}),
|
|
1810
1852
|
...(Array.isArray(body.embeddedNodeIds) ? { embeddedNodeIds: body.embeddedNodeIds } : {}),
|
|
1811
1853
|
...(Array.isArray(body.embeddedUrls) ? { embeddedUrls: body.embeddedUrls } : {}),
|
|
1854
|
+
...(topAxCapabilities ? { axCapabilities: topAxCapabilities } : {}),
|
|
1812
1855
|
}
|
|
1813
1856
|
: extraData;
|
|
1814
1857
|
let added: ReturnType<typeof addCanvasNode>;
|
|
@@ -2077,7 +2120,8 @@ async function handleCanvasUpdateNode(nodeId: string, req: Request): Promise<Res
|
|
|
2077
2120
|
body.data ||
|
|
2078
2121
|
typeof body.arrangeLocked === 'boolean' ||
|
|
2079
2122
|
typeof body.strictSize === 'boolean' ||
|
|
2080
|
-
(existing.type === 'trace' && hasTraceNodeDataFields(body))
|
|
2123
|
+
(existing.type === 'trace' && hasTraceNodeDataFields(body)) ||
|
|
2124
|
+
(existing.type === 'html' && (body.html !== undefined || body.axCapabilities !== undefined))
|
|
2081
2125
|
) {
|
|
2082
2126
|
const data = { ...existing.data };
|
|
2083
2127
|
if (body.title !== undefined) {
|
|
@@ -2093,6 +2137,18 @@ async function handleCanvasUpdateNode(nodeId: string, req: Request): Promise<Res
|
|
|
2093
2137
|
if (body.data && typeof body.data === 'object' && !Array.isArray(body.data)) {
|
|
2094
2138
|
Object.assign(data, body.data as Record<string, unknown>);
|
|
2095
2139
|
}
|
|
2140
|
+
// Report #53: for html nodes, accept top-level `html` / `axCapabilities` on PATCH
|
|
2141
|
+
// too (top-level overrides the `data.*` merge above — matches POST + MCP parity).
|
|
2142
|
+
if (existing.type === 'html') {
|
|
2143
|
+
if (body.html !== undefined) {
|
|
2144
|
+
if (typeof body.html !== 'string') {
|
|
2145
|
+
return responseJson({ ok: false, error: 'HTML node field "html" must be a string.' }, 400);
|
|
2146
|
+
}
|
|
2147
|
+
data.html = resolveHtmlContent(body.html);
|
|
2148
|
+
}
|
|
2149
|
+
const patchAxCapabilities = normalizeNodeAxCapabilities(body.axCapabilities);
|
|
2150
|
+
if (patchAxCapabilities) data.axCapabilities = patchAxCapabilities;
|
|
2151
|
+
}
|
|
2096
2152
|
if (existing.type === 'webpage') {
|
|
2097
2153
|
const nextUrl = typeof body.url === 'string'
|
|
2098
2154
|
? body.url
|
|
@@ -2495,8 +2551,12 @@ async function handleCanvasAddGraph(req: Request): Promise<Response> {
|
|
|
2495
2551
|
}
|
|
2496
2552
|
|
|
2497
2553
|
async function handleCanvasBatch(req: Request): Promise<Response> {
|
|
2498
|
-
|
|
2499
|
-
|
|
2554
|
+
// Accept both documented shapes: { operations: [...] } and a bare [...] array.
|
|
2555
|
+
// Uses the array-preserving reader so the bare-array form isn't coerced to {}.
|
|
2556
|
+
const body = await readJsonObjectOrArray(req);
|
|
2557
|
+
const operations = Array.isArray(body)
|
|
2558
|
+
? body
|
|
2559
|
+
: Array.isArray(body.operations) ? body.operations : [];
|
|
2500
2560
|
const normalized = operations
|
|
2501
2561
|
.filter((operation): operation is Record<string, unknown> => operation && typeof operation === 'object' && !Array.isArray(operation))
|
|
2502
2562
|
.map((operation) => ({
|
|
@@ -3880,8 +3940,132 @@ function handleGetAxState(): Response {
|
|
|
3880
3940
|
return responseJson({ ok: true, state: canvasState.getAxState() });
|
|
3881
3941
|
}
|
|
3882
3942
|
|
|
3883
|
-
function handleGetAxContext(): Response {
|
|
3884
|
-
|
|
3943
|
+
function handleGetAxContext(url: URL): Response {
|
|
3944
|
+
// Optional ?consumer= filters the compact `delivery` lead block (loop-safe — a
|
|
3945
|
+
// consumer never sees steering/activity it originated), so a host adapter can
|
|
3946
|
+
// inject its own un-truncated pending block per turn (report #54 hardening).
|
|
3947
|
+
const consumer = url.searchParams.get('consumer') ?? undefined;
|
|
3948
|
+
return responseJson(buildCanvasAxContext(consumer));
|
|
3949
|
+
}
|
|
3950
|
+
|
|
3951
|
+
// Clamp ?waitMs= to [0, AX_WAIT_MAX_MS]. 0 (or absent/NaN) = a plain single read.
|
|
3952
|
+
function parseAxWaitMs(url: URL): number {
|
|
3953
|
+
const raw = Number(url.searchParams.get('waitMs') ?? '');
|
|
3954
|
+
return Number.isFinite(raw) && raw > 0 ? Math.min(raw, AX_WAIT_MAX_MS) : 0;
|
|
3955
|
+
}
|
|
3956
|
+
|
|
3957
|
+
function isReviewSeverity(v: unknown): v is PmxAxReviewSeverity {
|
|
3958
|
+
return v === 'info' || v === 'warning' || v === 'error';
|
|
3959
|
+
}
|
|
3960
|
+
function isReviewKind(v: unknown): v is PmxAxReviewKind {
|
|
3961
|
+
return v === 'comment' || v === 'finding';
|
|
3962
|
+
}
|
|
3963
|
+
function isReviewAnchor(v: unknown): v is PmxAxReviewAnchorType {
|
|
3964
|
+
return v === 'node' || v === 'file' || v === 'region';
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
// Validate untrusted activity `reactions` from an HTTP body into the typed override
|
|
3968
|
+
// shape ingestActivity expects. `false` suppresses a default reaction; an object
|
|
3969
|
+
// overrides its fields (invalid fields are dropped, not stored raw).
|
|
3970
|
+
function normalizeActivityReactions(input: Record<string, unknown>): {
|
|
3971
|
+
workItem?: false | { status?: PmxAxWorkItemStatus; detail?: string | null };
|
|
3972
|
+
evidence?: false | { kind?: PmxAxEvidenceKind; body?: string | null };
|
|
3973
|
+
review?: false | { severity?: PmxAxReviewSeverity; kind?: PmxAxReviewKind; anchorType?: PmxAxReviewAnchorType; nodeId?: string | null };
|
|
3974
|
+
} {
|
|
3975
|
+
const out: ReturnType<typeof normalizeActivityReactions> = {};
|
|
3976
|
+
if (input.workItem === false) out.workItem = false;
|
|
3977
|
+
else if (isRecord(input.workItem)) {
|
|
3978
|
+
const status = normalizeAxWorkItemStatus(input.workItem.status);
|
|
3979
|
+
out.workItem = {
|
|
3980
|
+
...(status ? { status } : {}),
|
|
3981
|
+
...(typeof input.workItem.detail === 'string' ? { detail: input.workItem.detail } : {}),
|
|
3982
|
+
};
|
|
3983
|
+
}
|
|
3984
|
+
if (input.evidence === false) out.evidence = false;
|
|
3985
|
+
else if (isRecord(input.evidence)) {
|
|
3986
|
+
out.evidence = {
|
|
3987
|
+
...(isAxEvidenceKind(input.evidence.kind) ? { kind: input.evidence.kind } : {}),
|
|
3988
|
+
...(typeof input.evidence.body === 'string' ? { body: input.evidence.body } : {}),
|
|
3989
|
+
};
|
|
3990
|
+
}
|
|
3991
|
+
if (input.review === false) out.review = false;
|
|
3992
|
+
else if (isRecord(input.review)) {
|
|
3993
|
+
out.review = {
|
|
3994
|
+
...(isReviewSeverity(input.review.severity) ? { severity: input.review.severity } : {}),
|
|
3995
|
+
...(isReviewKind(input.review.kind) ? { kind: input.review.kind } : {}),
|
|
3996
|
+
...(isReviewAnchor(input.review.anchorType) ? { anchorType: input.review.anchorType } : {}),
|
|
3997
|
+
...(typeof input.review.nodeId === 'string' ? { nodeId: input.review.nodeId } : {}),
|
|
3998
|
+
};
|
|
3999
|
+
}
|
|
4000
|
+
return out;
|
|
4001
|
+
}
|
|
4002
|
+
|
|
4003
|
+
// Report primitive A: ingest a harness-forwarded agent activity; the board auto-reacts.
|
|
4004
|
+
async function handleAxActivityIngest(req: Request): Promise<Response> {
|
|
4005
|
+
const body = await readJson(req);
|
|
4006
|
+
if (!isAxActivityKind(body.kind)) {
|
|
4007
|
+
return responseJson({ ok: false, error: "activity requires a valid 'kind': one of tool-start, tool-result, failure, error, session-start, session-end, command, note." }, 400);
|
|
4008
|
+
}
|
|
4009
|
+
if (typeof body.title !== 'string' || !body.title.trim()) {
|
|
4010
|
+
return responseJson({ ok: false, error: 'activity requires a title.' }, 400);
|
|
4011
|
+
}
|
|
4012
|
+
const result = canvasState.ingestActivity(
|
|
4013
|
+
{
|
|
4014
|
+
kind: body.kind,
|
|
4015
|
+
title: body.title,
|
|
4016
|
+
...(typeof body.summary === 'string' ? { summary: body.summary } : {}),
|
|
4017
|
+
...(body.outcome === 'success' || body.outcome === 'failure' ? { outcome: body.outcome } : {}),
|
|
4018
|
+
...(typeof body.ref === 'string' ? { ref: body.ref } : {}),
|
|
4019
|
+
...(Array.isArray(body.nodeIds) ? { nodeIds: normalizeAxNodeIds(body.nodeIds) } : {}),
|
|
4020
|
+
...(isRecord(body.data) ? { data: body.data } : {}),
|
|
4021
|
+
...(isRecord(body.reactions) ? { reactions: normalizeActivityReactions(body.reactions) } : {}),
|
|
4022
|
+
},
|
|
4023
|
+
{ source: normalizeAxSource(body.source, 'api') },
|
|
4024
|
+
);
|
|
4025
|
+
const meta = { sessionId: primaryWorkbenchSessionId, timestamp: new Date().toISOString() };
|
|
4026
|
+
broadcastWorkbenchEvent('ax-event-created', { event: result.event, ...meta });
|
|
4027
|
+
if (result.workItem) broadcastWorkbenchEvent('ax-state-changed', { workItem: result.workItem, ...meta });
|
|
4028
|
+
if (result.evidence) broadcastWorkbenchEvent('ax-event-created', { evidence: result.evidence, ...meta });
|
|
4029
|
+
if (result.review) broadcastWorkbenchEvent('ax-state-changed', { reviewAnnotation: result.review, ...meta });
|
|
4030
|
+
return responseJson({ ok: true, ...result });
|
|
4031
|
+
}
|
|
4032
|
+
|
|
4033
|
+
// Report primitive D: single-item read of a gate, with optional ?waitMs= long-poll
|
|
4034
|
+
// that resolves when the human resolves it in the browser (gates that actually gate).
|
|
4035
|
+
async function handleAxApprovalGet(url: URL, id: string, req: Request): Promise<Response> {
|
|
4036
|
+
const waitMs = parseAxWaitMs(url);
|
|
4037
|
+
const { value, pending } = await waitForAxResolution({
|
|
4038
|
+
read: () => canvasState.getApproval(id),
|
|
4039
|
+
isResolved: (g) => g.status !== 'pending',
|
|
4040
|
+
timeoutMs: waitMs,
|
|
4041
|
+
signal: req.signal,
|
|
4042
|
+
});
|
|
4043
|
+
if (!value) return responseJson({ ok: false, error: 'approval gate not found.' }, 404);
|
|
4044
|
+
return responseJson({ ok: true, approvalGate: value, pending });
|
|
4045
|
+
}
|
|
4046
|
+
|
|
4047
|
+
async function handleAxElicitationGet(url: URL, id: string, req: Request): Promise<Response> {
|
|
4048
|
+
const waitMs = parseAxWaitMs(url);
|
|
4049
|
+
const { value, pending } = await waitForAxResolution({
|
|
4050
|
+
read: () => canvasState.getElicitation(id),
|
|
4051
|
+
isResolved: (e) => e.status !== 'pending',
|
|
4052
|
+
timeoutMs: waitMs,
|
|
4053
|
+
signal: req.signal,
|
|
4054
|
+
});
|
|
4055
|
+
if (!value) return responseJson({ ok: false, error: 'elicitation not found.' }, 404);
|
|
4056
|
+
return responseJson({ ok: true, elicitation: value, pending });
|
|
4057
|
+
}
|
|
4058
|
+
|
|
4059
|
+
async function handleAxModeGet(url: URL, id: string, req: Request): Promise<Response> {
|
|
4060
|
+
const waitMs = parseAxWaitMs(url);
|
|
4061
|
+
const { value, pending } = await waitForAxResolution({
|
|
4062
|
+
read: () => canvasState.getModeRequest(id),
|
|
4063
|
+
isResolved: (m) => m.status !== 'pending',
|
|
4064
|
+
timeoutMs: waitMs,
|
|
4065
|
+
signal: req.signal,
|
|
4066
|
+
});
|
|
4067
|
+
if (!value) return responseJson({ ok: false, error: 'mode request not found.' }, 404);
|
|
4068
|
+
return responseJson({ ok: true, modeRequest: value, pending });
|
|
3885
4069
|
}
|
|
3886
4070
|
|
|
3887
4071
|
// Compact AX state for surfaces (the same shape seeded into AX-enabled iframes).
|
|
@@ -4140,6 +4324,11 @@ async function handleAxWorkAdd(req: Request): Promise<Response> {
|
|
|
4140
4324
|
if (typeof body.title !== 'string' || !body.title.trim()) {
|
|
4141
4325
|
return responseJson({ ok: false, error: 'work item requires a title.' }, 400);
|
|
4142
4326
|
}
|
|
4327
|
+
// Report #56: reject an unknown status (e.g. "in_progress") instead of silently
|
|
4328
|
+
// dropping it — the accepted tokens use hyphens.
|
|
4329
|
+
if (body.status !== undefined && !normalizeAxWorkItemStatus(body.status)) {
|
|
4330
|
+
return responseJson({ ok: false, error: `invalid work item status "${String(body.status)}"; expected one of: todo, in-progress, blocked, done, cancelled.` }, 400);
|
|
4331
|
+
}
|
|
4143
4332
|
const status = normalizeAxWorkItemStatus(body.status);
|
|
4144
4333
|
const workItem = canvasState.addWorkItem(
|
|
4145
4334
|
{
|
|
@@ -4160,6 +4349,10 @@ async function handleAxWorkAdd(req: Request): Promise<Response> {
|
|
|
4160
4349
|
|
|
4161
4350
|
async function handleAxWorkUpdate(req: Request, id: string): Promise<Response> {
|
|
4162
4351
|
const body = await readJson(req);
|
|
4352
|
+
// Report #56: reject an unknown status instead of returning ok:true + no-op.
|
|
4353
|
+
if (body.status !== undefined && !normalizeAxWorkItemStatus(body.status)) {
|
|
4354
|
+
return responseJson({ ok: false, error: `invalid work item status "${String(body.status)}"; expected one of: todo, in-progress, blocked, done, cancelled.` }, 400);
|
|
4355
|
+
}
|
|
4163
4356
|
const status = normalizeAxWorkItemStatus(body.status);
|
|
4164
4357
|
const workItem = canvasState.updateWorkItem(
|
|
4165
4358
|
id,
|
|
@@ -5390,7 +5583,11 @@ export function startCanvasServer(options: CanvasServerOptions = {}): string | n
|
|
|
5390
5583
|
}
|
|
5391
5584
|
|
|
5392
5585
|
if (url.pathname === '/api/canvas/ax/context' && req.method === 'GET') {
|
|
5393
|
-
return handleGetAxContext();
|
|
5586
|
+
return handleGetAxContext(url);
|
|
5587
|
+
}
|
|
5588
|
+
|
|
5589
|
+
if (url.pathname === '/api/canvas/ax/activity' && req.method === 'POST') {
|
|
5590
|
+
return handleAxActivityIngest(req);
|
|
5394
5591
|
}
|
|
5395
5592
|
|
|
5396
5593
|
if (url.pathname === '/api/canvas/ax/surface-snapshot' && req.method === 'GET') {
|
|
@@ -5445,6 +5642,11 @@ export function startCanvasServer(options: CanvasServerOptions = {}): string | n
|
|
|
5445
5642
|
return handleAxApprovalResolve(req, approvalId);
|
|
5446
5643
|
}
|
|
5447
5644
|
|
|
5645
|
+
if (url.pathname.startsWith('/api/canvas/ax/approval/') && !url.pathname.endsWith('/resolve') && req.method === 'GET') {
|
|
5646
|
+
const approvalId = decodeURIComponent(url.pathname.slice('/api/canvas/ax/approval/'.length));
|
|
5647
|
+
return handleAxApprovalGet(url, approvalId, req);
|
|
5648
|
+
}
|
|
5649
|
+
|
|
5448
5650
|
if (url.pathname === '/api/canvas/ax/evidence' && req.method === 'POST') {
|
|
5449
5651
|
return handleAxEvidenceAdd(req);
|
|
5450
5652
|
}
|
|
@@ -5500,6 +5702,11 @@ export function startCanvasServer(options: CanvasServerOptions = {}): string | n
|
|
|
5500
5702
|
return handleAxElicitationRespond(req, elicitationId);
|
|
5501
5703
|
}
|
|
5502
5704
|
|
|
5705
|
+
if (url.pathname.startsWith('/api/canvas/ax/elicitation/') && !url.pathname.endsWith('/respond') && req.method === 'GET') {
|
|
5706
|
+
const elicitationId = decodeURIComponent(url.pathname.slice('/api/canvas/ax/elicitation/'.length));
|
|
5707
|
+
return handleAxElicitationGet(url, elicitationId, req);
|
|
5708
|
+
}
|
|
5709
|
+
|
|
5503
5710
|
if (url.pathname === '/api/canvas/ax/mode' && req.method === 'GET') {
|
|
5504
5711
|
return handleAxModeList();
|
|
5505
5712
|
}
|
|
@@ -5515,6 +5722,11 @@ export function startCanvasServer(options: CanvasServerOptions = {}): string | n
|
|
|
5515
5722
|
return handleAxModeResolve(req, modeId);
|
|
5516
5723
|
}
|
|
5517
5724
|
|
|
5725
|
+
if (url.pathname.startsWith('/api/canvas/ax/mode/') && !url.pathname.endsWith('/resolve') && req.method === 'GET') {
|
|
5726
|
+
const modeId = decodeURIComponent(url.pathname.slice('/api/canvas/ax/mode/'.length));
|
|
5727
|
+
return handleAxModeGet(url, modeId, req);
|
|
5728
|
+
}
|
|
5729
|
+
|
|
5518
5730
|
if (url.pathname === '/api/canvas/ax/command' && req.method === 'GET') {
|
|
5519
5731
|
return handleAxCommandList();
|
|
5520
5732
|
}
|