@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.
@@ -1,7 +1,9 @@
1
1
  export const analyzeExtractSelection = (nodes, connections, selectedIds) => {
2
2
  const selected = new Set(selectedIds);
3
- if (selected.size < 2) {
4
- return { ok: false, reason: "2つ以上のノードを選択してください" };
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
- const entryNodeIds = new Set(inbound.map((c) => nodeIdByPortId.get(c.to_port_id) ?? ""));
29
- if (entryNodeIds.size > 1) {
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: "外部からの入力が複数のノードに入っています(入口は1つにしてください)",
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
- entryNodeId: [...entryNodeIds][0] ?? null,
45
- exitPortId: [...exitPortIds][0] ?? null,
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, childEndInPortId, newId, } = params;
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
- // 境界の配線:start.main → 入口ノードの in/出口ポート → end.in
82
- if (analysis.entryNodeId !== null) {
83
- const entry = childNodes.find((n) => n.id === nodeIdMap.get(analysis.entryNodeId ?? ""));
84
- const entryIn = entry?.ports.find((p) => p.io === "in");
85
- if (entryIn) {
86
- childConnections.push({
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
- if (analysis.exitPortId !== null) {
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(analysis.exitPortId) ?? analysis.exitPortId,
95
- to_port_id: childEndInPortId,
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
- { id: newId(), io: "out", key: "main", name: "出力", schema: {} },
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
- const groupMain = groupNode.ports.find((p) => p.io === "out");
121
- if (!groupIn || !groupMain) {
183
+ if (!groupIn) {
122
184
  return { ok: false, reason: "groupNode のポート生成に失敗しました" };
123
185
  }
124
- // 親の置換:選択関連の線(内部・境界)を除き、境界を group in/main へ繋ぎ直す
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
- ...analysis.inbound.map((c) => ({
129
- from_port_id: c.from_port_id,
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.map((c) => ({
133
- from_port_id: groupMain.id,
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,
@@ -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">;
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { graphPortSchema, jsonSchemaSchema, nodeKindSchema, portKeySchema, } from "./node.js";
3
- import { AI_INSTRUCTION_PORT_KEY, hasInboundConflict, isAiEnvelopeSchema, isSchemaSubset, kindPortsViolation, resolvePortSchemas, UNDECLARED_SCHEMA, } from "./port-spec.js";
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 は2つ(上流データの in 差し戻しの instruction)。他 kind は1つ固定。
269
+ // ai も1つ以上(in は自由ポート:追加・削除・key 自由)。他 kind は1つ固定。
260
270
  // key の妥当性は kindPortsViolation が別途見る
261
- const expectIn = node.kind === "ai" ? 2 : 1;
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
- : `in ポートはちょうど${expectIn}つ必要です`,
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 の組み合わせは kind で縛らない:両端とも
411
- // instructionSchema()({ prompt, files })で型が固定されているので、下の型検証だけで
412
- // 「型が合う口にしか繋がらない」が成立する。差し戻し先が AI である必要はない
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 の契約・ai の instruction・宣言のあるビューの human.in)になる
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)) {
@@ -37,6 +37,7 @@ export declare const graphPortSchema: z.ZodObject<{
37
37
  key: z.ZodString;
38
38
  name: z.ZodString;
39
39
  schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
40
+ resume: z.ZodOptional<z.ZodBoolean>;
40
41
  side: z.ZodOptional<z.ZodEnum<{
41
42
  top: "top";
42
43
  right: "right";
@@ -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
- out: DeclaredPortSchema;
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>;