@mawaru/sdk 0.11.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.
Files changed (43) hide show
  1. package/_schemas/ai-manifest.test.ts +54 -59
  2. package/_schemas/ai-manifest.ts +38 -23
  3. package/_schemas/duplicate-selection.test.ts +235 -0
  4. package/_schemas/duplicate-selection.ts +48 -0
  5. package/_schemas/extract-component.test.ts +51 -2
  6. package/_schemas/extract-component.ts +4 -2
  7. package/_schemas/graph.test.ts +105 -86
  8. package/_schemas/graph.ts +20 -77
  9. package/_schemas/node.ts +4 -0
  10. package/_schemas/port-spec.test.ts +194 -113
  11. package/_schemas/port-spec.ts +95 -107
  12. package/dist/_cli/credentials.d.ts +14 -0
  13. package/dist/_cli/credentials.js +81 -0
  14. package/dist/_cli/login.d.ts +5 -0
  15. package/dist/_cli/login.js +82 -0
  16. package/dist/_cli/resumeSession.d.ts +21 -0
  17. package/dist/_cli/resumeSession.js +125 -0
  18. package/dist/_cli/sessionCommand.d.ts +3 -0
  19. package/dist/_cli/sessionCommand.js +55 -0
  20. package/dist/_schemas/ai-manifest.d.ts +12 -1
  21. package/dist/_schemas/ai-manifest.js +35 -20
  22. package/dist/_schemas/duplicate-selection.d.ts +12 -0
  23. package/dist/_schemas/duplicate-selection.js +34 -0
  24. package/dist/_schemas/extract-component.js +4 -2
  25. package/dist/_schemas/graph.d.ts +14 -12
  26. package/dist/_schemas/graph.js +21 -60
  27. package/dist/_schemas/hook-manifest.d.ts +2 -2
  28. package/dist/_schemas/node.d.ts +3 -2
  29. package/dist/_schemas/node.js +4 -0
  30. package/dist/_schemas/port-spec.d.ts +11 -6
  31. package/dist/_schemas/port-spec.js +72 -97
  32. package/dist/cli.js +50 -5
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.js +1 -0
  35. package/dist/init.js +1 -1
  36. package/dist/typegen.js +6 -6
  37. package/docs/development.md +5 -1
  38. package/index.ts +1 -0
  39. package/package.json +11 -18
  40. package/skills/create-loop/SKILL.md +1 -1
  41. package/templates/CLAUDE.md +1 -1
  42. package/templates/echo/main.ts +1 -1
  43. package/templates/mawaru-runner.yml +1 -1
@@ -0,0 +1,55 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ import { requireCredentials } from "./credentials.js";
3
+ import { downloadTranscript, fetchTranscriptRef, parseStepUrl, placeTranscript, recordedCwds, spawnClaudeResume, } from "./resumeSession.js";
4
+ const confirm = async (question) => {
5
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
6
+ try {
7
+ const answer = await rl.question(`${question} [y/N] `);
8
+ return /^y(es)?$/i.test(answer.trim());
9
+ }
10
+ finally {
11
+ rl.close();
12
+ }
13
+ };
14
+ // `mawaru session resume <step URL> [--yes]`
15
+ //
16
+ // 取得 → 作業ディレクトリの確認 → 配置 → `claude --resume`。
17
+ // **どこに cd すべきかは mawaru には分からない**(AI ノードの実行 repo と、エージェントが
18
+ // 実際に触った repo は別物で、後者は setup.sh が workspace/ 配下に clone した対象 repo)。
19
+ // なので推定せず、記録された cwd をそのまま見せて人間に判断させる
20
+ export const runSessionResume = async (stepUrl, options) => {
21
+ if (!stepUrl) {
22
+ console.error("使い方: mawaru session resume <step URL> [--yes]\n" +
23
+ "run 詳細の AI ノードにあるコピーボタンで URL を取得できます。");
24
+ return 1;
25
+ }
26
+ // URL の誤りは通信より先に落とす
27
+ const ref = parseStepUrl(stepUrl);
28
+ const credentials = requireCredentials();
29
+ const transcriptRef = await fetchTranscriptRef(credentials, ref);
30
+ const transcript = await downloadTranscript(transcriptRef.url);
31
+ console.log(`取得: セッション ${transcriptRef.session_id}`);
32
+ if (transcriptRef.redacted) {
33
+ console.log(`(退避時に secret を ${transcriptRef.redacted} 箇所マスクしています)`);
34
+ }
35
+ const { all, last } = recordedCwds(transcript);
36
+ const cwd = process.cwd();
37
+ if (all.length > 0) {
38
+ console.log("このセッションの作業ディレクトリ:");
39
+ for (const dir of all) {
40
+ console.log(` ${dir}${dir === last ? "(最後に居た場所)" : ""}`);
41
+ }
42
+ }
43
+ console.log(`現在地:\n ${cwd}`);
44
+ if (!all.includes(cwd)) {
45
+ console.log("※ 記録された作業ディレクトリとは違います。記録上の絶対パスはここでは解決しません" +
46
+ "(エージェントは読めないパスに当たれば自分で見つけ直します)。");
47
+ }
48
+ if (!options.yes && !(await confirm("続けますか?"))) {
49
+ console.log("中止しました。");
50
+ return 1;
51
+ }
52
+ const file = placeTranscript(cwd, transcriptRef.session_id, transcript);
53
+ console.log(`配置: ${file}\n再開します…`);
54
+ return await spawnClaudeResume(transcriptRef.session_id);
55
+ };
@@ -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.ZodRecord<z.ZodString, z.ZodUnknown>>;
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(), jsonSchemaSchema),
50
+ inputs: z.record(z.string(), aiInputValueSchema),
38
51
  outputs: z.record(z.string(), jsonSchemaSchema),
39
52
  })
40
53
  .superRefine((manifest, ctx) => {
41
- // in の構成は kind が固定する(ai は上流データの in 差し戻しの instruction の2口)。
42
- // ルールの正は port-spec 側で、ここは参照するだけ
43
- const inViolation = manifestInKeysViolation("ai", Object.keys(manifest.inputs));
44
- if (inViolation) {
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: ["outputs"],
52
- message: "outputs は1件以上宣言してください",
60
+ path: ["inputs"],
61
+ message: "inputs は1件以上宣言してください",
53
62
  });
54
63
  }
55
- // Human 承認 UI が依存する封筒型 { data: T, description: string } を全出口に強制
56
- // (どの出口も human に接続できる、という AI ノードの不変条件。saveGraph と同じ判定。
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: ["outputs", key],
63
- message: `outputs.${key} は封筒型 { data, description } で宣言してください(両方必須・description は string)`,
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
  });
@@ -0,0 +1,12 @@
1
+ import type { GraphConnection, GraphNode } from "./graph.js";
2
+ export declare const duplicateSelection: (params: {
3
+ nodes: GraphNode[];
4
+ connections: GraphConnection[];
5
+ selectedIds: readonly string[];
6
+ dx: number;
7
+ dy: number;
8
+ newId: () => string;
9
+ }) => {
10
+ nodes: GraphNode[];
11
+ connections: GraphConnection[];
12
+ };
@@ -0,0 +1,34 @@
1
+ // 選択ノードのコピー&ペースト・複製(ノードのコピー&ペーストと複数選択.md)。
2
+ // 純関数(id 採番は newId で注入)。エディタとテストの両方から使う。
3
+ //
4
+ // グラフは全量置換 PUT なので、複製は「id を全部採番し直して足す」だけで成立する。
5
+ // ポート数の制約(in は kind ごとに固定・out は1以上)や (io,key) のノード内一意は
6
+ // ノードを丸ごと写すので自動的に保たれる。
7
+ export const duplicateSelection = (params) => {
8
+ const { nodes, connections, selectedIds, dx, dy, newId } = params;
9
+ const selected = new Set(selectedIds);
10
+ // start / end は回路の構造ノードなので複製しない(選択に混ざっていても落とす)。
11
+ // 並び順は元グラフのまま(選択の指定順に依存させない)
12
+ const portIdMap = new Map();
13
+ const copied = nodes
14
+ .filter((n) => selected.has(n.id) && n.kind !== "start" && n.kind !== "end")
15
+ .map((n) => ({
16
+ ...n,
17
+ id: newId(),
18
+ ports: n.ports.map((p) => {
19
+ const portId = newId();
20
+ portIdMap.set(p.id, portId);
21
+ return { ...p, id: portId };
22
+ }),
23
+ position_x: n.position_x + dx,
24
+ position_y: n.position_y + dy,
25
+ }));
26
+ // 両端が複製対象のポートである線(=選択内で完結する線)だけを張り替えて写す。
27
+ // 外部との線(入口・出口)はコピーしない
28
+ const copiedConnections = connections.flatMap((c) => {
29
+ const from = portIdMap.get(c.from_port_id);
30
+ const to = portIdMap.get(c.to_port_id);
31
+ return from && to ? [{ from_port_id: from, to_port_id: to }] : [];
32
+ });
33
+ return { nodes: copied, connections: copiedConnections };
34
+ };
@@ -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");
@@ -17,12 +17,12 @@ export declare const editableNodeKindSchema: z.ZodEnum<{
17
17
  }>;
18
18
  export type EditableNodeKind = z.infer<typeof editableNodeKindSchema>;
19
19
  export declare const graphNodeKindSchema: z.ZodEnum<{
20
+ start: "start";
21
+ end: "end";
20
22
  ai: "ai";
21
23
  human: "human";
22
24
  program: "program";
23
25
  wait: "wait";
24
- start: "start";
25
- end: "end";
26
26
  component: "component";
27
27
  }>;
28
28
  export type GraphNodeKind = z.infer<typeof graphNodeKindSchema>;
@@ -52,15 +52,14 @@ export declare const assignMemberSchema: z.ZodUnion<readonly [z.ZodUUID, z.ZodLi
52
52
  export type AssignMember = z.infer<typeof assignMemberSchema>;
53
53
  export declare const humanConfigSchema: z.ZodObject<{
54
54
  view: z.ZodOptional<z.ZodString>;
55
- view_config: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
56
55
  assignment: z.ZodObject<{
57
56
  strategy: z.ZodDefault<z.ZodEnum<{
58
57
  fixed: "fixed";
59
58
  round_robin: "round_robin";
60
59
  }>>;
61
60
  approval_mode: z.ZodDefault<z.ZodEnum<{
62
- any: "any";
63
61
  all: "all";
62
+ any: "any";
64
63
  }>>;
65
64
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
66
65
  }, z.core.$strip>;
@@ -68,6 +67,7 @@ export declare const humanConfigSchema: z.ZodObject<{
68
67
  export type HumanConfig = z.infer<typeof humanConfigSchema>;
69
68
  export declare const componentConfigSchema: z.ZodObject<{
70
69
  component_loop_id: z.ZodUUID;
70
+ refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
71
71
  }, z.core.$strip>;
72
72
  export type ComponentConfig = z.infer<typeof componentConfigSchema>;
73
73
  export declare const graphNodeHookSchema: z.ZodObject<{
@@ -81,12 +81,12 @@ export type GraphNodeHook = z.infer<typeof graphNodeHookSchema>;
81
81
  export declare const graphNodeSchema: z.ZodObject<{
82
82
  id: z.ZodUUID;
83
83
  kind: z.ZodEnum<{
84
+ start: "start";
85
+ end: "end";
84
86
  ai: "ai";
85
87
  human: "human";
86
88
  program: "program";
87
89
  wait: "wait";
88
- start: "start";
89
- end: "end";
90
90
  component: "component";
91
91
  }>;
92
92
  name: z.ZodString;
@@ -99,6 +99,7 @@ export declare const graphNodeSchema: z.ZodObject<{
99
99
  key: z.ZodString;
100
100
  name: z.ZodString;
101
101
  schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
102
+ resume: z.ZodOptional<z.ZodBoolean>;
102
103
  side: z.ZodOptional<z.ZodEnum<{
103
104
  top: "top";
104
105
  right: "right";
@@ -136,21 +137,21 @@ export declare const graphNodeSchema: z.ZodObject<{
136
137
  }, z.core.$strip>>;
137
138
  human: z.ZodOptional<z.ZodObject<{
138
139
  view: z.ZodOptional<z.ZodString>;
139
- view_config: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
140
140
  assignment: z.ZodObject<{
141
141
  strategy: z.ZodDefault<z.ZodEnum<{
142
142
  fixed: "fixed";
143
143
  round_robin: "round_robin";
144
144
  }>>;
145
145
  approval_mode: z.ZodDefault<z.ZodEnum<{
146
- any: "any";
147
146
  all: "all";
147
+ any: "any";
148
148
  }>>;
149
149
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
150
150
  }, z.core.$strip>;
151
151
  }, z.core.$strip>>;
152
152
  component: z.ZodOptional<z.ZodObject<{
153
153
  component_loop_id: z.ZodUUID;
154
+ refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
154
155
  }, z.core.$strip>>;
155
156
  }, z.core.$strip>;
156
157
  export type GraphNode = z.infer<typeof graphNodeSchema>;
@@ -163,12 +164,12 @@ export declare const saveGraphBodySchema: z.ZodObject<{
163
164
  nodes: z.ZodArray<z.ZodObject<{
164
165
  id: z.ZodUUID;
165
166
  kind: z.ZodEnum<{
167
+ start: "start";
168
+ end: "end";
166
169
  ai: "ai";
167
170
  human: "human";
168
171
  program: "program";
169
172
  wait: "wait";
170
- start: "start";
171
- end: "end";
172
173
  component: "component";
173
174
  }>;
174
175
  name: z.ZodString;
@@ -181,6 +182,7 @@ export declare const saveGraphBodySchema: z.ZodObject<{
181
182
  key: z.ZodString;
182
183
  name: z.ZodString;
183
184
  schema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
185
+ resume: z.ZodOptional<z.ZodBoolean>;
184
186
  side: z.ZodOptional<z.ZodEnum<{
185
187
  top: "top";
186
188
  right: "right";
@@ -218,21 +220,21 @@ export declare const saveGraphBodySchema: z.ZodObject<{
218
220
  }, z.core.$strip>>;
219
221
  human: z.ZodOptional<z.ZodObject<{
220
222
  view: z.ZodOptional<z.ZodString>;
221
- view_config: z.ZodOptional<z.ZodNullable<z.ZodUnknown>>;
222
223
  assignment: z.ZodObject<{
223
224
  strategy: z.ZodDefault<z.ZodEnum<{
224
225
  fixed: "fixed";
225
226
  round_robin: "round_robin";
226
227
  }>>;
227
228
  approval_mode: z.ZodDefault<z.ZodEnum<{
228
- any: "any";
229
229
  all: "all";
230
+ any: "any";
230
231
  }>>;
231
232
  units: z.ZodDefault<z.ZodArray<z.ZodArray<z.ZodUnion<readonly [z.ZodUUID, z.ZodLiteral<"run_starter">]>>>>;
232
233
  }, z.core.$strip>;
233
234
  }, z.core.$strip>>;
234
235
  component: z.ZodOptional<z.ZodObject<{
235
236
  component_loop_id: z.ZodUUID;
237
+ refs: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
236
238
  }, z.core.$strip>>;
237
239
  }, z.core.$strip>>;
238
240
  connections: z.ZodArray<z.ZodObject<{
@@ -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_]*$)。実行系ボディ
@@ -107,8 +107,6 @@ export const humanConfigSchema = z.object({
107
107
  // どのキーが実在するか・そのテナントが選べるかは保存時に mawaru 側が検証する
108
108
  // (カタログは非公開。@mawaru/common の approval-view.ts)。省略時は標準ビュー
109
109
  view: z.string().min(1).optional(),
110
- // ビューに渡すパラメータ(列・ラベル・閾値など)。形はビューごとに違うので任意の JSON
111
- view_config: z.unknown().nullish(),
112
110
  assignment: z
113
111
  .object({
114
112
  strategy: z.enum(["fixed", "round_robin"]).default("fixed"),
@@ -158,6 +156,10 @@ export const humanConfigSchema = z.object({
158
156
  // (docs/tasks/wip/コンポーネント(ループの部品化と再利用).md)
159
157
  export const componentConfigSchema = z.object({
160
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(),
161
163
  });
162
164
  // ノードにアタッチされた IOHook(node_hooks に対応。アタッチできるのは program ノードのみ)。
163
165
  // on_input / on_output / on_signal はアタッチ時に repo の config.json(hookManifestSchema)から
@@ -258,17 +260,16 @@ export const saveGraphBodySchema = z
258
260
  outCount++;
259
261
  });
260
262
  // end は「in は1つ以上」(複数ルートを別々の in で集約する)。
261
- // ai は2つ(上流データの in 差し戻しの instruction)。他 kind は1つ固定。
263
+ // ai も1つ以上(in は自由ポート:追加・削除・key 自由)。他 kind は1つ固定。
262
264
  // key の妥当性は kindPortsViolation が別途見る
263
- const expectIn = node.kind === "ai" ? 2 : 1;
264
- const inCountOk = node.kind === "end" ? inCount >= 1 : inCount === expectIn;
265
+ const inCountOk = node.kind === "end" || node.kind === "ai" ? inCount >= 1 : inCount === 1;
265
266
  if (!inCountOk) {
266
267
  ctx.addIssue({
267
268
  code: "custom",
268
269
  path: ["nodes", i, "ports"],
269
- message: node.kind === "end"
270
+ message: node.kind === "end" || node.kind === "ai"
270
271
  ? "in ポートが1つ以上必要です"
271
- : `in ポートはちょうど${expectIn}つ必要です`,
272
+ : "in ポートはちょうど1つ必要です",
272
273
  });
273
274
  }
274
275
  if (outCount < 1) {
@@ -307,7 +308,8 @@ export const saveGraphBodySchema = z
307
308
  message: "component ノードには component_loop_id が必要です",
308
309
  });
309
310
  }
310
- // kind 別のポート構成(program 以外は数・key・io 固定。ポートのkind別仕様.md
311
+ // kind 別のポート構成(program 以外は数・key・io 固定。ポートのkind別仕様.md)。
312
+ // ポートのスキーマ自体は kind によらず任意の JSON Schema(形の強制はしない)
311
313
  const violation = kindPortsViolation(node.kind, node.ports);
312
314
  if (violation) {
313
315
  ctx.addIssue({
@@ -316,19 +318,6 @@ export const saveGraphBodySchema = z
316
318
  message: violation,
317
319
  });
318
320
  }
319
- // AI の出力宣言は封筒型 { data: T, description: string }(T は任意の JSON Schema)。
320
- // 複数出口でも全出口に強制する(どの出口も human に接続できる不変条件)
321
- if (node.kind === "ai") {
322
- node.ports.forEach((port, j) => {
323
- if (port.io === "out" && !isAiEnvelopeSchema(port.schema)) {
324
- ctx.addIssue({
325
- code: "custom",
326
- path: ["nodes", i, "ports", j],
327
- message: "AI の出力は封筒型 { data, description } で宣言してください",
328
- });
329
- }
330
- });
331
- }
332
321
  });
333
322
  // 接続:両端がグラフ内の port を指し、from=out / to=in。
334
323
  // 同じ out ポートから複数の in への fan-out は許す(並行分岐)。
@@ -409,49 +398,21 @@ export const saveGraphBodySchema = z
409
398
  message: "終了ノードからは接続できません",
410
399
  });
411
400
  }
412
- // human の reject の接続先は AI instruction ポートのみ。
413
- // 差し戻しは「元のセッションに指示を与える」操作で、上流データの口(in)とは
414
- // 意味が違う(docs/tasks/wip/ポートスキーマのany撲滅.md
415
- if (fromNode.kind === "human" &&
416
- from.key === "reject" &&
417
- !(toNode.kind === "ai" && to.key === AI_INSTRUCTION_PORT_KEY)) {
418
- ctx.addIssue({
419
- code: "custom",
420
- path: ["connections", i, "to_port_id"],
421
- message: "差し戻し(reject)の接続先は AI ノードの instruction ポートのみです",
422
- });
423
- }
424
- // instruction ポートに繋げるのは human の reject だけ(データを流し込ませない)
425
- if (toNode.kind === "ai" &&
426
- to.key === AI_INSTRUCTION_PORT_KEY &&
427
- !(fromNode.kind === "human" && from.key === "reject")) {
428
- ctx.addIssue({
429
- code: "custom",
430
- path: ["connections", i, "to_port_id"],
431
- message: "instruction ポートに接続できるのは human の差し戻し(reject)だけです",
432
- });
433
- }
401
+ // 差し戻し(reject)と instruction の組み合わせに kind の縛りも型固定も無い:
402
+ // reject の型はビューの宣言、instruction は ai の in の1つ(接続鏡映)でしかない
403
+ // docs/tasks/wip/ポート型固定の撤去とセッション再開のポート属性化.md
404
+ // 型検証は kind によらず常に 伝播型(out) ⊆ 伝播型(in)。
405
+ // 未宣言は unknown=判断できないので通す(作りかけのグラフを保存できなくしない)。
406
+ // 接続鏡映する口(program / ai in)とコピー宣言のビューは接続元の型を採用する
407
+ // ので自明に通り、実質の検証対象は宣言で固定された口
408
+ // (component の契約・宣言のあるビューの human.in)になる
434
409
  const fromSchema = resolved.get(conn.from_port_id) ?? from.schema;
435
- // human の in はどの型でも接続できる(上流が AI なら封筒 { data, description }、
436
- // それ以外は input 全体を data として表示する。型制約は課さない)
437
- // program の in への接続は 伝播型 ⊆ 宣言スキーマ。in は接続鏡映(sticky)なので
438
- // 具体型の接続では自明に通り、接続元が未宣言 × 宣言維持のときだけ従来の意味を持つ
439
- // (未宣言は unknown=判断できないので通す。作りかけのグラフを保存できなくしない)
440
410
  const toSchema = resolved.get(conn.to_port_id) ?? to.schema;
441
- if (toNode.kind === "program" && !isSchemaSubset(fromSchema, toSchema)) {
442
- ctx.addIssue({
443
- code: "custom",
444
- path: ["connections", i, "to_port_id"],
445
- message: "接続元の型が program の入力スキーマに適合しません(伝播型 ⊆ 宣言スキーマ)",
446
- });
447
- }
448
- // component の in は子 loop の契約(焼き込み済み宣言)。program と同じ包含チェック
449
- if (toNode.kind === "component" &&
450
- !isSchemaSubset(fromSchema, toSchema)) {
411
+ if (!isSchemaSubset(fromSchema, toSchema)) {
451
412
  ctx.addIssue({
452
413
  code: "custom",
453
414
  path: ["connections", i, "to_port_id"],
454
- message: "接続元の型がコンポーネントの入力契約に適合しません(伝播型 ⊆ 契約)",
415
+ message: `「${fromNode.name}」の出力が「${toNode.name}」の入力スキーマに適合しません`,
455
416
  });
456
417
  }
457
418
  });
@@ -1,7 +1,7 @@
1
1
  import { z } from "zod";
2
2
  export declare const hookEventSchema: z.ZodEnum<{
3
- output: "output";
4
3
  input: "input";
4
+ output: "output";
5
5
  signal: "signal";
6
6
  }>;
7
7
  export type HookEvent = z.infer<typeof hookEventSchema>;
@@ -9,8 +9,8 @@ export declare const hookManifestSchema: z.ZodObject<{
9
9
  name: z.ZodOptional<z.ZodString>;
10
10
  description: z.ZodOptional<z.ZodString>;
11
11
  on: z.ZodArray<z.ZodEnum<{
12
- output: "output";
13
12
  input: "input";
13
+ output: "output";
14
14
  signal: "signal";
15
15
  }>>;
16
16
  env: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -1,12 +1,12 @@
1
1
  import { z } from "zod";
2
2
  export declare const nodeKindSchema: z.ZodEnum<{
3
+ start: "start";
4
+ end: "end";
3
5
  ai: "ai";
4
6
  human: "human";
5
7
  program: "program";
6
8
  guardrail: "guardrail";
7
9
  wait: "wait";
8
- start: "start";
9
- end: "end";
10
10
  component: "component";
11
11
  }>;
12
12
  export type NodeKind = z.infer<typeof nodeKindSchema>;
@@ -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;
@@ -62,8 +58,17 @@ type PortLink = {
62
58
  from_port_id: string;
63
59
  to_port_id: string;
64
60
  };
65
- export declare const resolvePortSchemas: <N extends PropagationNode>(nodes: N[], connections: PortLink[]) => Map<string, JsonSchema>;
66
- export declare const propagateDerivedPorts: <N extends PropagationNode>(nodes: N[], connections: PortLink[]) => N[];
61
+ export declare const COPY_UPSTREAM_SCHEMA = "T";
62
+ export type DeclaredPortSchema = JsonSchema | typeof COPY_UPSTREAM_SCHEMA;
63
+ export type PropagationOptions<N> = {
64
+ viewPortSchemasOf?: (node: N) => {
65
+ in: DeclaredPortSchema;
66
+ approve: DeclaredPortSchema;
67
+ reject: JsonSchema;
68
+ } | null | undefined;
69
+ };
70
+ export declare const resolvePortSchemas: <N extends PropagationNode>(nodes: N[], connections: PortLink[], options?: PropagationOptions<N>) => Map<string, JsonSchema>;
71
+ export declare const propagateDerivedPorts: <N extends PropagationNode>(nodes: N[], connections: PortLink[], options?: PropagationOptions<N>) => N[];
67
72
  export type SchemaFit = "ok" | "mismatch" | "unknown";
68
73
  export declare const schemaFit: (sub: JsonSchema, sup: JsonSchema) => SchemaFit;
69
74
  export declare const isSchemaSubset: (sub: JsonSchema, sup: JsonSchema) => boolean;