@exaudeus/workrail 3.14.0 → 3.16.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 (156) hide show
  1. package/dist/application/services/validation-engine.js +4 -9
  2. package/dist/application/services/workflow-compiler.js +4 -6
  3. package/dist/application/services/workflow-service.d.ts +2 -0
  4. package/dist/application/services/workflow-service.js +3 -0
  5. package/dist/console/assets/index-BE5PAgPO.js +28 -0
  6. package/dist/console/assets/index-BZNM03t1.css +1 -0
  7. package/dist/console/index.html +2 -2
  8. package/dist/engine/engine-factory.js +2 -2
  9. package/dist/engine/types.d.ts +1 -1
  10. package/dist/env-flags.d.ts +1 -0
  11. package/dist/env-flags.js +4 -0
  12. package/dist/infrastructure/session/HttpServer.d.ts +3 -3
  13. package/dist/infrastructure/session/HttpServer.js +68 -74
  14. package/dist/infrastructure/storage/caching-workflow-storage.d.ts +2 -0
  15. package/dist/infrastructure/storage/caching-workflow-storage.js +15 -6
  16. package/dist/infrastructure/storage/file-workflow-storage.js +3 -4
  17. package/dist/infrastructure/storage/schema-validating-workflow-storage.js +9 -8
  18. package/dist/manifest.json +283 -219
  19. package/dist/mcp/assert-output.d.ts +37 -0
  20. package/dist/mcp/assert-output.js +52 -0
  21. package/dist/mcp/boundary-coercion.d.ts +1 -0
  22. package/dist/mcp/boundary-coercion.js +44 -0
  23. package/dist/mcp/dev-mode.d.ts +1 -0
  24. package/dist/mcp/dev-mode.js +4 -0
  25. package/dist/mcp/handler-factory.js +12 -9
  26. package/dist/mcp/handlers/session.js +8 -9
  27. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +5 -0
  28. package/dist/mcp/handlers/shared/request-workflow-reader.js +47 -2
  29. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.d.ts +1 -1
  30. package/dist/mcp/handlers/v2-advance-core/assessment-consequences.js +4 -5
  31. package/dist/mcp/handlers/v2-advance-core/event-builders.d.ts +2 -0
  32. package/dist/mcp/handlers/v2-advance-core/event-builders.js +6 -6
  33. package/dist/mcp/handlers/v2-advance-core/index.d.ts +2 -0
  34. package/dist/mcp/handlers/v2-advance-core/index.js +5 -4
  35. package/dist/mcp/handlers/v2-advance-core/input-validation.d.ts +2 -0
  36. package/dist/mcp/handlers/v2-advance-core/input-validation.js +32 -9
  37. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.d.ts +2 -0
  38. package/dist/mcp/handlers/v2-advance-core/outcome-blocked.js +2 -2
  39. package/dist/mcp/handlers/v2-advance-core/outcome-success.d.ts +2 -0
  40. package/dist/mcp/handlers/v2-advance-core/outcome-success.js +1 -1
  41. package/dist/mcp/handlers/v2-checkpoint.d.ts +1 -1
  42. package/dist/mcp/handlers/v2-checkpoint.js +5 -6
  43. package/dist/mcp/handlers/v2-execution/advance.d.ts +4 -2
  44. package/dist/mcp/handlers/v2-execution/advance.js +5 -7
  45. package/dist/mcp/handlers/v2-execution/continue-advance.js +56 -26
  46. package/dist/mcp/handlers/v2-execution/continue-rehydrate.d.ts +1 -1
  47. package/dist/mcp/handlers/v2-execution/continue-rehydrate.js +9 -9
  48. package/dist/mcp/handlers/v2-execution/replay.d.ts +6 -4
  49. package/dist/mcp/handlers/v2-execution/replay.js +47 -30
  50. package/dist/mcp/handlers/v2-execution/start.d.ts +3 -3
  51. package/dist/mcp/handlers/v2-execution/start.js +31 -12
  52. package/dist/mcp/handlers/v2-execution/workflow-object-cache.d.ts +5 -0
  53. package/dist/mcp/handlers/v2-execution/workflow-object-cache.js +19 -0
  54. package/dist/mcp/handlers/v2-execution-helpers.d.ts +1 -0
  55. package/dist/mcp/handlers/v2-execution-helpers.js +23 -7
  56. package/dist/mcp/handlers/v2-resume.d.ts +1 -1
  57. package/dist/mcp/handlers/v2-resume.js +3 -4
  58. package/dist/mcp/handlers/v2-state-conversion.js +5 -1
  59. package/dist/mcp/handlers/v2-workflow.d.ts +100 -0
  60. package/dist/mcp/handlers/v2-workflow.js +155 -31
  61. package/dist/mcp/handlers/workflow.d.ts +2 -5
  62. package/dist/mcp/handlers/workflow.js +15 -12
  63. package/dist/mcp/output-schemas.d.ts +123 -29
  64. package/dist/mcp/output-schemas.js +36 -18
  65. package/dist/mcp/server.js +70 -5
  66. package/dist/mcp/tool-call-timing.d.ts +24 -0
  67. package/dist/mcp/tool-call-timing.js +85 -0
  68. package/dist/mcp/tool-descriptions.js +17 -9
  69. package/dist/mcp/transports/http-entry.js +3 -2
  70. package/dist/mcp/transports/http-listener.d.ts +1 -0
  71. package/dist/mcp/transports/http-listener.js +25 -0
  72. package/dist/mcp/transports/shutdown-hooks.d.ts +4 -1
  73. package/dist/mcp/transports/shutdown-hooks.js +3 -2
  74. package/dist/mcp/transports/stdio-entry.js +6 -28
  75. package/dist/mcp/v2/tools.d.ts +6 -0
  76. package/dist/mcp/v2/tools.js +2 -0
  77. package/dist/mcp/v2-response-formatter.js +2 -4
  78. package/dist/mcp/validation/schema-introspection.d.ts +1 -0
  79. package/dist/mcp/validation/schema-introspection.js +15 -5
  80. package/dist/mcp/validation/suggestion-generator.js +2 -2
  81. package/dist/mcp/workflow-protocol-contracts.js +5 -1
  82. package/dist/runtime/adapters/node-process-signals.d.ts +1 -0
  83. package/dist/runtime/adapters/node-process-signals.js +5 -0
  84. package/dist/runtime/adapters/noop-process-signals.d.ts +1 -0
  85. package/dist/runtime/adapters/noop-process-signals.js +2 -0
  86. package/dist/runtime/ports/process-signals.d.ts +1 -0
  87. package/dist/types/workflow-definition.d.ts +3 -2
  88. package/dist/types/workflow.d.ts +3 -0
  89. package/dist/types/workflow.js +35 -26
  90. package/dist/v2/durable-core/domain/context-template-resolver.js +2 -2
  91. package/dist/v2/durable-core/domain/function-definition-expander.js +2 -17
  92. package/dist/v2/durable-core/domain/prompt-renderer.d.ts +1 -0
  93. package/dist/v2/durable-core/domain/prompt-renderer.js +23 -18
  94. package/dist/v2/durable-core/domain/recap-recovery.js +23 -16
  95. package/dist/v2/durable-core/domain/retrieval-contract.js +13 -7
  96. package/dist/v2/durable-core/session-index.d.ts +22 -0
  97. package/dist/v2/durable-core/session-index.js +58 -0
  98. package/dist/v2/durable-core/sorted-event-log.d.ts +6 -0
  99. package/dist/v2/durable-core/sorted-event-log.js +15 -0
  100. package/dist/v2/infra/local/fs/index.js +8 -8
  101. package/dist/v2/infra/local/session-store/index.d.ts +1 -1
  102. package/dist/v2/infra/local/session-store/index.js +71 -61
  103. package/dist/v2/infra/local/session-summary-provider/index.js +9 -4
  104. package/dist/v2/infra/local/snapshot-store/index.js +2 -1
  105. package/dist/v2/infra/local/workspace-anchor/index.js +4 -1
  106. package/dist/v2/ports/session-event-log-store.port.d.ts +1 -1
  107. package/dist/v2/projections/assessment-consequences.d.ts +2 -1
  108. package/dist/v2/projections/assessment-consequences.js +0 -5
  109. package/dist/v2/projections/assessments.d.ts +2 -1
  110. package/dist/v2/projections/assessments.js +2 -4
  111. package/dist/v2/projections/gaps.d.ts +2 -1
  112. package/dist/v2/projections/gaps.js +0 -5
  113. package/dist/v2/projections/preferences.d.ts +2 -1
  114. package/dist/v2/projections/preferences.js +0 -5
  115. package/dist/v2/projections/run-context.d.ts +2 -2
  116. package/dist/v2/projections/run-context.js +0 -5
  117. package/dist/v2/projections/run-dag.js +7 -1
  118. package/dist/v2/projections/run-execution-trace.d.ts +8 -0
  119. package/dist/v2/projections/run-execution-trace.js +124 -0
  120. package/dist/v2/projections/run-status-signals.d.ts +2 -2
  121. package/dist/v2/usecases/console-routes.d.ts +3 -1
  122. package/dist/v2/usecases/console-routes.js +149 -3
  123. package/dist/v2/usecases/console-service.d.ts +2 -0
  124. package/dist/v2/usecases/console-service.js +87 -26
  125. package/dist/v2/usecases/console-types.d.ts +65 -0
  126. package/dist/v2/usecases/worktree-service.js +87 -8
  127. package/package.json +7 -6
  128. package/spec/authoring-spec.json +82 -1
  129. package/spec/workflow-tags.json +132 -0
  130. package/spec/workflow.schema.json +21 -11
  131. package/workflows/adaptive-ticket-creation.json +33 -8
  132. package/workflows/architecture-scalability-audit.json +50 -9
  133. package/workflows/bug-investigation.agentic.v2.json +43 -14
  134. package/workflows/coding-task-workflow-agentic.json +57 -38
  135. package/workflows/coding-task-workflow-agentic.lean.v2.json +129 -34
  136. package/workflows/coding-task-workflow-agentic.v2.json +97 -30
  137. package/workflows/cross-platform-code-conversion.v2.json +175 -48
  138. package/workflows/document-creation-workflow.json +49 -12
  139. package/workflows/documentation-update-workflow.json +9 -2
  140. package/workflows/intelligent-test-case-generation.json +9 -2
  141. package/workflows/learner-centered-course-workflow.json +273 -266
  142. package/workflows/mr-review-workflow.agentic.v2.json +88 -14
  143. package/workflows/personal-learning-materials-creation-branched.json +181 -174
  144. package/workflows/presentation-creation.json +167 -160
  145. package/workflows/production-readiness-audit.json +61 -15
  146. package/workflows/relocation-workflow-us.json +21 -5
  147. package/workflows/routines/tension-driven-design.json +1 -1
  148. package/workflows/scoped-documentation-workflow.json +9 -2
  149. package/workflows/test-artifact-loop-control.json +1 -2
  150. package/workflows/ui-ux-design-workflow.json +334 -0
  151. package/workflows/workflow-diagnose-environment.json +7 -1
  152. package/workflows/workflow-for-workflows.json +514 -484
  153. package/workflows/workflow-for-workflows.v2.json +55 -11
  154. package/workflows/wr.discovery.json +118 -29
  155. package/dist/console/assets/index-DW78t31j.css +0 -1
  156. package/dist/console/assets/index-EsSXrC_a.js +0 -28
@@ -6,15 +6,14 @@ exports.mintStartTokens = mintStartTokens;
6
6
  exports.executeStartWorkflow = executeStartWorkflow;
7
7
  const output_schemas_js_1 = require("../../output-schemas.js");
8
8
  const step_instance_key_js_1 = require("../../../v2/durable-core/schemas/execution-snapshot/step-instance-key.js");
9
- const workflow_js_1 = require("../../../types/workflow.js");
10
9
  const index_js_1 = require("../../../v2/durable-core/ids/index.js");
11
10
  const workflow_hash_ref_js_1 = require("../../../v2/durable-core/ids/workflow-hash-ref.js");
12
11
  const neverthrow_1 = require("neverthrow");
13
12
  const workflow_validation_pipeline_js_1 = require("../../../application/services/workflow-validation-pipeline.js");
14
13
  const hashing_js_1 = require("../../../v2/durable-core/canonical/hashing.js");
15
14
  const observation_builder_js_1 = require("../../../v2/durable-core/domain/observation-builder.js");
16
- const workflow_source_js_1 = require("../../../types/workflow-source.js");
17
15
  const workflow_definition_js_1 = require("../../../types/workflow-definition.js");
16
+ const workflow_object_cache_js_1 = require("./workflow-object-cache.js");
18
17
  const prompt_renderer_js_1 = require("../../../v2/durable-core/domain/prompt-renderer.js");
19
18
  const v2_workspace_resolution_js_1 = require("../v2-workspace-resolution.js");
20
19
  const v2_token_ops_js_1 = require("../v2-token-ops.js");
@@ -73,13 +72,13 @@ function loadAndPinWorkflow(args) {
73
72
  return pinnedStore.get(workflowHash)
74
73
  .mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
75
74
  .andThen((existingPinned) => {
76
- if (!existingPinned) {
77
- return pinnedStore.put(workflowHash, enrichedCompiled)
78
- .mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }));
75
+ if (existingPinned) {
76
+ return (0, neverthrow_1.okAsync)(existingPinned);
79
77
  }
80
- return (0, neverthrow_1.okAsync)(undefined);
78
+ return pinnedStore.put(workflowHash, enrichedCompiled)
79
+ .mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause }))
80
+ .andThen(() => pinnedStore.get(workflowHash).mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause })));
81
81
  })
82
- .andThen(() => pinnedStore.get(workflowHash).mapErr((cause) => ({ kind: 'pinned_workflow_store_failed', cause })))
83
82
  .andThen((pinned) => {
84
83
  if (!pinned || pinned.sourceKind !== 'v1_pinned' || !(0, workflow_definition_js_1.hasWorkflowDefinitionShape)(pinned.definition)) {
85
84
  return (0, neverthrow_1.errAsync)({
@@ -87,7 +86,7 @@ function loadAndPinWorkflow(args) {
87
86
  message: 'Failed to pin executable workflow snapshot (missing or invalid pinned workflow).',
88
87
  });
89
88
  }
90
- const pinnedWorkflow = (0, workflow_js_1.createWorkflow)(pinned.definition, (0, workflow_source_js_1.createBundledSource)());
89
+ const pinnedWorkflow = (0, workflow_object_cache_js_1.getCachedWorkflow)(workflowHash, pinned.definition);
91
90
  const resolution = (0, start_construction_js_1.resolveFirstStep)(workflow, pinned);
92
91
  if (resolution.isErr()) {
93
92
  const error = resolution.error.reason === 'no_steps'
@@ -108,7 +107,7 @@ function loadAndPinWorkflow(args) {
108
107
  });
109
108
  }
110
109
  function buildInitialEvents(args) {
111
- const { sessionId, runId, nodeId, workflowId, workflowHash, workflowSourceKind, workflowSourceRef, snapshotRef, observations, idFactory, } = args;
110
+ const { sessionId, runId, nodeId, workflowId, workflowHash, workflowSourceKind, workflowSourceRef, snapshotRef, observations, idFactory, goal, } = args;
112
111
  const evtSessionCreated = idFactory.mintEventId();
113
112
  const evtRunStarted = idFactory.mintEventId();
114
113
  const evtNodeCreated = idFactory.mintEventId();
@@ -177,6 +176,24 @@ function buildInitialEvents(args) {
177
176
  },
178
177
  ];
179
178
  const mutableEvents = [...baseEvents];
179
+ {
180
+ const contextEventId = idFactory.mintEventId();
181
+ const contextId = idFactory.mintEventId();
182
+ mutableEvents.push({
183
+ v: 1,
184
+ eventId: contextEventId,
185
+ eventIndex: mutableEvents.length,
186
+ sessionId,
187
+ kind: constants_js_1.EVENT_KIND.CONTEXT_SET,
188
+ dedupeKey: `context_set:${sessionId}:${String(runId)}:initial`,
189
+ scope: { runId: String(runId) },
190
+ data: {
191
+ contextId,
192
+ context: { goal },
193
+ source: 'initial',
194
+ },
195
+ });
196
+ }
180
197
  for (const obs of observations) {
181
198
  const obsEventId = idFactory.mintEventId();
182
199
  mutableEvents.push({
@@ -289,11 +306,13 @@ function executeStartWorkflow(input, ctx) {
289
306
  snapshotRef,
290
307
  observations,
291
308
  idFactory,
309
+ goal: input.goal,
292
310
  });
311
+ const emptyTruth = { manifest: [], events: [] };
293
312
  return gate.withHealthySessionLock(sessionId, (lock) => sessionStore.append(lock, {
294
313
  events,
295
314
  snapshotPins: [{ snapshotRef, eventIndex: 2, createdByEventId: events[2].eventId }],
296
- }))
315
+ }, emptyTruth))
297
316
  .mapErr((cause) => ({ kind: 'session_append_failed', cause }))
298
317
  .map(() => ({ workflow, firstStep, workflowHash, pinnedWorkflow, resolvedReferences, sessionId, runId, nodeId }));
299
318
  });
@@ -342,7 +361,7 @@ function executeStartWorkflow(input, ctx) {
342
361
  meta,
343
362
  references: resolvedReferences,
344
363
  });
345
- const parsed = output_schemas_js_1.V2StartWorkflowOutputSchema.parse({
364
+ const parsed = {
346
365
  continueToken: tokens.continueToken,
347
366
  checkpointToken: tokens.checkpointToken,
348
367
  isComplete: false,
@@ -351,7 +370,7 @@ function executeStartWorkflow(input, ctx) {
351
370
  nextIntent,
352
371
  nextCall: (0, index_js_2.buildNextCall)({ continueToken: tokens.continueToken, isComplete: false, pending }),
353
372
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
354
- });
373
+ };
355
374
  return (0, neverthrow_1.okAsync)({ response: parsed, contentEnvelope });
356
375
  });
357
376
  }));
@@ -0,0 +1,5 @@
1
+ import type { WorkflowDefinition } from '../../../types/workflow-definition.js';
2
+ import type { Workflow } from '../../../types/workflow.js';
3
+ import type { WorkflowHash } from '../../../v2/durable-core/ids/index.js';
4
+ export declare function getCachedWorkflow(workflowHash: WorkflowHash, definition: WorkflowDefinition): Workflow;
5
+ export declare function clearWorkflowObjectCacheForTesting(): void;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCachedWorkflow = getCachedWorkflow;
4
+ exports.clearWorkflowObjectCacheForTesting = clearWorkflowObjectCacheForTesting;
5
+ const workflow_js_1 = require("../../../types/workflow.js");
6
+ const workflow_source_js_1 = require("../../../types/workflow-source.js");
7
+ const _cache = new Map();
8
+ function getCachedWorkflow(workflowHash, definition) {
9
+ const key = String(workflowHash);
10
+ const existing = _cache.get(key);
11
+ if (existing !== undefined)
12
+ return existing;
13
+ const wf = (0, workflow_js_1.createWorkflow)(definition, (0, workflow_source_js_1.createBundledSource)());
14
+ _cache.set(key, wf);
15
+ return wf;
16
+ }
17
+ function clearWorkflowObjectCacheForTesting() {
18
+ _cache.clear();
19
+ }
@@ -120,4 +120,5 @@ export declare function derivePreferencesOrDefault(args: {
120
120
  readonly truth: LoadedSessionTruthV2;
121
121
  readonly runId: RunId;
122
122
  readonly nodeId: NodeId;
123
+ readonly precomputedIndex?: import('../../v2/durable-core/session-index.js').SessionIndex;
123
124
  }): PreferencesV2;
@@ -266,20 +266,36 @@ function mapSessionOrGateErrorToToolError(e) {
266
266
  }
267
267
  const constants_js_1 = require("../../v2/durable-core/constants.js");
268
268
  const preferences_js_1 = require("../../v2/projections/preferences.js");
269
+ const sorted_event_log_js_1 = require("../../v2/durable-core/sorted-event-log.js");
269
270
  exports.defaultPreferences = {
270
271
  autonomy: 'guided',
271
272
  riskPolicy: 'conservative'
272
273
  };
273
274
  function derivePreferencesOrDefault(args) {
275
+ let sortedEventsForPrefs;
274
276
  const parentByNodeId = {};
275
- for (const e of args.truth.events) {
276
- if (e.kind !== constants_js_1.EVENT_KIND.NODE_CREATED)
277
- continue;
278
- if (e.scope?.runId !== String(args.runId))
279
- continue;
280
- parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
277
+ if (args.precomputedIndex) {
278
+ sortedEventsForPrefs = args.precomputedIndex.sortedEvents;
279
+ for (const [nodeId, evt] of args.precomputedIndex.nodeCreatedByNodeId) {
280
+ if (evt.scope.runId === String(args.runId)) {
281
+ parentByNodeId[nodeId] = evt.data.parentNodeId ?? null;
282
+ }
283
+ }
284
+ }
285
+ else {
286
+ for (const e of args.truth.events) {
287
+ if (e.kind !== constants_js_1.EVENT_KIND.NODE_CREATED)
288
+ continue;
289
+ if (e.scope?.runId !== String(args.runId))
290
+ continue;
291
+ parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
292
+ }
293
+ const sortedEventsRes = (0, sorted_event_log_js_1.asSortedEventLog)(args.truth.events);
294
+ if (sortedEventsRes.isErr())
295
+ return exports.defaultPreferences;
296
+ sortedEventsForPrefs = sortedEventsRes.value;
281
297
  }
282
- const prefs = (0, preferences_js_1.projectPreferencesV2)(args.truth.events, parentByNodeId);
298
+ const prefs = (0, preferences_js_1.projectPreferencesV2)(sortedEventsForPrefs, parentByNodeId);
283
299
  if (prefs.isErr())
284
300
  return exports.defaultPreferences;
285
301
  const p = prefs.value.byNodeId[String(args.nodeId)]?.effective;
@@ -1,7 +1,7 @@
1
1
  import type { z } from 'zod';
2
2
  import type { V2ResumeSessionInput } from '../v2/tools.js';
3
3
  import type { ToolContext, ToolResult } from '../types.js';
4
- import { V2ResumeSessionOutputSchema } from '../output-schemas.js';
4
+ import type { V2ResumeSessionOutputSchema } from '../output-schemas.js';
5
5
  type ResumeInput = z.infer<typeof V2ResumeSessionInput>;
6
6
  type ResumeOutput = z.infer<typeof V2ResumeSessionOutputSchema>;
7
7
  export declare function handleV2ResumeSession(input: ResumeInput, ctx: ToolContext): Promise<ToolResult<ResumeOutput>>;
@@ -2,7 +2,6 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handleV2ResumeSession = handleV2ResumeSession;
4
4
  const types_js_1 = require("../types.js");
5
- const output_schemas_js_1 = require("../output-schemas.js");
6
5
  const v2_token_ops_js_1 = require("./v2-token-ops.js");
7
6
  const index_js_1 = require("../../v2/durable-core/ids/index.js");
8
7
  const resume_session_js_1 = require("../../v2/usecases/resume-session.js");
@@ -43,10 +42,10 @@ async function handleV2ResumeSession(input, ctx) {
43
42
  if (skipped > 0) {
44
43
  console.error(`[workrail:resume] ${skipped}/${candidates.length} candidate(s) skipped: token minting failed (workflowHashRef derivation or signing error)`);
45
44
  }
46
- const output = output_schemas_js_1.V2ResumeSessionOutputSchema.parse({
47
- candidates: outputCandidates,
45
+ const output = {
46
+ candidates: [...outputCandidates],
48
47
  totalEligible: totalFound,
49
- });
48
+ };
50
49
  return {
51
50
  type: 'success',
52
51
  data: output,
@@ -9,6 +9,7 @@ exports.derivePreferencesForNode = derivePreferencesForNode;
9
9
  exports.deriveNextIntent = deriveNextIntent;
10
10
  const step_instance_key_js_1 = require("../../v2/durable-core/schemas/execution-snapshot/step-instance-key.js");
11
11
  const preferences_js_1 = require("../../v2/projections/preferences.js");
12
+ const sorted_event_log_js_1 = require("../../v2/durable-core/sorted-event-log.js");
12
13
  const constants_js_1 = require("../../v2/durable-core/constants.js");
13
14
  function toV1ExecutionState(engineState) {
14
15
  if (engineState.kind === 'init')
@@ -94,7 +95,10 @@ function derivePreferencesForNode(args) {
94
95
  continue;
95
96
  parentByNodeId[String(e.scope.nodeId)] = e.data.parentNodeId;
96
97
  }
97
- const prefs = (0, preferences_js_1.projectPreferencesV2)(args.truth.events, parentByNodeId);
98
+ const sortedEventsRes = (0, sorted_event_log_js_1.asSortedEventLog)(args.truth.events);
99
+ if (sortedEventsRes.isErr())
100
+ return exports.defaultPreferences;
101
+ const prefs = (0, preferences_js_1.projectPreferencesV2)(sortedEventsRes.value, parentByNodeId);
98
102
  if (prefs.isErr())
99
103
  return exports.defaultPreferences;
100
104
  const p = prefs.value.byNodeId[String(args.nodeId)]?.effective;
@@ -1,7 +1,107 @@
1
1
  import type { ToolContext, ToolResult } from '../types.js';
2
2
  import type { V2InspectWorkflowInput, V2ListWorkflowsInput } from '../v2/tools.js';
3
3
  import type { StalenessSummary } from '../output-schemas.js';
4
+ import type { RememberedRootRecordV2 } from '../../v2/ports/remembered-roots-store.port.js';
5
+ import type { CryptoPortV2 } from '../../v2/durable-core/canonical/hashing.js';
6
+ import type { PinnedWorkflowStorePortV2 } from '../../v2/ports/pinned-workflow-store.port.js';
7
+ import type { Workflow } from '../../types/workflow.js';
4
8
  export declare function shouldShowStaleness(category: string | undefined, devMode?: boolean): boolean;
9
+ interface WorkflowTagsFile {
10
+ readonly tags: ReadonlyArray<{
11
+ readonly id: string;
12
+ readonly displayName: string;
13
+ readonly when: readonly string[];
14
+ readonly examples: readonly string[];
15
+ }>;
16
+ readonly workflows: Readonly<Record<string, {
17
+ readonly tags: readonly string[];
18
+ readonly hidden?: boolean;
19
+ }>>;
20
+ }
21
+ export declare function buildTagSummary(tagsFile: WorkflowTagsFile, compiledWorkflowIds: readonly string[]): Array<{
22
+ id: string;
23
+ displayName: string;
24
+ count: number;
25
+ when: string[];
26
+ examples: string[];
27
+ }>;
5
28
  export declare function computeWorkflowStaleness(stamp: number | undefined, currentVersion: number | null): StalenessSummary | undefined;
29
+ interface WorkflowLookupReader {
30
+ readonly getWorkflowById: (id: string) => Promise<Workflow | null>;
31
+ readonly listWorkflowSummaries: () => Promise<readonly {
32
+ readonly id: string;
33
+ readonly name: string;
34
+ readonly description: string;
35
+ readonly version: string;
36
+ }[]>;
37
+ readonly loadAllWorkflows: () => Promise<readonly Workflow[]>;
38
+ }
6
39
  export declare function handleV2ListWorkflows(input: V2ListWorkflowsInput, ctx: ToolContext): Promise<ToolResult<unknown>>;
7
40
  export declare function handleV2InspectWorkflow(input: V2InspectWorkflowInput, ctx: ToolContext): Promise<ToolResult<unknown>>;
41
+ export declare function buildV2WorkflowListItem(options: {
42
+ readonly workflow: Workflow | null;
43
+ readonly summary: {
44
+ readonly id: string;
45
+ readonly name: string;
46
+ readonly description: string;
47
+ readonly version: string;
48
+ };
49
+ readonly workflowReader: WorkflowLookupReader;
50
+ readonly rememberedRootRecords: readonly RememberedRootRecordV2[];
51
+ readonly crypto: CryptoPortV2;
52
+ readonly pinnedStore: PinnedWorkflowStorePortV2;
53
+ }): Promise<{
54
+ workflowId: string;
55
+ name: string;
56
+ description: string;
57
+ version: string;
58
+ workflowHash: null;
59
+ kind: "workflow";
60
+ } | {
61
+ examples: string[];
62
+ workflowId: string;
63
+ name: string;
64
+ description: string;
65
+ version: string;
66
+ workflowHash: null;
67
+ kind: "workflow";
68
+ visibility: import("./shared/workflow-source-visibility.js").WorkflowVisibility;
69
+ } | {
70
+ examples?: undefined;
71
+ workflowId: string;
72
+ name: string;
73
+ description: string;
74
+ version: string;
75
+ workflowHash: null;
76
+ kind: "workflow";
77
+ visibility: import("./shared/workflow-source-visibility.js").WorkflowVisibility;
78
+ } | {
79
+ examples: string[];
80
+ staleness?: {
81
+ level: "none" | "possible" | "likely";
82
+ reason: string;
83
+ specVersionAtLastReview?: number | undefined;
84
+ } | undefined;
85
+ workflowId: string;
86
+ name: string;
87
+ description: string;
88
+ version: string;
89
+ workflowHash: never;
90
+ kind: "workflow";
91
+ visibility: import("./shared/workflow-source-visibility.js").WorkflowVisibility;
92
+ } | {
93
+ examples?: undefined;
94
+ staleness?: {
95
+ level: "none" | "possible" | "likely";
96
+ reason: string;
97
+ specVersionAtLastReview?: number | undefined;
98
+ } | undefined;
99
+ workflowId: string;
100
+ name: string;
101
+ description: string;
102
+ version: string;
103
+ workflowHash: never;
104
+ kind: "workflow";
105
+ visibility: import("./shared/workflow-source-visibility.js").WorkflowVisibility;
106
+ }>;
107
+ export {};
@@ -4,16 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.shouldShowStaleness = shouldShowStaleness;
7
+ exports.buildTagSummary = buildTagSummary;
7
8
  exports.computeWorkflowStaleness = computeWorkflowStaleness;
8
9
  exports.handleV2ListWorkflows = handleV2ListWorkflows;
9
10
  exports.handleV2InspectWorkflow = handleV2InspectWorkflow;
11
+ exports.buildV2WorkflowListItem = buildV2WorkflowListItem;
10
12
  const path_1 = __importDefault(require("path"));
11
13
  const fs_1 = __importDefault(require("fs"));
12
14
  const neverthrow_1 = require("neverthrow");
13
15
  const types_js_1 = require("../types.js");
14
16
  const error_mapper_js_1 = require("../error-mapper.js");
15
17
  const v2_execution_helpers_js_1 = require("./v2-execution-helpers.js");
16
- const output_schemas_js_1 = require("../output-schemas.js");
17
18
  const v1_to_v2_shim_js_1 = require("../../v2/read-only/v1-to-v2-shim.js");
18
19
  const hashing_js_1 = require("../../v2/durable-core/canonical/hashing.js");
19
20
  const TIMEOUT_MS = 30000;
@@ -40,6 +41,39 @@ function shouldShowStaleness(category, devMode = DEV_STALENESS) {
40
41
  return true;
41
42
  return category === 'personal' || category === 'rooted_sharing' || category === 'external';
42
43
  }
44
+ function readWorkflowTags() {
45
+ try {
46
+ const tagsPath = path_1.default.resolve(__dirname, '../../../spec/workflow-tags.json');
47
+ const raw = fs_1.default.readFileSync(tagsPath, 'utf-8');
48
+ return JSON.parse(raw);
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ const WORKFLOW_TAGS = readWorkflowTags();
55
+ function buildTagSummary(tagsFile, compiledWorkflowIds) {
56
+ const idSet = new Set(compiledWorkflowIds);
57
+ return tagsFile.tags.map((tag) => {
58
+ const count = Object.entries(tagsFile.workflows)
59
+ .filter(([wid, meta]) => !meta.hidden && idSet.has(wid) && meta.tags.includes(tag.id))
60
+ .length;
61
+ return {
62
+ id: tag.id,
63
+ displayName: tag.displayName,
64
+ count,
65
+ when: [...tag.when],
66
+ examples: [...tag.examples],
67
+ };
68
+ });
69
+ }
70
+ function filterByTags(tagsFile, compiledWorkflowIds, requestedTags) {
71
+ const tagSet = new Set(requestedTags);
72
+ const matching = new Set(Object.entries(tagsFile.workflows)
73
+ .filter(([, meta]) => !meta.hidden && meta.tags.some((t) => tagSet.has(t)))
74
+ .map(([wid]) => wid));
75
+ return compiledWorkflowIds.filter((id) => matching.has(id));
76
+ }
43
77
  function computeWorkflowStaleness(stamp, currentVersion) {
44
78
  if (currentVersion === null)
45
79
  return undefined;
@@ -90,40 +124,94 @@ async function handleV2ListWorkflows(input, ctx) {
90
124
  workspacePath: input.workspacePath,
91
125
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
92
126
  rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
127
+ managedSourceStore: guard.ctx.v2.managedSourceStore,
93
128
  })
94
- : { reader: ctx.workflowService, stalePaths: [] };
129
+ : { reader: ctx.workflowService, stalePaths: [], managedSourceRecords: [], staleManagedRecords: [], managedStoreError: undefined };
95
130
  const workflowReader = readerResult.reader;
96
131
  const stalePaths = readerResult.stalePaths;
97
- return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.listWorkflowSummaries(), TIMEOUT_MS, 'list_workflows'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
98
- .andThen((summaries) => neverthrow_1.ResultAsync.combine(summaries.map((s) => neverthrow_1.ResultAsync.fromPromise(buildV2WorkflowListItem({
99
- summary: s,
100
- workflowReader,
101
- rememberedRootRecords,
102
- crypto,
103
- pinnedStore,
104
- }), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)))))
132
+ const managedSourceRecords = readerResult.managedSourceRecords;
133
+ const staleManagedRecords = readerResult.staleManagedRecords;
134
+ const managedStoreError = readerResult.managedStoreError;
135
+ const warnings = managedStoreError
136
+ ? [`Managed workflow source store was temporarily unavailable (${managedStoreError}). Managed sources were not loaded.`]
137
+ : undefined;
138
+ return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.loadAllWorkflows(), TIMEOUT_MS, 'list_workflows'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
139
+ .andThen((allWorkflows) => {
140
+ const workflowMap = new Map(allWorkflows.map((w) => [w.definition.id, w]));
141
+ const summaries = allWorkflows.map((w) => ({
142
+ id: w.definition.id,
143
+ name: w.definition.name,
144
+ description: w.definition.description,
145
+ version: w.definition.version,
146
+ }));
147
+ return neverthrow_1.ResultAsync.combine(summaries.map((s) => neverthrow_1.ResultAsync.fromPromise(buildV2WorkflowListItem({
148
+ workflow: workflowMap.get(s.id) ?? null,
149
+ summary: s,
150
+ workflowReader,
151
+ rememberedRootRecords,
152
+ crypto,
153
+ pinnedStore,
154
+ }), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))));
155
+ })
105
156
  .andThen((compiled) => {
157
+ const sortedIds = compiled.map((w) => w.workflowId).sort((a, b) => a.localeCompare(b));
158
+ const sortedCompiled = [...compiled].sort((a, b) => a.workflowId.localeCompare(b.workflowId));
159
+ const tagFilteredCompiled = (() => {
160
+ if (input.includeSources)
161
+ return sortedCompiled;
162
+ if (!WORKFLOW_TAGS)
163
+ return sortedCompiled;
164
+ if (input.tags && input.tags.length > 0) {
165
+ const filteredIds = new Set(filterByTags(WORKFLOW_TAGS, sortedIds, input.tags));
166
+ return sortedCompiled.filter((w) => filteredIds.has(w.workflowId));
167
+ }
168
+ return [];
169
+ })();
170
+ const tagSummaryEntry = (() => {
171
+ if (input.includeSources)
172
+ return undefined;
173
+ if (!WORKFLOW_TAGS)
174
+ return undefined;
175
+ if (input.tags && input.tags.length > 0)
176
+ return undefined;
177
+ return buildTagSummary(WORKFLOW_TAGS, sortedIds);
178
+ })();
179
+ const nextStepHint = tagSummaryEntry
180
+ ? 'Pick a tag from tagSummary that fits the user\'s goal, then call list_workflows with tags=["<tagId>"]. ' +
181
+ 'If a workflow ID in examples[] already matches, call start_workflow directly — no second list call needed. ' +
182
+ 'If multiple tags could apply, pick the most specific one.'
183
+ : undefined;
106
184
  if (!input.includeSources) {
107
- const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
108
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
109
- ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
110
- });
185
+ const includeStaleRoots = !tagSummaryEntry && stalePaths.length > 0;
186
+ const payload = {
187
+ workflows: tagFilteredCompiled,
188
+ ...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
189
+ ...(nextStepHint ? { _nextStep: nextStepHint } : {}),
190
+ ...(includeStaleRoots ? { staleRoots: [...stalePaths] } : {}),
191
+ ...(warnings ? { warnings } : {}),
192
+ };
111
193
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
112
194
  }
113
195
  if (!(0, workflow_source_visibility_js_1.isCompositeWorkflowReader)(workflowReader)) {
114
- const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
115
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
196
+ const payload = {
197
+ workflows: tagFilteredCompiled,
198
+ ...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
199
+ ...(nextStepHint ? { _nextStep: nextStepHint } : {}),
116
200
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
201
+ ...(warnings ? { warnings } : {}),
117
202
  sources: [],
118
- });
203
+ };
119
204
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
120
205
  }
121
- return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(buildSourceCatalog(workflowReader, rememberedRootRecords), TIMEOUT_MS, 'list_workflow_sources'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)).map((sources) => {
122
- const payload = output_schemas_js_1.V2WorkflowListOutputSchema.parse({
123
- workflows: compiled.sort((a, b) => a.workflowId.localeCompare(b.workflowId)),
206
+ return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(buildSourceCatalog(workflowReader, rememberedRootRecords, managedSourceRecords, staleManagedRecords), TIMEOUT_MS, 'list_workflow_sources'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err)).map((sources) => {
207
+ const payload = {
208
+ workflows: tagFilteredCompiled,
209
+ ...(tagSummaryEntry ? { tagSummary: tagSummaryEntry } : {}),
210
+ ...(nextStepHint ? { _nextStep: nextStepHint } : {}),
124
211
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
125
- sources,
126
- });
212
+ ...(warnings ? { warnings } : {}),
213
+ sources: [...sources],
214
+ };
127
215
  return (0, types_js_1.success)(payload);
128
216
  });
129
217
  })
@@ -150,10 +238,14 @@ async function handleV2InspectWorkflow(input, ctx) {
150
238
  workspacePath: input.workspacePath,
151
239
  resolvedRootUris: guard.ctx.v2.resolvedRootUris,
152
240
  rememberedRootsStore: guard.ctx.v2.rememberedRootsStore,
241
+ managedSourceStore: guard.ctx.v2.managedSourceStore,
153
242
  })
154
- : { reader: ctx.workflowService, stalePaths: [] };
243
+ : { reader: ctx.workflowService, stalePaths: [], managedSourceRecords: [], staleManagedRecords: [], managedStoreError: undefined };
155
244
  const workflowReader = readerResult.reader;
156
245
  const stalePaths = readerResult.stalePaths;
246
+ const inspectWarnings = readerResult.managedStoreError
247
+ ? [`Managed workflow source store was temporarily unavailable (${readerResult.managedStoreError}). Managed sources were not loaded.`]
248
+ : undefined;
157
249
  return neverthrow_1.ResultAsync.fromPromise((0, with_timeout_js_1.withTimeout)(workflowReader.getWorkflowById(input.workflowId), TIMEOUT_MS, 'inspect_workflow'), (err) => (0, error_mapper_js_1.mapUnknownErrorToToolError)(err))
158
250
  .andThen((workflow) => {
159
251
  if (!workflow) {
@@ -182,21 +274,22 @@ async function handleV2InspectWorkflow(input, ctx) {
182
274
  ? { schemaVersion: compiled.schemaVersion, sourceKind: compiled.sourceKind, workflowId: compiled.workflowId }
183
275
  : compiled;
184
276
  const references = workflow.definition.references;
185
- const payload = output_schemas_js_1.V2WorkflowInspectOutputSchema.parse({
277
+ const payload = {
186
278
  workflowId: input.workflowId,
187
279
  workflowHash,
188
280
  mode: input.mode,
189
281
  compiled: body,
190
282
  ...(visibility ? { visibility } : {}),
191
283
  ...(stalePaths.length > 0 ? { staleRoots: [...stalePaths] } : {}),
192
- ...(references != null && references.length > 0 ? { references } : {}),
284
+ ...(inspectWarnings ? { warnings: inspectWarnings } : {}),
285
+ ...(references != null && references.length > 0 ? { references: [...references] } : {}),
193
286
  ...(() => {
194
287
  const staleness = shouldShowStaleness(visibility?.category)
195
288
  ? computeWorkflowStaleness(workflow.definition.validatedAgainstSpecVersion, CURRENT_SPEC_VERSION)
196
289
  : undefined;
197
290
  return staleness !== undefined ? { staleness } : {};
198
291
  })(),
199
- });
292
+ };
200
293
  return (0, neverthrow_1.okAsync)((0, types_js_1.success)(payload));
201
294
  }));
202
295
  })
@@ -211,8 +304,7 @@ async function buildWorkflowVisibility(workflow, workflowReader, rememberedRootR
211
304
  return (0, workflow_source_visibility_js_1.toWorkflowVisibility)(workflow, rememberedRootRecords, { migration });
212
305
  }
213
306
  async function buildV2WorkflowListItem(options) {
214
- const { summary, workflowReader, rememberedRootRecords, crypto, pinnedStore } = options;
215
- const workflow = await workflowReader.getWorkflowById(summary.id);
307
+ const { workflow, summary, workflowReader, rememberedRootRecords, crypto, pinnedStore } = options;
216
308
  if (!workflow) {
217
309
  return {
218
310
  workflowId: summary.id,
@@ -224,6 +316,9 @@ async function buildV2WorkflowListItem(options) {
224
316
  };
225
317
  }
226
318
  const visibility = await buildWorkflowVisibility(workflow, workflowReader, rememberedRootRecords);
319
+ const examples = workflow.definition.examples?.length
320
+ ? { examples: [...workflow.definition.examples] }
321
+ : {};
227
322
  const snapshot = (0, v1_to_v2_shim_js_1.compileV1WorkflowToV2PreviewSnapshot)(workflow);
228
323
  const hashRes = (0, hashing_js_1.workflowHashForCompiledSnapshot)(snapshot, crypto);
229
324
  if (hashRes.isErr()) {
@@ -235,6 +330,7 @@ async function buildV2WorkflowListItem(options) {
235
330
  workflowHash: null,
236
331
  kind: 'workflow',
237
332
  visibility,
333
+ ...examples,
238
334
  };
239
335
  }
240
336
  const hash = hashRes.value;
@@ -250,6 +346,7 @@ async function buildV2WorkflowListItem(options) {
250
346
  workflowHash: hash,
251
347
  kind: 'workflow',
252
348
  visibility,
349
+ ...examples,
253
350
  };
254
351
  }
255
352
  }
@@ -265,9 +362,10 @@ async function buildV2WorkflowListItem(options) {
265
362
  kind: 'workflow',
266
363
  visibility,
267
364
  ...(staleness !== undefined ? { staleness } : {}),
365
+ ...examples,
268
366
  };
269
367
  }
270
- async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
368
+ async function buildSourceCatalog(workflowReader, rememberedRootRecords, managedSourceRecords, staleManagedRecords) {
271
369
  const instances = workflowReader.getStorageInstances();
272
370
  const seenIds = new Set();
273
371
  const sourceEntryDataReversed = [];
@@ -281,10 +379,22 @@ async function buildSourceCatalog(workflowReader, rememberedRootRecords) {
281
379
  sourceEntryDataReversed.push({ source: instance.source, allIds, effectiveIds });
282
380
  }
283
381
  const sourceEntryData = sourceEntryDataReversed.reverse();
284
- return sourceEntryData.map((data) => deriveSourceCatalogEntry({ ...data, rememberedRootRecords, sourceEntryData }));
382
+ const activeEntries = sourceEntryData.map((data) => deriveSourceCatalogEntry({ ...data, rememberedRootRecords, managedSourceRecords, sourceEntryData }));
383
+ const staleEntries = staleManagedRecords.map((record) => ({
384
+ sourceKey: `custom:${record.path}`,
385
+ category: 'managed',
386
+ source: { kind: 'custom', displayName: path_1.default.basename(record.path) },
387
+ sourceMode: 'live_directory',
388
+ effectiveWorkflowCount: 0,
389
+ totalWorkflowCount: 0,
390
+ shadowedWorkflowCount: 0,
391
+ managed: { addedAtMs: record.addedAtMs },
392
+ stale: true,
393
+ }));
394
+ return [...activeEntries, ...staleEntries];
285
395
  }
286
396
  function deriveSourceCatalogEntry(options) {
287
- const { source, allIds, effectiveIds, rememberedRootRecords, sourceEntryData } = options;
397
+ const { source, allIds, effectiveIds, rememberedRootRecords, managedSourceRecords, sourceEntryData } = options;
288
398
  const total = allIds.length;
289
399
  const effective = effectiveIds.length;
290
400
  const shadowed = total - effective;
@@ -310,6 +420,20 @@ function deriveSourceCatalogEntry(options) {
310
420
  }
311
421
  case 'custom': {
312
422
  const rootedSharing = deriveRootedSharingForPath(source.directoryPath, rememberedRootRecords);
423
+ const managedRecord = managedSourceRecords.find((r) => path_1.default.resolve(r.path) === path_1.default.resolve(source.directoryPath));
424
+ if (managedRecord) {
425
+ return {
426
+ sourceKey,
427
+ category: 'managed',
428
+ source: { kind: source.kind, displayName },
429
+ sourceMode: 'live_directory',
430
+ effectiveWorkflowCount: effective,
431
+ totalWorkflowCount: total,
432
+ shadowedWorkflowCount: shadowed,
433
+ managed: { addedAtMs: managedRecord.addedAtMs },
434
+ ...(rootedSharing ? { rootedSharing } : {}),
435
+ };
436
+ }
313
437
  const category = rootedSharing ? 'rooted_sharing' : 'external';
314
438
  const sourceMode = rootedSharing ? 'rooted_sharing' : 'live_directory';
315
439
  return { sourceKey, category, source: { kind: source.kind, displayName }, sourceMode, effectiveWorkflowCount: effective, totalWorkflowCount: total, shadowedWorkflowCount: shadowed, ...(rootedSharing ? { rootedSharing } : {}) };