@workglow/task-graph 0.1.1 → 0.2.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 (87) hide show
  1. package/dist/browser.js +899 -187
  2. package/dist/browser.js.map +40 -32
  3. package/dist/bun.js +899 -187
  4. package/dist/bun.js.map +40 -32
  5. package/dist/common.d.ts +2 -0
  6. package/dist/common.d.ts.map +1 -1
  7. package/dist/node.js +899 -187
  8. package/dist/node.js.map +40 -32
  9. package/dist/storage/TaskGraphRepository.d.ts.map +1 -1
  10. package/dist/storage/TaskGraphTabularRepository.d.ts.map +1 -1
  11. package/dist/storage/TaskOutputRepository.d.ts.map +1 -1
  12. package/dist/storage/TaskOutputTabularRepository.d.ts +1 -1
  13. package/dist/storage/TaskOutputTabularRepository.d.ts.map +1 -1
  14. package/dist/task/ConditionalTask.d.ts +7 -2
  15. package/dist/task/ConditionalTask.d.ts.map +1 -1
  16. package/dist/task/EntitlementEnforcer.d.ts +55 -0
  17. package/dist/task/EntitlementEnforcer.d.ts.map +1 -0
  18. package/dist/task/EntitlementPolicy.d.ts +60 -0
  19. package/dist/task/EntitlementPolicy.d.ts.map +1 -0
  20. package/dist/task/EntitlementProfiles.d.ts +49 -0
  21. package/dist/task/EntitlementProfiles.d.ts.map +1 -0
  22. package/dist/task/EntitlementResolver.d.ts +50 -0
  23. package/dist/task/EntitlementResolver.d.ts.map +1 -0
  24. package/dist/task/FallbackTask.d.ts +31 -24
  25. package/dist/task/FallbackTask.d.ts.map +1 -1
  26. package/dist/task/FallbackTaskRunner.d.ts +2 -2
  27. package/dist/task/FallbackTaskRunner.d.ts.map +1 -1
  28. package/dist/task/GraphAsTask.d.ts +18 -6
  29. package/dist/task/GraphAsTask.d.ts.map +1 -1
  30. package/dist/task/GraphAsTaskRunner.d.ts +3 -2
  31. package/dist/task/GraphAsTaskRunner.d.ts.map +1 -1
  32. package/dist/task/ITask.d.ts +12 -1
  33. package/dist/task/ITask.d.ts.map +1 -1
  34. package/dist/task/InputCompactor.d.ts +37 -0
  35. package/dist/task/InputCompactor.d.ts.map +1 -0
  36. package/dist/task/InputResolver.d.ts +19 -1
  37. package/dist/task/InputResolver.d.ts.map +1 -1
  38. package/dist/task/IteratorTask.d.ts +9 -4
  39. package/dist/task/IteratorTask.d.ts.map +1 -1
  40. package/dist/task/IteratorTaskRunner.d.ts +1 -1
  41. package/dist/task/IteratorTaskRunner.d.ts.map +1 -1
  42. package/dist/task/MapTask.d.ts +8 -3
  43. package/dist/task/MapTask.d.ts.map +1 -1
  44. package/dist/task/ReduceTask.d.ts +9 -4
  45. package/dist/task/ReduceTask.d.ts.map +1 -1
  46. package/dist/task/StreamTypes.d.ts +6 -2
  47. package/dist/task/StreamTypes.d.ts.map +1 -1
  48. package/dist/task/Task.d.ts +37 -7
  49. package/dist/task/Task.d.ts.map +1 -1
  50. package/dist/task/TaskEntitlements.d.ts +134 -0
  51. package/dist/task/TaskEntitlements.d.ts.map +1 -0
  52. package/dist/task/TaskError.d.ts +7 -0
  53. package/dist/task/TaskError.d.ts.map +1 -1
  54. package/dist/task/TaskEvents.d.ts +3 -0
  55. package/dist/task/TaskEvents.d.ts.map +1 -1
  56. package/dist/task/TaskJSON.d.ts +18 -5
  57. package/dist/task/TaskJSON.d.ts.map +1 -1
  58. package/dist/task/TaskRegistry.d.ts +16 -3
  59. package/dist/task/TaskRegistry.d.ts.map +1 -1
  60. package/dist/task/TaskRunner.d.ts +5 -4
  61. package/dist/task/TaskRunner.d.ts.map +1 -1
  62. package/dist/task/TaskTypes.d.ts +10 -2
  63. package/dist/task/TaskTypes.d.ts.map +1 -1
  64. package/dist/task/WhileTask.d.ts +8 -4
  65. package/dist/task/WhileTask.d.ts.map +1 -1
  66. package/dist/task/WhileTaskRunner.d.ts +1 -1
  67. package/dist/task/WhileTaskRunner.d.ts.map +1 -1
  68. package/dist/task/index.d.ts +6 -0
  69. package/dist/task/index.d.ts.map +1 -1
  70. package/dist/task/iterationSchema.d.ts +2 -1
  71. package/dist/task/iterationSchema.d.ts.map +1 -1
  72. package/dist/task-graph/Dataflow.d.ts +1 -1
  73. package/dist/task-graph/Dataflow.d.ts.map +1 -1
  74. package/dist/task-graph/GraphEntitlementUtils.d.ts +30 -0
  75. package/dist/task-graph/GraphEntitlementUtils.d.ts.map +1 -0
  76. package/dist/task-graph/GraphFormatScanner.d.ts +41 -0
  77. package/dist/task-graph/GraphFormatScanner.d.ts.map +1 -0
  78. package/dist/task-graph/GraphToWorkflowCode.d.ts.map +1 -1
  79. package/dist/task-graph/TaskGraph.d.ts +23 -5
  80. package/dist/task-graph/TaskGraph.d.ts.map +1 -1
  81. package/dist/task-graph/TaskGraphEvents.d.ts +3 -0
  82. package/dist/task-graph/TaskGraphEvents.d.ts.map +1 -1
  83. package/dist/task-graph/TaskGraphRunner.d.ts +13 -1
  84. package/dist/task-graph/TaskGraphRunner.d.ts.map +1 -1
  85. package/dist/task-graph/Workflow.d.ts +32 -17
  86. package/dist/task-graph/Workflow.d.ts.map +1 -1
  87. package/package.json +12 -7
package/dist/bun.js CHANGED
@@ -39,6 +39,11 @@ var TaskConfigSchema = {
39
39
  type: "object",
40
40
  additionalProperties: true,
41
41
  "x-ui-hidden": true
42
+ },
43
+ defaults: {
44
+ type: "object",
45
+ additionalProperties: true,
46
+ "x-ui-hidden": true
42
47
  }
43
48
  },
44
49
  additionalProperties: false
@@ -257,6 +262,316 @@ class DataflowArrow extends Dataflow {
257
262
  super(sourceTaskId, sourceTaskPortId, targetTaskId, targetTaskPortId);
258
263
  }
259
264
  }
265
+ // src/task/TaskEntitlements.ts
266
+ var Entitlements = {
267
+ NETWORK: "network",
268
+ NETWORK_HTTP: "network:http",
269
+ NETWORK_WEBSOCKET: "network:websocket",
270
+ NETWORK_PRIVATE: "network:private",
271
+ FILESYSTEM: "filesystem",
272
+ FILESYSTEM_READ: "filesystem:read",
273
+ FILESYSTEM_WRITE: "filesystem:write",
274
+ CODE_EXECUTION: "code-execution",
275
+ CODE_EXECUTION_JS: "code-execution:javascript",
276
+ CREDENTIAL: "credential",
277
+ AI: "ai",
278
+ AI_MODEL: "ai:model",
279
+ AI_INFERENCE: "ai:inference",
280
+ MCP: "mcp",
281
+ MCP_TOOL_CALL: "mcp:tool-call",
282
+ MCP_RESOURCE_READ: "mcp:resource-read",
283
+ MCP_PROMPT_GET: "mcp:prompt-get",
284
+ MCP_STDIO: "mcp:stdio",
285
+ STORAGE: "storage",
286
+ STORAGE_READ: "storage:read",
287
+ STORAGE_WRITE: "storage:write"
288
+ };
289
+ var EMPTY_ENTITLEMENTS = Object.freeze({
290
+ entitlements: Object.freeze([])
291
+ });
292
+ function entitlementCovers(granted, required) {
293
+ return required === granted || required.startsWith(granted + ":");
294
+ }
295
+ function resourcePatternMatches(grantPattern, requiredResource) {
296
+ if (grantPattern === requiredResource)
297
+ return true;
298
+ const starIdx = grantPattern.indexOf("*");
299
+ if (starIdx === -1)
300
+ return false;
301
+ const prefix = grantPattern.slice(0, starIdx);
302
+ const suffix = grantPattern.slice(starIdx + 1);
303
+ if (!requiredResource.startsWith(prefix))
304
+ return false;
305
+ if (!requiredResource.endsWith(suffix))
306
+ return false;
307
+ return requiredResource.length >= prefix.length + suffix.length;
308
+ }
309
+ function grantCoversResources(grant, required) {
310
+ if (grant.resources === undefined)
311
+ return true;
312
+ if (required.resources === undefined)
313
+ return false;
314
+ return required.resources.every((req) => grant.resources.some((pat) => resourcePatternMatches(pat, req)));
315
+ }
316
+ function mergeEntitlements(a, b) {
317
+ if (a.entitlements.length === 0)
318
+ return b;
319
+ if (b.entitlements.length === 0)
320
+ return a;
321
+ const merged = new Map;
322
+ for (const entitlement of a.entitlements) {
323
+ merged.set(entitlement.id, entitlement);
324
+ }
325
+ for (const entitlement of b.entitlements) {
326
+ const existing = merged.get(entitlement.id);
327
+ if (existing) {
328
+ merged.set(entitlement.id, mergeEntitlementPair(existing, entitlement));
329
+ } else {
330
+ merged.set(entitlement.id, entitlement);
331
+ }
332
+ }
333
+ return { entitlements: Array.from(merged.values()) };
334
+ }
335
+ function mergeEntitlementPair(a, b) {
336
+ const optional = (a.optional ?? false) && (b.optional ?? false) ? true : undefined;
337
+ const reason = a.reason ?? b.reason;
338
+ const resources = mergeResources(a.resources, b.resources);
339
+ const result = {
340
+ id: a.id,
341
+ ...reason !== undefined && { reason },
342
+ ...optional === true && { optional: true },
343
+ ...resources !== undefined && { resources }
344
+ };
345
+ return result;
346
+ }
347
+ function mergeResources(a, b) {
348
+ if (a === undefined || b === undefined)
349
+ return;
350
+ const set = new Set([...a, ...b]);
351
+ return Array.from(set);
352
+ }
353
+
354
+ // src/task-graph/GraphEntitlementUtils.ts
355
+ function computeGraphEntitlements(graph, options) {
356
+ const tasks = graph.getTasks();
357
+ if (tasks.length === 0)
358
+ return EMPTY_ENTITLEMENTS;
359
+ const trackOrigins = options?.trackOrigins ?? false;
360
+ const conditionalBranches = options?.conditionalBranches ?? "all";
361
+ const merged = new Map;
362
+ for (const task of tasks) {
363
+ if (conditionalBranches === "active" && task.status !== undefined) {
364
+ if (task.status === TaskStatus.DISABLED)
365
+ continue;
366
+ }
367
+ const taskEntitlements = task.entitlements();
368
+ for (const entitlement of taskEntitlements.entitlements) {
369
+ const existing = merged.get(entitlement.id);
370
+ if (existing) {
371
+ existing.entitlement = mergeEntitlementPair(existing.entitlement, entitlement);
372
+ if (trackOrigins) {
373
+ existing.sourceTaskIds.push(task.id);
374
+ }
375
+ } else {
376
+ merged.set(entitlement.id, {
377
+ entitlement,
378
+ sourceTaskIds: trackOrigins ? [task.id] : []
379
+ });
380
+ }
381
+ }
382
+ }
383
+ if (merged.size === 0)
384
+ return EMPTY_ENTITLEMENTS;
385
+ if (trackOrigins) {
386
+ const entitlements = [];
387
+ for (const { entitlement, sourceTaskIds } of merged.values()) {
388
+ entitlements.push({ ...entitlement, sourceTaskIds });
389
+ }
390
+ return { entitlements };
391
+ }
392
+ return { entitlements: Array.from(merged.values()).map((e) => e.entitlement) };
393
+ }
394
+ // src/task/InputResolver.ts
395
+ import { getInputResolvers } from "@workglow/util";
396
+ function getSchemaFormat(schema, visited = new WeakSet) {
397
+ if (typeof schema !== "object" || schema === null)
398
+ return;
399
+ if (visited.has(schema))
400
+ return;
401
+ visited.add(schema);
402
+ const s = schema;
403
+ if (typeof s.format === "string")
404
+ return s.format;
405
+ const variants = s.oneOf ?? s.anyOf;
406
+ if (Array.isArray(variants)) {
407
+ for (const variant of variants) {
408
+ if (typeof variant === "object" && variant !== null) {
409
+ const v = variant;
410
+ if (typeof v.format === "string")
411
+ return v.format;
412
+ }
413
+ }
414
+ }
415
+ const allOf = s.allOf;
416
+ if (Array.isArray(allOf)) {
417
+ for (const sub of allOf) {
418
+ const fmt = getSchemaFormat(sub, visited);
419
+ if (fmt !== undefined)
420
+ return fmt;
421
+ }
422
+ }
423
+ return;
424
+ }
425
+ function getObjectSchema(schema, visited = new WeakSet) {
426
+ if (typeof schema !== "object" || schema === null)
427
+ return;
428
+ if (visited.has(schema))
429
+ return;
430
+ visited.add(schema);
431
+ const s = schema;
432
+ if (s.type === "object" && s.properties && typeof s.properties === "object") {
433
+ return s;
434
+ }
435
+ const variants = s.oneOf ?? s.anyOf;
436
+ if (Array.isArray(variants)) {
437
+ for (const variant of variants) {
438
+ if (typeof variant === "object" && variant !== null) {
439
+ const v = variant;
440
+ if (v.type === "object" && v.properties && typeof v.properties === "object") {
441
+ return v;
442
+ }
443
+ }
444
+ }
445
+ }
446
+ const allOf = s.allOf;
447
+ if (Array.isArray(allOf)) {
448
+ for (const sub of allOf) {
449
+ const result = getObjectSchema(sub, visited);
450
+ if (result !== undefined)
451
+ return result;
452
+ }
453
+ }
454
+ return;
455
+ }
456
+ function getFormatPrefix(format) {
457
+ const colonIndex = format.indexOf(":");
458
+ return colonIndex >= 0 ? format.substring(0, colonIndex) : format;
459
+ }
460
+ function schemaHasFormatAnnotations(schema) {
461
+ if (typeof schema === "boolean")
462
+ return false;
463
+ const properties = schema.properties;
464
+ if (!properties || typeof properties !== "object")
465
+ return false;
466
+ for (const propSchema of Object.values(properties)) {
467
+ if (getSchemaFormat(propSchema) !== undefined)
468
+ return true;
469
+ }
470
+ return false;
471
+ }
472
+ async function resolveSchemaInputs(input, schema, config, visited = new Set) {
473
+ if (typeof schema === "boolean")
474
+ return input;
475
+ const properties = schema.properties;
476
+ if (!properties || typeof properties !== "object")
477
+ return input;
478
+ const resolvers = getInputResolvers();
479
+ const resolved = { ...input };
480
+ for (const [key, propSchema] of Object.entries(properties)) {
481
+ let value = resolved[key];
482
+ const format = getSchemaFormat(propSchema);
483
+ if (format) {
484
+ let resolver = resolvers.get(format);
485
+ if (!resolver) {
486
+ const prefix = getFormatPrefix(format);
487
+ resolver = resolvers.get(prefix);
488
+ }
489
+ if (resolver) {
490
+ if (typeof value === "string") {
491
+ value = await resolver(value, format, config.registry);
492
+ resolved[key] = value;
493
+ } else if (Array.isArray(value) && value.some((item) => typeof item === "string")) {
494
+ const results = await Promise.all(value.map((item) => typeof item === "string" ? resolver(item, format, config.registry) : item));
495
+ value = results.filter((result) => result !== undefined);
496
+ resolved[key] = value;
497
+ }
498
+ }
499
+ }
500
+ if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value)) {
501
+ const objectSchema = getObjectSchema(propSchema);
502
+ if (objectSchema && !visited.has(objectSchema)) {
503
+ visited.add(objectSchema);
504
+ try {
505
+ resolved[key] = await resolveSchemaInputs(value, objectSchema, config, visited);
506
+ } finally {
507
+ visited.delete(objectSchema);
508
+ }
509
+ }
510
+ }
511
+ }
512
+ return resolved;
513
+ }
514
+
515
+ // src/task-graph/GraphFormatScanner.ts
516
+ function schemaHasFormat(schema, targetFormat) {
517
+ if (typeof schema !== "object" || schema === null)
518
+ return false;
519
+ const s = schema;
520
+ const properties = s.properties;
521
+ if (properties && typeof properties === "object") {
522
+ for (const propSchema of Object.values(properties)) {
523
+ const format = getSchemaFormat(propSchema);
524
+ if (format === targetFormat)
525
+ return true;
526
+ const objectSchema = getObjectSchema(propSchema);
527
+ if (objectSchema && schemaHasFormat(objectSchema, targetFormat))
528
+ return true;
529
+ }
530
+ }
531
+ return false;
532
+ }
533
+ function scanGraphForFormat(graph, targetFormat) {
534
+ for (const task of graph.getTasks()) {
535
+ const inputSchema = task.inputSchema();
536
+ if (typeof inputSchema !== "boolean" && schemaHasFormat(inputSchema, targetFormat)) {
537
+ return true;
538
+ }
539
+ const configSchema = task.configSchema();
540
+ if (typeof configSchema !== "boolean" && schemaHasFormat(configSchema, targetFormat)) {
541
+ return true;
542
+ }
543
+ }
544
+ return false;
545
+ }
546
+ function scanGraphForCredentials(graph) {
547
+ const credentialFormats = new Set;
548
+ for (const task of graph.getTasks()) {
549
+ collectCredentialFormats(task.inputSchema(), credentialFormats);
550
+ collectCredentialFormats(task.configSchema(), credentialFormats);
551
+ }
552
+ return {
553
+ needsCredentials: credentialFormats.size > 0,
554
+ credentialFormats
555
+ };
556
+ }
557
+ function collectCredentialFormats(schema, formats) {
558
+ if (typeof schema === "boolean" || typeof schema !== "object" || schema === null)
559
+ return;
560
+ const s = schema;
561
+ const properties = s.properties;
562
+ if (!properties || typeof properties !== "object")
563
+ return;
564
+ for (const propSchema of Object.values(properties)) {
565
+ const format = getSchemaFormat(propSchema);
566
+ if (format === "credential") {
567
+ formats.add(format);
568
+ }
569
+ const objectSchema = getObjectSchema(propSchema);
570
+ if (objectSchema) {
571
+ collectCredentialFormats(objectSchema, formats);
572
+ }
573
+ }
574
+ }
260
575
  // src/task-graph/GraphSchemaUtils.ts
261
576
  import { uuid4 } from "@workglow/util";
262
577
  function calculateNodeDepths(graph) {
@@ -606,8 +921,8 @@ function addBoundaryNodesToDependencyJson(items, graph) {
606
921
  return [...prependItems, ...items, ...appendItems];
607
922
  }
608
923
  // src/task-graph/TaskGraph.ts
609
- import { DirectedAcyclicGraph } from "@workglow/util/graph";
610
924
  import { EventEmitter as EventEmitter4, uuid4 as uuid44 } from "@workglow/util";
925
+ import { DirectedAcyclicGraph } from "@workglow/util/graph";
611
926
 
612
927
  // src/task/GraphAsTask.ts
613
928
  import { getLogger as getLogger4 } from "@workglow/util";
@@ -725,8 +1040,8 @@ function getNestedValue(obj, path) {
725
1040
  }
726
1041
 
727
1042
  // src/task/Task.ts
728
- import { compileSchema } from "@workglow/util/schema";
729
1043
  import { deepEqual, EventEmitter as EventEmitter3, uuid4 as uuid42 } from "@workglow/util";
1044
+ import { compileSchema } from "@workglow/util/schema";
730
1045
 
731
1046
  // src/task/TaskError.ts
732
1047
  import { BaseError } from "@workglow/util";
@@ -806,6 +1121,13 @@ class TaskInvalidInputError extends TaskError {
806
1121
  }
807
1122
  }
808
1123
 
1124
+ class TaskEntitlementError extends TaskError {
1125
+ static type = "TaskEntitlementError";
1126
+ constructor(message = "Required entitlements denied") {
1127
+ super(message);
1128
+ }
1129
+ }
1130
+
809
1131
  class TaskSerializationError extends TaskError {
810
1132
  static type = "TaskSerializationError";
811
1133
  constructor(taskType) {
@@ -821,100 +1143,6 @@ import {
821
1143
  SpanStatusCode
822
1144
  } from "@workglow/util";
823
1145
 
824
- // src/task/InputResolver.ts
825
- import { getInputResolvers } from "@workglow/util";
826
- function getSchemaFormat(schema) {
827
- if (typeof schema !== "object" || schema === null)
828
- return;
829
- const s = schema;
830
- if (typeof s.format === "string")
831
- return s.format;
832
- const variants = s.oneOf ?? s.anyOf;
833
- if (Array.isArray(variants)) {
834
- for (const variant of variants) {
835
- if (typeof variant === "object" && variant !== null) {
836
- const v = variant;
837
- if (typeof v.format === "string")
838
- return v.format;
839
- }
840
- }
841
- }
842
- return;
843
- }
844
- function getObjectSchema(schema) {
845
- if (typeof schema !== "object" || schema === null)
846
- return;
847
- const s = schema;
848
- if (s.type === "object" && s.properties && typeof s.properties === "object") {
849
- return s;
850
- }
851
- const variants = s.oneOf ?? s.anyOf;
852
- if (Array.isArray(variants)) {
853
- for (const variant of variants) {
854
- if (typeof variant === "object" && variant !== null) {
855
- const v = variant;
856
- if (v.type === "object" && v.properties && typeof v.properties === "object") {
857
- return v;
858
- }
859
- }
860
- }
861
- }
862
- return;
863
- }
864
- function getFormatPrefix(format) {
865
- const colonIndex = format.indexOf(":");
866
- return colonIndex >= 0 ? format.substring(0, colonIndex) : format;
867
- }
868
- function schemaHasFormatAnnotations(schema) {
869
- if (typeof schema === "boolean")
870
- return false;
871
- const properties = schema.properties;
872
- if (!properties || typeof properties !== "object")
873
- return false;
874
- for (const propSchema of Object.values(properties)) {
875
- if (getSchemaFormat(propSchema) !== undefined)
876
- return true;
877
- }
878
- return false;
879
- }
880
- async function resolveSchemaInputs(input, schema, config) {
881
- if (typeof schema === "boolean")
882
- return input;
883
- const properties = schema.properties;
884
- if (!properties || typeof properties !== "object")
885
- return input;
886
- const resolvers = getInputResolvers();
887
- const resolved = { ...input };
888
- for (const [key, propSchema] of Object.entries(properties)) {
889
- let value = resolved[key];
890
- const format = getSchemaFormat(propSchema);
891
- if (format) {
892
- let resolver = resolvers.get(format);
893
- if (!resolver) {
894
- const prefix = getFormatPrefix(format);
895
- resolver = resolvers.get(prefix);
896
- }
897
- if (resolver) {
898
- if (typeof value === "string") {
899
- value = await resolver(value, format, config.registry);
900
- resolved[key] = value;
901
- } else if (Array.isArray(value) && value.some((item) => typeof item === "string")) {
902
- const results = await Promise.all(value.map((item) => typeof item === "string" ? resolver(item, format, config.registry) : item));
903
- value = results.filter((result) => result !== undefined);
904
- resolved[key] = value;
905
- }
906
- }
907
- }
908
- if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value)) {
909
- const objectSchema = getObjectSchema(propSchema);
910
- if (objectSchema) {
911
- resolved[key] = await resolveSchemaInputs(value, objectSchema, config);
912
- }
913
- }
914
- }
915
- return resolved;
916
- }
917
-
918
1146
  // src/task/StreamTypes.ts
919
1147
  function getPortStreamMode(schema, portId) {
920
1148
  if (typeof schema === "boolean")
@@ -1206,11 +1434,29 @@ class TaskRunner {
1206
1434
  }
1207
1435
  case "object-delta": {
1208
1436
  if (accumulatedObjects) {
1209
- accumulatedObjects.set(event.port, event.objectDelta);
1437
+ const existing = accumulatedObjects.get(event.port);
1438
+ if (Array.isArray(event.objectDelta)) {
1439
+ const arr = Array.isArray(existing) ? [...existing] : [];
1440
+ for (const item of event.objectDelta) {
1441
+ const itemObj = item;
1442
+ if (itemObj && typeof itemObj === "object" && "id" in itemObj) {
1443
+ const idx = arr.findIndex((e) => e.id === itemObj.id);
1444
+ if (idx >= 0)
1445
+ arr[idx] = item;
1446
+ else
1447
+ arr.push(item);
1448
+ } else {
1449
+ arr.push(item);
1450
+ }
1451
+ }
1452
+ accumulatedObjects.set(event.port, arr);
1453
+ } else {
1454
+ accumulatedObjects.set(event.port, event.objectDelta);
1455
+ }
1210
1456
  }
1211
1457
  this.task.runOutputData = {
1212
1458
  ...this.task.runOutputData,
1213
- [event.port]: event.objectDelta
1459
+ [event.port]: accumulatedObjects?.get(event.port) ?? event.objectDelta
1214
1460
  };
1215
1461
  this.task.emit("stream_chunk", event);
1216
1462
  const progress = Math.min(99, Math.round(100 * (1 - Math.exp(-0.05 * chunkCount))));
@@ -1271,11 +1517,6 @@ class TaskRunner {
1271
1517
  this.abortController.signal.addEventListener("abort", () => {
1272
1518
  this.handleAbort();
1273
1519
  });
1274
- if (config.signal?.aborted) {
1275
- this.abortController.abort();
1276
- } else if (config.signal) {
1277
- config.signal.addEventListener("abort", () => this.abortController.abort(), { once: true });
1278
- }
1279
1520
  const cache = config.outputCache ?? this.task.runConfig?.outputCache;
1280
1521
  if (cache === true) {
1281
1522
  let instance = globalServiceRegistry.get(TASK_OUTPUT_REPOSITORY);
@@ -1286,6 +1527,21 @@ class TaskRunner {
1286
1527
  this.outputCache = cache;
1287
1528
  }
1288
1529
  this.shouldAccumulate = config.shouldAccumulate !== false;
1530
+ if (config.updateProgress) {
1531
+ this.updateProgress = config.updateProgress;
1532
+ }
1533
+ if (config.registry) {
1534
+ this.registry = config.registry;
1535
+ }
1536
+ if (config.signal) {
1537
+ const onAbort = () => this.abortController.abort();
1538
+ config.signal.addEventListener("abort", onAbort, { once: true });
1539
+ if (config.signal.aborted) {
1540
+ config.signal.removeEventListener("abort", onAbort);
1541
+ this.abortController.abort();
1542
+ return;
1543
+ }
1544
+ }
1289
1545
  const timeout = this.task.config.timeout;
1290
1546
  if (timeout !== undefined && timeout > 0) {
1291
1547
  this.pendingTimeoutError = new TaskTimeoutError(timeout);
@@ -1293,12 +1549,6 @@ class TaskRunner {
1293
1549
  this.abort();
1294
1550
  }, timeout);
1295
1551
  }
1296
- if (config.updateProgress) {
1297
- this.updateProgress = config.updateProgress;
1298
- }
1299
- if (config.registry) {
1300
- this.registry = config.registry;
1301
- }
1302
1552
  const telemetry = getTelemetryProvider();
1303
1553
  if (telemetry.isEnabled) {
1304
1554
  this.telemetrySpan = telemetry.startSpan("workglow.task.run", {
@@ -1435,6 +1685,11 @@ class Task {
1435
1685
  static hasDynamicSchemas = false;
1436
1686
  static passthroughInputsToOutputs = false;
1437
1687
  static customizable = false;
1688
+ static isGraphOutput = false;
1689
+ static hasDynamicEntitlements = false;
1690
+ static entitlements() {
1691
+ return EMPTY_ENTITLEMENTS;
1692
+ }
1438
1693
  static inputSchema() {
1439
1694
  return {
1440
1695
  type: "object",
@@ -1489,6 +1744,13 @@ class Task {
1489
1744
  configSchema() {
1490
1745
  return this.constructor.configSchema();
1491
1746
  }
1747
+ entitlements() {
1748
+ return this.constructor.entitlements();
1749
+ }
1750
+ emitEntitlementChange(entitlements) {
1751
+ const final = entitlements ?? this.entitlements();
1752
+ this.emit("entitlementChange", final);
1753
+ }
1492
1754
  get type() {
1493
1755
  return this.constructor.type;
1494
1756
  }
@@ -1526,15 +1788,16 @@ class Task {
1526
1788
  return this._events;
1527
1789
  }
1528
1790
  _events;
1529
- constructor(callerDefaultInputs = {}, config = {}, runConfig = {}) {
1791
+ constructor(config = {}, runConfig = {}) {
1792
+ const { defaults: callerDefaultInputs, ...restConfig } = config;
1530
1793
  const inputDefaults = this.getDefaultInputsFromStaticInputDefinitions();
1531
- const mergedDefaults = Object.assign(inputDefaults, callerDefaultInputs);
1794
+ const mergedDefaults = Object.assign(inputDefaults, callerDefaultInputs ?? {});
1532
1795
  this.defaults = this.stripSymbols(mergedDefaults);
1533
1796
  this.resetInputData();
1534
1797
  const title = this.constructor.title || undefined;
1535
1798
  const baseConfig = Object.assign({
1536
1799
  ...title ? { title } : {}
1537
- }, config);
1800
+ }, restConfig);
1538
1801
  if (baseConfig.id === undefined) {
1539
1802
  baseConfig.id = uuid42();
1540
1803
  }
@@ -1881,6 +2144,10 @@ class Task {
1881
2144
  if (Object.keys(config).length > 0) {
1882
2145
  base.config = config;
1883
2146
  }
2147
+ const taskEntitlements = this.entitlements();
2148
+ if (taskEntitlements.entitlements.length > 0) {
2149
+ base.entitlements = taskEntitlements;
2150
+ }
1884
2151
  return this.stripSymbols(base);
1885
2152
  }
1886
2153
  toDependencyJSON(options) {
@@ -2010,7 +2277,7 @@ class ConditionalTask extends Task {
2010
2277
  }
2011
2278
  }
2012
2279
  } catch (error) {
2013
- getLogger2().warn(`Condition evaluation failed for branch "${branch.id}":`, { error });
2280
+ getLogger2().error(`Condition evaluation failed for branch "${branch.id}":`, { error });
2014
2281
  }
2015
2282
  }
2016
2283
  if (this.activeBranches.size === 0 && defaultBranch) {
@@ -2134,6 +2401,111 @@ class ConditionalTask extends Task {
2134
2401
  }
2135
2402
  }
2136
2403
 
2404
+ // src/task/EntitlementEnforcer.ts
2405
+ import { createServiceToken as createServiceToken3 } from "@workglow/util";
2406
+
2407
+ // src/task/EntitlementPolicy.ts
2408
+ var EMPTY_POLICY = Object.freeze({
2409
+ deny: Object.freeze([]),
2410
+ grant: Object.freeze([]),
2411
+ ask: Object.freeze([])
2412
+ });
2413
+ function ruleCovers(rule, required) {
2414
+ if (!entitlementCovers(rule.id, required.id))
2415
+ return false;
2416
+ return grantCoversResources(rule, required);
2417
+ }
2418
+ function evaluatePolicy(policy, required) {
2419
+ const results = [];
2420
+ for (const entitlement of required.entitlements) {
2421
+ if (entitlement.optional)
2422
+ continue;
2423
+ const denyMatch = policy.deny.find((rule) => ruleCovers(rule, entitlement));
2424
+ if (denyMatch) {
2425
+ results.push({ verdict: "denied", entitlement, matchedRule: denyMatch });
2426
+ continue;
2427
+ }
2428
+ const grantMatch = policy.grant.find((rule) => ruleCovers(rule, entitlement));
2429
+ if (grantMatch) {
2430
+ results.push({ verdict: "granted", entitlement, matchedRule: grantMatch });
2431
+ continue;
2432
+ }
2433
+ const askMatch = policy.ask.find((rule) => ruleCovers(rule, entitlement));
2434
+ if (askMatch) {
2435
+ results.push({ verdict: "ask", entitlement, matchedRule: askMatch });
2436
+ continue;
2437
+ }
2438
+ results.push({ verdict: "denied", entitlement });
2439
+ }
2440
+ return results;
2441
+ }
2442
+
2443
+ // src/task/EntitlementResolver.ts
2444
+ import { createServiceToken as createServiceToken2 } from "@workglow/util";
2445
+ var PERMISSIVE_RESOLVER = {
2446
+ lookup: () => "grant",
2447
+ prompt: async () => "grant",
2448
+ save: () => {}
2449
+ };
2450
+ var DENY_ALL_RESOLVER = {
2451
+ lookup: () => "deny",
2452
+ prompt: async () => "deny",
2453
+ save: () => {}
2454
+ };
2455
+ var ENTITLEMENT_RESOLVER = createServiceToken2("workglow.entitlementResolver");
2456
+
2457
+ // src/task/EntitlementEnforcer.ts
2458
+ var PERMISSIVE_ENFORCER = {
2459
+ checkAll: async () => [],
2460
+ checkTask: async () => []
2461
+ };
2462
+ function createPolicyEnforcer(policy, resolver = PERMISSIVE_RESOLVER) {
2463
+ async function resolveAsks(required, taskType, taskId) {
2464
+ const results = evaluatePolicy(policy, required);
2465
+ const denied = [];
2466
+ for (const result of results) {
2467
+ if (result.verdict === "denied") {
2468
+ denied.push(result.entitlement);
2469
+ } else if (result.verdict === "ask") {
2470
+ const request = {
2471
+ entitlement: result.entitlement,
2472
+ taskType: taskType ?? "unknown",
2473
+ taskId: taskId ?? "unknown"
2474
+ };
2475
+ const saved = resolver.lookup(request);
2476
+ if (saved !== undefined) {
2477
+ if (saved === "deny") {
2478
+ denied.push(result.entitlement);
2479
+ }
2480
+ continue;
2481
+ }
2482
+ const answer = await resolver.prompt(request);
2483
+ resolver.save(request, answer);
2484
+ if (answer === "deny") {
2485
+ denied.push(result.entitlement);
2486
+ }
2487
+ }
2488
+ }
2489
+ return denied;
2490
+ }
2491
+ return {
2492
+ async checkAll(required) {
2493
+ return resolveAsks(required);
2494
+ },
2495
+ async checkTask(task) {
2496
+ const entitlements = task.entitlements();
2497
+ return resolveAsks(entitlements, task.constructor.type, task.id);
2498
+ }
2499
+ };
2500
+ }
2501
+ function createScopedEnforcer(grants) {
2502
+ return createPolicyEnforcer({ deny: [], grant: grants, ask: [] });
2503
+ }
2504
+ function createGrantListEnforcer(grants) {
2505
+ return createScopedEnforcer(grants.map((id) => ({ id })));
2506
+ }
2507
+ var ENTITLEMENT_ENFORCER = createServiceToken3("workglow.entitlementEnforcer");
2508
+
2137
2509
  // src/task-graph/TaskGraphScheduler.ts
2138
2510
  class TopologicalScheduler {
2139
2511
  dag;
@@ -2302,6 +2674,7 @@ class TaskGraphRunner {
2302
2674
  telemetrySpan;
2303
2675
  graphTimeoutTimer;
2304
2676
  pendingGraphTimeoutError;
2677
+ activeEnforcer;
2305
2678
  constructor(graph, outputCache, processScheduler = new DependencyBasedScheduler(graph), reactiveScheduler = new TopologicalScheduler(graph)) {
2306
2679
  this.processScheduler = processScheduler;
2307
2680
  this.reactiveScheduler = reactiveScheduler;
@@ -2370,7 +2743,7 @@ class TaskGraphRunner {
2370
2743
  throw new TaskAbortedError;
2371
2744
  }
2372
2745
  await this.handleComplete();
2373
- return results;
2746
+ return this.filterLeafResults(results);
2374
2747
  }
2375
2748
  async runGraphReactive(input = {}, config) {
2376
2749
  await this.handleStartReactive(config);
@@ -2394,7 +2767,7 @@ class TaskGraphRunner {
2394
2767
  }
2395
2768
  }
2396
2769
  await this.handleCompleteReactive();
2397
- return results;
2770
+ return this.filterLeafResults(results);
2398
2771
  } catch (error) {
2399
2772
  await this.handleErrorReactive();
2400
2773
  throw error;
@@ -2426,6 +2799,15 @@ class TaskGraphRunner {
2426
2799
  task.regenerateGraph();
2427
2800
  }
2428
2801
  }
2802
+ filterLeafResults(results) {
2803
+ if (results.length <= 1)
2804
+ return results;
2805
+ const graphOutputResults = results.filter((r) => {
2806
+ const task = this.graph.getTask(r.id);
2807
+ return task && task.constructor.isGraphOutput;
2808
+ });
2809
+ return graphOutputResults.length > 0 ? graphOutputResults : results;
2810
+ }
2429
2811
  mergeExecuteOutputsToRunOutput(results, compoundMerge) {
2430
2812
  if (compoundMerge === GRAPH_RESULT_ARRAY) {
2431
2813
  return results;
@@ -2607,6 +2989,12 @@ class TaskGraphRunner {
2607
2989
  }
2608
2990
  await this.awaitStreamInputs(task);
2609
2991
  this.copyInputFromEdgesToNode(task);
2992
+ if (this.activeEnforcer && task.constructor.hasDynamicEntitlements) {
2993
+ const denied = await this.activeEnforcer.checkTask(task);
2994
+ if (denied.length > 0) {
2995
+ throw new TaskEntitlementError(`Task ${task.constructor.type} denied entitlements: ${denied.map((e) => e.id).join(", ")}`);
2996
+ }
2997
+ }
2610
2998
  if (isStreamable) {
2611
2999
  return this.runStreamingTask(task, input);
2612
3000
  }
@@ -2776,12 +3164,6 @@ class TaskGraphRunner {
2776
3164
  }
2777
3165
  this.graph.outputCache = this.outputCache;
2778
3166
  }
2779
- if (config?.maxTasks !== undefined && config.maxTasks > 0) {
2780
- const taskCount = this.graph.getTasks().length;
2781
- if (taskCount > config.maxTasks) {
2782
- throw new TaskConfigurationError(`Graph has ${taskCount} tasks, exceeding the limit of ${config.maxTasks}`);
2783
- }
2784
- }
2785
3167
  if (this.running || this.reactiveRunning) {
2786
3168
  throw new TaskConfigurationError("Graph is already running");
2787
3169
  }
@@ -2797,13 +3179,16 @@ class TaskGraphRunner {
2797
3179
  this.abortController?.abort();
2798
3180
  }, config.timeout);
2799
3181
  }
2800
- if (config?.parentSignal?.aborted) {
2801
- this.abortController.abort();
2802
- return;
2803
- } else {
2804
- config?.parentSignal?.addEventListener("abort", () => {
3182
+ if (config?.parentSignal) {
3183
+ const onParentAbort = () => {
2805
3184
  this.abortController?.abort();
2806
- }, { once: true });
3185
+ };
3186
+ config.parentSignal.addEventListener("abort", onParentAbort, { once: true });
3187
+ if (config.parentSignal.aborted) {
3188
+ config.parentSignal.removeEventListener("abort", onParentAbort);
3189
+ this.abortController.abort();
3190
+ return;
3191
+ }
2807
3192
  }
2808
3193
  this.runId = uuid43();
2809
3194
  this.resetGraph(this.graph, this.runId);
@@ -2811,6 +3196,36 @@ class TaskGraphRunner {
2811
3196
  this.inProgressTasks.clear();
2812
3197
  this.inProgressFunctions.clear();
2813
3198
  this.failedTaskErrors.clear();
3199
+ try {
3200
+ if (config?.maxTasks !== undefined && config.maxTasks > 0) {
3201
+ const taskCount = this.graph.getTasks().length;
3202
+ if (taskCount > config.maxTasks) {
3203
+ throw new TaskConfigurationError(`Graph has ${taskCount} tasks, exceeding the limit of ${config.maxTasks}`);
3204
+ }
3205
+ }
3206
+ if (config?.enforceEntitlements) {
3207
+ if (!this.registry.has(ENTITLEMENT_ENFORCER)) {
3208
+ throw new TaskConfigurationError("enforceEntitlements is enabled but no IEntitlementEnforcer is registered. " + "Register an enforcer via ENTITLEMENT_ENFORCER before running the graph.");
3209
+ }
3210
+ const enforcer = this.registry.get(ENTITLEMENT_ENFORCER);
3211
+ const denied = await enforcer.checkAll(computeGraphEntitlements(this.graph));
3212
+ if (denied.length > 0) {
3213
+ throw new TaskEntitlementError(`Denied entitlements: ${denied.map((e) => e.id).join(", ")}`);
3214
+ }
3215
+ this.activeEnforcer = enforcer;
3216
+ } else {
3217
+ this.activeEnforcer = undefined;
3218
+ }
3219
+ } catch (err) {
3220
+ if (this.graphTimeoutTimer !== undefined) {
3221
+ clearTimeout(this.graphTimeoutTimer);
3222
+ this.graphTimeoutTimer = undefined;
3223
+ }
3224
+ this.abortController = undefined;
3225
+ this.activeEnforcer = undefined;
3226
+ this.running = false;
3227
+ throw err;
3228
+ }
2814
3229
  const telemetry = getTelemetryProvider2();
2815
3230
  if (telemetry.isEnabled) {
2816
3231
  this.telemetrySpan = telemetry.startSpan("workglow.graph.run", {
@@ -2848,6 +3263,7 @@ class TaskGraphRunner {
2848
3263
  async handleComplete() {
2849
3264
  this.clearGraphTimeout();
2850
3265
  this.running = false;
3266
+ this.activeEnforcer = undefined;
2851
3267
  if (this.telemetrySpan) {
2852
3268
  this.telemetrySpan.setStatus(SpanStatusCode2.OK);
2853
3269
  this.telemetrySpan.end();
@@ -2866,6 +3282,7 @@ class TaskGraphRunner {
2866
3282
  }
2867
3283
  }));
2868
3284
  this.running = false;
3285
+ this.activeEnforcer = undefined;
2869
3286
  if (this.telemetrySpan) {
2870
3287
  this.telemetrySpan.setStatus(SpanStatusCode2.ERROR, error.message);
2871
3288
  this.telemetrySpan.setAttributes({ "workglow.graph.error": error.message });
@@ -2885,6 +3302,7 @@ class TaskGraphRunner {
2885
3302
  }
2886
3303
  }));
2887
3304
  this.running = false;
3305
+ this.activeEnforcer = undefined;
2888
3306
  if (this.telemetrySpan) {
2889
3307
  this.telemetrySpan.setStatus(SpanStatusCode2.ERROR, "aborted");
2890
3308
  this.telemetrySpan.addEvent("workglow.graph.aborted");
@@ -2986,9 +3404,10 @@ class GraphAsTask extends Task {
2986
3404
  static category = "Flow Control";
2987
3405
  static compoundMerge = PROPERTY_ARRAY;
2988
3406
  static hasDynamicSchemas = true;
2989
- constructor(input = {}, config = {}) {
3407
+ static hasDynamicEntitlements = true;
3408
+ constructor(config = {}) {
2990
3409
  const { subGraph, ...rest } = config;
2991
- super(input, rest);
3410
+ super(rest);
2992
3411
  if (subGraph) {
2993
3412
  this.subGraph = subGraph;
2994
3413
  }
@@ -3035,6 +3454,12 @@ class GraphAsTask extends Task {
3035
3454
  }
3036
3455
  return computeGraphOutputSchema(this.subGraph);
3037
3456
  }
3457
+ entitlements() {
3458
+ if (!this.hasChildren()) {
3459
+ return this.constructor.entitlements();
3460
+ }
3461
+ return computeGraphEntitlements(this.subGraph);
3462
+ }
3038
3463
  resetInputData() {
3039
3464
  super.resetInputData();
3040
3465
  if (this.hasChildren()) {
@@ -3073,26 +3498,44 @@ class GraphAsTask extends Task {
3073
3498
  }
3074
3499
  }
3075
3500
  const eventQueue = [];
3076
- let resolveWaiting;
3077
3501
  let subgraphDone = false;
3502
+ let { promise: notifyPromise, resolve: notifyResolve } = Promise.withResolvers();
3503
+ let isWaiting = false;
3504
+ let hasPending = false;
3505
+ const notify = () => {
3506
+ if (isWaiting) {
3507
+ notifyResolve();
3508
+ ({ promise: notifyPromise, resolve: notifyResolve } = Promise.withResolvers());
3509
+ isWaiting = false;
3510
+ } else {
3511
+ hasPending = true;
3512
+ }
3513
+ };
3078
3514
  const unsub = this.subGraph.subscribeToTaskStreaming({
3079
3515
  onStreamChunk: (taskId, event) => {
3080
3516
  if (endingNodeIds.has(taskId) && event.type !== "finish") {
3081
3517
  eventQueue.push(event);
3082
- resolveWaiting?.();
3518
+ notify();
3083
3519
  }
3084
3520
  }
3085
3521
  });
3086
3522
  const runPromise = this.subGraph.run(input, { parentSignal: context.signal, accumulateLeafOutputs: false }).then((results2) => {
3087
3523
  subgraphDone = true;
3088
- resolveWaiting?.();
3524
+ notify();
3089
3525
  return results2;
3526
+ }, (err) => {
3527
+ subgraphDone = true;
3528
+ notify();
3529
+ throw err;
3090
3530
  });
3091
3531
  while (!subgraphDone) {
3092
3532
  if (eventQueue.length === 0) {
3093
- await new Promise((resolve) => {
3094
- resolveWaiting = resolve;
3095
- });
3533
+ if (hasPending) {
3534
+ hasPending = false;
3535
+ } else {
3536
+ isWaiting = true;
3537
+ await notifyPromise;
3538
+ }
3096
3539
  }
3097
3540
  while (eventQueue.length > 0) {
3098
3541
  yield eventQueue.shift();
@@ -3112,6 +3555,7 @@ class GraphAsTask extends Task {
3112
3555
  regenerateGraph() {
3113
3556
  this._inputSchemaNode = undefined;
3114
3557
  this.events.emit("regenerate");
3558
+ this.emitEntitlementChange();
3115
3559
  }
3116
3560
  toJSON(options) {
3117
3561
  let json = super.toJSON(options);
@@ -3146,8 +3590,8 @@ function getWrapperClasses() {
3146
3590
  if (!_OwnGraphTask) {
3147
3591
 
3148
3592
  class ListeningGraphAsTask extends GraphAsTask {
3149
- constructor(input, config) {
3150
- super(input, config);
3593
+ constructor(config) {
3594
+ super(config);
3151
3595
  this.subGraph.on("start", () => {
3152
3596
  this.emit("start");
3153
3597
  });
@@ -3208,7 +3652,7 @@ function convertPipeFunctionToTask(fn, config) {
3208
3652
  return fn(input, context);
3209
3653
  }
3210
3654
  }
3211
- return new QuickTask({}, config);
3655
+ return new QuickTask(config);
3212
3656
  }
3213
3657
  function isWorkflowLike(arg) {
3214
3658
  return arg != null && typeof arg === "object" && "graph" in arg && arg.graph instanceof TaskGraph && "run" in arg && typeof arg.run === "function";
@@ -3221,18 +3665,18 @@ function ensureTask(arg, config = {}) {
3221
3665
  getWrapperClasses();
3222
3666
  const { isOwned, ...cleanConfig } = config;
3223
3667
  if (isOwned) {
3224
- return new _OwnGraphTask({}, { ...cleanConfig, subGraph: arg });
3668
+ return new _OwnGraphTask({ ...cleanConfig, subGraph: arg });
3225
3669
  } else {
3226
- return new _GraphTask({}, { ...cleanConfig, subGraph: arg });
3670
+ return new _GraphTask({ ...cleanConfig, subGraph: arg });
3227
3671
  }
3228
3672
  }
3229
3673
  if (isWorkflowLike(arg)) {
3230
3674
  getWrapperClasses();
3231
3675
  const { isOwned, ...cleanConfig } = config;
3232
3676
  if (isOwned) {
3233
- return new _OwnWorkflowTask({}, { ...cleanConfig, subGraph: arg.graph });
3677
+ return new _OwnWorkflowTask({ ...cleanConfig, subGraph: arg.graph });
3234
3678
  } else {
3235
- return new _ConvWorkflowTask({}, { ...cleanConfig, subGraph: arg.graph });
3679
+ return new _ConvWorkflowTask({ ...cleanConfig, subGraph: arg.graph });
3236
3680
  }
3237
3681
  }
3238
3682
  return convertPipeFunctionToTask(arg, config);
@@ -3500,6 +3944,43 @@ class TaskGraph {
3500
3944
  unsubscribes.forEach((unsub) => unsub());
3501
3945
  };
3502
3946
  }
3947
+ subscribeToTaskEntitlements(callback) {
3948
+ const globalUnsubs = [];
3949
+ const taskUnsubs = new Map;
3950
+ const emitChange = () => {
3951
+ const entitlements = computeGraphEntitlements(this);
3952
+ this.emit("entitlementChange", entitlements);
3953
+ callback(entitlements);
3954
+ };
3955
+ const subscribeTask = (taskId) => {
3956
+ const task = this.getTask(taskId);
3957
+ if (!task || typeof task.subscribe !== "function")
3958
+ return;
3959
+ const unsub = task.subscribe("entitlementChange", () => emitChange());
3960
+ taskUnsubs.set(taskId, unsub);
3961
+ };
3962
+ for (const task of this.getTasks()) {
3963
+ subscribeTask(task.id);
3964
+ }
3965
+ emitChange();
3966
+ globalUnsubs.push(this.subscribe("task_added", (taskId) => {
3967
+ subscribeTask(taskId);
3968
+ emitChange();
3969
+ }));
3970
+ globalUnsubs.push(this.subscribe("task_removed", (taskId) => {
3971
+ const unsub = taskUnsubs.get(taskId);
3972
+ if (unsub) {
3973
+ unsub();
3974
+ taskUnsubs.delete(taskId);
3975
+ }
3976
+ emitChange();
3977
+ }));
3978
+ return () => {
3979
+ globalUnsubs.forEach((unsub) => unsub());
3980
+ taskUnsubs.forEach((unsub) => unsub());
3981
+ taskUnsubs.clear();
3982
+ };
3983
+ }
3503
3984
  on(name, fn) {
3504
3985
  const dagEvent = EventTaskGraphToDagMapping[name];
3505
3986
  if (dagEvent) {
@@ -3544,11 +4025,7 @@ function serialGraph(tasks, inputHandle, outputHandle) {
3544
4025
  return graph;
3545
4026
  }
3546
4027
  // src/task-graph/Workflow.ts
3547
- import {
3548
- EventEmitter as EventEmitter5,
3549
- getLogger as getLogger5,
3550
- uuid4 as uuid45
3551
- } from "@workglow/util";
4028
+ import { EventEmitter as EventEmitter5, getLogger as getLogger5, uuid4 as uuid45 } from "@workglow/util";
3552
4029
  function getLastTask(workflow) {
3553
4030
  const tasks = workflow.graph.getTasks();
3554
4031
  return tasks.length > 0 ? tasks[tasks.length - 1] : undefined;
@@ -3571,7 +4048,6 @@ function pipe(args, workflow = new Workflow) {
3571
4048
  function parallel(args, mergeFn = PROPERTY_ARRAY, workflow = new Workflow) {
3572
4049
  let previousTask = getLastTask(workflow);
3573
4050
  const tasks = args.map((arg) => ensureTask(arg));
3574
- const input = {};
3575
4051
  const config = {
3576
4052
  compoundMerge: mergeFn
3577
4053
  };
@@ -3580,7 +4056,7 @@ function parallel(args, mergeFn = PROPERTY_ARRAY, workflow = new Workflow) {
3580
4056
  class ParallelTask extends GraphAsTask {
3581
4057
  static type = name;
3582
4058
  }
3583
- const mergeTask = new ParallelTask(input, config);
4059
+ const mergeTask = new ParallelTask(config);
3584
4060
  mergeTask.subGraph.addTasks(tasks);
3585
4061
  workflow.graph.addTask(mergeTask);
3586
4062
  if (previousTask) {
@@ -3659,10 +4135,11 @@ class WorkflowTask extends GraphAsTask {
3659
4135
  }
3660
4136
 
3661
4137
  class Workflow {
3662
- constructor(cache, parent, iteratorTask) {
4138
+ constructor(cache, parent, iteratorTask, registry) {
3663
4139
  this._outputCache = cache;
3664
4140
  this._parentWorkflow = parent;
3665
4141
  this._iteratorTask = iteratorTask;
4142
+ this._registry = registry ?? parent?._registry;
3666
4143
  this._graph = new TaskGraph({ outputCache: this._outputCache });
3667
4144
  if (!parent) {
3668
4145
  this._onChanged = this._onChanged.bind(this);
@@ -3673,6 +4150,8 @@ class Workflow {
3673
4150
  _dataFlows = [];
3674
4151
  _error = "";
3675
4152
  _outputCache;
4153
+ _registry;
4154
+ _entitlementUnsub;
3676
4155
  _abortController;
3677
4156
  _parentWorkflow;
3678
4157
  _iteratorTask;
@@ -3688,7 +4167,11 @@ class Workflow {
3688
4167
  const helper = function(input = {}, config = {}) {
3689
4168
  this._error = "";
3690
4169
  const parent = getLastTask(this);
3691
- const task = this.addTaskToGraph(taskClass, input, { id: uuid45(), ...config });
4170
+ const task = this.addTaskToGraph(taskClass, {
4171
+ id: uuid45(),
4172
+ ...config,
4173
+ defaults: input
4174
+ });
3692
4175
  if (this._dataFlows.length > 0) {
3693
4176
  this._dataFlows.forEach((dataflow) => {
3694
4177
  const taskSchema = task.inputSchema();
@@ -3786,7 +4269,7 @@ class Workflow {
3786
4269
  const output = await this.graph.run(input, {
3787
4270
  parentSignal: this._abortController.signal,
3788
4271
  outputCache: this._outputCache,
3789
- registry: config?.registry
4272
+ registry: config?.registry ?? this._registry
3790
4273
  });
3791
4274
  const results = this.graph.mergeExecuteOutputsToRunOutput(output, PROPERTY_ARRAY);
3792
4275
  this.events.emit("complete");
@@ -3823,6 +4306,9 @@ class Workflow {
3823
4306
  toDependencyJSON(options = { withBoundaryNodes: true }) {
3824
4307
  return this._graph.toDependencyJSON(options);
3825
4308
  }
4309
+ entitlements(options) {
4310
+ return computeGraphEntitlements(this._graph, options);
4311
+ }
3826
4312
  pipe(...args) {
3827
4313
  return pipe(args, this);
3828
4314
  }
@@ -3907,6 +4393,7 @@ class Workflow {
3907
4393
  this._graph.on("dataflow_added", this._onChanged);
3908
4394
  this._graph.on("dataflow_replaced", this._onChanged);
3909
4395
  this._graph.on("dataflow_removed", this._onChanged);
4396
+ this._entitlementUnsub = this._graph.subscribeToTaskEntitlements((entitlements) => this.events.emit("entitlementChange", entitlements));
3910
4397
  }
3911
4398
  clearEvents() {
3912
4399
  this._graph.off("task_added", this._onChanged);
@@ -3915,6 +4402,8 @@ class Workflow {
3915
4402
  this._graph.off("dataflow_added", this._onChanged);
3916
4403
  this._graph.off("dataflow_replaced", this._onChanged);
3917
4404
  this._graph.off("dataflow_removed", this._onChanged);
4405
+ this._entitlementUnsub?.();
4406
+ this._entitlementUnsub = undefined;
3918
4407
  }
3919
4408
  _onChanged(id) {
3920
4409
  this.events.emit("changed", id);
@@ -3946,8 +4435,8 @@ class Workflow {
3946
4435
  this.graph.addDataflow(dataflow);
3947
4436
  return this;
3948
4437
  }
3949
- addTaskToGraph(taskClass, input, config) {
3950
- const task = new taskClass(input, config);
4438
+ addTaskToGraph(taskClass, config) {
4439
+ const task = new taskClass(config, this._registry ? { registry: this._registry } : undefined);
3951
4440
  const id = this.graph.addTask(task);
3952
4441
  this.events.emit("changed", id);
3953
4442
  return task;
@@ -3959,7 +4448,7 @@ class Workflow {
3959
4448
  addLoopTask(taskClass, config = {}) {
3960
4449
  this._error = "";
3961
4450
  const parent = getLastTask(this);
3962
- const task = this.addTaskToGraph(taskClass, {}, { id: uuid45(), ...config });
4451
+ const task = this.addTaskToGraph(taskClass, { id: uuid45(), ...config });
3963
4452
  if (this._dataFlows.length > 0) {
3964
4453
  this._dataFlows.forEach((dataflow) => {
3965
4454
  const taskSchema = task.inputSchema();
@@ -3973,7 +4462,7 @@ class Workflow {
3973
4462
  });
3974
4463
  this._dataFlows = [];
3975
4464
  }
3976
- const loopBuilder = new Workflow(this.outputCache(), this, task);
4465
+ const loopBuilder = new Workflow(this.outputCache(), this, task, this._registry);
3977
4466
  if (parent) {
3978
4467
  loopBuilder._pendingLoopConnect = { parent, iteratorTask: task };
3979
4468
  }
@@ -4056,10 +4545,29 @@ class Workflow {
4056
4545
  const sourceSchema = sourceTask.outputSchema();
4057
4546
  if (typeof sourceSchema === "boolean")
4058
4547
  continue;
4059
- const prop = sourceSchema.properties?.[df.sourceTaskPortId];
4548
+ let prop = sourceSchema.properties?.[df.sourceTaskPortId];
4549
+ let propRequired = sourceSchema.required?.includes(df.sourceTaskPortId) ?? false;
4550
+ if (!prop && sourceSchema.additionalProperties === true && sourceTask.constructor.passthroughInputsToOutputs === true) {
4551
+ const upstreamDfs = graph.getSourceDataflows(sourceTask.id);
4552
+ for (const udf of upstreamDfs) {
4553
+ if (udf.targetTaskPortId !== df.sourceTaskPortId)
4554
+ continue;
4555
+ const upstreamTask = graph.getTask(udf.sourceTaskId);
4556
+ if (!upstreamTask)
4557
+ continue;
4558
+ const upstreamSchema = upstreamTask.outputSchema();
4559
+ if (typeof upstreamSchema === "boolean")
4560
+ continue;
4561
+ prop = upstreamSchema.properties?.[udf.sourceTaskPortId];
4562
+ if (prop) {
4563
+ propRequired = upstreamSchema.required?.includes(udf.sourceTaskPortId) ?? false;
4564
+ break;
4565
+ }
4566
+ }
4567
+ }
4060
4568
  if (prop && typeof prop !== "boolean") {
4061
4569
  properties[df.targetTaskPortId] = prop;
4062
- if (sourceSchema.required?.includes(df.sourceTaskPortId) && !required.includes(df.targetTaskPortId)) {
4570
+ if (propRequired && !required.includes(df.targetTaskPortId)) {
4063
4571
  required.push(df.targetTaskPortId);
4064
4572
  }
4065
4573
  }
@@ -4159,11 +4667,30 @@ class Workflow {
4159
4667
  const makeMatch = (fromSchema, toSchema, fromTaskId, toTaskId, comparator) => {
4160
4668
  if (typeof fromSchema === "object") {
4161
4669
  if (toSchema === true || typeof toSchema === "object" && toSchema.additionalProperties === true) {
4162
- for (const fromOutputPortId of Object.keys(fromSchema.properties || {})) {
4163
- if (matches.has(fromOutputPortId))
4164
- continue;
4165
- matches.set(fromOutputPortId, fromOutputPortId);
4166
- graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
4670
+ const outputKeys = Object.keys(fromSchema.properties || {});
4671
+ if (outputKeys.length > 0) {
4672
+ for (const fromOutputPortId of outputKeys) {
4673
+ if (matches.has(fromOutputPortId))
4674
+ continue;
4675
+ matches.set(fromOutputPortId, fromOutputPortId);
4676
+ graph.addDataflow(new Dataflow(fromTaskId, fromOutputPortId, toTaskId, fromOutputPortId));
4677
+ }
4678
+ } else if (fromSchema.additionalProperties === true) {
4679
+ const sourceGraphTask = graph.getTask(fromTaskId);
4680
+ if (sourceGraphTask && sourceGraphTask.constructor.passthroughInputsToOutputs === true) {
4681
+ const incomingDfs = graph.getSourceDataflows(fromTaskId);
4682
+ for (const df of incomingDfs) {
4683
+ const portId = df.targetTaskPortId;
4684
+ if (portId === DATAFLOW_ALL_PORTS)
4685
+ continue;
4686
+ if (matches.has(portId))
4687
+ continue;
4688
+ if (connectedInputKeys.has(portId))
4689
+ continue;
4690
+ matches.set(portId, portId);
4691
+ graph.addDataflow(new Dataflow(fromTaskId, portId, toTaskId, portId));
4692
+ }
4693
+ }
4167
4694
  }
4168
4695
  return;
4169
4696
  }
@@ -4559,7 +5086,18 @@ function extractLoopConfig(task) {
4559
5086
  }
4560
5087
  return config;
4561
5088
  }
5089
+ function stripUndefined(obj) {
5090
+ if (!obj)
5091
+ return obj;
5092
+ const result = {};
5093
+ for (const [k, v] of Object.entries(obj)) {
5094
+ if (v !== undefined)
5095
+ result[k] = v;
5096
+ }
5097
+ return Object.keys(result).length > 0 ? result : undefined;
5098
+ }
4562
5099
  function buildMethodArgs(defaults, config, baseIndent = "", columnOffset = 0) {
5100
+ defaults = stripUndefined(defaults);
4563
5101
  const hasDefaults = defaults && Object.keys(defaults).length > 0;
4564
5102
  const hasConfig = Object.keys(config).length > 0;
4565
5103
  if (!hasDefaults && !hasConfig)
@@ -4635,6 +5173,37 @@ ${baseIndent}}`;
4635
5173
  function resetMethodNameCache() {
4636
5174
  methodNameCache = undefined;
4637
5175
  }
5176
+ // src/task/EntitlementProfiles.ts
5177
+ var BROWSER_GRANTS = [
5178
+ { id: Entitlements.NETWORK },
5179
+ { id: Entitlements.AI },
5180
+ { id: Entitlements.MCP_TOOL_CALL },
5181
+ { id: Entitlements.MCP_RESOURCE_READ },
5182
+ { id: Entitlements.MCP_PROMPT_GET },
5183
+ { id: Entitlements.STORAGE },
5184
+ { id: Entitlements.CREDENTIAL }
5185
+ ];
5186
+ var DESKTOP_GRANTS = [
5187
+ ...BROWSER_GRANTS,
5188
+ { id: Entitlements.FILESYSTEM },
5189
+ { id: Entitlements.CODE_EXECUTION },
5190
+ { id: Entitlements.MCP_STDIO }
5191
+ ];
5192
+ var SERVER_GRANTS = [...DESKTOP_GRANTS];
5193
+ var PROFILE_GRANTS = {
5194
+ browser: BROWSER_GRANTS,
5195
+ desktop: DESKTOP_GRANTS,
5196
+ server: SERVER_GRANTS
5197
+ };
5198
+ function createProfilePolicy(profile) {
5199
+ return { deny: [], grant: PROFILE_GRANTS[profile], ask: [] };
5200
+ }
5201
+ function createProfileEnforcer(profile, resolver) {
5202
+ return createPolicyEnforcer(createProfilePolicy(profile), resolver);
5203
+ }
5204
+ function getProfileGrants(profile) {
5205
+ return PROFILE_GRANTS[profile];
5206
+ }
4638
5207
  // src/task/FallbackTaskRunner.ts
4639
5208
  class FallbackTaskRunner extends GraphAsTaskRunner {
4640
5209
  async executeTask(input) {
@@ -4834,6 +5403,83 @@ queueMicrotask(() => {
4834
5403
  };
4835
5404
  Workflow.prototype.endFallbackWith = CreateEndLoopWorkflow("endFallbackWith");
4836
5405
  });
5406
+ // src/task/InputCompactor.ts
5407
+ import { getInputCompactors } from "@workglow/util";
5408
+ function schemaAllowsString(schema, visited = new WeakSet) {
5409
+ if (typeof schema !== "object" || schema === null)
5410
+ return false;
5411
+ if (visited.has(schema))
5412
+ return false;
5413
+ visited.add(schema);
5414
+ const s = schema;
5415
+ if (s.type === "string")
5416
+ return true;
5417
+ const variants = s.oneOf ?? s.anyOf;
5418
+ if (Array.isArray(variants)) {
5419
+ for (const variant of variants) {
5420
+ if (schemaAllowsString(variant, visited))
5421
+ return true;
5422
+ }
5423
+ }
5424
+ const allOf = s.allOf;
5425
+ if (Array.isArray(allOf)) {
5426
+ for (const sub of allOf) {
5427
+ if (schemaAllowsString(sub, visited))
5428
+ return true;
5429
+ }
5430
+ }
5431
+ return false;
5432
+ }
5433
+ async function compactSchemaInputs(input, schema, config, visited = new Set) {
5434
+ if (typeof schema === "boolean")
5435
+ return input;
5436
+ const properties = schema.properties;
5437
+ if (!properties || typeof properties !== "object")
5438
+ return input;
5439
+ const compactors = getInputCompactors();
5440
+ const compacted = { ...input };
5441
+ for (const [key, propSchema] of Object.entries(properties)) {
5442
+ let value = compacted[key];
5443
+ const format = getSchemaFormat(propSchema);
5444
+ if (format) {
5445
+ let compactor = compactors.get(format);
5446
+ if (!compactor) {
5447
+ const prefix = getFormatPrefix(format);
5448
+ compactor = compactors.get(prefix);
5449
+ }
5450
+ if (compactor) {
5451
+ if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value) && schemaAllowsString(propSchema)) {
5452
+ const id = await compactor(value, format, config.registry);
5453
+ if (id !== undefined) {
5454
+ compacted[key] = id;
5455
+ continue;
5456
+ }
5457
+ } else if (Array.isArray(value)) {
5458
+ compacted[key] = await Promise.all(value.map(async (item) => {
5459
+ if (item !== null && item !== undefined && typeof item === "object" && !Array.isArray(item)) {
5460
+ const id = await compactor(item, format, config.registry);
5461
+ return id !== undefined ? id : item;
5462
+ }
5463
+ return item;
5464
+ }));
5465
+ continue;
5466
+ }
5467
+ }
5468
+ }
5469
+ if (value !== null && value !== undefined && typeof value === "object" && !Array.isArray(value)) {
5470
+ const objectSchema = getObjectSchema(propSchema);
5471
+ if (objectSchema && !visited.has(objectSchema)) {
5472
+ visited.add(objectSchema);
5473
+ try {
5474
+ compacted[key] = await compactSchemaInputs(value, objectSchema, config, visited);
5475
+ } finally {
5476
+ visited.delete(objectSchema);
5477
+ }
5478
+ }
5479
+ }
5480
+ }
5481
+ return compacted;
5482
+ }
4837
5483
  // src/task/IteratorTaskRunner.ts
4838
5484
  import { uuid4 as uuid46 } from "@workglow/util";
4839
5485
  class IteratorTaskRunner extends GraphAsTaskRunner {
@@ -4952,7 +5598,7 @@ class IteratorTaskRunner extends GraphAsTaskRunner {
4952
5598
  const newId = uuid46();
4953
5599
  idMap.set(task.config.id, newId);
4954
5600
  const clonedConfig = { ...task.config, id: newId };
4955
- const newTask = new ctor(task.defaults, clonedConfig, task.runConfig);
5601
+ const newTask = new ctor({ ...clonedConfig, defaults: task.defaults }, task.runConfig);
4956
5602
  if (task.hasChildren()) {
4957
5603
  newTask.subGraph = this.cloneGraph(task.subGraph);
4958
5604
  }
@@ -5152,9 +5798,6 @@ class IteratorTask extends GraphAsTask {
5152
5798
  }
5153
5799
  _iteratorPortInfo;
5154
5800
  _iterationInputSchema;
5155
- constructor(input = {}, config = {}) {
5156
- super(input, config);
5157
- }
5158
5801
  get runner() {
5159
5802
  if (!this._runner) {
5160
5803
  this._runner = new IteratorTaskRunner(this);
@@ -5565,9 +6208,6 @@ class WhileTask extends GraphAsTask {
5565
6208
  canSerializeConfig() {
5566
6209
  return typeof this.config.condition !== "function";
5567
6210
  }
5568
- constructor(input = {}, config = {}) {
5569
- super(input, config);
5570
- }
5571
6211
  get runner() {
5572
6212
  if (!this._runner) {
5573
6213
  this._runner = new WhileTaskRunner(this);
@@ -6089,8 +6729,8 @@ import {
6089
6729
  JobQueueServer
6090
6730
  } from "@workglow/job-queue";
6091
6731
  import { InMemoryQueueStorage } from "@workglow/storage";
6092
- import { createServiceToken as createServiceToken2, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
6093
- var JOB_QUEUE_FACTORY = createServiceToken2("taskgraph.jobQueueFactory");
6732
+ import { createServiceToken as createServiceToken4, globalServiceRegistry as globalServiceRegistry3 } from "@workglow/util";
6733
+ var JOB_QUEUE_FACTORY = createServiceToken4("taskgraph.jobQueueFactory");
6094
6734
  var defaultJobQueueFactory = async ({
6095
6735
  queueName,
6096
6736
  jobClass,
@@ -6255,13 +6895,13 @@ class ReduceTask extends IteratorTask {
6255
6895
  static configSchema() {
6256
6896
  return reduceTaskConfigSchema;
6257
6897
  }
6258
- constructor(input = {}, config = {}) {
6898
+ constructor(config = {}) {
6259
6899
  const reduceConfig = {
6260
6900
  ...config,
6261
6901
  concurrencyLimit: 1,
6262
6902
  batchSize: 1
6263
6903
  };
6264
- super(input, reduceConfig);
6904
+ super(reduceConfig);
6265
6905
  }
6266
6906
  get initialValue() {
6267
6907
  return this.config.initialValue ?? {};
@@ -6333,23 +6973,48 @@ queueMicrotask(() => {
6333
6973
  });
6334
6974
  // src/task/TaskRegistry.ts
6335
6975
  import {
6336
- createServiceToken as createServiceToken3,
6976
+ createServiceToken as createServiceToken5,
6977
+ getLogger as getLogger6,
6337
6978
  globalServiceRegistry as globalServiceRegistry4,
6979
+ registerInputCompactor,
6338
6980
  registerInputResolver
6339
6981
  } from "@workglow/util";
6982
+ import { validateSchema } from "@workglow/util/schema";
6340
6983
  var taskConstructors = new Map;
6341
6984
  function registerTask(baseClass) {
6342
- if (taskConstructors.has(baseClass.type)) {}
6985
+ const existing = taskConstructors.get(baseClass.type);
6986
+ if (existing) {
6987
+ if (existing === baseClass)
6988
+ return;
6989
+ throw new Error(`Task type "${baseClass.type}" is already registered. Unregister it first to replace.`);
6990
+ }
6343
6991
  taskConstructors.set(baseClass.type, baseClass);
6992
+ const schemas = [
6993
+ { name: "inputSchema", schema: baseClass.inputSchema() },
6994
+ { name: "outputSchema", schema: baseClass.outputSchema() }
6995
+ ];
6996
+ for (const { name, schema } of schemas) {
6997
+ const result = validateSchema(schema);
6998
+ if (!result.valid) {
6999
+ const messages = result.errors.map((e) => `${e.path}: ${e.message}`).join("; ");
7000
+ getLogger6().warn(`Task "${baseClass.type}" has invalid ${name}: ${messages}`, {
7001
+ taskType: baseClass.type,
7002
+ schemaName: name,
7003
+ errors: result.errors
7004
+ });
7005
+ }
7006
+ }
7007
+ }
7008
+ function unregisterTask(type) {
7009
+ return taskConstructors.delete(type);
6344
7010
  }
6345
7011
  var TaskRegistry = {
6346
7012
  all: taskConstructors,
6347
- registerTask
7013
+ registerTask,
7014
+ unregisterTask
6348
7015
  };
6349
- var TASK_CONSTRUCTORS = createServiceToken3("task.constructors");
6350
- if (!globalServiceRegistry4.has(TASK_CONSTRUCTORS)) {
6351
- globalServiceRegistry4.register(TASK_CONSTRUCTORS, () => TaskRegistry.all, true);
6352
- }
7016
+ var TASK_CONSTRUCTORS = createServiceToken5("task.constructors");
7017
+ globalServiceRegistry4.registerIfAbsent(TASK_CONSTRUCTORS, () => TaskRegistry.all, true);
6353
7018
  function getGlobalTaskConstructors() {
6354
7019
  return globalServiceRegistry4.get(TASK_CONSTRUCTORS);
6355
7020
  }
@@ -6377,6 +7042,17 @@ function resolveTaskFromRegistry(id, _format, registry) {
6377
7042
  };
6378
7043
  }
6379
7044
  registerInputResolver("tasks", resolveTaskFromRegistry);
7045
+ registerInputCompactor("tasks", (value, _format, registry) => {
7046
+ if (typeof value === "object" && value !== null && "name" in value) {
7047
+ const name = value.name;
7048
+ if (typeof name !== "string")
7049
+ return;
7050
+ const constructors = getTaskConstructors(registry);
7051
+ const ctor = constructors.get(name);
7052
+ return ctor ? name : undefined;
7053
+ }
7054
+ return;
7055
+ });
6380
7056
 
6381
7057
  // src/task/TaskJSON.ts
6382
7058
  var createSingleTaskFromJSON = (item, registry, options) => {
@@ -6396,11 +7072,15 @@ var createSingleTaskFromJSON = (item, registry, options) => {
6396
7072
  const taskClass = constructors.get(item.type);
6397
7073
  if (!taskClass)
6398
7074
  throw new TaskJSONError(`Task type ${item.type} not found, perhaps not registered?`);
7075
+ if (typeof taskClass !== "function" || typeof taskClass.type !== "string") {
7076
+ throw new TaskJSONError(`Task type ${item.type} resolved to an invalid constructor`);
7077
+ }
6399
7078
  const taskConfig = {
6400
7079
  ...item.config,
6401
- id: item.id
7080
+ id: item.id,
7081
+ defaults: item.defaults ?? {}
6402
7082
  };
6403
- const task = new taskClass(item.defaults ?? {}, taskConfig, registry ? { registry } : {});
7083
+ const task = new taskClass(taskConfig, registry ? { registry } : {});
6404
7084
  return task;
6405
7085
  };
6406
7086
  var createTaskFromDependencyJSON = (item, registry, options) => {
@@ -6494,8 +7174,8 @@ var registerBaseTasks = () => {
6494
7174
  return tasks;
6495
7175
  };
6496
7176
  // src/storage/TaskGraphRepository.ts
6497
- import { createServiceToken as createServiceToken4, EventEmitter as EventEmitter7 } from "@workglow/util";
6498
- var TASK_GRAPH_REPOSITORY = createServiceToken4("taskgraph.taskGraphRepository");
7177
+ import { createServiceToken as createServiceToken6, EventEmitter as EventEmitter7 } from "@workglow/util";
7178
+ var TASK_GRAPH_REPOSITORY = createServiceToken6("taskgraph.taskGraphRepository");
6499
7179
 
6500
7180
  class TaskGraphRepository {
6501
7181
  type = "TaskGraphRepository";
@@ -6661,6 +7341,9 @@ export {
6661
7341
  serialGraph,
6662
7342
  schemaHasFormatAnnotations,
6663
7343
  schemaAcceptsArray,
7344
+ scanGraphForFormat,
7345
+ scanGraphForCredentials,
7346
+ resourcePatternMatches,
6664
7347
  resolveSchemaInputs,
6665
7348
  resetMethodNameCache,
6666
7349
  removeIterationProperties,
@@ -6669,6 +7352,9 @@ export {
6669
7352
  reduceTaskConfigSchema,
6670
7353
  pipe,
6671
7354
  parallel,
7355
+ mergeResources,
7356
+ mergeEntitlements,
7357
+ mergeEntitlementPair,
6672
7358
  mergeChainedOutputToInput,
6673
7359
  mapTaskConfigSchema,
6674
7360
  iteratorTaskConfigSchema,
@@ -6681,12 +7367,16 @@ export {
6681
7367
  hasStructuredOutput,
6682
7368
  graphToWorkflowCode,
6683
7369
  graphAsTaskConfigSchema,
7370
+ grantCoversResources,
6684
7371
  getTaskQueueRegistry,
6685
7372
  getTaskConstructors,
6686
7373
  getStructuredOutputSchemas,
6687
7374
  getStreamingPorts,
7375
+ getSchemaFormat,
7376
+ getProfileGrants,
6688
7377
  getPortStreamMode,
6689
7378
  getOutputStreamMode,
7379
+ getObjectSchema,
6690
7380
  getObjectPortId,
6691
7381
  getNestedValue,
6692
7382
  getLastTask,
@@ -6694,6 +7384,7 @@ export {
6694
7384
  getIterationContextSchemaForType,
6695
7385
  getInputModeFromSchema,
6696
7386
  getGlobalTaskConstructors,
7387
+ getFormatPrefix,
6697
7388
  getAppendPortId,
6698
7389
  formatValue,
6699
7390
  findArrayPorts,
@@ -6701,20 +7392,29 @@ export {
6701
7392
  fallbackTaskConfigSchema,
6702
7393
  extractIterationProperties,
6703
7394
  extractBaseSchema,
7395
+ evaluatePolicy,
6704
7396
  evaluateCondition,
7397
+ entitlementCovers,
6705
7398
  ensureTask,
6706
7399
  edgeNeedsAccumulation,
6707
7400
  createTaskFromGraphJSON,
6708
7401
  createTaskFromDependencyJSON,
7402
+ createScopedEnforcer,
7403
+ createProfilePolicy,
7404
+ createProfileEnforcer,
7405
+ createPolicyEnforcer,
6709
7406
  createJobQueueFactoryWithOptions,
6710
7407
  createGraphFromGraphJSON,
6711
7408
  createGraphFromDependencyJSON,
7409
+ createGrantListEnforcer,
6712
7410
  createFlexibleSchema,
6713
7411
  createArraySchema,
6714
7412
  connect,
6715
7413
  conditionalTaskConfigSchema,
6716
7414
  computeGraphOutputSchema,
6717
7415
  computeGraphInputSchema,
7416
+ computeGraphEntitlements,
7417
+ compactSchemaInputs,
6718
7418
  calculateNodeDepths,
6719
7419
  buildIterationInputSchema,
6720
7420
  addIterationContextToSchema,
@@ -6745,6 +7445,7 @@ export {
6745
7445
  TaskGraph,
6746
7446
  TaskFailedError,
6747
7447
  TaskError,
7448
+ TaskEntitlementError,
6748
7449
  TaskConfigurationError,
6749
7450
  TaskConfigSchema,
6750
7451
  TaskAbortedError,
@@ -6752,8 +7453,11 @@ export {
6752
7453
  TASK_OUTPUT_REPOSITORY,
6753
7454
  TASK_GRAPH_REPOSITORY,
6754
7455
  TASK_CONSTRUCTORS,
7456
+ SERVER_GRANTS,
6755
7457
  ReduceTask,
6756
7458
  PROPERTY_ARRAY,
7459
+ PERMISSIVE_RESOLVER,
7460
+ PERMISSIVE_ENFORCER,
6757
7461
  MapTask,
6758
7462
  JobTaskFailedError,
6759
7463
  JOB_QUEUE_FACTORY,
@@ -6767,15 +7471,23 @@ export {
6767
7471
  FallbackTask,
6768
7472
  EventTaskGraphToDagMapping,
6769
7473
  EventDagToTaskGraphMapping,
7474
+ Entitlements,
7475
+ ENTITLEMENT_RESOLVER,
7476
+ ENTITLEMENT_ENFORCER,
7477
+ EMPTY_POLICY,
7478
+ EMPTY_ENTITLEMENTS,
6770
7479
  DataflowArrow,
6771
7480
  Dataflow,
7481
+ DESKTOP_GRANTS,
7482
+ DENY_ALL_RESOLVER,
6772
7483
  DATAFLOW_ERROR_PORT,
6773
7484
  DATAFLOW_ALL_PORTS,
6774
7485
  CreateWorkflow,
6775
7486
  CreateLoopWorkflow,
6776
7487
  CreateEndLoopWorkflow,
6777
7488
  CreateAdaptiveWorkflow,
6778
- ConditionalTask
7489
+ ConditionalTask,
7490
+ BROWSER_GRANTS
6779
7491
  };
6780
7492
 
6781
- //# debugId=8BC5BADB1209138664756E2164756E21
7493
+ //# debugId=022B1E4C55D2D5D864756E2164756E21