qcanvas-local-agent 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/dist/canvas-session.js +18 -1
- package/dist/mcp-server.js +2 -0
- package/dist/node-introspection.d.ts +14 -0
- package/dist/node-introspection.js +270 -0
- package/dist/schemas.d.ts +491 -1
- package/dist/schemas.js +70 -0
- package/dist/types.d.ts +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# qcanvas-local-agent
|
|
2
2
|
|
|
3
|
+
## 发布新版固定流程
|
|
4
|
+
|
|
5
|
+
每次修改本地 Agent 后,如需让 `npx -y qcanvas-local-agent` 获取新能力,必须按以下顺序发布:
|
|
6
|
+
|
|
7
|
+
```powershell
|
|
8
|
+
cd "D:\胡东山\py学习\QCanvas\apps\qcanvas-local-agent"
|
|
9
|
+
npm run build
|
|
10
|
+
# 手动将 package.json 的 version 提升到一个从未发布过的新版本
|
|
11
|
+
npm publish
|
|
12
|
+
# npm 要求双重验证时:npm publish --otp=手机显示的6位验证码
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
发布后使用 `npm view qcanvas-local-agent version` 验证 registry 已更新。禁止重复使用已经发布过的版本号,也不要使用会自动创建 Git tag 的 `npm version`。
|
|
16
|
+
|
|
3
17
|
QCanvas 「本机 Agent」:在你自己的电脑上运行一个本地桥接服务,让 QCanvas 网页画布可以调用**本机登录的 Codex / Claude** 来读写画布。
|
|
4
18
|
|
|
5
19
|
## 一条命令启动
|
|
@@ -34,3 +48,16 @@ npx -y qcanvas-local-agent
|
|
|
34
48
|
|
|
35
49
|
- 所有对画布的写操作都会由浏览器二次确认后才真正应用。
|
|
36
50
|
- 令牌仅用于本机 `127.0.0.1` 回环连接的鉴权。
|
|
51
|
+
|
|
52
|
+
## 画布 MCP 工具
|
|
53
|
+
|
|
54
|
+
- `tapcanvas_flow_get`:读取浏览器实时画布快照。
|
|
55
|
+
- `tapcanvas_node_context_bundle_get`:按 `nodeId` 读取节点、上下游、节点契约、已绑定预设和当前启用模型;省略 `nodeId` 时只允许画布恰好选中一个节点。
|
|
56
|
+
- `tapcanvas_node_presets_get`:读取浏览器已同步的真实节点预设,可按精确 `id` 和 `type` 筛选。
|
|
57
|
+
- `tapcanvas_node_prompt_preview`:查看节点当前提示词、绑定预设、精确差异和结构性就绪状态;它不会伪装成生成 runner 的最终提示词编译结果。省略 `nodeId` 时同样要求恰好选中一个节点。
|
|
58
|
+
|
|
59
|
+
上述三个节点取证工具与云端 Agents Bridge 使用同一组顶层响应契约(`nodeContract`、`presetBinding`、`modelCatalog`、`divergence`、`structuralReadiness`);浏览器会同时同步运行时节点 kind 到规范契约的真实映射,因此 `storyboardImage`、`composeVideo` 等派生 kind 不需要本地 Agent 维护第二套节点知识。
|
|
60
|
+
- `tapcanvas_create_generation_flow`:创建并连接单个生成节点,但不会自动提交生成。
|
|
61
|
+
- `tapcanvas_flow_patch`:提交需浏览器确认的画布结构变更。
|
|
62
|
+
|
|
63
|
+
以上只读工具只使用已连接浏览器同步的实时快照。未知节点、未知预设,以及预设或模型目录加载失败都会显式报错,不会返回伪造数据或静默兜底。
|
package/dist/canvas-session.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
import { isLocalAgentToolName, localAgentToolInputSchemas } from "./schemas.js";
|
|
2
|
+
import { isLocalAgentToolName, localAgentToolInputSchemas, localCanvasCapabilitiesSchema, } from "./schemas.js";
|
|
3
|
+
import { getNodeContextBundle, getNodePresets, getNodePromptPreview, resolveNodeId, } from "./node-introspection.js";
|
|
3
4
|
import { compileGenerationFlow, GenerationFlowCompileError, } from "./generation-flow-compiler.js";
|
|
4
5
|
function isRecord(value) {
|
|
5
6
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
@@ -31,6 +32,7 @@ function parseCanvasSnapshot(value, clientId) {
|
|
|
31
32
|
throw new Error("canvas state missing nodes array");
|
|
32
33
|
if (!edges)
|
|
33
34
|
throw new Error("canvas state missing edges array");
|
|
35
|
+
const capabilities = localCanvasCapabilitiesSchema.parse(value);
|
|
34
36
|
return {
|
|
35
37
|
clientId,
|
|
36
38
|
url: readString(value.url),
|
|
@@ -42,6 +44,7 @@ function parseCanvasSnapshot(value, clientId) {
|
|
|
42
44
|
edges,
|
|
43
45
|
selectedNodeIds: readStringArray(value.selectedNodeIds),
|
|
44
46
|
viewport: readViewport(value.viewport),
|
|
47
|
+
...capabilities,
|
|
45
48
|
updatedAt: readString(value.updatedAt) || new Date().toISOString(),
|
|
46
49
|
};
|
|
47
50
|
}
|
|
@@ -105,6 +108,20 @@ export class CanvasSession {
|
|
|
105
108
|
}
|
|
106
109
|
if (rawName === "tapcanvas_flow_get")
|
|
107
110
|
return this.readFlow();
|
|
111
|
+
if (rawName === "tapcanvas_node_context_bundle_get") {
|
|
112
|
+
const parsed = localAgentToolInputSchemas.tapcanvas_node_context_bundle_get.parse(input);
|
|
113
|
+
const snapshot = this.readFlow();
|
|
114
|
+
return getNodeContextBundle(snapshot, resolveNodeId(snapshot, parsed.nodeId));
|
|
115
|
+
}
|
|
116
|
+
if (rawName === "tapcanvas_node_presets_get") {
|
|
117
|
+
const parsed = localAgentToolInputSchemas.tapcanvas_node_presets_get.parse(input);
|
|
118
|
+
return getNodePresets(this.readFlow(), parsed);
|
|
119
|
+
}
|
|
120
|
+
if (rawName === "tapcanvas_node_prompt_preview") {
|
|
121
|
+
const parsed = localAgentToolInputSchemas.tapcanvas_node_prompt_preview.parse(input);
|
|
122
|
+
const snapshot = this.readFlow();
|
|
123
|
+
return getNodePromptPreview(snapshot, resolveNodeId(snapshot, parsed.nodeId));
|
|
124
|
+
}
|
|
108
125
|
if (rawName === "tapcanvas_create_generation_flow")
|
|
109
126
|
return this.createGenerationFlow(input, agent);
|
|
110
127
|
return this.requestBrowserPatch(rawName, input, agent);
|
package/dist/mcp-server.js
CHANGED
|
@@ -5,6 +5,8 @@ import { localAgentToolDescriptions, localAgentToolInputSchemas, localAgentToolN
|
|
|
5
5
|
const INSTRUCTIONS = [
|
|
6
6
|
"Use QCanvas local tools only for the connected live browser canvas.",
|
|
7
7
|
"Read tapcanvas_flow_get before writing.",
|
|
8
|
+
"Use tapcanvas_node_context_bundle_get to inspect a node's live contract, graph neighbors, bound preset, and enabled models before deciding how to edit or execute it.",
|
|
9
|
+
"Use tapcanvas_node_presets_get for on-demand preset lookup and tapcanvas_node_prompt_preview for factual prompt/readiness inspection. The preview never claims to reproduce runner prompt compilation.",
|
|
8
10
|
"Prefer tapcanvas_create_generation_flow over tapcanvas_flow_patch when creating a single generation node with reference wiring; it auto-computes placement, handles, and reference asset inputs. It only creates the node and never submits generation by itself.",
|
|
9
11
|
"All tapcanvas_flow_patch writes (including those issued by tapcanvas_create_generation_flow) are confirmed by the browser before they apply.",
|
|
10
12
|
"If no live browser tab is connected, report the error directly.",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { QCanvasNodePreset, QCanvasSnapshot } from "./types.js";
|
|
2
|
+
export declare function resolveNodeId(snapshot: QCanvasSnapshot, requestedNodeId?: string): string;
|
|
3
|
+
export declare function getNodePresets(snapshot: QCanvasSnapshot, input: {
|
|
4
|
+
id?: string;
|
|
5
|
+
type?: "text" | "image" | "video";
|
|
6
|
+
}): {
|
|
7
|
+
items: Array<QCanvasNodePreset | Omit<QCanvasNodePreset, "prompt">>;
|
|
8
|
+
filter: {
|
|
9
|
+
id: string | null;
|
|
10
|
+
type: "text" | "image" | "video" | null;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
export declare function getNodeContextBundle(snapshot: QCanvasSnapshot, nodeId: string): Record<string, unknown>;
|
|
14
|
+
export declare function getNodePromptPreview(snapshot: QCanvasSnapshot, nodeId: string): Record<string, unknown>;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
export function resolveNodeId(snapshot, requestedNodeId) {
|
|
2
|
+
if (requestedNodeId)
|
|
3
|
+
return requestedNodeId;
|
|
4
|
+
if (snapshot.selectedNodeIds.length === 1) {
|
|
5
|
+
const selectedNodeId = snapshot.selectedNodeIds[0];
|
|
6
|
+
if (selectedNodeId)
|
|
7
|
+
return selectedNodeId;
|
|
8
|
+
}
|
|
9
|
+
throw new Error(`nodeId is required when the live canvas has ${snapshot.selectedNodeIds.length} selected nodes; select exactly one node or provide nodeId`);
|
|
10
|
+
}
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
13
|
+
}
|
|
14
|
+
function requiredString(record, key, label) {
|
|
15
|
+
const value = record[key];
|
|
16
|
+
if (typeof value !== "string" || !value.trim())
|
|
17
|
+
throw new Error(`${label} missing ${key}`);
|
|
18
|
+
return value.trim();
|
|
19
|
+
}
|
|
20
|
+
function optionalString(value) {
|
|
21
|
+
return typeof value === "string" && value.trim() ? value.trim() : null;
|
|
22
|
+
}
|
|
23
|
+
function readNodes(snapshot) {
|
|
24
|
+
return snapshot.nodes.map((value, index) => {
|
|
25
|
+
if (!isRecord(value))
|
|
26
|
+
throw new Error(`canvas node at index ${index} must be an object`);
|
|
27
|
+
const id = requiredString(value, "id", `canvas node at index ${index}`);
|
|
28
|
+
if (!isRecord(value.data))
|
|
29
|
+
throw new Error(`canvas node ${id} missing data object`);
|
|
30
|
+
return { ...value, id, data: value.data };
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function readEdges(snapshot) {
|
|
34
|
+
return snapshot.edges.map((value, index) => {
|
|
35
|
+
if (!isRecord(value))
|
|
36
|
+
throw new Error(`canvas edge at index ${index} must be an object`);
|
|
37
|
+
return {
|
|
38
|
+
...value,
|
|
39
|
+
id: requiredString(value, "id", `canvas edge at index ${index}`),
|
|
40
|
+
source: requiredString(value, "source", `canvas edge at index ${index}`),
|
|
41
|
+
target: requiredString(value, "target", `canvas edge at index ${index}`),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function assertCapability(snapshot, capability) {
|
|
46
|
+
const error = snapshot.capabilityErrors.find((item) => item.startsWith(`${capability}:`));
|
|
47
|
+
if (error)
|
|
48
|
+
throw new Error(`browser snapshot capability unavailable: ${error}`);
|
|
49
|
+
}
|
|
50
|
+
function findNode(snapshot, nodeId) {
|
|
51
|
+
const node = readNodes(snapshot).find((item) => item.id === nodeId);
|
|
52
|
+
if (!node)
|
|
53
|
+
throw new Error(`QCanvas node does not exist: ${nodeId}`);
|
|
54
|
+
return node;
|
|
55
|
+
}
|
|
56
|
+
function findContract(snapshot, node) {
|
|
57
|
+
const kind = requiredString(node.data, "kind", `QCanvas node ${node.id}.data`);
|
|
58
|
+
const normalizedKind = kind.toLowerCase();
|
|
59
|
+
const contract = snapshot.nodeContracts.find((item) => item.kind === kind || item.runtimeKinds.some((runtimeKind) => runtimeKind.toLowerCase() === normalizedKind));
|
|
60
|
+
if (!contract)
|
|
61
|
+
throw new Error(`QCanvas node contract does not exist for kind: ${kind}`);
|
|
62
|
+
return contract;
|
|
63
|
+
}
|
|
64
|
+
function resolvePreset(snapshot, node) {
|
|
65
|
+
const presetId = optionalString(node.data.llmPresetId);
|
|
66
|
+
if (!presetId)
|
|
67
|
+
return null;
|
|
68
|
+
assertCapability(snapshot, "nodePresets");
|
|
69
|
+
return snapshot.nodePresets.find((item) => item.id === presetId) ?? null;
|
|
70
|
+
}
|
|
71
|
+
function findModel(snapshot, identifier) {
|
|
72
|
+
return Object.values(snapshot.enabledModels)
|
|
73
|
+
.flat()
|
|
74
|
+
.find((item) => item.modelKey === identifier || item.modelAlias === identifier) ?? null;
|
|
75
|
+
}
|
|
76
|
+
function readModelBinding(node, contract) {
|
|
77
|
+
const fields = contract.promptEditor?.presetType === "image"
|
|
78
|
+
? ["imageModel", "modelSelect", "modelKey", "geminiModel"]
|
|
79
|
+
: contract.promptEditor?.presetType === "video"
|
|
80
|
+
? ["videoModel", "modelSelect", "modelKey", "geminiModel"]
|
|
81
|
+
: contract.promptEditor?.presetType === "text"
|
|
82
|
+
? ["geminiModel", "modelSelect", "modelKey"]
|
|
83
|
+
: contract.kind === "audio"
|
|
84
|
+
? ["model", "modelSelect", "modelKey"]
|
|
85
|
+
: ["modelKey", "modelSelect", "geminiModel"];
|
|
86
|
+
for (const field of fields) {
|
|
87
|
+
const modelKey = optionalString(node.data[field]);
|
|
88
|
+
if (modelKey)
|
|
89
|
+
return { field, modelKey };
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function toNodeContract(contract, runtimeKind) {
|
|
94
|
+
return {
|
|
95
|
+
kind: runtimeKind,
|
|
96
|
+
known: true,
|
|
97
|
+
label: contract.label,
|
|
98
|
+
purpose: contract.promptEditor?.purpose ?? null,
|
|
99
|
+
fields: {},
|
|
100
|
+
input: { handles: contract.handles.targets },
|
|
101
|
+
output: { handles: contract.handles.sources },
|
|
102
|
+
editorGuidance: contract.promptEditor
|
|
103
|
+
? {
|
|
104
|
+
promptField: "prompt",
|
|
105
|
+
placeholder: contract.promptEditor.placeholder,
|
|
106
|
+
presetType: contract.promptEditor.presetType,
|
|
107
|
+
supportsMentions: contract.promptEditor.supportsMentions,
|
|
108
|
+
upstreamBehavior: contract.promptEditor.upstreamBehavior,
|
|
109
|
+
previewBoundary: contract.promptEditor.previewBoundary,
|
|
110
|
+
}
|
|
111
|
+
: null,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function buildPresetBinding(node, contract, preset) {
|
|
115
|
+
const presetId = optionalString(node.data.llmPresetId);
|
|
116
|
+
return {
|
|
117
|
+
presetId,
|
|
118
|
+
presetType: contract.promptEditor?.presetType ?? null,
|
|
119
|
+
presetTitle: preset?.title ?? optionalString(node.data.llmPresetTitle),
|
|
120
|
+
status: !presetId ? "not_bound" : preset ? "resolved" : "unavailable",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function buildModelCatalog(snapshot) {
|
|
124
|
+
return {
|
|
125
|
+
status: "available",
|
|
126
|
+
summary: {
|
|
127
|
+
textModels: snapshot.enabledModels.text,
|
|
128
|
+
imageModels: snapshot.enabledModels.image,
|
|
129
|
+
videoModels: snapshot.enabledModels.video,
|
|
130
|
+
audioModels: snapshot.enabledModels.audio,
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function readPrimaryAssetUrl(node, field) {
|
|
135
|
+
return optionalString(node.data[field]);
|
|
136
|
+
}
|
|
137
|
+
function collectUpstreamAssetUrls(nodes) {
|
|
138
|
+
const urls = new Set();
|
|
139
|
+
nodes.forEach((node) => {
|
|
140
|
+
const imageUrl = readPrimaryAssetUrl(node, "imageUrl");
|
|
141
|
+
const videoUrl = readPrimaryAssetUrl(node, "videoUrl");
|
|
142
|
+
if (imageUrl)
|
|
143
|
+
urls.add(imageUrl);
|
|
144
|
+
if (videoUrl)
|
|
145
|
+
urls.add(videoUrl);
|
|
146
|
+
});
|
|
147
|
+
return Array.from(urls);
|
|
148
|
+
}
|
|
149
|
+
function enabledModelsForContract(snapshot, contract) {
|
|
150
|
+
if (contract.kind === "audio")
|
|
151
|
+
return snapshot.enabledModels.audio;
|
|
152
|
+
const presetType = contract.promptEditor?.presetType;
|
|
153
|
+
return presetType ? snapshot.enabledModels[presetType] : null;
|
|
154
|
+
}
|
|
155
|
+
function enabledModelMissingKey(contract) {
|
|
156
|
+
if (contract.kind === "audio")
|
|
157
|
+
return "enabledAudioModel";
|
|
158
|
+
if (contract.promptEditor?.presetType === "image")
|
|
159
|
+
return "enabledImageModel";
|
|
160
|
+
if (contract.promptEditor?.presetType === "video")
|
|
161
|
+
return "enabledVideoModel";
|
|
162
|
+
return "enabledTextModel";
|
|
163
|
+
}
|
|
164
|
+
export function getNodePresets(snapshot, input) {
|
|
165
|
+
assertCapability(snapshot, "nodePresets");
|
|
166
|
+
if (input.id && !snapshot.nodePresets.some((item) => item.id === input.id)) {
|
|
167
|
+
throw new Error(`QCanvas node preset does not exist: ${input.id}`);
|
|
168
|
+
}
|
|
169
|
+
const matched = snapshot.nodePresets.filter((item) => (!input.id || item.id === input.id) && (!input.type || item.type === input.type));
|
|
170
|
+
if (input.id && matched.length === 0) {
|
|
171
|
+
throw new Error(`QCanvas node preset ${input.id} does not match requested type: ${input.type}`);
|
|
172
|
+
}
|
|
173
|
+
const items = input.id
|
|
174
|
+
? matched
|
|
175
|
+
: matched.map(({ prompt: _prompt, ...summary }) => summary);
|
|
176
|
+
return { items, filter: { id: input.id ?? null, type: input.type ?? null } };
|
|
177
|
+
}
|
|
178
|
+
export function getNodeContextBundle(snapshot, nodeId) {
|
|
179
|
+
const node = findNode(snapshot, nodeId);
|
|
180
|
+
const contract = findContract(snapshot, node);
|
|
181
|
+
assertCapability(snapshot, "nodePresets");
|
|
182
|
+
assertCapability(snapshot, "enabledModels");
|
|
183
|
+
const resolvedPreset = resolvePreset(snapshot, node);
|
|
184
|
+
const runtimeKind = requiredString(node.data, "kind", `QCanvas node ${node.id}.data`);
|
|
185
|
+
const nodes = readNodes(snapshot);
|
|
186
|
+
const nodeById = new Map(nodes.map((item) => [item.id, item]));
|
|
187
|
+
const edges = readEdges(snapshot);
|
|
188
|
+
const upstreamEdges = edges.filter((edge) => edge.target === nodeId);
|
|
189
|
+
const downstreamEdges = edges.filter((edge) => edge.source === nodeId);
|
|
190
|
+
const resolveConnectedNode = (id) => {
|
|
191
|
+
const connected = nodeById.get(id);
|
|
192
|
+
if (!connected)
|
|
193
|
+
throw new Error(`canvas edge references unknown node: ${id}`);
|
|
194
|
+
return connected;
|
|
195
|
+
};
|
|
196
|
+
const upstreamNodes = upstreamEdges.map((edge) => resolveConnectedNode(edge.source));
|
|
197
|
+
const downstreamNodes = downstreamEdges.map((edge) => resolveConnectedNode(edge.target));
|
|
198
|
+
return {
|
|
199
|
+
projectId: snapshot.projectId,
|
|
200
|
+
flowId: snapshot.flowId,
|
|
201
|
+
nodeId,
|
|
202
|
+
node,
|
|
203
|
+
nodeContract: toNodeContract(contract, runtimeKind),
|
|
204
|
+
presetBinding: buildPresetBinding(node, contract, resolvedPreset),
|
|
205
|
+
selectedPreset: resolvedPreset,
|
|
206
|
+
modelCatalog: buildModelCatalog(snapshot),
|
|
207
|
+
upstreamNodes,
|
|
208
|
+
downstreamNodes,
|
|
209
|
+
snapshotUpdatedAt: snapshot.updatedAt,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
export function getNodePromptPreview(snapshot, nodeId) {
|
|
213
|
+
const node = findNode(snapshot, nodeId);
|
|
214
|
+
const contract = findContract(snapshot, node);
|
|
215
|
+
if (!contract.promptEditor) {
|
|
216
|
+
throw new Error(`QCanvas node prompt editor contract is not declared for kind: ${contract.kind}`);
|
|
217
|
+
}
|
|
218
|
+
assertCapability(snapshot, "nodePresets");
|
|
219
|
+
assertCapability(snapshot, "enabledModels");
|
|
220
|
+
const preset = resolvePreset(snapshot, node);
|
|
221
|
+
const currentPrompt = optionalString(node.data.prompt);
|
|
222
|
+
const modelBinding = readModelBinding(node, contract);
|
|
223
|
+
const model = modelBinding ? findModel(snapshot, modelBinding.modelKey) : null;
|
|
224
|
+
const presetBinding = buildPresetBinding(node, contract, preset);
|
|
225
|
+
const presetId = optionalString(node.data.llmPresetId);
|
|
226
|
+
const divergence = !presetId
|
|
227
|
+
? "not_bound"
|
|
228
|
+
: !preset
|
|
229
|
+
? "bound_preset_unavailable"
|
|
230
|
+
: currentPrompt === preset.prompt.trim()
|
|
231
|
+
? "matches_preset"
|
|
232
|
+
: "differs_from_preset";
|
|
233
|
+
const enabledModels = enabledModelsForContract(snapshot, contract);
|
|
234
|
+
const missing = [];
|
|
235
|
+
if (["image", "panorama360", "video", "audio"].includes(contract.kind) && !currentPrompt)
|
|
236
|
+
missing.push("prompt");
|
|
237
|
+
if (enabledModels && enabledModels.length === 0)
|
|
238
|
+
missing.push(enabledModelMissingKey(contract));
|
|
239
|
+
else if (modelBinding && !model)
|
|
240
|
+
missing.push("selectedModelEnabled");
|
|
241
|
+
const nodes = readNodes(snapshot);
|
|
242
|
+
const nodeById = new Map(nodes.map((item) => [item.id, item]));
|
|
243
|
+
const upstreamNodes = readEdges(snapshot)
|
|
244
|
+
.filter((edge) => edge.target === nodeId)
|
|
245
|
+
.map((edge) => {
|
|
246
|
+
const upstream = nodeById.get(edge.source);
|
|
247
|
+
if (!upstream)
|
|
248
|
+
throw new Error(`canvas edge references unknown node: ${edge.source}`);
|
|
249
|
+
return upstream;
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
projectId: snapshot.projectId,
|
|
253
|
+
flowId: snapshot.flowId,
|
|
254
|
+
nodeId,
|
|
255
|
+
currentPrompt,
|
|
256
|
+
presetBinding,
|
|
257
|
+
resolvedPreset: preset,
|
|
258
|
+
divergence,
|
|
259
|
+
structuralReadiness: {
|
|
260
|
+
ready: missing.length === 0,
|
|
261
|
+
missing,
|
|
262
|
+
selectedModelKey: modelBinding?.modelKey ?? null,
|
|
263
|
+
selectedModelEnabled: modelBinding ? Boolean(model) : null,
|
|
264
|
+
enabledModelCount: enabledModels?.length ?? null,
|
|
265
|
+
upstreamAssetUrls: collectUpstreamAssetUrls(upstreamNodes),
|
|
266
|
+
note: "This result validates browser snapshot fields, the dynamic model catalog, and real upstream asset URLs only; it does not reproduce runner prompt or model-request compilation.",
|
|
267
|
+
},
|
|
268
|
+
snapshotUpdatedAt: snapshot.updatedAt,
|
|
269
|
+
};
|
|
270
|
+
}
|
package/dist/schemas.d.ts
CHANGED
|
@@ -1,8 +1,498 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const localCanvasCapabilitiesSchema: z.ZodObject<{
|
|
3
|
+
nodeContracts: z.ZodArray<z.ZodObject<{
|
|
4
|
+
kind: z.ZodString;
|
|
5
|
+
runtimeKinds: z.ZodArray<z.ZodString, "many">;
|
|
6
|
+
label: z.ZodString;
|
|
7
|
+
category: z.ZodString;
|
|
8
|
+
features: z.ZodArray<z.ZodString, "many">;
|
|
9
|
+
handles: z.ZodObject<{
|
|
10
|
+
dynamic: z.ZodBoolean;
|
|
11
|
+
targets: z.ZodArray<z.ZodObject<{
|
|
12
|
+
id: z.ZodString;
|
|
13
|
+
type: z.ZodString;
|
|
14
|
+
position: z.ZodOptional<z.ZodString>;
|
|
15
|
+
}, "strip", z.ZodTypeAny, {
|
|
16
|
+
id: string;
|
|
17
|
+
type: string;
|
|
18
|
+
position?: string | undefined;
|
|
19
|
+
}, {
|
|
20
|
+
id: string;
|
|
21
|
+
type: string;
|
|
22
|
+
position?: string | undefined;
|
|
23
|
+
}>, "many">;
|
|
24
|
+
sources: z.ZodArray<z.ZodObject<{
|
|
25
|
+
id: z.ZodString;
|
|
26
|
+
type: z.ZodString;
|
|
27
|
+
position: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
id: string;
|
|
30
|
+
type: string;
|
|
31
|
+
position?: string | undefined;
|
|
32
|
+
}, {
|
|
33
|
+
id: string;
|
|
34
|
+
type: string;
|
|
35
|
+
position?: string | undefined;
|
|
36
|
+
}>, "many">;
|
|
37
|
+
}, "strip", z.ZodTypeAny, {
|
|
38
|
+
dynamic: boolean;
|
|
39
|
+
targets: {
|
|
40
|
+
id: string;
|
|
41
|
+
type: string;
|
|
42
|
+
position?: string | undefined;
|
|
43
|
+
}[];
|
|
44
|
+
sources: {
|
|
45
|
+
id: string;
|
|
46
|
+
type: string;
|
|
47
|
+
position?: string | undefined;
|
|
48
|
+
}[];
|
|
49
|
+
}, {
|
|
50
|
+
dynamic: boolean;
|
|
51
|
+
targets: {
|
|
52
|
+
id: string;
|
|
53
|
+
type: string;
|
|
54
|
+
position?: string | undefined;
|
|
55
|
+
}[];
|
|
56
|
+
sources: {
|
|
57
|
+
id: string;
|
|
58
|
+
type: string;
|
|
59
|
+
position?: string | undefined;
|
|
60
|
+
}[];
|
|
61
|
+
}>;
|
|
62
|
+
promptEditor: z.ZodNullable<z.ZodObject<{
|
|
63
|
+
purpose: z.ZodString;
|
|
64
|
+
placeholder: z.ZodString;
|
|
65
|
+
presetType: z.ZodNullable<z.ZodEnum<["text", "image", "video"]>>;
|
|
66
|
+
supportsMentions: z.ZodBoolean;
|
|
67
|
+
upstreamBehavior: z.ZodString;
|
|
68
|
+
previewBoundary: z.ZodString;
|
|
69
|
+
}, "strip", z.ZodTypeAny, {
|
|
70
|
+
purpose: string;
|
|
71
|
+
placeholder: string;
|
|
72
|
+
presetType: "text" | "image" | "video" | null;
|
|
73
|
+
supportsMentions: boolean;
|
|
74
|
+
upstreamBehavior: string;
|
|
75
|
+
previewBoundary: string;
|
|
76
|
+
}, {
|
|
77
|
+
purpose: string;
|
|
78
|
+
placeholder: string;
|
|
79
|
+
presetType: "text" | "image" | "video" | null;
|
|
80
|
+
supportsMentions: boolean;
|
|
81
|
+
upstreamBehavior: string;
|
|
82
|
+
previewBoundary: string;
|
|
83
|
+
}>>;
|
|
84
|
+
}, "strip", z.ZodTypeAny, {
|
|
85
|
+
kind: string;
|
|
86
|
+
runtimeKinds: string[];
|
|
87
|
+
label: string;
|
|
88
|
+
category: string;
|
|
89
|
+
features: string[];
|
|
90
|
+
handles: {
|
|
91
|
+
dynamic: boolean;
|
|
92
|
+
targets: {
|
|
93
|
+
id: string;
|
|
94
|
+
type: string;
|
|
95
|
+
position?: string | undefined;
|
|
96
|
+
}[];
|
|
97
|
+
sources: {
|
|
98
|
+
id: string;
|
|
99
|
+
type: string;
|
|
100
|
+
position?: string | undefined;
|
|
101
|
+
}[];
|
|
102
|
+
};
|
|
103
|
+
promptEditor: {
|
|
104
|
+
purpose: string;
|
|
105
|
+
placeholder: string;
|
|
106
|
+
presetType: "text" | "image" | "video" | null;
|
|
107
|
+
supportsMentions: boolean;
|
|
108
|
+
upstreamBehavior: string;
|
|
109
|
+
previewBoundary: string;
|
|
110
|
+
} | null;
|
|
111
|
+
}, {
|
|
112
|
+
kind: string;
|
|
113
|
+
runtimeKinds: string[];
|
|
114
|
+
label: string;
|
|
115
|
+
category: string;
|
|
116
|
+
features: string[];
|
|
117
|
+
handles: {
|
|
118
|
+
dynamic: boolean;
|
|
119
|
+
targets: {
|
|
120
|
+
id: string;
|
|
121
|
+
type: string;
|
|
122
|
+
position?: string | undefined;
|
|
123
|
+
}[];
|
|
124
|
+
sources: {
|
|
125
|
+
id: string;
|
|
126
|
+
type: string;
|
|
127
|
+
position?: string | undefined;
|
|
128
|
+
}[];
|
|
129
|
+
};
|
|
130
|
+
promptEditor: {
|
|
131
|
+
purpose: string;
|
|
132
|
+
placeholder: string;
|
|
133
|
+
presetType: "text" | "image" | "video" | null;
|
|
134
|
+
supportsMentions: boolean;
|
|
135
|
+
upstreamBehavior: string;
|
|
136
|
+
previewBoundary: string;
|
|
137
|
+
} | null;
|
|
138
|
+
}>, "many">;
|
|
139
|
+
nodePresets: z.ZodArray<z.ZodObject<{
|
|
140
|
+
id: z.ZodString;
|
|
141
|
+
title: z.ZodString;
|
|
142
|
+
type: z.ZodEnum<["text", "image", "video"]>;
|
|
143
|
+
prompt: z.ZodString;
|
|
144
|
+
description: z.ZodNullable<z.ZodString>;
|
|
145
|
+
scope: z.ZodEnum<["base", "user"]>;
|
|
146
|
+
updatedAt: z.ZodString;
|
|
147
|
+
}, "strip", z.ZodTypeAny, {
|
|
148
|
+
id: string;
|
|
149
|
+
updatedAt: string;
|
|
150
|
+
type: "text" | "image" | "video";
|
|
151
|
+
title: string;
|
|
152
|
+
prompt: string;
|
|
153
|
+
description: string | null;
|
|
154
|
+
scope: "base" | "user";
|
|
155
|
+
}, {
|
|
156
|
+
id: string;
|
|
157
|
+
updatedAt: string;
|
|
158
|
+
type: "text" | "image" | "video";
|
|
159
|
+
title: string;
|
|
160
|
+
prompt: string;
|
|
161
|
+
description: string | null;
|
|
162
|
+
scope: "base" | "user";
|
|
163
|
+
}>, "many">;
|
|
164
|
+
enabledModels: z.ZodObject<{
|
|
165
|
+
text: z.ZodArray<z.ZodObject<{
|
|
166
|
+
modelKey: z.ZodString;
|
|
167
|
+
modelAlias: z.ZodNullable<z.ZodString>;
|
|
168
|
+
label: z.ZodString;
|
|
169
|
+
vendorKey: z.ZodString;
|
|
170
|
+
kind: z.ZodString;
|
|
171
|
+
enabled: z.ZodBoolean;
|
|
172
|
+
}, "strip", z.ZodTypeAny, {
|
|
173
|
+
kind: string;
|
|
174
|
+
label: string;
|
|
175
|
+
modelKey: string;
|
|
176
|
+
modelAlias: string | null;
|
|
177
|
+
vendorKey: string;
|
|
178
|
+
enabled: boolean;
|
|
179
|
+
}, {
|
|
180
|
+
kind: string;
|
|
181
|
+
label: string;
|
|
182
|
+
modelKey: string;
|
|
183
|
+
modelAlias: string | null;
|
|
184
|
+
vendorKey: string;
|
|
185
|
+
enabled: boolean;
|
|
186
|
+
}>, "many">;
|
|
187
|
+
image: z.ZodArray<z.ZodObject<{
|
|
188
|
+
modelKey: z.ZodString;
|
|
189
|
+
modelAlias: z.ZodNullable<z.ZodString>;
|
|
190
|
+
label: z.ZodString;
|
|
191
|
+
vendorKey: z.ZodString;
|
|
192
|
+
kind: z.ZodString;
|
|
193
|
+
enabled: z.ZodBoolean;
|
|
194
|
+
}, "strip", z.ZodTypeAny, {
|
|
195
|
+
kind: string;
|
|
196
|
+
label: string;
|
|
197
|
+
modelKey: string;
|
|
198
|
+
modelAlias: string | null;
|
|
199
|
+
vendorKey: string;
|
|
200
|
+
enabled: boolean;
|
|
201
|
+
}, {
|
|
202
|
+
kind: string;
|
|
203
|
+
label: string;
|
|
204
|
+
modelKey: string;
|
|
205
|
+
modelAlias: string | null;
|
|
206
|
+
vendorKey: string;
|
|
207
|
+
enabled: boolean;
|
|
208
|
+
}>, "many">;
|
|
209
|
+
video: z.ZodArray<z.ZodObject<{
|
|
210
|
+
modelKey: z.ZodString;
|
|
211
|
+
modelAlias: z.ZodNullable<z.ZodString>;
|
|
212
|
+
label: z.ZodString;
|
|
213
|
+
vendorKey: z.ZodString;
|
|
214
|
+
kind: z.ZodString;
|
|
215
|
+
enabled: z.ZodBoolean;
|
|
216
|
+
}, "strip", z.ZodTypeAny, {
|
|
217
|
+
kind: string;
|
|
218
|
+
label: string;
|
|
219
|
+
modelKey: string;
|
|
220
|
+
modelAlias: string | null;
|
|
221
|
+
vendorKey: string;
|
|
222
|
+
enabled: boolean;
|
|
223
|
+
}, {
|
|
224
|
+
kind: string;
|
|
225
|
+
label: string;
|
|
226
|
+
modelKey: string;
|
|
227
|
+
modelAlias: string | null;
|
|
228
|
+
vendorKey: string;
|
|
229
|
+
enabled: boolean;
|
|
230
|
+
}>, "many">;
|
|
231
|
+
audio: z.ZodArray<z.ZodObject<{
|
|
232
|
+
modelKey: z.ZodString;
|
|
233
|
+
modelAlias: z.ZodNullable<z.ZodString>;
|
|
234
|
+
label: z.ZodString;
|
|
235
|
+
vendorKey: z.ZodString;
|
|
236
|
+
kind: z.ZodString;
|
|
237
|
+
enabled: z.ZodBoolean;
|
|
238
|
+
}, "strip", z.ZodTypeAny, {
|
|
239
|
+
kind: string;
|
|
240
|
+
label: string;
|
|
241
|
+
modelKey: string;
|
|
242
|
+
modelAlias: string | null;
|
|
243
|
+
vendorKey: string;
|
|
244
|
+
enabled: boolean;
|
|
245
|
+
}, {
|
|
246
|
+
kind: string;
|
|
247
|
+
label: string;
|
|
248
|
+
modelKey: string;
|
|
249
|
+
modelAlias: string | null;
|
|
250
|
+
vendorKey: string;
|
|
251
|
+
enabled: boolean;
|
|
252
|
+
}>, "many">;
|
|
253
|
+
}, "strip", z.ZodTypeAny, {
|
|
254
|
+
text: {
|
|
255
|
+
kind: string;
|
|
256
|
+
label: string;
|
|
257
|
+
modelKey: string;
|
|
258
|
+
modelAlias: string | null;
|
|
259
|
+
vendorKey: string;
|
|
260
|
+
enabled: boolean;
|
|
261
|
+
}[];
|
|
262
|
+
image: {
|
|
263
|
+
kind: string;
|
|
264
|
+
label: string;
|
|
265
|
+
modelKey: string;
|
|
266
|
+
modelAlias: string | null;
|
|
267
|
+
vendorKey: string;
|
|
268
|
+
enabled: boolean;
|
|
269
|
+
}[];
|
|
270
|
+
video: {
|
|
271
|
+
kind: string;
|
|
272
|
+
label: string;
|
|
273
|
+
modelKey: string;
|
|
274
|
+
modelAlias: string | null;
|
|
275
|
+
vendorKey: string;
|
|
276
|
+
enabled: boolean;
|
|
277
|
+
}[];
|
|
278
|
+
audio: {
|
|
279
|
+
kind: string;
|
|
280
|
+
label: string;
|
|
281
|
+
modelKey: string;
|
|
282
|
+
modelAlias: string | null;
|
|
283
|
+
vendorKey: string;
|
|
284
|
+
enabled: boolean;
|
|
285
|
+
}[];
|
|
286
|
+
}, {
|
|
287
|
+
text: {
|
|
288
|
+
kind: string;
|
|
289
|
+
label: string;
|
|
290
|
+
modelKey: string;
|
|
291
|
+
modelAlias: string | null;
|
|
292
|
+
vendorKey: string;
|
|
293
|
+
enabled: boolean;
|
|
294
|
+
}[];
|
|
295
|
+
image: {
|
|
296
|
+
kind: string;
|
|
297
|
+
label: string;
|
|
298
|
+
modelKey: string;
|
|
299
|
+
modelAlias: string | null;
|
|
300
|
+
vendorKey: string;
|
|
301
|
+
enabled: boolean;
|
|
302
|
+
}[];
|
|
303
|
+
video: {
|
|
304
|
+
kind: string;
|
|
305
|
+
label: string;
|
|
306
|
+
modelKey: string;
|
|
307
|
+
modelAlias: string | null;
|
|
308
|
+
vendorKey: string;
|
|
309
|
+
enabled: boolean;
|
|
310
|
+
}[];
|
|
311
|
+
audio: {
|
|
312
|
+
kind: string;
|
|
313
|
+
label: string;
|
|
314
|
+
modelKey: string;
|
|
315
|
+
modelAlias: string | null;
|
|
316
|
+
vendorKey: string;
|
|
317
|
+
enabled: boolean;
|
|
318
|
+
}[];
|
|
319
|
+
}>;
|
|
320
|
+
capabilityErrors: z.ZodArray<z.ZodString, "many">;
|
|
321
|
+
}, "strip", z.ZodTypeAny, {
|
|
322
|
+
nodeContracts: {
|
|
323
|
+
kind: string;
|
|
324
|
+
runtimeKinds: string[];
|
|
325
|
+
label: string;
|
|
326
|
+
category: string;
|
|
327
|
+
features: string[];
|
|
328
|
+
handles: {
|
|
329
|
+
dynamic: boolean;
|
|
330
|
+
targets: {
|
|
331
|
+
id: string;
|
|
332
|
+
type: string;
|
|
333
|
+
position?: string | undefined;
|
|
334
|
+
}[];
|
|
335
|
+
sources: {
|
|
336
|
+
id: string;
|
|
337
|
+
type: string;
|
|
338
|
+
position?: string | undefined;
|
|
339
|
+
}[];
|
|
340
|
+
};
|
|
341
|
+
promptEditor: {
|
|
342
|
+
purpose: string;
|
|
343
|
+
placeholder: string;
|
|
344
|
+
presetType: "text" | "image" | "video" | null;
|
|
345
|
+
supportsMentions: boolean;
|
|
346
|
+
upstreamBehavior: string;
|
|
347
|
+
previewBoundary: string;
|
|
348
|
+
} | null;
|
|
349
|
+
}[];
|
|
350
|
+
nodePresets: {
|
|
351
|
+
id: string;
|
|
352
|
+
updatedAt: string;
|
|
353
|
+
type: "text" | "image" | "video";
|
|
354
|
+
title: string;
|
|
355
|
+
prompt: string;
|
|
356
|
+
description: string | null;
|
|
357
|
+
scope: "base" | "user";
|
|
358
|
+
}[];
|
|
359
|
+
enabledModels: {
|
|
360
|
+
text: {
|
|
361
|
+
kind: string;
|
|
362
|
+
label: string;
|
|
363
|
+
modelKey: string;
|
|
364
|
+
modelAlias: string | null;
|
|
365
|
+
vendorKey: string;
|
|
366
|
+
enabled: boolean;
|
|
367
|
+
}[];
|
|
368
|
+
image: {
|
|
369
|
+
kind: string;
|
|
370
|
+
label: string;
|
|
371
|
+
modelKey: string;
|
|
372
|
+
modelAlias: string | null;
|
|
373
|
+
vendorKey: string;
|
|
374
|
+
enabled: boolean;
|
|
375
|
+
}[];
|
|
376
|
+
video: {
|
|
377
|
+
kind: string;
|
|
378
|
+
label: string;
|
|
379
|
+
modelKey: string;
|
|
380
|
+
modelAlias: string | null;
|
|
381
|
+
vendorKey: string;
|
|
382
|
+
enabled: boolean;
|
|
383
|
+
}[];
|
|
384
|
+
audio: {
|
|
385
|
+
kind: string;
|
|
386
|
+
label: string;
|
|
387
|
+
modelKey: string;
|
|
388
|
+
modelAlias: string | null;
|
|
389
|
+
vendorKey: string;
|
|
390
|
+
enabled: boolean;
|
|
391
|
+
}[];
|
|
392
|
+
};
|
|
393
|
+
capabilityErrors: string[];
|
|
394
|
+
}, {
|
|
395
|
+
nodeContracts: {
|
|
396
|
+
kind: string;
|
|
397
|
+
runtimeKinds: string[];
|
|
398
|
+
label: string;
|
|
399
|
+
category: string;
|
|
400
|
+
features: string[];
|
|
401
|
+
handles: {
|
|
402
|
+
dynamic: boolean;
|
|
403
|
+
targets: {
|
|
404
|
+
id: string;
|
|
405
|
+
type: string;
|
|
406
|
+
position?: string | undefined;
|
|
407
|
+
}[];
|
|
408
|
+
sources: {
|
|
409
|
+
id: string;
|
|
410
|
+
type: string;
|
|
411
|
+
position?: string | undefined;
|
|
412
|
+
}[];
|
|
413
|
+
};
|
|
414
|
+
promptEditor: {
|
|
415
|
+
purpose: string;
|
|
416
|
+
placeholder: string;
|
|
417
|
+
presetType: "text" | "image" | "video" | null;
|
|
418
|
+
supportsMentions: boolean;
|
|
419
|
+
upstreamBehavior: string;
|
|
420
|
+
previewBoundary: string;
|
|
421
|
+
} | null;
|
|
422
|
+
}[];
|
|
423
|
+
nodePresets: {
|
|
424
|
+
id: string;
|
|
425
|
+
updatedAt: string;
|
|
426
|
+
type: "text" | "image" | "video";
|
|
427
|
+
title: string;
|
|
428
|
+
prompt: string;
|
|
429
|
+
description: string | null;
|
|
430
|
+
scope: "base" | "user";
|
|
431
|
+
}[];
|
|
432
|
+
enabledModels: {
|
|
433
|
+
text: {
|
|
434
|
+
kind: string;
|
|
435
|
+
label: string;
|
|
436
|
+
modelKey: string;
|
|
437
|
+
modelAlias: string | null;
|
|
438
|
+
vendorKey: string;
|
|
439
|
+
enabled: boolean;
|
|
440
|
+
}[];
|
|
441
|
+
image: {
|
|
442
|
+
kind: string;
|
|
443
|
+
label: string;
|
|
444
|
+
modelKey: string;
|
|
445
|
+
modelAlias: string | null;
|
|
446
|
+
vendorKey: string;
|
|
447
|
+
enabled: boolean;
|
|
448
|
+
}[];
|
|
449
|
+
video: {
|
|
450
|
+
kind: string;
|
|
451
|
+
label: string;
|
|
452
|
+
modelKey: string;
|
|
453
|
+
modelAlias: string | null;
|
|
454
|
+
vendorKey: string;
|
|
455
|
+
enabled: boolean;
|
|
456
|
+
}[];
|
|
457
|
+
audio: {
|
|
458
|
+
kind: string;
|
|
459
|
+
label: string;
|
|
460
|
+
modelKey: string;
|
|
461
|
+
modelAlias: string | null;
|
|
462
|
+
vendorKey: string;
|
|
463
|
+
enabled: boolean;
|
|
464
|
+
}[];
|
|
465
|
+
};
|
|
466
|
+
capabilityErrors: string[];
|
|
467
|
+
}>;
|
|
468
|
+
export declare const localAgentToolNames: readonly ["tapcanvas_flow_get", "tapcanvas_node_context_bundle_get", "tapcanvas_node_presets_get", "tapcanvas_node_prompt_preview", "tapcanvas_create_generation_flow", "tapcanvas_flow_patch"];
|
|
3
469
|
export type LocalAgentToolName = (typeof localAgentToolNames)[number];
|
|
4
470
|
export declare const localAgentToolInputSchemas: {
|
|
5
471
|
tapcanvas_flow_get: z.ZodObject<{}, "passthrough", z.ZodTypeAny, z.objectOutputType<{}, z.ZodTypeAny, "passthrough">, z.objectInputType<{}, z.ZodTypeAny, "passthrough">>;
|
|
472
|
+
tapcanvas_node_context_bundle_get: z.ZodObject<{
|
|
473
|
+
nodeId: z.ZodOptional<z.ZodString>;
|
|
474
|
+
}, "strip", z.ZodTypeAny, {
|
|
475
|
+
nodeId?: string | undefined;
|
|
476
|
+
}, {
|
|
477
|
+
nodeId?: string | undefined;
|
|
478
|
+
}>;
|
|
479
|
+
tapcanvas_node_presets_get: z.ZodObject<{
|
|
480
|
+
id: z.ZodOptional<z.ZodString>;
|
|
481
|
+
type: z.ZodOptional<z.ZodEnum<["text", "image", "video"]>>;
|
|
482
|
+
}, "strip", z.ZodTypeAny, {
|
|
483
|
+
id?: string | undefined;
|
|
484
|
+
type?: "text" | "image" | "video" | undefined;
|
|
485
|
+
}, {
|
|
486
|
+
id?: string | undefined;
|
|
487
|
+
type?: "text" | "image" | "video" | undefined;
|
|
488
|
+
}>;
|
|
489
|
+
tapcanvas_node_prompt_preview: z.ZodObject<{
|
|
490
|
+
nodeId: z.ZodOptional<z.ZodString>;
|
|
491
|
+
}, "strip", z.ZodTypeAny, {
|
|
492
|
+
nodeId?: string | undefined;
|
|
493
|
+
}, {
|
|
494
|
+
nodeId?: string | undefined;
|
|
495
|
+
}>;
|
|
6
496
|
tapcanvas_create_generation_flow: z.ZodObject<{
|
|
7
497
|
kind: z.ZodString;
|
|
8
498
|
prompt: z.ZodString;
|
package/dist/schemas.js
CHANGED
|
@@ -4,6 +4,60 @@ const finitePositionSchema = z.object({
|
|
|
4
4
|
x: z.number().finite(),
|
|
5
5
|
y: z.number().finite(),
|
|
6
6
|
});
|
|
7
|
+
const nodeHandleFactSchema = z.object({
|
|
8
|
+
id: z.string().min(1),
|
|
9
|
+
type: z.string().min(1),
|
|
10
|
+
position: z.string().min(1).optional(),
|
|
11
|
+
});
|
|
12
|
+
const promptEditorContractSchema = z.object({
|
|
13
|
+
purpose: z.string(),
|
|
14
|
+
placeholder: z.string(),
|
|
15
|
+
presetType: z.enum(["text", "image", "video"]).nullable(),
|
|
16
|
+
supportsMentions: z.boolean(),
|
|
17
|
+
upstreamBehavior: z.string(),
|
|
18
|
+
previewBoundary: z.string(),
|
|
19
|
+
});
|
|
20
|
+
const nodeContractSchema = z.object({
|
|
21
|
+
kind: z.string().min(1),
|
|
22
|
+
runtimeKinds: z.array(z.string().min(1)),
|
|
23
|
+
label: z.string().min(1),
|
|
24
|
+
category: z.string().min(1),
|
|
25
|
+
features: z.array(z.string()),
|
|
26
|
+
handles: z.object({
|
|
27
|
+
dynamic: z.boolean(),
|
|
28
|
+
targets: z.array(nodeHandleFactSchema),
|
|
29
|
+
sources: z.array(nodeHandleFactSchema),
|
|
30
|
+
}),
|
|
31
|
+
promptEditor: promptEditorContractSchema.nullable(),
|
|
32
|
+
});
|
|
33
|
+
const nodePresetSchema = z.object({
|
|
34
|
+
id: z.string().min(1),
|
|
35
|
+
title: z.string().min(1),
|
|
36
|
+
type: z.enum(["text", "image", "video"]),
|
|
37
|
+
prompt: z.string(),
|
|
38
|
+
description: z.string().nullable(),
|
|
39
|
+
scope: z.enum(["base", "user"]),
|
|
40
|
+
updatedAt: z.string().min(1),
|
|
41
|
+
});
|
|
42
|
+
const modelFactSchema = z.object({
|
|
43
|
+
modelKey: z.string().min(1),
|
|
44
|
+
modelAlias: z.string().min(1).nullable(),
|
|
45
|
+
label: z.string().min(1),
|
|
46
|
+
vendorKey: z.string().min(1),
|
|
47
|
+
kind: z.string().min(1),
|
|
48
|
+
enabled: z.boolean(),
|
|
49
|
+
});
|
|
50
|
+
export const localCanvasCapabilitiesSchema = z.object({
|
|
51
|
+
nodeContracts: z.array(nodeContractSchema),
|
|
52
|
+
nodePresets: z.array(nodePresetSchema),
|
|
53
|
+
enabledModels: z.object({
|
|
54
|
+
text: z.array(modelFactSchema),
|
|
55
|
+
image: z.array(modelFactSchema),
|
|
56
|
+
video: z.array(modelFactSchema),
|
|
57
|
+
audio: z.array(modelFactSchema),
|
|
58
|
+
}),
|
|
59
|
+
capabilityErrors: z.array(z.string()),
|
|
60
|
+
});
|
|
7
61
|
const createNodeSchema = z
|
|
8
62
|
.object({
|
|
9
63
|
id: z.string().min(1),
|
|
@@ -50,13 +104,26 @@ const createGenerationFlowSchema = z.object({
|
|
|
50
104
|
groupId: z.string().min(1).optional(),
|
|
51
105
|
nodeId: z.string().min(1).optional(),
|
|
52
106
|
});
|
|
107
|
+
const nodeIdSchema = z.object({
|
|
108
|
+
nodeId: z.string().min(1).optional(),
|
|
109
|
+
});
|
|
110
|
+
const nodePresetsGetSchema = z.object({
|
|
111
|
+
id: z.string().min(1).optional(),
|
|
112
|
+
type: z.enum(["text", "image", "video"]).optional(),
|
|
113
|
+
});
|
|
53
114
|
export const localAgentToolNames = [
|
|
54
115
|
"tapcanvas_flow_get",
|
|
116
|
+
"tapcanvas_node_context_bundle_get",
|
|
117
|
+
"tapcanvas_node_presets_get",
|
|
118
|
+
"tapcanvas_node_prompt_preview",
|
|
55
119
|
"tapcanvas_create_generation_flow",
|
|
56
120
|
"tapcanvas_flow_patch",
|
|
57
121
|
];
|
|
58
122
|
export const localAgentToolInputSchemas = {
|
|
59
123
|
tapcanvas_flow_get: z.object({}).passthrough(),
|
|
124
|
+
tapcanvas_node_context_bundle_get: nodeIdSchema,
|
|
125
|
+
tapcanvas_node_presets_get: nodePresetsGetSchema,
|
|
126
|
+
tapcanvas_node_prompt_preview: nodeIdSchema,
|
|
60
127
|
tapcanvas_create_generation_flow: createGenerationFlowSchema,
|
|
61
128
|
tapcanvas_flow_patch: z.object({
|
|
62
129
|
allowOverwrite: z.boolean().optional(),
|
|
@@ -70,6 +137,9 @@ export const localAgentToolInputSchemas = {
|
|
|
70
137
|
};
|
|
71
138
|
export const localAgentToolDescriptions = {
|
|
72
139
|
tapcanvas_flow_get: "Read the live QCanvas graph from the connected browser tab. Use this before any patch.",
|
|
140
|
+
tapcanvas_node_context_bundle_get: "Read one live node with its upstream/downstream graph neighbors, node contract, bound prompt preset, and enabled model catalog from the browser snapshot. nodeId may be omitted only when exactly one node is selected. Unknown nodes, contracts, or bound presets fail explicitly.",
|
|
141
|
+
tapcanvas_node_presets_get: "Read live node prompt presets cached by the connected browser. Without id it returns compact metadata only; request an exact id to read the full prompt. Optionally filter by type. An unknown requested id fails explicitly.",
|
|
142
|
+
tapcanvas_node_prompt_preview: "Inspect a node's current prompt, bound preset, exact prompt divergence, and structural readiness from the live browser snapshot. nodeId may be omitted only when exactly one node is selected. This is factual inspection, not a simulation of the generation runner's compiled prompt.",
|
|
73
143
|
tapcanvas_create_generation_flow: "Create one runnable taskNode for a generation intent, auto-placed next to existing nodes and auto-wired to referenceNodeIds via real edges + assetInputs/referenceImages carrying those nodes' current output image. This only creates and wires the node; it does NOT submit generation. Call tapcanvas_flow_patch's browser-side runner (or ask the user to run the node) to actually generate.",
|
|
74
144
|
tapcanvas_flow_patch: "Request a browser-confirmed QCanvas graph patch. The browser must approve the write before it is applied.",
|
|
75
145
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,54 @@ export type QCanvasViewport = {
|
|
|
4
4
|
y: number;
|
|
5
5
|
zoom: number;
|
|
6
6
|
};
|
|
7
|
+
export type QCanvasNodeHandleFact = {
|
|
8
|
+
id: string;
|
|
9
|
+
type: string;
|
|
10
|
+
position?: string;
|
|
11
|
+
};
|
|
12
|
+
export type QCanvasNodeContract = {
|
|
13
|
+
kind: string;
|
|
14
|
+
runtimeKinds: string[];
|
|
15
|
+
label: string;
|
|
16
|
+
category: string;
|
|
17
|
+
features: string[];
|
|
18
|
+
handles: {
|
|
19
|
+
dynamic: boolean;
|
|
20
|
+
targets: QCanvasNodeHandleFact[];
|
|
21
|
+
sources: QCanvasNodeHandleFact[];
|
|
22
|
+
};
|
|
23
|
+
promptEditor: {
|
|
24
|
+
purpose: string;
|
|
25
|
+
placeholder: string;
|
|
26
|
+
presetType: "text" | "image" | "video" | null;
|
|
27
|
+
supportsMentions: boolean;
|
|
28
|
+
upstreamBehavior: string;
|
|
29
|
+
previewBoundary: string;
|
|
30
|
+
} | null;
|
|
31
|
+
};
|
|
32
|
+
export type QCanvasNodePreset = {
|
|
33
|
+
id: string;
|
|
34
|
+
title: string;
|
|
35
|
+
type: "text" | "image" | "video";
|
|
36
|
+
prompt: string;
|
|
37
|
+
description: string | null;
|
|
38
|
+
scope: "base" | "user";
|
|
39
|
+
updatedAt: string;
|
|
40
|
+
};
|
|
41
|
+
export type QCanvasModelFact = {
|
|
42
|
+
modelKey: string;
|
|
43
|
+
modelAlias: string | null;
|
|
44
|
+
label: string;
|
|
45
|
+
vendorKey: string;
|
|
46
|
+
kind: string;
|
|
47
|
+
enabled: boolean;
|
|
48
|
+
};
|
|
49
|
+
export type QCanvasEnabledModels = {
|
|
50
|
+
text: QCanvasModelFact[];
|
|
51
|
+
image: QCanvasModelFact[];
|
|
52
|
+
video: QCanvasModelFact[];
|
|
53
|
+
audio: QCanvasModelFact[];
|
|
54
|
+
};
|
|
7
55
|
export type QCanvasSnapshot = {
|
|
8
56
|
clientId: string;
|
|
9
57
|
url: string | null;
|
|
@@ -15,6 +63,10 @@ export type QCanvasSnapshot = {
|
|
|
15
63
|
edges: unknown[];
|
|
16
64
|
selectedNodeIds: string[];
|
|
17
65
|
viewport: QCanvasViewport | null;
|
|
66
|
+
nodeContracts: QCanvasNodeContract[];
|
|
67
|
+
nodePresets: QCanvasNodePreset[];
|
|
68
|
+
enabledModels: QCanvasEnabledModels;
|
|
69
|
+
capabilityErrors: string[];
|
|
18
70
|
updatedAt: string;
|
|
19
71
|
};
|
|
20
72
|
export type LocalToolResultEnvelope = {
|