@mawaru/sdk 0.13.0 → 0.15.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/_schemas/ai-manifest.test.ts +54 -59
- package/_schemas/ai-manifest.ts +38 -23
- package/_schemas/duplicate-selection.test.ts +20 -0
- package/_schemas/duplicate-selection.ts +18 -2
- package/_schemas/extract-component.test.ts +201 -24
- package/_schemas/extract-component.ts +137 -54
- package/_schemas/graph.test.ts +95 -72
- package/_schemas/graph.ts +22 -29
- package/_schemas/node.ts +4 -0
- package/_schemas/port-spec.test.ts +120 -129
- package/_schemas/port-spec.ts +76 -111
- package/dist/_schemas/ai-manifest.d.ts +12 -1
- package/dist/_schemas/ai-manifest.js +35 -20
- package/dist/_schemas/duplicate-selection.js +18 -2
- package/dist/_schemas/extract-component.d.ts +4 -3
- package/dist/_schemas/extract-component.js +105 -38
- package/dist/_schemas/graph.d.ts +18 -0
- package/dist/_schemas/graph.js +21 -26
- package/dist/_schemas/node.d.ts +1 -0
- package/dist/_schemas/node.js +4 -0
- package/dist/_schemas/port-spec.d.ts +2 -5
- package/dist/_schemas/port-spec.js +63 -94
- package/dist/typegen.js +6 -6
- package/docs/development.md +5 -1
- package/package.json +2 -3
- package/skills/create-loop/SKILL.md +1 -1
- package/templates/CLAUDE.md +1 -1
- package/templates/echo/main.ts +1 -1
- package/templates/mawaru-runner.yml +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export const analyzeExtractSelection = (nodes, connections, selectedIds) => {
|
|
2
2
|
const selected = new Set(selectedIds);
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
// 1ノードからコンポーネント化できる(グループ化の「2つ以上」はエディタ側の
|
|
4
|
+
// 有効条件。コンポーネントの設定値(refs)と1ノードのコンポーネント化.md)
|
|
5
|
+
if (selected.size < 1) {
|
|
6
|
+
return { ok: false, reason: "ノードを選択してください" };
|
|
5
7
|
}
|
|
6
8
|
const selectedNodes = nodes.filter((n) => selected.has(n.id));
|
|
7
9
|
const flow = selectedNodes.find((n) => n.kind === "start" || n.kind === "end");
|
|
@@ -12,6 +14,7 @@ export const analyzeExtractSelection = (nodes, connections, selectedIds) => {
|
|
|
12
14
|
};
|
|
13
15
|
}
|
|
14
16
|
const nodeIdByPortId = new Map(nodes.flatMap((n) => n.ports.map((p) => [p.id, n.id])));
|
|
17
|
+
const portById = new Map(nodes.flatMap((n) => n.ports.map((p) => [p.id, p])));
|
|
15
18
|
const internal = [];
|
|
16
19
|
const inbound = [];
|
|
17
20
|
const outbound = [];
|
|
@@ -25,31 +28,33 @@ export const analyzeExtractSelection = (nodes, connections, selectedIds) => {
|
|
|
25
28
|
else if (fromIn && !toIn)
|
|
26
29
|
outbound.push(conn);
|
|
27
30
|
}
|
|
28
|
-
|
|
29
|
-
|
|
31
|
+
// 入口ポート(重複排除・出現順)。条件は「全入口の型が同一」だけ:
|
|
32
|
+
// 起点(どの外部ポートから入るか)は見ない。ノードが前のノードを知らないのと
|
|
33
|
+
// 同じ思想を境界にも適用する(グループ化=入口の合流の宣言)
|
|
34
|
+
const entryPortIds = [...new Set(inbound.map((c) => c.to_port_id))];
|
|
35
|
+
const entrySchemas = new Set(entryPortIds.map((id) => JSON.stringify(portById.get(id)?.schema ?? {})));
|
|
36
|
+
if (entrySchemas.size > 1) {
|
|
30
37
|
return {
|
|
31
38
|
ok: false,
|
|
32
|
-
reason: "
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const exitPortIds = new Set(outbound.map((c) => c.from_port_id));
|
|
36
|
-
if (exitPortIds.size > 1) {
|
|
37
|
-
return {
|
|
38
|
-
ok: false,
|
|
39
|
-
reason: "外部への出力が複数のポートから出ています(出口は1つにしてください)",
|
|
39
|
+
reason: "入口の型が揃っていません(同じ型の入口だけを1つのグループにまとめられます)",
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
// 出口は無条件(出発ポートごとに end の in = component の out になる)
|
|
43
|
+
const exitPortIds = [...new Set(outbound.map((c) => c.from_port_id))];
|
|
42
44
|
return {
|
|
43
45
|
ok: true,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
entryPortIds,
|
|
47
|
+
exitPortIds,
|
|
46
48
|
internal,
|
|
47
49
|
inbound,
|
|
48
50
|
outbound,
|
|
49
51
|
};
|
|
50
52
|
};
|
|
53
|
+
// 出口が1つのときの component out キー(既存コンポーネントとの互換。
|
|
54
|
+
// end の in が1つの loop は out main の1口として振る舞う)
|
|
55
|
+
const SINGLE_OUT_KEY = "main";
|
|
51
56
|
export const buildExtractPlan = (params) => {
|
|
52
|
-
const { nodes, connections, selectedIds, name, componentLoopId, childStartOutPortId,
|
|
57
|
+
const { nodes, connections, selectedIds, name, componentLoopId, childStartOutPortId, childEnd, newId, } = params;
|
|
53
58
|
const analysis = analyzeExtractSelection(nodes, connections, selectedIds);
|
|
54
59
|
if (!analysis.ok)
|
|
55
60
|
return analysis;
|
|
@@ -73,26 +78,60 @@ export const buildExtractPlan = (params) => {
|
|
|
73
78
|
}),
|
|
74
79
|
};
|
|
75
80
|
});
|
|
81
|
+
const portById = new Map(nodes.flatMap((n) => n.ports.map((p) => [p.id, p])));
|
|
76
82
|
const mapConn = (conn) => ({
|
|
77
83
|
from_port_id: portIdMap.get(conn.from_port_id) ?? conn.from_port_id,
|
|
78
84
|
to_port_id: portIdMap.get(conn.to_port_id) ?? conn.to_port_id,
|
|
79
85
|
});
|
|
80
86
|
const childConnections = analysis.internal.map(mapConn);
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
from_port_id: childStartOutPortId,
|
|
88
|
-
to_port_id: entryIn.id,
|
|
89
|
-
});
|
|
90
|
-
}
|
|
87
|
+
// 境界の配線(入口):start.main → 各入口ポート(複数なら fan-out)
|
|
88
|
+
for (const entryPortId of analysis.entryPortIds) {
|
|
89
|
+
childConnections.push({
|
|
90
|
+
from_port_id: childStartOutPortId,
|
|
91
|
+
to_port_id: portIdMap.get(entryPortId) ?? entryPortId,
|
|
92
|
+
});
|
|
91
93
|
}
|
|
92
|
-
|
|
94
|
+
// 境界の配線(出口):出発ポートごとに end の in を1つ充てる。
|
|
95
|
+
// 1つ目は seed 済みの in を使い(名前を出口ポート名へ)、2つ目以降は追加する。
|
|
96
|
+
// component の out はこの in たちの射影(出口1つは互換のため main キー)
|
|
97
|
+
const seededEndIns = childEnd.ports.filter((p) => p.io === "in");
|
|
98
|
+
const endInKeys = new Set(seededEndIns.map((p) => p.key));
|
|
99
|
+
const nextEndInKey = () => {
|
|
100
|
+
for (let i = 2;; i++) {
|
|
101
|
+
const key = `in_${i}`;
|
|
102
|
+
if (!endInKeys.has(key)) {
|
|
103
|
+
endInKeys.add(key);
|
|
104
|
+
return key;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const exits = analysis.exitPortIds.map((exitPortId, i) => {
|
|
109
|
+
const exitName = portById.get(exitPortId)?.name ?? "出力";
|
|
110
|
+
const seeded = i === 0 ? seededEndIns[0] : undefined;
|
|
111
|
+
const endIn = seeded
|
|
112
|
+
? { ...seeded, name: exitName }
|
|
113
|
+
: {
|
|
114
|
+
id: newId(),
|
|
115
|
+
io: "in",
|
|
116
|
+
key: nextEndInKey(),
|
|
117
|
+
name: exitName,
|
|
118
|
+
schema: {},
|
|
119
|
+
};
|
|
120
|
+
return { exitPortId, endIn };
|
|
121
|
+
});
|
|
122
|
+
const resolvedChildEnd = exits.length === 0
|
|
123
|
+
? childEnd
|
|
124
|
+
: {
|
|
125
|
+
...childEnd,
|
|
126
|
+
ports: [
|
|
127
|
+
...exits.map((e) => e.endIn),
|
|
128
|
+
...childEnd.ports.filter((p) => p.io !== "in"),
|
|
129
|
+
],
|
|
130
|
+
};
|
|
131
|
+
for (const exit of exits) {
|
|
93
132
|
childConnections.push({
|
|
94
|
-
from_port_id: portIdMap.get(
|
|
95
|
-
to_port_id:
|
|
133
|
+
from_port_id: portIdMap.get(exit.exitPortId) ?? exit.exitPortId,
|
|
134
|
+
to_port_id: exit.endIn.id,
|
|
96
135
|
});
|
|
97
136
|
}
|
|
98
137
|
// 親に置くノード(位置は選択の重心。ポートスキーマは保存時にサーバが契約から焼き込む)
|
|
@@ -103,13 +142,37 @@ export const buildExtractPlan = (params) => {
|
|
|
103
142
|
y: selectedNodes.reduce((sum, n) => sum + n.position_y, 0) /
|
|
104
143
|
selectedNodes.length,
|
|
105
144
|
};
|
|
145
|
+
// out は出口ごと(キーは end の in の射影。出口1つ以下は互換の main 1口)
|
|
146
|
+
const groupOuts = exits.length <= 1
|
|
147
|
+
? [
|
|
148
|
+
{
|
|
149
|
+
exitPortId: exits[0]?.exitPortId ?? null,
|
|
150
|
+
port: {
|
|
151
|
+
id: newId(),
|
|
152
|
+
io: "out",
|
|
153
|
+
key: SINGLE_OUT_KEY,
|
|
154
|
+
name: exits[0] ? exits[0].endIn.name : "出力",
|
|
155
|
+
schema: {},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
]
|
|
159
|
+
: exits.map((e) => ({
|
|
160
|
+
exitPortId: e.exitPortId,
|
|
161
|
+
port: {
|
|
162
|
+
id: newId(),
|
|
163
|
+
io: "out",
|
|
164
|
+
key: e.endIn.key,
|
|
165
|
+
name: e.endIn.name,
|
|
166
|
+
schema: {},
|
|
167
|
+
},
|
|
168
|
+
}));
|
|
106
169
|
const groupNode = {
|
|
107
170
|
id: newId(),
|
|
108
171
|
kind: "component",
|
|
109
172
|
name,
|
|
110
173
|
ports: [
|
|
111
174
|
{ id: newId(), io: "in", key: "in", name: "入力", schema: {} },
|
|
112
|
-
|
|
175
|
+
...groupOuts.map((o) => o.port),
|
|
113
176
|
],
|
|
114
177
|
hooks: [],
|
|
115
178
|
position_x: centroid.x,
|
|
@@ -117,28 +180,32 @@ export const buildExtractPlan = (params) => {
|
|
|
117
180
|
component: { component_loop_id: componentLoopId },
|
|
118
181
|
};
|
|
119
182
|
const groupIn = groupNode.ports.find((p) => p.io === "in");
|
|
120
|
-
|
|
121
|
-
if (!groupIn || !groupMain) {
|
|
183
|
+
if (!groupIn) {
|
|
122
184
|
return { ok: false, reason: "groupNode のポート生成に失敗しました" };
|
|
123
185
|
}
|
|
124
|
-
|
|
186
|
+
const groupOutByExitPortId = new Map(groupOuts
|
|
187
|
+
.filter((o) => o.exitPortId !== null)
|
|
188
|
+
.map((o) => [o.exitPortId, o.port]));
|
|
189
|
+
// 親の置換:選択関連の線(内部・境界)を除き、境界を group の in/対応する out へ繋ぎ直す
|
|
125
190
|
const removed = new Set([...analysis.internal, ...analysis.inbound, ...analysis.outbound].map((c) => `${c.from_port_id}→${c.to_port_id}`));
|
|
126
191
|
const parentConnections = [
|
|
127
192
|
...connections.filter((c) => !removed.has(`${c.from_port_id}→${c.to_port_id}`)),
|
|
128
|
-
|
|
129
|
-
|
|
193
|
+
// 同一起点から複数入口へ入っていた線は group.in への1本に畳む(重複線を作らない)
|
|
194
|
+
...[...new Set(analysis.inbound.map((c) => c.from_port_id))].map((from_port_id) => ({
|
|
195
|
+
from_port_id,
|
|
130
196
|
to_port_id: groupIn.id,
|
|
131
197
|
})),
|
|
132
|
-
...analysis.outbound.
|
|
133
|
-
|
|
134
|
-
to_port_id: c.to_port_id
|
|
135
|
-
})
|
|
198
|
+
...analysis.outbound.flatMap((c) => {
|
|
199
|
+
const out = groupOutByExitPortId.get(c.from_port_id);
|
|
200
|
+
return out ? [{ from_port_id: out.id, to_port_id: c.to_port_id }] : [];
|
|
201
|
+
}),
|
|
136
202
|
];
|
|
137
203
|
const parentNodes = [...nodes.filter((n) => !selected.has(n.id)), groupNode];
|
|
138
204
|
return {
|
|
139
205
|
ok: true,
|
|
140
206
|
childNodes,
|
|
141
207
|
childConnections,
|
|
208
|
+
childEnd: resolvedChildEnd,
|
|
142
209
|
groupNode,
|
|
143
210
|
parentNodes,
|
|
144
211
|
parentConnections,
|
package/dist/_schemas/graph.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ export declare const humanConfigSchema: z.ZodObject<{
|
|
|
67
67
|
export type HumanConfig = z.infer<typeof humanConfigSchema>;
|
|
68
68
|
export declare const componentConfigSchema: z.ZodObject<{
|
|
69
69
|
component_loop_id: z.ZodUUID;
|
|
70
|
+
refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
70
71
|
}, z.core.$strip>;
|
|
71
72
|
export type ComponentConfig = z.infer<typeof componentConfigSchema>;
|
|
72
73
|
export declare const graphNodeHookSchema: z.ZodObject<{
|
|
@@ -98,6 +99,7 @@ export declare const graphNodeSchema: z.ZodObject<{
|
|
|
98
99
|
key: z.ZodString;
|
|
99
100
|
name: z.ZodString;
|
|
100
101
|
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
102
|
+
resume: z.ZodOptional<z.ZodBoolean>;
|
|
101
103
|
side: z.ZodOptional<z.ZodEnum<{
|
|
102
104
|
top: "top";
|
|
103
105
|
right: "right";
|
|
@@ -149,12 +151,22 @@ export declare const graphNodeSchema: z.ZodObject<{
|
|
|
149
151
|
}, z.core.$strip>>;
|
|
150
152
|
component: z.ZodOptional<z.ZodObject<{
|
|
151
153
|
component_loop_id: z.ZodUUID;
|
|
154
|
+
refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
152
155
|
}, z.core.$strip>>;
|
|
153
156
|
}, z.core.$strip>;
|
|
154
157
|
export type GraphNode = z.infer<typeof graphNodeSchema>;
|
|
158
|
+
export declare const waypointSchema: z.ZodObject<{
|
|
159
|
+
x: z.ZodNumber;
|
|
160
|
+
y: z.ZodNumber;
|
|
161
|
+
}, z.core.$strip>;
|
|
162
|
+
export type Waypoint = z.infer<typeof waypointSchema>;
|
|
155
163
|
export declare const graphConnectionSchema: z.ZodObject<{
|
|
156
164
|
from_port_id: z.ZodUUID;
|
|
157
165
|
to_port_id: z.ZodUUID;
|
|
166
|
+
waypoints: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
167
|
+
x: z.ZodNumber;
|
|
168
|
+
y: z.ZodNumber;
|
|
169
|
+
}, z.core.$strip>>>;
|
|
158
170
|
}, z.core.$strip>;
|
|
159
171
|
export type GraphConnection = z.infer<typeof graphConnectionSchema>;
|
|
160
172
|
export declare const saveGraphBodySchema: z.ZodObject<{
|
|
@@ -179,6 +191,7 @@ export declare const saveGraphBodySchema: z.ZodObject<{
|
|
|
179
191
|
key: z.ZodString;
|
|
180
192
|
name: z.ZodString;
|
|
181
193
|
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
194
|
+
resume: z.ZodOptional<z.ZodBoolean>;
|
|
182
195
|
side: z.ZodOptional<z.ZodEnum<{
|
|
183
196
|
top: "top";
|
|
184
197
|
right: "right";
|
|
@@ -230,11 +243,16 @@ export declare const saveGraphBodySchema: z.ZodObject<{
|
|
|
230
243
|
}, z.core.$strip>>;
|
|
231
244
|
component: z.ZodOptional<z.ZodObject<{
|
|
232
245
|
component_loop_id: z.ZodUUID;
|
|
246
|
+
refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
233
247
|
}, z.core.$strip>>;
|
|
234
248
|
}, z.core.$strip>>;
|
|
235
249
|
connections: z.ZodArray<z.ZodObject<{
|
|
236
250
|
from_port_id: z.ZodUUID;
|
|
237
251
|
to_port_id: z.ZodUUID;
|
|
252
|
+
waypoints: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
253
|
+
x: z.ZodNumber;
|
|
254
|
+
y: z.ZodNumber;
|
|
255
|
+
}, z.core.$strip>>>;
|
|
238
256
|
}, z.core.$strip>>;
|
|
239
257
|
refs: z.ZodDefault<z.ZodObject<{
|
|
240
258
|
type: z.ZodLiteral<"object">;
|
package/dist/_schemas/graph.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { graphPortSchema, jsonSchemaSchema, nodeKindSchema, portKeySchema, } from "./node.js";
|
|
3
|
-
import {
|
|
3
|
+
import { hasInboundConflict, isSchemaSubset, kindPortsViolation, resolvePortSchemas, UNDECLARED_SCHEMA, } from "./port-spec.js";
|
|
4
4
|
// refs=ループ変数の袋(key→任意の JSON 値。上流出力の参照(refs).md)。
|
|
5
5
|
// run 開始時に定義から複製され、全 step に全量差し込まれる。
|
|
6
6
|
// key はポート key と同じ規則(^[a-z][a-z0-9_]*$)。実行系ボディ
|
|
@@ -156,6 +156,10 @@ export const humanConfigSchema = z.object({
|
|
|
156
156
|
// (docs/tasks/wip/コンポーネント(ループの部品化と再利用).md)
|
|
157
157
|
export const componentConfigSchema = z.object({
|
|
158
158
|
component_loop_id: z.uuid(),
|
|
159
|
+
// 配置の設定値:子 loop の refs 定義(default/required)への上書き。
|
|
160
|
+
// 子 run 起動時の起動時指定として渡る(省略=上書きなし。検証は保存 API と
|
|
161
|
+
// 起動時の mergeRefs が行う。コンポーネントの設定値(refs)と1ノードのコンポーネント化.md)
|
|
162
|
+
refs: z.record(z.string(), z.unknown()).optional(),
|
|
159
163
|
});
|
|
160
164
|
// ノードにアタッチされた IOHook(node_hooks に対応。アタッチできるのは program ノードのみ)。
|
|
161
165
|
// on_input / on_output / on_signal はアタッチ時に repo の config.json(hookManifestSchema)から
|
|
@@ -189,9 +193,15 @@ export const graphNodeSchema = z.object({
|
|
|
189
193
|
human: humanConfigSchema.optional(),
|
|
190
194
|
component: componentConfigSchema.optional(),
|
|
191
195
|
});
|
|
196
|
+
// 手動ルートの折れ点(loop 座標系の絶対座標)。connection の waypoints に並べる
|
|
197
|
+
// (docs/tasks/wip/connection線ルートの手動編集(ウェイポイント).md)
|
|
198
|
+
export const waypointSchema = z.object({ x: z.number(), y: z.number() });
|
|
192
199
|
export const graphConnectionSchema = z.object({
|
|
193
200
|
from_port_id: z.uuid(),
|
|
194
201
|
to_port_id: z.uuid(),
|
|
202
|
+
// 手動ルート:線の内部コーナーの座標列。省略=自動ルート。
|
|
203
|
+
// 「手動だが折れ無し」という状態は作らない(正規化で折れが消えたら省略へ戻す)ので最低1点
|
|
204
|
+
waypoints: z.array(waypointSchema).min(1).optional(),
|
|
195
205
|
});
|
|
196
206
|
// PUT /tenants/:tenantId/loops/:loopId/graph のボディ(グラフ一括保存)。
|
|
197
207
|
// refs=ループ変数の定義スキーマ(loops.refs)。回路と同じ「エディタの保存」で全置換する。
|
|
@@ -256,17 +266,16 @@ export const saveGraphBodySchema = z
|
|
|
256
266
|
outCount++;
|
|
257
267
|
});
|
|
258
268
|
// end は「in は1つ以上」(複数ルートを別々の in で集約する)。
|
|
259
|
-
// ai
|
|
269
|
+
// ai も1つ以上(in は自由ポート:追加・削除・key 自由)。他 kind は1つ固定。
|
|
260
270
|
// key の妥当性は kindPortsViolation が別途見る
|
|
261
|
-
const
|
|
262
|
-
const inCountOk = node.kind === "end" ? inCount >= 1 : inCount === expectIn;
|
|
271
|
+
const inCountOk = node.kind === "end" || node.kind === "ai" ? inCount >= 1 : inCount === 1;
|
|
263
272
|
if (!inCountOk) {
|
|
264
273
|
ctx.addIssue({
|
|
265
274
|
code: "custom",
|
|
266
275
|
path: ["nodes", i, "ports"],
|
|
267
|
-
message: node.kind === "end"
|
|
276
|
+
message: node.kind === "end" || node.kind === "ai"
|
|
268
277
|
? "in ポートが1つ以上必要です"
|
|
269
|
-
:
|
|
278
|
+
: "in ポートはちょうど1つ必要です",
|
|
270
279
|
});
|
|
271
280
|
}
|
|
272
281
|
if (outCount < 1) {
|
|
@@ -305,7 +314,8 @@ export const saveGraphBodySchema = z
|
|
|
305
314
|
message: "component ノードには component_loop_id が必要です",
|
|
306
315
|
});
|
|
307
316
|
}
|
|
308
|
-
// kind 別のポート構成(program 以外は数・key・io 固定。ポートのkind別仕様.md
|
|
317
|
+
// kind 別のポート構成(program 以外は数・key・io 固定。ポートのkind別仕様.md)。
|
|
318
|
+
// ポートのスキーマ自体は kind によらず任意の JSON Schema(形の強制はしない)
|
|
309
319
|
const violation = kindPortsViolation(node.kind, node.ports);
|
|
310
320
|
if (violation) {
|
|
311
321
|
ctx.addIssue({
|
|
@@ -314,19 +324,6 @@ export const saveGraphBodySchema = z
|
|
|
314
324
|
message: violation,
|
|
315
325
|
});
|
|
316
326
|
}
|
|
317
|
-
// AI の出力宣言は封筒型 { data: T, description: string }(T は任意の JSON Schema)。
|
|
318
|
-
// 複数出口でも全出口に強制する(どの出口も human に接続できる不変条件)
|
|
319
|
-
if (node.kind === "ai") {
|
|
320
|
-
node.ports.forEach((port, j) => {
|
|
321
|
-
if (port.io === "out" && !isAiEnvelopeSchema(port.schema)) {
|
|
322
|
-
ctx.addIssue({
|
|
323
|
-
code: "custom",
|
|
324
|
-
path: ["nodes", i, "ports", j],
|
|
325
|
-
message: "AI の出力は封筒型 { data, description } で宣言してください",
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
327
|
});
|
|
331
328
|
// 接続:両端がグラフ内の port を指し、from=out / to=in。
|
|
332
329
|
// 同じ out ポートから複数の in への fan-out は許す(並行分岐)。
|
|
@@ -407,16 +404,14 @@ export const saveGraphBodySchema = z
|
|
|
407
404
|
message: "終了ノードからは接続できません",
|
|
408
405
|
});
|
|
409
406
|
}
|
|
410
|
-
// 差し戻し(reject)と instruction
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
// ({ prompt, files } を受ける program へも繋げる)
|
|
407
|
+
// 差し戻し(reject)と instruction の組み合わせに kind の縛りも型固定も無い:
|
|
408
|
+
// reject の型はビューの宣言、instruction は ai の in の1つ(接続鏡映)でしかない
|
|
409
|
+
// → docs/tasks/wip/ポート型固定の撤去とセッション再開のポート属性化.md
|
|
414
410
|
// 型検証は kind によらず常に 伝播型(out) ⊆ 伝播型(in)。
|
|
415
411
|
// 未宣言は unknown=判断できないので通す(作りかけのグラフを保存できなくしない)。
|
|
416
412
|
// 接続鏡映する口(program / ai の in)とコピー宣言のビューは接続元の型を採用する
|
|
417
413
|
// ので自明に通り、実質の検証対象は宣言で固定された口
|
|
418
|
-
// (component
|
|
419
|
-
// → docs/tasks/wip/Humanの入出力を承認ビューの宣言に一本化する.md
|
|
414
|
+
// (component の契約・宣言のあるビューの human.in)になる
|
|
420
415
|
const fromSchema = resolved.get(conn.from_port_id) ?? from.schema;
|
|
421
416
|
const toSchema = resolved.get(conn.to_port_id) ?? to.schema;
|
|
422
417
|
if (!isSchemaSubset(fromSchema, toSchema)) {
|
package/dist/_schemas/node.d.ts
CHANGED
package/dist/_schemas/node.js
CHANGED
|
@@ -36,6 +36,10 @@ export const graphPortSchema = z
|
|
|
36
36
|
key: portKeySchema,
|
|
37
37
|
name: z.string().min(1),
|
|
38
38
|
schema: jsonSchemaSchema,
|
|
39
|
+
// in ポートの実行属性:true ならこの口から届いた実行は前回セッションを復元して
|
|
40
|
+
// 再開する(AI ノードで意味を持つ。key ではなくこの属性が再開を決める。
|
|
41
|
+
// ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
42
|
+
resume: z.boolean().optional(),
|
|
39
43
|
side: portSideSchema.optional(),
|
|
40
44
|
offset: z.number().min(0).max(1).optional(),
|
|
41
45
|
})
|
|
@@ -6,7 +6,6 @@ export declare const FILE_SCHEMA_MARKER = "x-mawaru-type";
|
|
|
6
6
|
export declare const FILE_SCHEMA_TYPE = "file";
|
|
7
7
|
export declare const fileSchema: () => JsonSchema;
|
|
8
8
|
export declare const isFileSchema: (schema: JsonSchema) => boolean;
|
|
9
|
-
export declare const AI_INSTRUCTION_PORT_KEY = "instruction";
|
|
10
9
|
export declare const instructionSchema: () => JsonSchema;
|
|
11
10
|
export declare const LIST_COLUMN_TYPES: readonly ["string", "number", "boolean", "date"];
|
|
12
11
|
export declare const listColumnTypeSchema: z.ZodEnum<{
|
|
@@ -45,9 +44,6 @@ export declare const dataFormatSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
45
44
|
export type DataFormat = z.infer<typeof dataFormatSchema>;
|
|
46
45
|
export declare const dataSchemaOf: (format: DataFormat) => JsonSchema;
|
|
47
46
|
export declare const dataFormatOf: (schema: JsonSchema) => DataFormat | null;
|
|
48
|
-
export declare const envelopeSchemaOf: (dataSchema: JsonSchema) => JsonSchema;
|
|
49
|
-
export declare const envelopeDataSchemaOf: (schema: JsonSchema) => JsonSchema | null;
|
|
50
|
-
export declare const isAiEnvelopeSchema: (schema: JsonSchema) => boolean;
|
|
51
47
|
export declare const fixedInKeysOf: (kind: string) => string[] | null;
|
|
52
48
|
export declare const manifestInKeysViolation: (kind: string, keys: string[]) => string | null;
|
|
53
49
|
export declare const kindPortsViolation: (kind: string, ports: Pick<GraphPort, "io" | "key">[]) => string | null;
|
|
@@ -67,7 +63,8 @@ export type DeclaredPortSchema = JsonSchema | typeof COPY_UPSTREAM_SCHEMA;
|
|
|
67
63
|
export type PropagationOptions<N> = {
|
|
68
64
|
viewPortSchemasOf?: (node: N) => {
|
|
69
65
|
in: DeclaredPortSchema;
|
|
70
|
-
|
|
66
|
+
approve: DeclaredPortSchema;
|
|
67
|
+
reject: JsonSchema;
|
|
71
68
|
} | null | undefined;
|
|
72
69
|
};
|
|
73
70
|
export declare const resolvePortSchemas: <N extends PropagationNode>(nodes: N[], connections: PortLink[], options?: PropagationOptions<N>) => Map<string, JsonSchema>;
|