@mawaru/sdk 0.13.0 → 0.14.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/_schemas/ai-manifest.test.ts +54 -59
- package/_schemas/ai-manifest.ts +38 -23
- package/_schemas/extract-component.test.ts +51 -2
- package/_schemas/extract-component.ts +4 -2
- package/_schemas/graph.test.ts +87 -66
- package/_schemas/graph.ts +14 -29
- package/_schemas/node.ts +4 -0
- package/_schemas/port-spec.test.ts +120 -129
- package/_schemas/port-spec.ts +70 -106
- package/dist/_schemas/ai-manifest.d.ts +12 -1
- package/dist/_schemas/ai-manifest.js +35 -20
- package/dist/_schemas/extract-component.js +4 -2
- package/dist/_schemas/graph.d.ts +5 -0
- package/dist/_schemas/graph.js +15 -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 +57 -89
- 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
package/_schemas/port-spec.ts
CHANGED
|
@@ -2,12 +2,14 @@ import { z } from "zod"
|
|
|
2
2
|
import type { GraphPort, JsonSchema, PortIo } from "./node.js"
|
|
3
3
|
|
|
4
4
|
// ポートの kind 別型システム(docs/tasks/wip/ポートのkind別仕様.md)。
|
|
5
|
-
// 型の決まり方は
|
|
6
|
-
//
|
|
5
|
+
// 型の決まり方は2分類:宣言(program / ai の in・out。human はビューの宣言を転写)/
|
|
6
|
+
// 導出(それ以外。接続元から伝播し、結果は ports.schema にキャッシュする)。
|
|
7
|
+
// mawaru がコードで型を固定するポートは無い
|
|
8
|
+
// (docs/tasks/wip/ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
7
9
|
|
|
8
10
|
// 「まだ型が決まっていない」=未宣言を {} で表す(未接続・上流が未宣言・fan-in の型割れ)。
|
|
9
|
-
// 「何でも受ける」型は存在しない:
|
|
10
|
-
//
|
|
11
|
+
// 「何でも受ける」型は存在しない:program / ai の in は接続元の具体型を鏡映する
|
|
12
|
+
// (docs/tasks/wip/ポートスキーマのany撲滅.md)
|
|
11
13
|
export const UNDECLARED_SCHEMA: JsonSchema = {}
|
|
12
14
|
|
|
13
15
|
export const isUndeclaredSchema = (schema: JsonSchema): boolean =>
|
|
@@ -38,14 +40,12 @@ export const fileSchema = (): JsonSchema => ({
|
|
|
38
40
|
export const isFileSchema = (schema: JsonSchema): boolean =>
|
|
39
41
|
schema[FILE_SCHEMA_MARKER] === FILE_SCHEMA_TYPE
|
|
40
42
|
|
|
41
|
-
// ----
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
export const AI_INSTRUCTION_PORT_KEY = "instruction"
|
|
48
|
-
|
|
43
|
+
// ---- 差し戻しの指示のデフォルト型 ----
|
|
44
|
+
// 雛形(エディタの既定ポート・ais/create)と承認ビューの既定宣言が参照する
|
|
45
|
+
// **デフォルトであって強制ではない**:ユーザーはポートの宣言を自由に変えられ、
|
|
46
|
+
// 接続の成立は通常の型検証だけが決める。配管が効く根拠は files が file 型として
|
|
47
|
+
// 宣言されていることであって、キー名ではない(runner がマーカーで位置を見つけて
|
|
48
|
+
// materialize する)。スクリーンショットを添えて差し戻すケースがあるので文章だけにしない
|
|
49
49
|
export const instructionSchema = (): JsonSchema => ({
|
|
50
50
|
type: "object",
|
|
51
51
|
properties: {
|
|
@@ -138,38 +138,6 @@ export const dataFormatOf = (schema: JsonSchema): DataFormat | null => {
|
|
|
138
138
|
return { format: "list", columns }
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
// 封筒型 { data: T, description: string }。T は任意の JSON Schema、description は
|
|
142
|
-
// AI によるデータの説明(AI が必ず生成する)。AI の全出口に強制する
|
|
143
|
-
export const envelopeSchemaOf = (dataSchema: JsonSchema): JsonSchema => ({
|
|
144
|
-
type: "object",
|
|
145
|
-
properties: {
|
|
146
|
-
data: dataSchema,
|
|
147
|
-
description: { type: "string" },
|
|
148
|
-
},
|
|
149
|
-
required: ["data", "description"],
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
// 封筒型なら data のサブスキーマを返す(data キー必須の object。それ以外は null)
|
|
153
|
-
export const envelopeDataSchemaOf = (schema: JsonSchema): JsonSchema | null => {
|
|
154
|
-
if (schema.type !== "object") return null
|
|
155
|
-
const properties = (schema.properties ?? {}) as Record<string, JsonSchema>
|
|
156
|
-
const required = (schema.required ?? []) as string[]
|
|
157
|
-
const data = properties.data
|
|
158
|
-
if (!data || !required.includes("data")) return null
|
|
159
|
-
return data
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// AI の出口として妥当な封筒型か:data と description: string の両方が必須
|
|
163
|
-
export const isAiEnvelopeSchema = (schema: JsonSchema): boolean => {
|
|
164
|
-
if (envelopeDataSchemaOf(schema) === null) return false
|
|
165
|
-
const properties = (schema.properties ?? {}) as Record<string, JsonSchema>
|
|
166
|
-
const required = (schema.required ?? []) as string[]
|
|
167
|
-
return (
|
|
168
|
-
properties.description?.type === "string" &&
|
|
169
|
-
required.includes("description")
|
|
170
|
-
)
|
|
171
|
-
}
|
|
172
|
-
|
|
173
141
|
// ---- kind 別ポート構成 ----
|
|
174
142
|
// human / wait は構成(数・key・io)が固定。program / ai は in 1つ+out 1つ以上
|
|
175
143
|
// (key 自由。複数 out=分岐。AIノードの複数出口(分岐).md)
|
|
@@ -187,22 +155,19 @@ const FIXED_PORT_KEYS: Record<string, { in: string[]; out: string[] }> = {
|
|
|
187
155
|
|
|
188
156
|
const FREE_OUT_KINDS = new Set(["program", "ai"])
|
|
189
157
|
|
|
190
|
-
// out
|
|
191
|
-
//
|
|
192
|
-
//
|
|
158
|
+
// out は自由(複数出口=分岐)。program は in の構成も固定(1口)だが、
|
|
159
|
+
// ai は in も自由(追加・削除・key 自由。セッション再開は key ではなくポートの
|
|
160
|
+
// resume 属性で決まる。ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
193
161
|
const FIXED_IN_KEYS: Record<string, string[]> = {
|
|
194
162
|
program: ["in"],
|
|
195
|
-
ai: ["in", AI_INSTRUCTION_PORT_KEY],
|
|
196
163
|
}
|
|
197
164
|
|
|
198
165
|
export const fixedInKeysOf = (kind: string): string[] | null =>
|
|
199
166
|
FIXED_IN_KEYS[kind] ?? null
|
|
200
167
|
|
|
201
168
|
// config.json(manifest)の inputs 検査(適合なら null)。同じルールを二重に持たないため
|
|
202
|
-
// manifest
|
|
203
|
-
//
|
|
204
|
-
// constSchemaOf が焼き込む固定型なのでエディタ側で補え、in を2口に分ける前に同期された
|
|
205
|
-
// config.json もそのまま読める
|
|
169
|
+
// manifest 側はここを参照する。in の構成が固定の kind(program)だけが使う
|
|
170
|
+
// (ai の inputs は key 自由なので ai-manifest 側で件数と key の規則だけ見る)
|
|
206
171
|
export const manifestInKeysViolation = (
|
|
207
172
|
kind: string,
|
|
208
173
|
keys: string[],
|
|
@@ -265,13 +230,12 @@ export const kindPortsViolation = (
|
|
|
265
230
|
|
|
266
231
|
// ---- 型伝播(導出ポートの解決) ----
|
|
267
232
|
// 接続の両端は同じスキーマになる(値入力フォームのSchemaForm統一.md)。
|
|
268
|
-
// wait
|
|
269
|
-
//
|
|
270
|
-
// human の reject は未宣言のまま(段階2 で instruction エンベロープを宣言する)。
|
|
233
|
+
// wait は素通し(パラメトリック)ノード:in は接続元の out、out は in と同型。
|
|
234
|
+
// human はビューの宣言を転写("T" は素通しと同じ挙動。reject もビューの宣言)。
|
|
271
235
|
// 宣言 out(program / ai)は触らない。
|
|
272
|
-
// program / ai の in
|
|
273
|
-
//
|
|
274
|
-
// へも書き戻される。docs/tasks/wip/ポート編集のrepo書き戻し同期.md)
|
|
236
|
+
// program / ai の in は**全ての口が**接続鏡映(sticky):接続元 out が具体型のときだけ
|
|
237
|
+
// 上書きし、未接続・型割れ・未宣言は現スキーマ(宣言)を維持する(鏡映結果は repo の
|
|
238
|
+
// config.json へも書き戻される。docs/tasks/wip/ポート編集のrepo書き戻し同期.md)
|
|
275
239
|
|
|
276
240
|
// 複数の流入(fan-in)から型を1つに決める:未宣言を除いて具体型が1種類ならその型、
|
|
277
241
|
// 0種類なら未宣言。2種類以上は型が割れているので未宣言に落とす
|
|
@@ -311,13 +275,19 @@ export type DeclaredPortSchema = JsonSchema | typeof COPY_UPSTREAM_SCHEMA
|
|
|
311
275
|
|
|
312
276
|
// 伝播のオプション。sdk は承認ビューのカタログを知らない(非公開。企業向けビューの
|
|
313
277
|
// キーが npm 公開物に載るのを避ける)ので、呼び出し元が解決して渡す
|
|
314
|
-
// → docs/tasks/wip
|
|
278
|
+
// → docs/tasks/wip/ポート型固定の撤去とセッション再開のポート属性化.md
|
|
315
279
|
export type PropagationOptions<N> = {
|
|
316
|
-
// human の in / approve に転写する型(承認ビューの宣言)。null / undefined
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
280
|
+
// human の in / approve / reject に転写する型(承認ビューの宣言)。null / undefined
|
|
281
|
+
// ならその node は対象外(human 以外)。上流ノードの kind は一切見ない。
|
|
282
|
+
// reject は具体型のみ(上流からのコピー指示は意味を持たない)
|
|
283
|
+
viewPortSchemasOf?: (node: N) =>
|
|
284
|
+
| {
|
|
285
|
+
in: DeclaredPortSchema
|
|
286
|
+
approve: DeclaredPortSchema
|
|
287
|
+
reject: JsonSchema
|
|
288
|
+
}
|
|
289
|
+
| null
|
|
290
|
+
| undefined
|
|
321
291
|
}
|
|
322
292
|
|
|
323
293
|
// end も導出ノード(各 in は上流から、out main は未宣言)。個別処理は
|
|
@@ -331,32 +301,16 @@ const MIRROR_KINDS = new Set(["program", "ai"])
|
|
|
331
301
|
|
|
332
302
|
const isDerivedPort = (kind: string, port: Pick<GraphPort, "io" | "key">) =>
|
|
333
303
|
DERIVED_KINDS.has(kind) &&
|
|
334
|
-
(port.io === "in" ||
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
// 接続バリデーションの「ai の in は常に許可」は維持)
|
|
339
|
-
// 宣言型で固定されるポート(導出も鏡映もしない)。差し戻しの指示エンベロープを
|
|
340
|
-
// human の出口と AI の入口の両方に焼き込むことで、両端が同じ具体型で一致する
|
|
341
|
-
const constSchemaOf = (
|
|
342
|
-
kind: string,
|
|
343
|
-
port: Pick<GraphPort, "io" | "key">,
|
|
344
|
-
): JsonSchema | null => {
|
|
345
|
-
if (kind === "human" && port.io === "out" && port.key === "reject") {
|
|
346
|
-
return instructionSchema()
|
|
347
|
-
}
|
|
348
|
-
if (
|
|
349
|
-
kind === "ai" &&
|
|
350
|
-
port.io === "in" &&
|
|
351
|
-
port.key === AI_INSTRUCTION_PORT_KEY
|
|
352
|
-
) {
|
|
353
|
-
return instructionSchema()
|
|
354
|
-
}
|
|
355
|
-
return null
|
|
356
|
-
}
|
|
304
|
+
(port.io === "in" ||
|
|
305
|
+
port.key === "main" ||
|
|
306
|
+
port.key === "approve" ||
|
|
307
|
+
port.key === "reject")
|
|
357
308
|
|
|
358
309
|
// port_id → 解決済みスキーマ。導出は接続を遡って固定点まで反復し、
|
|
359
|
-
// 未接続・純パラメトリック閉路・fan-in
|
|
310
|
+
// 未接続・純パラメトリック閉路・fan-in の型割れは未宣言に落とす。
|
|
311
|
+
// ai の in は nodes/ai/<dir>/config.json の inputs[key].schema 由来の宣言型が初期値で、
|
|
312
|
+
// 接続時は program と同じ鏡映で接続元と同値に同期される(どの in も対等。
|
|
313
|
+
// instruction への型の焼き込みは廃止)
|
|
360
314
|
export const resolvePortSchemas = <N extends PropagationNode>(
|
|
361
315
|
nodes: N[],
|
|
362
316
|
connections: PortLink[],
|
|
@@ -367,11 +321,9 @@ export const resolvePortSchemas = <N extends PropagationNode>(
|
|
|
367
321
|
for (const node of nodes) {
|
|
368
322
|
for (const port of node.ports) {
|
|
369
323
|
kindByPortId.set(port.id, node.kind)
|
|
370
|
-
const constSchema = constSchemaOf(node.kind, port)
|
|
371
324
|
resolved.set(
|
|
372
325
|
port.id,
|
|
373
|
-
|
|
374
|
-
(isDerivedPort(node.kind, port) ? UNDECLARED_SCHEMA : port.schema),
|
|
326
|
+
isDerivedPort(node.kind, port) ? UNDECLARED_SCHEMA : port.schema,
|
|
375
327
|
)
|
|
376
328
|
}
|
|
377
329
|
}
|
|
@@ -434,20 +386,29 @@ export const resolvePortSchemas = <N extends PropagationNode>(
|
|
|
434
386
|
}
|
|
435
387
|
const isMirror = MIRROR_KINDS.has(node.kind)
|
|
436
388
|
if (!DERIVED_KINDS.has(node.kind) && !isMirror) continue
|
|
437
|
-
// ai
|
|
438
|
-
|
|
389
|
+
// 鏡映(program / ai):全ての in が対象。具体型が流れてきたときだけ上書きする
|
|
390
|
+
// (宣言を未宣言で潰さない)。ai の in はどの口も対等(instruction の特別扱いは無い)
|
|
391
|
+
if (isMirror) {
|
|
392
|
+
for (const port of node.ports) {
|
|
393
|
+
if (port.io !== "in") continue
|
|
394
|
+
const sources = inbound.get(port.id) ?? []
|
|
395
|
+
const inSchema = mergeInboundSchemas(
|
|
396
|
+
sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA),
|
|
397
|
+
)
|
|
398
|
+
if (!isUndeclaredSchema(inSchema)) assign(port.id, inSchema)
|
|
399
|
+
}
|
|
400
|
+
continue
|
|
401
|
+
}
|
|
402
|
+
// 導出(wait / human):in は上流から
|
|
403
|
+
const inPort = node.ports.find((p) => p.io === "in")
|
|
439
404
|
if (!inPort) continue
|
|
440
405
|
const sources = inbound.get(inPort.id) ?? []
|
|
441
406
|
const inSchema = mergeInboundSchemas(
|
|
442
407
|
sources.map((id) => resolved.get(id) ?? UNDECLARED_SCHEMA),
|
|
443
408
|
)
|
|
444
|
-
// 鏡映(program / ai)は具体型が流れてきたときだけ上書き(宣言を未宣言で潰さない)
|
|
445
|
-
if (isMirror) {
|
|
446
|
-
if (!isUndeclaredSchema(inSchema)) assign(inPort.id, inSchema)
|
|
447
|
-
continue
|
|
448
|
-
}
|
|
449
409
|
// 承認ビューの宣言を human のポートへそのまま転写する(上流ノードの kind は見ない)。
|
|
450
|
-
// "T" は「上流の out をコピー」(in)/「in と同型」(approve)。
|
|
410
|
+
// "T" は「上流の out をコピー」(in)/「in と同型」(approve)。reject は具体型の宣言
|
|
411
|
+
// のみで、宣言が無ければ未宣言のまま(mawaru が型を焼き込むことはしない)。
|
|
451
412
|
// 上流の out は書き換えない:逆伝播すると fan-out 先のビューが食い違ったとき
|
|
452
413
|
// 型割れが config.json へ write-through され、元の出力宣言を黙って壊す。
|
|
453
414
|
// ズレは接続の型エラーで見せてユーザーの [修正する] で直す
|
|
@@ -458,14 +419,17 @@ export const resolvePortSchemas = <N extends PropagationNode>(
|
|
|
458
419
|
: inSchema
|
|
459
420
|
assign(inPort.id, effectiveIn)
|
|
460
421
|
for (const port of node.ports) {
|
|
461
|
-
if (port.io
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
? declared.out
|
|
466
|
-
: effectiveIn,
|
|
467
|
-
)
|
|
422
|
+
if (port.io !== "out" || !isDerivedPort(node.kind, port)) continue
|
|
423
|
+
if (node.kind === "human" && port.key === "reject") {
|
|
424
|
+
if (declared) assign(port.id, declared.reject)
|
|
425
|
+
continue
|
|
468
426
|
}
|
|
427
|
+
assign(
|
|
428
|
+
port.id,
|
|
429
|
+
declared && declared.approve !== COPY_UPSTREAM_SCHEMA
|
|
430
|
+
? declared.approve
|
|
431
|
+
: effectiveIn,
|
|
432
|
+
)
|
|
469
433
|
}
|
|
470
434
|
}
|
|
471
435
|
if (!changed) break
|
|
@@ -8,6 +8,11 @@ export declare const aiModelSchema: z.ZodEnum<{
|
|
|
8
8
|
}>;
|
|
9
9
|
export type AiModel = z.infer<typeof aiModelSchema>;
|
|
10
10
|
export declare const DEFAULT_AI_MODEL = "claude-opus-5";
|
|
11
|
+
export declare const aiInputDeclSchema: z.ZodObject<{
|
|
12
|
+
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
13
|
+
resume: z.ZodOptional<z.ZodBoolean>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
export type AiInputDecl = z.infer<typeof aiInputDeclSchema>;
|
|
11
16
|
export declare const aiManifestSchema: z.ZodObject<{
|
|
12
17
|
name: z.ZodOptional<z.ZodString>;
|
|
13
18
|
description: z.ZodOptional<z.ZodString>;
|
|
@@ -21,7 +26,13 @@ export declare const aiManifestSchema: z.ZodObject<{
|
|
|
21
26
|
skills: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
22
27
|
env: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
23
28
|
verbose: z.ZodOptional<z.ZodBoolean>;
|
|
24
|
-
inputs: z.ZodRecord<z.ZodString, z.
|
|
29
|
+
inputs: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
|
|
30
|
+
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
31
|
+
resume: z.ZodOptional<z.ZodBoolean>;
|
|
32
|
+
}, z.core.$strip>, z.ZodPipe<z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodTransform<{
|
|
33
|
+
schema: Record<string, unknown>;
|
|
34
|
+
resume?: boolean | undefined;
|
|
35
|
+
}, Record<string, unknown>>>]>>;
|
|
25
36
|
outputs: z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
26
37
|
}, z.core.$strip>;
|
|
27
38
|
export type AiManifest = z.infer<typeof aiManifestSchema>;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { jsonSchemaSchema } from "./node.js";
|
|
3
|
-
import { isAiEnvelopeSchema, manifestInKeysViolation } from "./port-spec.js";
|
|
2
|
+
import { jsonSchemaSchema, PORT_KEY_PATTERN } from "./node.js";
|
|
4
3
|
// AI ノードのモデル選択肢(既定は Opus:下書き品質が価値の中心。費用はユーザー持ち)。
|
|
5
4
|
// エイリアス("opus" 等)は採らずバージョンを固定する:実行のたびに黙って別モデルへ
|
|
6
5
|
// 乗り換わると品質も費用も再現しないため。新モデルが出たらこの配列を足して SDK を publish する
|
|
@@ -12,6 +11,20 @@ export const AI_MODELS = [
|
|
|
12
11
|
];
|
|
13
12
|
export const aiModelSchema = z.enum(AI_MODELS);
|
|
14
13
|
export const DEFAULT_AI_MODEL = "claude-opus-5";
|
|
14
|
+
// 入力口の宣言:スキーマ+実行属性。resume: true の口から入力が届いた実行は
|
|
15
|
+
// 同じノードの直近セッションを復元して再開する(backend が ports.resume へ同期して
|
|
16
|
+
// payload 発行時に参照する。ポート型固定の撤去とセッション再開のポート属性化.md)
|
|
17
|
+
export const aiInputDeclSchema = z.object({
|
|
18
|
+
schema: jsonSchemaSchema,
|
|
19
|
+
resume: z.boolean().optional(),
|
|
20
|
+
});
|
|
21
|
+
// 旧形式(値が素の JSON Schema)は { schema: 値 } として読む。判別は「schema キーを
|
|
22
|
+
// 持つか」:JSON Schema のトップレベルに "schema" キーは現れない(メタスキーマ参照は
|
|
23
|
+
// "$schema")ので実用上一意。書き戻し(backend の syncNodeConfigs)は常に新形式で書く
|
|
24
|
+
const aiInputValueSchema = z.union([
|
|
25
|
+
aiInputDeclSchema,
|
|
26
|
+
jsonSchemaSchema.transform((schema) => ({ schema })),
|
|
27
|
+
]);
|
|
15
28
|
// nodes/ai/<dir>/config.json(実行repoの構造見直し)。AI ノードの定義(prompt/model/skills/
|
|
16
29
|
// 入出力スキーマ/env)は repo 側が正で、エディタが取り込んで ports を自動生成する
|
|
17
30
|
// (実行時の正は ports テーブルのまま。プログラム規約 v2 と同じ関係)。
|
|
@@ -34,34 +47,36 @@ export const aiManifestSchema = z
|
|
|
34
47
|
// 実行中の経過(発話・ツール呼び出し)を Actions ログへ逐次出すデバッグフラグ。
|
|
35
48
|
// 通常時は config.json にキーを書かない(インスペクタも false ならキーを消す)
|
|
36
49
|
verbose: z.boolean().optional(),
|
|
37
|
-
inputs: z.record(z.string(),
|
|
50
|
+
inputs: z.record(z.string(), aiInputValueSchema),
|
|
38
51
|
outputs: z.record(z.string(), jsonSchemaSchema),
|
|
39
52
|
})
|
|
40
53
|
.superRefine((manifest, ctx) => {
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
45
|
-
ctx.addIssue({ code: "custom", path: ["inputs"], message: inViolation });
|
|
46
|
-
}
|
|
47
|
-
// outputs は1件以上・key 自由(複数出口=分岐。AIノードの複数出口(分岐).md)
|
|
48
|
-
if (Object.keys(manifest.outputs).length < 1) {
|
|
54
|
+
// inputs は1件以上・key 自由(in / instruction の固定構成は廃止。
|
|
55
|
+
// セッション再開は key ではなく resume 属性で決まる)
|
|
56
|
+
const inKeys = Object.keys(manifest.inputs);
|
|
57
|
+
if (inKeys.length < 1) {
|
|
49
58
|
ctx.addIssue({
|
|
50
59
|
code: "custom",
|
|
51
|
-
path: ["
|
|
52
|
-
message: "
|
|
60
|
+
path: ["inputs"],
|
|
61
|
+
message: "inputs は1件以上宣言してください",
|
|
53
62
|
});
|
|
54
63
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// data=T は任意の JSON Schema)
|
|
58
|
-
for (const [key, schema] of Object.entries(manifest.outputs)) {
|
|
59
|
-
if (!isAiEnvelopeSchema(schema)) {
|
|
64
|
+
for (const key of inKeys) {
|
|
65
|
+
if (!PORT_KEY_PATTERN.test(key)) {
|
|
60
66
|
ctx.addIssue({
|
|
61
67
|
code: "custom",
|
|
62
|
-
path: ["
|
|
63
|
-
message: `
|
|
68
|
+
path: ["inputs", key],
|
|
69
|
+
message: `inputs の key が不正です: ${key}(英小文字始まり・英数字と _ のみ)`,
|
|
64
70
|
});
|
|
65
71
|
}
|
|
66
72
|
}
|
|
73
|
+
// outputs は1件以上・key 自由(複数出口=分岐。AIノードの複数出口(分岐).md)。
|
|
74
|
+
// スキーマは任意の JSON Schema(形の強制はしない)
|
|
75
|
+
if (Object.keys(manifest.outputs).length < 1) {
|
|
76
|
+
ctx.addIssue({
|
|
77
|
+
code: "custom",
|
|
78
|
+
path: ["outputs"],
|
|
79
|
+
message: "outputs は1件以上宣言してください",
|
|
80
|
+
});
|
|
81
|
+
}
|
|
67
82
|
});
|
|
@@ -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");
|
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,6 +151,7 @@ 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>;
|
|
@@ -179,6 +182,7 @@ export declare const saveGraphBodySchema: z.ZodObject<{
|
|
|
179
182
|
key: z.ZodString;
|
|
180
183
|
name: z.ZodString;
|
|
181
184
|
schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
185
|
+
resume: z.ZodOptional<z.ZodBoolean>;
|
|
182
186
|
side: z.ZodOptional<z.ZodEnum<{
|
|
183
187
|
top: "top";
|
|
184
188
|
right: "right";
|
|
@@ -230,6 +234,7 @@ export declare const saveGraphBodySchema: z.ZodObject<{
|
|
|
230
234
|
}, z.core.$strip>>;
|
|
231
235
|
component: z.ZodOptional<z.ZodObject<{
|
|
232
236
|
component_loop_id: z.ZodUUID;
|
|
237
|
+
refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
233
238
|
}, z.core.$strip>>;
|
|
234
239
|
}, z.core.$strip>>;
|
|
235
240
|
connections: z.ZodArray<z.ZodObject<{
|
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)から
|
|
@@ -256,17 +260,16 @@ export const saveGraphBodySchema = z
|
|
|
256
260
|
outCount++;
|
|
257
261
|
});
|
|
258
262
|
// end は「in は1つ以上」(複数ルートを別々の in で集約する)。
|
|
259
|
-
// ai
|
|
263
|
+
// ai も1つ以上(in は自由ポート:追加・削除・key 自由)。他 kind は1つ固定。
|
|
260
264
|
// key の妥当性は kindPortsViolation が別途見る
|
|
261
|
-
const
|
|
262
|
-
const inCountOk = node.kind === "end" ? inCount >= 1 : inCount === expectIn;
|
|
265
|
+
const inCountOk = node.kind === "end" || node.kind === "ai" ? inCount >= 1 : inCount === 1;
|
|
263
266
|
if (!inCountOk) {
|
|
264
267
|
ctx.addIssue({
|
|
265
268
|
code: "custom",
|
|
266
269
|
path: ["nodes", i, "ports"],
|
|
267
|
-
message: node.kind === "end"
|
|
270
|
+
message: node.kind === "end" || node.kind === "ai"
|
|
268
271
|
? "in ポートが1つ以上必要です"
|
|
269
|
-
:
|
|
272
|
+
: "in ポートはちょうど1つ必要です",
|
|
270
273
|
});
|
|
271
274
|
}
|
|
272
275
|
if (outCount < 1) {
|
|
@@ -305,7 +308,8 @@ export const saveGraphBodySchema = z
|
|
|
305
308
|
message: "component ノードには component_loop_id が必要です",
|
|
306
309
|
});
|
|
307
310
|
}
|
|
308
|
-
// kind 別のポート構成(program 以外は数・key・io 固定。ポートのkind別仕様.md
|
|
311
|
+
// kind 別のポート構成(program 以外は数・key・io 固定。ポートのkind別仕様.md)。
|
|
312
|
+
// ポートのスキーマ自体は kind によらず任意の JSON Schema(形の強制はしない)
|
|
309
313
|
const violation = kindPortsViolation(node.kind, node.ports);
|
|
310
314
|
if (violation) {
|
|
311
315
|
ctx.addIssue({
|
|
@@ -314,19 +318,6 @@ export const saveGraphBodySchema = z
|
|
|
314
318
|
message: violation,
|
|
315
319
|
});
|
|
316
320
|
}
|
|
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
321
|
});
|
|
331
322
|
// 接続:両端がグラフ内の port を指し、from=out / to=in。
|
|
332
323
|
// 同じ out ポートから複数の in への fan-out は許す(並行分岐)。
|
|
@@ -407,16 +398,14 @@ export const saveGraphBodySchema = z
|
|
|
407
398
|
message: "終了ノードからは接続できません",
|
|
408
399
|
});
|
|
409
400
|
}
|
|
410
|
-
// 差し戻し(reject)と instruction
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
// ({ prompt, files } を受ける program へも繋げる)
|
|
401
|
+
// 差し戻し(reject)と instruction の組み合わせに kind の縛りも型固定も無い:
|
|
402
|
+
// reject の型はビューの宣言、instruction は ai の in の1つ(接続鏡映)でしかない
|
|
403
|
+
// → docs/tasks/wip/ポート型固定の撤去とセッション再開のポート属性化.md
|
|
414
404
|
// 型検証は kind によらず常に 伝播型(out) ⊆ 伝播型(in)。
|
|
415
405
|
// 未宣言は unknown=判断できないので通す(作りかけのグラフを保存できなくしない)。
|
|
416
406
|
// 接続鏡映する口(program / ai の in)とコピー宣言のビューは接続元の型を採用する
|
|
417
407
|
// ので自明に通り、実質の検証対象は宣言で固定された口
|
|
418
|
-
// (component
|
|
419
|
-
// → docs/tasks/wip/Humanの入出力を承認ビューの宣言に一本化する.md
|
|
408
|
+
// (component の契約・宣言のあるビューの human.in)になる
|
|
420
409
|
const fromSchema = resolved.get(conn.from_port_id) ?? from.schema;
|
|
421
410
|
const toSchema = resolved.get(conn.to_port_id) ?? to.schema;
|
|
422
411
|
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>;
|