@loomwork/ui-hydration 0.0.0-stage → 0.1.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.
Files changed (179) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +398 -2
  4. package/dist/cache.d.ts +71 -0
  5. package/dist/cache.d.ts.map +1 -0
  6. package/dist/cache.js +157 -0
  7. package/dist/cache.js.map +1 -0
  8. package/dist/chatSource.d.ts +105 -0
  9. package/dist/chatSource.d.ts.map +1 -0
  10. package/dist/chatSource.js +259 -0
  11. package/dist/chatSource.js.map +1 -0
  12. package/dist/client.d.ts +17 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +36 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/contentRefs.d.ts +78 -0
  17. package/dist/contentRefs.d.ts.map +1 -0
  18. package/dist/contentRefs.js +129 -0
  19. package/dist/contentRefs.js.map +1 -0
  20. package/dist/controller.d.ts +228 -0
  21. package/dist/controller.d.ts.map +1 -0
  22. package/dist/controller.js +454 -0
  23. package/dist/controller.js.map +1 -0
  24. package/dist/dataProvider.d.ts +53 -0
  25. package/dist/dataProvider.d.ts.map +1 -0
  26. package/dist/dataProvider.js +77 -0
  27. package/dist/dataProvider.js.map +1 -0
  28. package/dist/esm/cache.js +151 -0
  29. package/dist/esm/cache.js.map +1 -0
  30. package/dist/esm/chatSource.js +254 -0
  31. package/dist/esm/chatSource.js.map +1 -0
  32. package/dist/esm/client.js +15 -0
  33. package/dist/esm/client.js.map +1 -0
  34. package/dist/esm/contentRefs.js +119 -0
  35. package/dist/esm/contentRefs.js.map +1 -0
  36. package/dist/esm/controller.js +449 -0
  37. package/dist/esm/controller.js.map +1 -0
  38. package/dist/esm/dataProvider.js +72 -0
  39. package/dist/esm/dataProvider.js.map +1 -0
  40. package/dist/esm/index.js +15 -0
  41. package/dist/esm/index.js.map +1 -0
  42. package/dist/esm/invoker.js +2 -0
  43. package/dist/esm/invoker.js.map +1 -0
  44. package/dist/esm/invokers.js +310 -0
  45. package/dist/esm/invokers.js.map +1 -0
  46. package/dist/esm/loomEffectProvider.js +96 -0
  47. package/dist/esm/loomEffectProvider.js.map +1 -0
  48. package/dist/esm/params.js +207 -0
  49. package/dist/esm/params.js.map +1 -0
  50. package/dist/esm/path.js +24 -0
  51. package/dist/esm/path.js.map +1 -0
  52. package/dist/esm/plan.js +190 -0
  53. package/dist/esm/plan.js.map +1 -0
  54. package/dist/esm/projection.js +230 -0
  55. package/dist/esm/projection.js.map +1 -0
  56. package/dist/esm/provider.js +416 -0
  57. package/dist/esm/provider.js.map +1 -0
  58. package/dist/esm/repair.js +53 -0
  59. package/dist/esm/repair.js.map +1 -0
  60. package/dist/esm/reshape.js +156 -0
  61. package/dist/esm/reshape.js.map +1 -0
  62. package/dist/esm/restricted.js +54 -0
  63. package/dist/esm/restricted.js.map +1 -0
  64. package/dist/esm/scheduler.js +247 -0
  65. package/dist/esm/scheduler.js.map +1 -0
  66. package/dist/esm/select.js +18 -0
  67. package/dist/esm/select.js.map +1 -0
  68. package/dist/esm/shape.js +34 -0
  69. package/dist/esm/shape.js.map +1 -0
  70. package/dist/esm/split/client.js +309 -0
  71. package/dist/esm/split/client.js.map +1 -0
  72. package/dist/esm/split/index.js +3 -0
  73. package/dist/esm/split/index.js.map +1 -0
  74. package/dist/esm/split/server.js +488 -0
  75. package/dist/esm/split/server.js.map +1 -0
  76. package/dist/esm/split/shared.js +13 -0
  77. package/dist/esm/split/shared.js.map +1 -0
  78. package/dist/index.d.ts +29 -0
  79. package/dist/index.d.ts.map +1 -0
  80. package/dist/index.js +63 -0
  81. package/dist/index.js.map +1 -0
  82. package/dist/invoker.d.ts +10 -0
  83. package/dist/invoker.d.ts.map +1 -0
  84. package/dist/invoker.js +3 -0
  85. package/dist/invoker.js.map +1 -0
  86. package/dist/invokers.d.ts +36 -0
  87. package/dist/invokers.d.ts.map +1 -0
  88. package/dist/invokers.js +315 -0
  89. package/dist/invokers.js.map +1 -0
  90. package/dist/loomEffectProvider.d.ts +67 -0
  91. package/dist/loomEffectProvider.d.ts.map +1 -0
  92. package/dist/loomEffectProvider.js +101 -0
  93. package/dist/loomEffectProvider.js.map +1 -0
  94. package/dist/params.d.ts +103 -0
  95. package/dist/params.d.ts.map +1 -0
  96. package/dist/params.js +221 -0
  97. package/dist/params.js.map +1 -0
  98. package/dist/path.d.ts +12 -0
  99. package/dist/path.d.ts.map +1 -0
  100. package/dist/path.js +28 -0
  101. package/dist/path.js.map +1 -0
  102. package/dist/plan.d.ts +56 -0
  103. package/dist/plan.d.ts.map +1 -0
  104. package/dist/plan.js +199 -0
  105. package/dist/plan.js.map +1 -0
  106. package/dist/projection.d.ts +122 -0
  107. package/dist/projection.d.ts.map +1 -0
  108. package/dist/projection.js +238 -0
  109. package/dist/projection.js.map +1 -0
  110. package/dist/provider.d.ts +152 -0
  111. package/dist/provider.d.ts.map +1 -0
  112. package/dist/provider.js +421 -0
  113. package/dist/provider.js.map +1 -0
  114. package/dist/repair.d.ts +18 -0
  115. package/dist/repair.d.ts.map +1 -0
  116. package/dist/repair.js +57 -0
  117. package/dist/repair.js.map +1 -0
  118. package/dist/reshape.d.ts +32 -0
  119. package/dist/reshape.d.ts.map +1 -0
  120. package/dist/reshape.js +164 -0
  121. package/dist/reshape.js.map +1 -0
  122. package/dist/restricted.d.ts +21 -0
  123. package/dist/restricted.d.ts.map +1 -0
  124. package/dist/restricted.js +61 -0
  125. package/dist/restricted.js.map +1 -0
  126. package/dist/scheduler.d.ts +82 -0
  127. package/dist/scheduler.d.ts.map +1 -0
  128. package/dist/scheduler.js +254 -0
  129. package/dist/scheduler.js.map +1 -0
  130. package/dist/select.d.ts +34 -0
  131. package/dist/select.d.ts.map +1 -0
  132. package/dist/select.js +21 -0
  133. package/dist/select.js.map +1 -0
  134. package/dist/shape.d.ts +3 -0
  135. package/dist/shape.d.ts.map +1 -0
  136. package/dist/shape.js +37 -0
  137. package/dist/shape.js.map +1 -0
  138. package/dist/split/client.d.ts +132 -0
  139. package/dist/split/client.d.ts.map +1 -0
  140. package/dist/split/client.js +313 -0
  141. package/dist/split/client.js.map +1 -0
  142. package/dist/split/index.d.ts +5 -0
  143. package/dist/split/index.d.ts.map +1 -0
  144. package/dist/split/index.js +14 -0
  145. package/dist/split/index.js.map +1 -0
  146. package/dist/split/server.d.ts +160 -0
  147. package/dist/split/server.d.ts.map +1 -0
  148. package/dist/split/server.js +496 -0
  149. package/dist/split/server.js.map +1 -0
  150. package/dist/split/shared.d.ts +6 -0
  151. package/dist/split/shared.d.ts.map +1 -0
  152. package/dist/split/shared.js +19 -0
  153. package/dist/split/shared.js.map +1 -0
  154. package/package.json +77 -3
  155. package/src/cache.ts +211 -0
  156. package/src/chatSource.ts +374 -0
  157. package/src/client.ts +46 -0
  158. package/src/contentRefs.ts +195 -0
  159. package/src/controller.ts +708 -0
  160. package/src/dataProvider.ts +141 -0
  161. package/src/index.ts +86 -0
  162. package/src/invoker.ts +14 -0
  163. package/src/invokers.ts +348 -0
  164. package/src/loomEffectProvider.ts +136 -0
  165. package/src/params.ts +314 -0
  166. package/src/path.ts +26 -0
  167. package/src/plan.ts +197 -0
  168. package/src/projection.ts +297 -0
  169. package/src/provider.ts +657 -0
  170. package/src/repair.ts +70 -0
  171. package/src/reshape.ts +193 -0
  172. package/src/restricted.ts +69 -0
  173. package/src/scheduler.ts +284 -0
  174. package/src/select.ts +59 -0
  175. package/src/shape.ts +39 -0
  176. package/src/split/client.ts +449 -0
  177. package/src/split/index.ts +18 -0
  178. package/src/split/server.ts +746 -0
  179. package/src/split/shared.ts +18 -0
@@ -0,0 +1,103 @@
1
+ import type { Finding, MCPToolNode, ProviderSpec } from "@loomwork/ui-core";
2
+ export type LoadParams = Readonly<Record<string, unknown>>;
3
+ /** A time-range default a host may pass as `ControllerOptions.defaultParams`: the last 24 hours, relative to each pass. */
4
+ export declare const DEFAULT_TIME_RANGE: LoadParams;
5
+ /** `$now`, `$now-7d`, `$now+1h` (units s m h d w) as an ISO-8601 instant; `undefined` otherwise. */
6
+ export declare function resolveTimeToken(token: string, now: number): string | undefined;
7
+ /** Every token-shaped string leaf is resolved, or the whole value is refused. Other leaves pass. */
8
+ export declare function resolveTimeTokens(value: unknown, now: number, where: string): {
9
+ ok: true;
10
+ value: unknown;
11
+ } | {
12
+ ok: false;
13
+ finding: Finding;
14
+ };
15
+ /** A plain object `canonicalJson` can represent (no NaN/±Infinity, bigint, function, over-deep nest). */
16
+ export declare function checkParams(params: unknown): Finding | undefined;
17
+ /** Canonical identity of a param set (key order irrelevant); `""` for none. Throws only on what `checkParams` refuses. */
18
+ export declare function paramsKey(params: LoadParams): string;
19
+ /** Every argument name any step of the provider declares overridable, sorted. */
20
+ export declare function providerParams(spec: ProviderSpec): string[];
21
+ /**
22
+ * The named params the set actually holds (own keys only), as a fresh, FROZEN object — this is
23
+ * the object every step's `prepareArguments` call (and its argument hook) receives as `ctx.params`
24
+ * for the whole load, so a hook mutating it would leak into every other step's view of the load.
25
+ * Reads `params[n]` once per name: on params that crossed a trust boundary, snapshot them first
26
+ * (`snapshotParams`) — a live accessor that answered safely for an earlier, separate check (e.g.
27
+ * `checkParams` alone) is free to answer differently by the time THIS read runs.
28
+ */
29
+ export declare function pickParams(params: LoadParams, names: readonly string[]): LoadParams;
30
+ /**
31
+ * ONE plain, detached copy of untrusted params — a JSON round-trip through `canonicalJson`, so a
32
+ * value it refuses (a function, bigint, non-finite number, a cycle, past the depth cap) is caught
33
+ * HERE, and `JSON.parse` builds the result from that already-captured string alone: nothing
34
+ * downstream (`checkParams`, `pickParams`, `relevantParams`) ever reads a property of the SOURCE
35
+ * object again. Required wherever params cross a trust boundary (`serveHandle(s)`) — validating a
36
+ * live accessor and then separately picking named keys off it re-reads it, and a hostile getter is
37
+ * free to answer only the validating read safely and answer a later, picking read with something
38
+ * else entirely (observed: a plain value for the first two reads, a function for the third).
39
+ */
40
+ export declare function snapshotParams(params: unknown): {
41
+ ok: true;
42
+ params: LoadParams;
43
+ } | {
44
+ ok: false;
45
+ finding: Finding;
46
+ };
47
+ /** The params this provider can use: everything else cannot change its answer. */
48
+ export declare function relevantParams(spec: ProviderSpec, params: LoadParams): LoadParams;
49
+ export declare function applyParams(node: MCPToolNode, params: LoadParams): {
50
+ args: Record<string, unknown>;
51
+ findings: Finding[];
52
+ };
53
+ /** {@link applyParams}, plus which top-level arguments a param actually replaced (U9b needs them). */
54
+ export declare function applyParamsDetailed(node: MCPToolNode, params: LoadParams): {
55
+ args: Record<string, unknown>;
56
+ findings: Finding[];
57
+ replaced: string[];
58
+ };
59
+ export interface ArgumentHookContext {
60
+ readonly handle: string;
61
+ readonly stepId: string;
62
+ readonly toolName: string;
63
+ /**
64
+ * The step's data-provider kind; absent for an MCP step. A hook that branches on `toolName`
65
+ * must also read this: an MCP tool `x` and a loom effect `x` are different targets.
66
+ */
67
+ readonly kind?: string;
68
+ /**
69
+ * The load's params as declared — under `executeProvider`, only those some step of the provider
70
+ * declares overridable — with time tokens NOT resolved (`now` is the instant they resolve against).
71
+ */
72
+ readonly params: LoadParams;
73
+ /** The instant this load resolved its time tokens against (epoch ms). */
74
+ readonly now: number;
75
+ }
76
+ /** The host's last word on a step's arguments: after params, time tokens and templates; before the call. */
77
+ export type ArgumentHook = (args: Record<string, unknown>, ctx: ArgumentHookContext) => Record<string, unknown>;
78
+ export interface PrepareContext {
79
+ readonly handle: string;
80
+ readonly params: LoadParams;
81
+ readonly now: number;
82
+ readonly hook?: ArgumentHook;
83
+ /**
84
+ * Runs after params are applied and before time tokens, templates or the hook (U9b's content-reference
85
+ * guard, `contentRefs.ts`, is the one user); a returned finding refuses the step. `replaced` names the
86
+ * top-level arguments a param actually replaced.
87
+ */
88
+ readonly guard?: (node: MCPToolNode, applied: {
89
+ readonly args: Record<string, unknown>;
90
+ readonly replaced: readonly string[];
91
+ }) => Finding | undefined;
92
+ /** Rewrites the arguments after templates resolve and before the hook (U9b strips JSON null at marked paths). */
93
+ readonly finish?: (args: Record<string, unknown>) => Record<string, unknown>;
94
+ }
95
+ export declare function prepareArguments(node: MCPToolNode, outputs: ReadonlyMap<string, unknown>, ctx: PrepareContext): {
96
+ ok: true;
97
+ args: Record<string, unknown>;
98
+ findings: Finding[];
99
+ } | {
100
+ ok: false;
101
+ finding: Finding;
102
+ };
103
+ //# sourceMappingURL=params.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.d.ts","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"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"}
package/dist/params.js ADDED
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_TIME_RANGE = void 0;
4
+ exports.resolveTimeToken = resolveTimeToken;
5
+ exports.resolveTimeTokens = resolveTimeTokens;
6
+ exports.checkParams = checkParams;
7
+ exports.paramsKey = paramsKey;
8
+ exports.providerParams = providerParams;
9
+ exports.pickParams = pickParams;
10
+ exports.snapshotParams = snapshotParams;
11
+ exports.relevantParams = relevantParams;
12
+ exports.applyParams = applyParams;
13
+ exports.applyParamsDetailed = applyParamsDetailed;
14
+ exports.prepareArguments = prepareArguments;
15
+ const core_1 = require("@loomwork/core");
16
+ const plan_1 = require("./plan");
17
+ /** A time-range default a host may pass as `ControllerOptions.defaultParams`: the last 24 hours, relative to each pass. */
18
+ exports.DEFAULT_TIME_RANGE = Object.freeze({ from: "$now-24h", to: "$now" });
19
+ /** Token-shaped: `$now` not followed by a word character. `$nowcast` is an ordinary string. */
20
+ const TOKEN_LIKE = /^\$now(?![A-Za-z0-9_])/;
21
+ const TIME_TOKEN = /^\$now(?:([+-])(\d{1,9})([smhdw]))?$/;
22
+ const UNIT_MS = {
23
+ s: 1_000,
24
+ m: 60_000,
25
+ h: 3_600_000,
26
+ d: 86_400_000,
27
+ w: 604_800_000,
28
+ };
29
+ const bad = (code, message) => ({ code, severity: "error", message });
30
+ const note = (code, message) => ({ code, severity: "warn", message });
31
+ /** An async argument hook returns one of these: `prepareArguments` never awaits it. */
32
+ function isThenable(v) {
33
+ return (v !== null && typeof v === "object" && typeof v.then === "function");
34
+ }
35
+ /** `$now`, `$now-7d`, `$now+1h` (units s m h d w) as an ISO-8601 instant; `undefined` otherwise. */
36
+ function resolveTimeToken(token, now) {
37
+ const m = TIME_TOKEN.exec(token);
38
+ if (!m)
39
+ return undefined;
40
+ const offset = m[1] === undefined
41
+ ? 0
42
+ : (m[1] === "-" ? -1 : 1) * Number(m[2]) * UNIT_MS[m[3]];
43
+ const at = new Date(now + offset);
44
+ // Past ±8.64e15 ms a Date is invalid and `toISOString` would throw: refuse instead.
45
+ return Number.isNaN(at.getTime()) ? undefined : at.toISOString();
46
+ }
47
+ /** Every token-shaped string leaf is resolved, or the whole value is refused. Other leaves pass. */
48
+ function resolveTimeTokens(value, now, where) {
49
+ let failure;
50
+ const visit = (v) => {
51
+ if (failure)
52
+ return v;
53
+ if (typeof v === "string") {
54
+ if (!TOKEN_LIKE.test(v))
55
+ return v;
56
+ const iso = resolveTimeToken(v, now);
57
+ if (iso !== undefined)
58
+ return iso;
59
+ failure = bad("invalid-time-token", `${where}: "${v.slice(0, 64)}" is not a relative-time token ($now, $now-7d, $now+1h; units s m h d w)`);
60
+ return v;
61
+ }
62
+ if (Array.isArray(v))
63
+ return v.map(visit);
64
+ if (v !== null && typeof v === "object")
65
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, visit(x)]));
66
+ return v;
67
+ };
68
+ const out = visit(value);
69
+ return failure ? { ok: false, finding: failure } : { ok: true, value: out };
70
+ }
71
+ const STEP_REF = /\{\{\s*steps\./;
72
+ function containsStepTemplate(v) {
73
+ if (typeof v === "string")
74
+ return STEP_REF.test(v);
75
+ if (Array.isArray(v))
76
+ return v.some(containsStepTemplate);
77
+ if (v !== null && typeof v === "object")
78
+ return Object.values(v).some(containsStepTemplate);
79
+ return false;
80
+ }
81
+ /** A plain object `canonicalJson` can represent (no NaN/±Infinity, bigint, function, over-deep nest). */
82
+ function checkParams(params) {
83
+ if (params === null || typeof params !== "object" || Array.isArray(params))
84
+ return bad("invalid-params", "params must be a plain object");
85
+ try {
86
+ (0, core_1.canonicalJson)(params);
87
+ return undefined;
88
+ }
89
+ catch (e) {
90
+ return bad("invalid-params", `params cannot be represented as JSON: ${e instanceof Error ? e.message : String(e)}`);
91
+ }
92
+ }
93
+ /** Canonical identity of a param set (key order irrelevant); `""` for none. Throws only on what `checkParams` refuses. */
94
+ function paramsKey(params) {
95
+ return Object.keys(params).length === 0 ? "" : (0, core_1.fnv1a64)((0, core_1.canonicalJson)(params));
96
+ }
97
+ /** Every argument name any step of the provider declares overridable, sorted. */
98
+ function providerParams(spec) {
99
+ const names = new Set();
100
+ for (const step of spec.executionGraph)
101
+ for (const n of step.overridable ?? [])
102
+ names.add(n);
103
+ return [...names].sort();
104
+ }
105
+ /**
106
+ * The named params the set actually holds (own keys only), as a fresh, FROZEN object — this is
107
+ * the object every step's `prepareArguments` call (and its argument hook) receives as `ctx.params`
108
+ * for the whole load, so a hook mutating it would leak into every other step's view of the load.
109
+ * Reads `params[n]` once per name: on params that crossed a trust boundary, snapshot them first
110
+ * (`snapshotParams`) — a live accessor that answered safely for an earlier, separate check (e.g.
111
+ * `checkParams` alone) is free to answer differently by the time THIS read runs.
112
+ */
113
+ function pickParams(params, names) {
114
+ return Object.freeze(Object.fromEntries(names.filter((n) => Object.hasOwn(params, n)).map((n) => [n, params[n]])));
115
+ }
116
+ /**
117
+ * ONE plain, detached copy of untrusted params — a JSON round-trip through `canonicalJson`, so a
118
+ * value it refuses (a function, bigint, non-finite number, a cycle, past the depth cap) is caught
119
+ * HERE, and `JSON.parse` builds the result from that already-captured string alone: nothing
120
+ * downstream (`checkParams`, `pickParams`, `relevantParams`) ever reads a property of the SOURCE
121
+ * object again. Required wherever params cross a trust boundary (`serveHandle(s)`) — validating a
122
+ * live accessor and then separately picking named keys off it re-reads it, and a hostile getter is
123
+ * free to answer only the validating read safely and answer a later, picking read with something
124
+ * else entirely (observed: a plain value for the first two reads, a function for the third).
125
+ */
126
+ function snapshotParams(params) {
127
+ if (params === null || typeof params !== "object" || Array.isArray(params))
128
+ return { ok: false, finding: bad("invalid-params", "params must be a plain object") };
129
+ try {
130
+ return { ok: true, params: JSON.parse((0, core_1.canonicalJson)(params)) };
131
+ }
132
+ catch (e) {
133
+ return {
134
+ ok: false,
135
+ finding: bad("invalid-params", `params cannot be represented as JSON: ${e instanceof Error ? e.message : String(e)}`),
136
+ };
137
+ }
138
+ }
139
+ /** The params this provider can use: everything else cannot change its answer. */
140
+ function relevantParams(spec, params) {
141
+ return pickParams(params, providerParams(spec));
142
+ }
143
+ function applyParams(node, params) {
144
+ const { args, findings } = applyParamsDetailed(node, params);
145
+ return { args, findings };
146
+ }
147
+ /** {@link applyParams}, plus which top-level arguments a param actually replaced (U9b needs them). */
148
+ function applyParamsDetailed(node, params) {
149
+ const names = node.overridable ?? [];
150
+ const findings = [];
151
+ const replaced = [];
152
+ if (names.length === 0)
153
+ return { args: node.arguments, findings, replaced };
154
+ // A Map, then `Object.fromEntries`: every write is CreateDataProperty, so an argument named
155
+ // `__proto__` lands as an own key instead of setting a prototype.
156
+ const args = new Map(Object.entries(node.arguments));
157
+ for (const name of names) {
158
+ if (!Object.hasOwn(params, name))
159
+ continue;
160
+ if (containsStepTemplate(args.get(name))) {
161
+ findings.push(note("param-templated", `step "${node.stepId}": argument "${name}" is a {{steps.…}} template — another step's output — so no param replaces it`));
162
+ continue;
163
+ }
164
+ const value = params[name];
165
+ if (containsStepTemplate(value)) {
166
+ findings.push(note("param-template-refused", `step "${node.stepId}": the param for "${name}" contains a {{steps.…}} template; a param is a literal, so it is not applied`));
167
+ continue;
168
+ }
169
+ args.set(name, value);
170
+ replaced.push(name);
171
+ }
172
+ return { args: Object.fromEntries(args), findings, replaced };
173
+ }
174
+ function prepareArguments(node, outputs, ctx) {
175
+ const applied = applyParamsDetailed(node, ctx.params);
176
+ const refused = ctx.guard?.(node, applied);
177
+ if (refused !== undefined)
178
+ return { ok: false, finding: refused };
179
+ const timed = resolveTimeTokens(applied.args, ctx.now, `step "${node.stepId}"`);
180
+ if (!timed.ok)
181
+ return timed;
182
+ const templated = (0, plan_1.resolveArguments)({ ...node, arguments: timed.value }, outputs);
183
+ if (!templated.ok)
184
+ return templated;
185
+ const finished = ctx.finish ? ctx.finish(templated.args) : templated.args;
186
+ if (ctx.hook === undefined)
187
+ return { ok: true, args: finished, findings: applied.findings };
188
+ let hooked;
189
+ try {
190
+ hooked = ctx.hook(finished, {
191
+ handle: ctx.handle,
192
+ stepId: node.stepId,
193
+ toolName: node.toolName,
194
+ ...(node.kind !== undefined && { kind: node.kind }),
195
+ params: ctx.params,
196
+ now: ctx.now,
197
+ });
198
+ }
199
+ catch (e) {
200
+ return {
201
+ ok: false,
202
+ finding: bad("argument-hook-failed", `step "${node.stepId}": the argument hook threw: ${e instanceof Error ? e.message : String(e)}`),
203
+ };
204
+ }
205
+ if (isThenable(hooked)) {
206
+ // The step callback never awaits a hook (it is documented as synchronous): swallow whatever
207
+ // this thenable eventually does, so an async hook never surfaces as an unhandled rejection.
208
+ hooked.then(() => { }, () => { });
209
+ return {
210
+ ok: false,
211
+ finding: bad("argument-hook-failed", `step "${node.stepId}": the argument hook returned a thenable — hooks must return the arguments object synchronously, not a Promise`),
212
+ };
213
+ }
214
+ if (hooked === null || typeof hooked !== "object" || Array.isArray(hooked))
215
+ return {
216
+ ok: false,
217
+ finding: bad("argument-hook-failed", `step "${node.stepId}": the argument hook returned ${hooked === null ? "null" : Array.isArray(hooked) ? "an array" : typeof hooked}, not an arguments object`),
218
+ };
219
+ return { ok: true, args: hooked, findings: applied.findings };
220
+ }
221
+ //# sourceMappingURL=params.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"params.js","sourceRoot":"","sources":["../src/params.ts"],"names":[],"mappings":"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"}
package/dist/path.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ /** The hydration projection's top-level key; equal to `@loomwork/ui-core`'s constant of the same name. */
2
+ export declare const HYDRATION_VALUES_PREFIX = "values";
3
+ /**
4
+ * The hydration handle a binding path names: the exact inverse of core's `hydrationPath`.
5
+ * `undefined` for a path not of the form `values.<handle>` with a non-empty handle (including the
6
+ * bare prefix and `values.`). Everything after the first `values.` is the handle, spelled exactly
7
+ * as `hydrationPath` spelled it. Note: blueprint-runtime resolves a binding path by splitting on
8
+ * `.`, so a handle that itself contains a dot round-trips here but will not resolve at render time —
9
+ * keep handles dot-free. A reserved handle (`__proto__` etc.) is returned as-is; the store refuses it.
10
+ */
11
+ export declare function handleOfPath(path: string): string | undefined;
12
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":"AAUA,0GAA0G;AAC1G,eAAO,MAAM,uBAAuB,WAA6C,CAAC;AAElF;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI7D"}
package/dist/path.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ // ── Binding path ↔ hydration handle, browser-safe ────────────────────────────────
3
+ // A compiled widget binds to `values.<handle>`. `@loomwork/ui-core` owns that spelling for the
4
+ // compile side (`hydrationPath`), but the client entry must not pull core's runtime module graph
5
+ // into a browser bundle, and core cannot import hydration (hydration depends on core). So the
6
+ // client entry carries its own definition of the prefix and the inverse, pinned against core's
7
+ // twice: at compile time (the `satisfies` below, type-only, erased at build) and by
8
+ // `path.test.ts`, which compares the values and round-trips `hydrationPath`.
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.HYDRATION_VALUES_PREFIX = void 0;
11
+ exports.handleOfPath = handleOfPath;
12
+ /** The hydration projection's top-level key; equal to `@loomwork/ui-core`'s constant of the same name. */
13
+ exports.HYDRATION_VALUES_PREFIX = "values";
14
+ /**
15
+ * The hydration handle a binding path names: the exact inverse of core's `hydrationPath`.
16
+ * `undefined` for a path not of the form `values.<handle>` with a non-empty handle (including the
17
+ * bare prefix and `values.`). Everything after the first `values.` is the handle, spelled exactly
18
+ * as `hydrationPath` spelled it. Note: blueprint-runtime resolves a binding path by splitting on
19
+ * `.`, so a handle that itself contains a dot round-trips here but will not resolve at render time —
20
+ * keep handles dot-free. A reserved handle (`__proto__` etc.) is returned as-is; the store refuses it.
21
+ */
22
+ function handleOfPath(path) {
23
+ const head = `${exports.HYDRATION_VALUES_PREFIX}.`;
24
+ if (!path.startsWith(head) || path.length === head.length)
25
+ return undefined;
26
+ return path.slice(head.length);
27
+ }
28
+ //# sourceMappingURL=path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.js","sourceRoot":"","sources":["../src/path.ts"],"names":[],"mappings":";AAAA,oFAAoF;AACpF,+FAA+F;AAC/F,iGAAiG;AACjG,8FAA8F;AAC9F,+FAA+F;AAC/F,oFAAoF;AACpF,6EAA6E;;;AAe7E,oCAIC;AAfD,0GAA0G;AAC7F,QAAA,uBAAuB,GAAG,QAA0C,CAAC;AAElF;;;;;;;GAOG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,MAAM,IAAI,GAAG,GAAG,+BAAuB,GAAG,CAAC;IAC3C,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC5E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC"}
package/dist/plan.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ import type { Finding, MCPToolNode } from "@loomwork/ui-core";
2
+ /**
3
+ * Kahn's algorithm over step IDS, O(V + E): in-degree counts (distinct in-graph dependencies per
4
+ * step) and a dependents list built once, then a FIFO of ids whose level never decreases. A step's
5
+ * wave is 1 + the latest wave among its in-graph dependencies (0 with none); an id is placed at the
6
+ * earliest wave any step carrying it is ready. Waves are then filled in the input's own order, so a
7
+ * wave lists its steps exactly as the input does. This is the semantics of the original
8
+ * re-filtering loop, pinned by an equivalence test: a step whose id another step already placed in
9
+ * an EARLIER wave never runs (a duplicate id), and `cycle` lists — in input order — every step
10
+ * whose id is never placed (a cycle, or anything behind one).
11
+ */
12
+ export declare function topoWaves(nodes: readonly MCPToolNode[]): {
13
+ waves: MCPToolNode[][];
14
+ cycle: string[];
15
+ };
16
+ export declare function terminalSteps(nodes: readonly MCPToolNode[]): MCPToolNode[];
17
+ /**
18
+ * Why a path walk found nothing. `out-of-range`: the walk stopped at an `[i]` applied to a value
19
+ * that IS an array, with `i >= length` — the path is right, the upstream just has no such row (the
20
+ * row count is DATA). `absent`: every other miss — a missing own key, `null` or a primitive
21
+ * mid-path, an index on a non-array, a malformed segment — a BAD PATH.
22
+ */
23
+ export type PathMiss = "out-of-range" | "absent";
24
+ /** Dotted path with optional `[i]` indices, own properties only. A present `null` resolves to `null`. */
25
+ export declare function walkPath(obj: unknown, dotted: string): {
26
+ ok: true;
27
+ value: unknown;
28
+ } | {
29
+ ok: false;
30
+ cause: PathMiss;
31
+ };
32
+ /** `walkPath`'s value, or `undefined` when anything is missing. */
33
+ export declare function resolvePath(obj: unknown, dotted: string): unknown;
34
+ export declare const STEP_TEMPLATE: RegExp;
35
+ /**
36
+ * Every step id `arguments` references through a `{{steps.<id>.output…}}` template, in first-seen
37
+ * order, deduplicated — the same template grammar `resolveArguments` substitutes, so a planner can
38
+ * check a graph it builds before it ships. At execution a reference resolves only against a step
39
+ * in a STRICTLY EARLIER `topoWaves` wave than the referencing step; a step outside the graph, or in
40
+ * the same or a later wave, has no output yet and is `unresolved-step-reference`.
41
+ */
42
+ export declare function stepReferences(args: unknown): string[];
43
+ /**
44
+ * Substitute every `{{steps.<id>.output.<path>}}` in the step's arguments. EVERY template is
45
+ * walked: a bad path anywhere (`absent`, or a step with no output) is `unresolved-step-reference`
46
+ * (an error) and wins; otherwise an index past the end of a present upstream array is
47
+ * `upstream-empty` (a warn — the upstream answered with no such row; loom ADR 0100's `empty`).
48
+ */
49
+ export declare function resolveArguments(node: MCPToolNode, outputs: ReadonlyMap<string, unknown>): {
50
+ ok: true;
51
+ args: Record<string, unknown>;
52
+ } | {
53
+ ok: false;
54
+ finding: Finding;
55
+ };
56
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAY9D;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG;IACxD,KAAK,EAAE,WAAW,EAAE,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CA+CA;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,WAAW,EAAE,GAAG,WAAW,EAAE,CAG1E;AAID;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,cAAc,GAAG,QAAQ,CAAC;AAEjD,yGAAyG;AACzG,wBAAgB,QAAQ,CACtB,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,MAAM,GACb;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAiB/D;AAED,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED,eAAO,MAAM,aAAa,QAAyD,CAAC;AAEpF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,EAAE,CAWtD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,GACpC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA+C/E"}
package/dist/plan.js ADDED
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STEP_TEMPLATE = void 0;
4
+ exports.topoWaves = topoWaves;
5
+ exports.terminalSteps = terminalSteps;
6
+ exports.walkPath = walkPath;
7
+ exports.resolvePath = resolvePath;
8
+ exports.stepReferences = stepReferences;
9
+ exports.resolveArguments = resolveArguments;
10
+ // ── Graph scheduling and argument templating ──────────────────────────────────
11
+ // A ProviderSpec's executionGraph is a DAG of MCP steps. Steps run in topological WAVES (every
12
+ // step in a wave has all its in-graph dependencies in earlier waves). A dependency naming a step
13
+ // outside the graph is treated as satisfied — the planner may reference a step it pruned — and a
14
+ // cycle is reported, never looped over. Arguments may embed `{{steps.<id>.output.<path>}}`,
15
+ // resolved from an earlier step's raw output over OWN properties only: the path is model-written
16
+ // and walked over a tool response, so an unguarded walk is a prototype-pollution read. An index
17
+ // past the end of a present array is not a bad path — the upstream answered with no such row —
18
+ // and is reported apart from every other miss (`walkPath`), so it can publish `empty`.
19
+ /**
20
+ * Kahn's algorithm over step IDS, O(V + E): in-degree counts (distinct in-graph dependencies per
21
+ * step) and a dependents list built once, then a FIFO of ids whose level never decreases. A step's
22
+ * wave is 1 + the latest wave among its in-graph dependencies (0 with none); an id is placed at the
23
+ * earliest wave any step carrying it is ready. Waves are then filled in the input's own order, so a
24
+ * wave lists its steps exactly as the input does. This is the semantics of the original
25
+ * re-filtering loop, pinned by an equivalence test: a step whose id another step already placed in
26
+ * an EARLIER wave never runs (a duplicate id), and `cycle` lists — in input order — every step
27
+ * whose id is never placed (a cycle, or anything behind one).
28
+ */
29
+ function topoWaves(nodes) {
30
+ const ids = new Set(nodes.map((n) => n.stepId));
31
+ const pending = new Array(nodes.length);
32
+ const readyAt = new Array(nodes.length).fill(-1);
33
+ const latestDep = new Array(nodes.length).fill(-1);
34
+ const dependents = new Map();
35
+ const placedAt = new Map();
36
+ const queue = [];
37
+ const ready = (i, level) => {
38
+ readyAt[i] = level;
39
+ const id = nodes[i].stepId;
40
+ if (placedAt.has(id))
41
+ return;
42
+ placedAt.set(id, level);
43
+ queue.push(id);
44
+ };
45
+ nodes.forEach((n, i) => {
46
+ const deps = new Set(n.dependsOn.filter((d) => ids.has(d)));
47
+ pending[i] = deps.size;
48
+ for (const d of deps) {
49
+ const list = dependents.get(d);
50
+ if (list === undefined)
51
+ dependents.set(d, [i]);
52
+ else
53
+ list.push(i);
54
+ }
55
+ });
56
+ nodes.forEach((_, i) => {
57
+ if (pending[i] === 0)
58
+ ready(i, 0);
59
+ });
60
+ for (let head = 0; head < queue.length; head++) {
61
+ const id = queue[head];
62
+ const level = placedAt.get(id);
63
+ for (const i of dependents.get(id) ?? []) {
64
+ latestDep[i] = Math.max(latestDep[i], level);
65
+ pending[i] = pending[i] - 1;
66
+ if (pending[i] === 0)
67
+ ready(i, latestDep[i] + 1);
68
+ }
69
+ }
70
+ const waves = [];
71
+ const cycle = [];
72
+ nodes.forEach((n, i) => {
73
+ const at = placedAt.get(n.stepId);
74
+ if (at === undefined)
75
+ cycle.push(n.stepId);
76
+ else if (readyAt[i] === at) {
77
+ while (waves.length <= at)
78
+ waves.push([]);
79
+ waves[at].push(n);
80
+ }
81
+ });
82
+ return { waves, cycle };
83
+ }
84
+ function terminalSteps(nodes) {
85
+ const depended = new Set(nodes.flatMap((n) => n.dependsOn));
86
+ return nodes.filter((n) => !depended.has(n.stepId));
87
+ }
88
+ const SEGMENT = /^([^[\]]+)((?:\[\d+\])*)$/;
89
+ /** Dotted path with optional `[i]` indices, own properties only. A present `null` resolves to `null`. */
90
+ function walkPath(obj, dotted) {
91
+ const absent = { ok: false, cause: "absent" };
92
+ let cur = obj;
93
+ for (const part of dotted.split(".")) {
94
+ const m = SEGMENT.exec(part);
95
+ if (!m || cur === null || typeof cur !== "object")
96
+ return absent;
97
+ const key = m[1];
98
+ if (!Object.hasOwn(cur, key))
99
+ return absent;
100
+ cur = cur[key];
101
+ for (const idx of (m[2] ?? "").match(/\d+/g) ?? []) {
102
+ if (!Array.isArray(cur))
103
+ return absent;
104
+ const i = Number(idx);
105
+ if (i >= cur.length)
106
+ return { ok: false, cause: "out-of-range" };
107
+ cur = cur[i];
108
+ }
109
+ }
110
+ return { ok: true, value: cur };
111
+ }
112
+ /** `walkPath`'s value, or `undefined` when anything is missing. */
113
+ function resolvePath(obj, dotted) {
114
+ const r = walkPath(obj, dotted);
115
+ return r.ok ? r.value : undefined;
116
+ }
117
+ exports.STEP_TEMPLATE = /^\{\{steps\.([A-Za-z0-9_-]+)\.output(?:\.(.+))?\}\}$/;
118
+ /**
119
+ * Every step id `arguments` references through a `{{steps.<id>.output…}}` template, in first-seen
120
+ * order, deduplicated — the same template grammar `resolveArguments` substitutes, so a planner can
121
+ * check a graph it builds before it ships. At execution a reference resolves only against a step
122
+ * in a STRICTLY EARLIER `topoWaves` wave than the referencing step; a step outside the graph, or in
123
+ * the same or a later wave, has no output yet and is `unresolved-step-reference`.
124
+ */
125
+ function stepReferences(args) {
126
+ const found = new Set();
127
+ const visit = (v) => {
128
+ if (typeof v === "string") {
129
+ const m = exports.STEP_TEMPLATE.exec(v);
130
+ if (m)
131
+ found.add(m[1]);
132
+ }
133
+ else if (Array.isArray(v))
134
+ for (const x of v)
135
+ visit(x);
136
+ else if (v !== null && typeof v === "object")
137
+ for (const x of Object.values(v))
138
+ visit(x);
139
+ };
140
+ visit(args);
141
+ return [...found];
142
+ }
143
+ /**
144
+ * Substitute every `{{steps.<id>.output.<path>}}` in the step's arguments. EVERY template is
145
+ * walked: a bad path anywhere (`absent`, or a step with no output) is `unresolved-step-reference`
146
+ * (an error) and wins; otherwise an index past the end of a present upstream array is
147
+ * `upstream-empty` (a warn — the upstream answered with no such row; loom ADR 0100's `empty`).
148
+ */
149
+ function resolveArguments(node, outputs) {
150
+ let bad;
151
+ let empty;
152
+ const visit = (v) => {
153
+ if (typeof v === "string") {
154
+ const m = exports.STEP_TEMPLATE.exec(v);
155
+ if (!m)
156
+ return v;
157
+ const stepId = m[1];
158
+ if (!outputs.has(stepId)) {
159
+ bad ??= {
160
+ code: "unresolved-step-reference",
161
+ severity: "error",
162
+ message: `step "${node.stepId}" references "${stepId}", which has no output`,
163
+ };
164
+ return v;
165
+ }
166
+ const path = m[2];
167
+ const out = path === undefined
168
+ ? { ok: true, value: outputs.get(stepId) }
169
+ : walkPath(outputs.get(stepId), path);
170
+ // A present `undefined` (never produced by JSON) is treated as nothing, as before.
171
+ if (out.ok && out.value !== undefined)
172
+ return out.value;
173
+ if (!out.ok && out.cause === "out-of-range")
174
+ empty ??= {
175
+ code: "upstream-empty",
176
+ severity: "warn",
177
+ message: `step "${node.stepId}" references "${stepId}" at path "${path ?? ""}": the upstream has no such row`,
178
+ };
179
+ else
180
+ bad ??= {
181
+ code: "unresolved-step-reference",
182
+ severity: "error",
183
+ message: `step "${node.stepId}" references "${stepId}" at path "${path ?? ""}", which resolves to nothing`,
184
+ };
185
+ return v;
186
+ }
187
+ if (Array.isArray(v))
188
+ return v.map(visit);
189
+ // `Object.fromEntries` writes with CreateDataProperty: an own `__proto__` argument stays an
190
+ // own key instead of becoming the args object's prototype (an assignment would set it).
191
+ if (v !== null && typeof v === "object")
192
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, visit(x)]));
193
+ return v;
194
+ };
195
+ const args = visit(node.arguments);
196
+ const failure = bad ?? empty;
197
+ return failure ? { ok: false, finding: failure } : { ok: true, args };
198
+ }
199
+ //# sourceMappingURL=plan.js.map