@smithers-orchestrator/graph 0.23.0 → 0.24.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smithers-orchestrator/graph",
3
- "version": "0.23.0",
3
+ "version": "0.24.2",
4
4
  "description": "Framework-neutral Smithers workflow graph model and extraction helpers",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "drizzle-orm": "^0.45.2",
24
24
  "zod": "^4.3.6",
25
- "@smithers-orchestrator/errors": "0.23.0"
25
+ "@smithers-orchestrator/errors": "0.24.2"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/bun": "latest",
@@ -0,0 +1 @@
1
+ export type { TaskAspects } from "./types";
@@ -4,10 +4,10 @@
4
4
  // @smithers-type-exports-end
5
5
 
6
6
  import { resolveStableId } from "@smithers-orchestrator/graph/utils/tree-ids";
7
- import { isAbsolute, resolve as resolvePath } from "node:path";
8
7
  import { getTableName } from "drizzle-orm";
9
8
  import { DEFAULT_MERGE_QUEUE_CONCURRENCY, WORKTREE_EMPTY_PATH_ERROR, } from "@smithers-orchestrator/graph/constants";
10
9
  import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
10
+ import { resolveWorktreePath } from "@smithers-orchestrator/graph/worktree-path";
11
11
 
12
12
  /** @typedef {import("../ExtractOptions.ts").ExtractOptions} ExtractOptions */
13
13
  /** @typedef {import("../ExtractResult.ts").ExtractResult} ExtractResult */
@@ -251,13 +251,7 @@ export function extractFromHost(root, opts) {
251
251
  if (!pathVal) {
252
252
  throw new SmithersError("WORKTREE_EMPTY_PATH", WORKTREE_EMPTY_PATH_ERROR);
253
253
  }
254
- const baseRoot = opts?.baseRootDir;
255
- const base = typeof baseRoot === "string" && baseRoot.length > 0
256
- ? baseRoot
257
- : process.cwd();
258
- const normPath = isAbsolute(pathVal)
259
- ? resolvePath(pathVal)
260
- : resolvePath(base, pathVal);
254
+ const normPath = resolveWorktreePath(pathVal, { baseRootDir: opts?.baseRootDir });
261
255
  const branch = node.rawProps?.branch ? String(node.rawProps.branch) : undefined;
262
256
  const baseBranch = node.rawProps?.baseBranch ? String(node.rawProps.baseBranch) : undefined;
263
257
  nextWorktreeStack = [...worktreeStack, { id, path: normPath, branch, baseBranch }];
@@ -464,6 +458,7 @@ export function extractFromHost(root, opts) {
464
458
  config: {
465
459
  image: raw.image,
466
460
  env: raw.env,
461
+ egress: raw.egress,
467
462
  ports: raw.ports,
468
463
  volumes: raw.volumes,
469
464
  memoryLimit: raw.memoryLimit,
@@ -488,6 +483,7 @@ export function extractFromHost(root, opts) {
488
483
  __sandboxConfig: {
489
484
  image: raw.image,
490
485
  env: raw.env,
486
+ egress: raw.egress,
491
487
  ports: raw.ports,
492
488
  volumes: raw.volumes,
493
489
  memoryLimit: raw.memoryLimit,
package/src/extract.js CHANGED
@@ -1,7 +1,7 @@
1
- import { isAbsolute, resolve as resolvePath } from "node:path";
2
1
  import { getTableName } from "drizzle-orm";
3
2
  import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
4
3
  import { validateForkSources } from "./validateForkSources.js";
4
+ import { resolveWorktreePath } from "./worktree-path.js";
5
5
  /** @typedef {import("./TaskDescriptor.ts").TaskDescriptor} TaskDescriptor */
6
6
  /** @typedef {import("./XmlNode.ts").XmlNode} XmlNode */
7
7
  /** @typedef {import("./ExtractOptions.ts").ExtractOptions} ExtractOptions */
@@ -248,6 +248,49 @@ function approvalAutoApprove(value) {
248
248
  : {}),
249
249
  };
250
250
  }
251
+ /**
252
+ * Normalize the `__aspects` element prop attached by `<Task>` into the
253
+ * `TaskAspects` budget metadata the engine enforces. Only the budget configs
254
+ * are kept; the render-time accumulator and tracking flags are dropped (the
255
+ * engine keeps its own durable per-run accumulator and budgets enforce
256
+ * regardless of tracking).
257
+ *
258
+ * @param {unknown} value
259
+ * @returns {import("./types").TaskAspects | undefined}
260
+ */
261
+ function aspects(value) {
262
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
263
+ return undefined;
264
+ }
265
+ const raw = /** @type {Record<string, unknown>} */ (value);
266
+ /** @type {import("./types").TaskAspects} */
267
+ const out = {};
268
+ const token = raw.tokenBudget;
269
+ if (token && typeof token === "object" && !Array.isArray(token) &&
270
+ typeof (/** @type {Record<string, unknown>} */ (token).max) === "number") {
271
+ const t = /** @type {Record<string, unknown>} */ (token);
272
+ out.tokenBudget = {
273
+ max: /** @type {number} */ (t.max),
274
+ ...(typeof t.perTask === "number" ? { perTask: t.perTask } : {}),
275
+ ...(t.onExceeded === "warn" || t.onExceeded === "skip-remaining" || t.onExceeded === "fail"
276
+ ? { onExceeded: /** @type {"fail" | "warn" | "skip-remaining"} */ (t.onExceeded) }
277
+ : {}),
278
+ };
279
+ }
280
+ const latency = raw.latencySlo;
281
+ if (latency && typeof latency === "object" && !Array.isArray(latency) &&
282
+ typeof (/** @type {Record<string, unknown>} */ (latency).maxMs) === "number") {
283
+ const l = /** @type {Record<string, unknown>} */ (latency);
284
+ out.latencySlo = {
285
+ maxMs: /** @type {number} */ (l.maxMs),
286
+ ...(typeof l.perTask === "number" ? { perTask: l.perTask } : {}),
287
+ ...(l.onExceeded === "warn" || l.onExceeded === "fail"
288
+ ? { onExceeded: /** @type {"fail" | "warn"} */ (l.onExceeded) }
289
+ : {}),
290
+ };
291
+ }
292
+ return out.tokenBudget || out.latencySlo ? out : undefined;
293
+ }
251
294
  /**
252
295
  * @param {"parallel" | "merge-queue"} tag
253
296
  * @param {Record<string, unknown>} raw
@@ -364,14 +407,11 @@ export function extractGraph(root, opts) {
364
407
  if (!pathVal) {
365
408
  throw new SmithersError("WORKTREE_EMPTY_PATH", WORKTREE_EMPTY_PATH_ERROR);
366
409
  }
367
- const base = typeof opts?.baseRootDir === "string" && opts.baseRootDir.length > 0
368
- ? opts.baseRootDir
369
- : process.cwd();
370
410
  nextWorktreeStack = [
371
411
  ...ctx.worktreeStack,
372
412
  {
373
413
  id,
374
- path: isAbsolute(pathVal) ? resolvePath(pathVal) : resolvePath(base, pathVal),
414
+ path: resolveWorktreePath(pathVal, opts),
375
415
  ...(raw.branch ? { branch: String(raw.branch) } : {}),
376
416
  ...(raw.baseBranch ? { baseBranch: String(raw.baseBranch) } : {}),
377
417
  },
@@ -463,6 +503,7 @@ export function extractGraph(root, opts) {
463
503
  __sandboxConfig: {
464
504
  image: raw.image,
465
505
  env: raw.env,
506
+ egress: raw.egress,
466
507
  ports: raw.ports,
467
508
  volumes: raw.volumes,
468
509
  memoryLimit: raw.memoryLimit,
@@ -630,6 +671,7 @@ export function extractGraph(root, opts) {
630
671
  memoryConfig: raw.memory && typeof raw.memory === "object" && !Array.isArray(raw.memory)
631
672
  ? raw.memory
632
673
  : undefined,
674
+ aspects: aspects(raw.__aspects),
633
675
  });
634
676
  }
635
677
  let elementIndex = 0;
package/src/index.d.ts CHANGED
@@ -26,9 +26,6 @@ type HostText$1 = {
26
26
  type RetryPolicy$1 = {
27
27
  backoff?: "fixed" | "linear" | "exponential";
28
28
  initialDelayMs?: number;
29
- maxDelayMs?: number;
30
- multiplier?: number;
31
- jitter?: boolean;
32
29
  };
33
30
  type CachePolicy$1<Ctx = unknown> = {
34
31
  by?: (ctx: Ctx) => unknown;
@@ -112,6 +109,18 @@ type ApprovalOption$1 = {
112
109
  summary?: string;
113
110
  metadata?: Record<string, unknown>;
114
111
  };
112
+ type TaskAspects$1 = {
113
+ tokenBudget?: {
114
+ max: number;
115
+ perTask?: number;
116
+ onExceeded?: "fail" | "warn" | "skip-remaining";
117
+ };
118
+ latencySlo?: {
119
+ maxMs: number;
120
+ perTask?: number;
121
+ onExceeded?: "fail" | "warn";
122
+ };
123
+ };
115
124
  type TaskDescriptor$1 = {
116
125
  nodeId: string;
117
126
  ordinal: number;
@@ -159,6 +168,7 @@ type TaskDescriptor$1 = {
159
168
  meta?: Record<string, unknown>;
160
169
  scorers?: ScorersMap$1;
161
170
  memoryConfig?: TaskMemoryConfig$1;
171
+ aspects?: TaskAspects$1;
162
172
  };
163
173
  type WorkflowGraph$2 = {
164
174
  readonly xml: XmlNode$1 | null;
@@ -192,6 +202,16 @@ declare function extractGraph(root: HostNode$1 | null, opts?: ExtractOptions$1):
192
202
  * @returns {WorkflowGraph}
193
203
  */
194
204
  declare function extractFromHost(root: HostNode$1 | null, opts?: ExtractOptions$1): WorkflowGraph$1;
205
+ /**
206
+ * Resolve a <Worktree path> prop exactly the way graph extraction resolves it.
207
+ *
208
+ * @param {unknown} path
209
+ * @param {{ baseRootDir?: string }} [opts]
210
+ * @returns {string}
211
+ */
212
+ declare function resolveWorktreePath(path: unknown, opts?: {
213
+ baseRootDir?: string;
214
+ }): string;
195
215
  type ExtractOptions$1 = ExtractOptions$2;
196
216
  type HostNode$1 = HostNode$2;
197
217
  type WorkflowGraph$1 = WorkflowGraph$2;
@@ -215,6 +235,7 @@ type ScorerBinding = ScorerBinding$1;
215
235
  type ScorerFn = ScorerFn$1;
216
236
  type ScorerInput = ScorerInput$1;
217
237
  type ScorersMap = ScorersMap$1;
238
+ type TaskAspects = TaskAspects$1;
218
239
  type TaskDescriptor = TaskDescriptor$1;
219
240
  type TaskMemoryConfig = TaskMemoryConfig$1;
220
241
  type WorkflowGraph = WorkflowGraph$2;
@@ -222,4 +243,4 @@ type XmlElement = XmlElement$1;
222
243
  type XmlNode = XmlNode$1;
223
244
  type XmlText = XmlText$1;
224
245
 
225
- export { type AgentLike, type ApprovalOption, type CachePolicy, type ExtractGraph, type ExtractOptions, type GraphSnapshot, type HostElement, type HostNode, type HostText, type MemoryNamespace, type MemoryNamespaceKind, type RetryPolicy, type SamplingConfig, type ScoreResult, type Scorer, type ScorerBinding, type ScorerFn, type ScorerInput, type ScorersMap, type TaskDescriptor, type TaskMemoryConfig, type WorkflowGraph, type XmlElement, type XmlNode, type XmlText, extractFromHost, extractGraph };
246
+ export { type AgentLike, type ApprovalOption, type CachePolicy, type ExtractGraph, type ExtractOptions, type GraphSnapshot, type HostElement, type HostNode, type HostText, type MemoryNamespace, type MemoryNamespaceKind, type RetryPolicy, type SamplingConfig, type ScoreResult, type Scorer, type ScorerBinding, type ScorerFn, type ScorerInput, type ScorersMap, type TaskAspects, type TaskDescriptor, type TaskMemoryConfig, type WorkflowGraph, type XmlElement, type XmlNode, type XmlText, extractFromHost, extractGraph, resolveWorktreePath };
package/src/index.js CHANGED
@@ -30,3 +30,4 @@
30
30
  // @smithers-type-exports-end
31
31
 
32
32
  export * from "./extract.js";
33
+ export * from "./worktree-path.js";
package/src/types.ts CHANGED
@@ -32,9 +32,6 @@ export type HostText = {
32
32
  export type RetryPolicy = {
33
33
  backoff?: "fixed" | "linear" | "exponential";
34
34
  initialDelayMs?: number;
35
- maxDelayMs?: number;
36
- multiplier?: number;
37
- jitter?: boolean;
38
35
  };
39
36
 
40
37
  export type CachePolicy<Ctx = unknown> = {
@@ -126,6 +123,26 @@ export type ApprovalOption = {
126
123
  metadata?: Record<string, unknown>;
127
124
  };
128
125
 
126
+ /**
127
+ * Resolved `<Aspects>` budget configuration that applies to a task, extracted
128
+ * from the `__aspects` element prop. The engine reads this at task-dispatch
129
+ * time to enforce per-run token and latency budgets. The render-time
130
+ * accumulator carried alongside the budgets in the component tree is dropped
131
+ * here; the engine keeps its own durable accumulator.
132
+ */
133
+ export type TaskAspects = {
134
+ tokenBudget?: {
135
+ max: number;
136
+ perTask?: number;
137
+ onExceeded?: "fail" | "warn" | "skip-remaining";
138
+ };
139
+ latencySlo?: {
140
+ maxMs: number;
141
+ perTask?: number;
142
+ onExceeded?: "fail" | "warn";
143
+ };
144
+ };
145
+
129
146
  export type TaskDescriptor = {
130
147
  nodeId: string;
131
148
  ordinal: number;
@@ -176,6 +193,8 @@ export type TaskDescriptor = {
176
193
  scorers?: ScorersMap;
177
194
 
178
195
  memoryConfig?: TaskMemoryConfig;
196
+ /** Resolved `<Aspects>` budget configuration enforced by the engine at dispatch. */
197
+ aspects?: TaskAspects;
179
198
  };
180
199
 
181
200
  export type WorkflowGraph = {
@@ -0,0 +1,24 @@
1
+ import { isAbsolute, resolve } from "node:path";
2
+ import { SmithersError } from "@smithers-orchestrator/errors/SmithersError";
3
+ import { WORKTREE_EMPTY_PATH_ERROR } from "./constants.js";
4
+
5
+ /**
6
+ * Resolve a <Worktree path> prop exactly the way graph extraction resolves it.
7
+ *
8
+ * @param {unknown} path
9
+ * @param {{ baseRootDir?: string }} [opts]
10
+ * @returns {string}
11
+ */
12
+ export function resolveWorktreePath(path, opts) {
13
+ const pathVal = String(path ?? "").trim();
14
+ if (!pathVal) {
15
+ throw new SmithersError("WORKTREE_EMPTY_PATH", WORKTREE_EMPTY_PATH_ERROR);
16
+ }
17
+ if (isAbsolute(pathVal)) {
18
+ return resolve(pathVal);
19
+ }
20
+ const base = typeof opts?.baseRootDir === "string" && opts.baseRootDir.length > 0
21
+ ? opts.baseRootDir
22
+ : process.cwd();
23
+ return resolve(base, pathVal);
24
+ }