@exaudeus/workrail 3.11.1 → 3.12.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 (37) hide show
  1. package/dist/console/assets/index-CRgjJiMS.js +28 -0
  2. package/dist/console/assets/index-DW78t31j.css +1 -0
  3. package/dist/console/index.html +2 -2
  4. package/dist/manifest.json +67 -59
  5. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +10 -2
  6. package/dist/mcp/handlers/shared/request-workflow-reader.js +27 -10
  7. package/dist/mcp/handlers/shared/workflow-source-visibility.d.ts +3 -1
  8. package/dist/mcp/handlers/shared/workflow-source-visibility.js +7 -3
  9. package/dist/mcp/handlers/v2-execution/replay.js +25 -1
  10. package/dist/mcp/handlers/v2-execution/start.js +23 -17
  11. package/dist/mcp/handlers/v2-workflow.js +123 -8
  12. package/dist/mcp/output-schemas.d.ts +393 -0
  13. package/dist/mcp/output-schemas.js +49 -1
  14. package/dist/mcp/tools.js +5 -5
  15. package/dist/mcp/v2/tools.d.ts +3 -0
  16. package/dist/mcp/v2/tools.js +3 -2
  17. package/dist/v2/durable-core/constants.d.ts +1 -0
  18. package/dist/v2/durable-core/constants.js +2 -1
  19. package/dist/v2/durable-core/domain/observation-builder.d.ts +4 -1
  20. package/dist/v2/durable-core/domain/observation-builder.js +9 -0
  21. package/dist/v2/durable-core/schemas/export-bundle/index.d.ts +76 -16
  22. package/dist/v2/durable-core/schemas/session/events.d.ts +26 -5
  23. package/dist/v2/durable-core/schemas/session/events.js +2 -1
  24. package/dist/v2/infra/local/session-summary-provider/index.js +2 -0
  25. package/dist/v2/infra/local/workspace-anchor/index.js +1 -0
  26. package/dist/v2/ports/workspace-anchor.port.d.ts +3 -0
  27. package/dist/v2/projections/resume-ranking.d.ts +1 -0
  28. package/dist/v2/usecases/console-routes.js +26 -0
  29. package/dist/v2/usecases/console-service.js +25 -6
  30. package/dist/v2/usecases/console-types.d.ts +22 -1
  31. package/dist/v2/usecases/worktree-service.d.ts +10 -0
  32. package/dist/v2/usecases/worktree-service.js +136 -0
  33. package/package.json +1 -1
  34. package/workflows/architecture-scalability-audit.json +317 -0
  35. package/workflows/routines/tension-driven-design.json +5 -5
  36. package/dist/console/assets/index-C5C4nDs4.css +0 -1
  37. package/dist/console/assets/index-CSUqsoQl.js +0 -28
@@ -16,14 +16,15 @@ const workspacePathField = zod_1.z.string()
16
16
  const optionalWorkspacePathField = workspacePathField.optional();
17
17
  exports.V2ListWorkflowsInput = zod_1.z.object({
18
18
  workspacePath: workspacePathField.describe('Required. Absolute path to your current workspace directory (e.g. the "Workspace:" value from your system parameters). WorkRail uses this to resolve project-scoped workflow variants against the correct workspace for discovery-sensitive workflow listing. Shared MCP servers cannot infer this safely.'),
19
+ includeSources: zod_1.z.boolean().optional().describe('When true, includes a source catalog in the response showing where workflows come from (built-in, project-scoped, rooted-sharing, external), with effective and shadowed workflow counts per source. Omit or set false for the default workflow-list-only response.'),
19
20
  });
20
21
  exports.V2InspectWorkflowInput = zod_1.z.object({
21
- workflowId: zod_1.z.string().min(1).regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores').describe('The workflow ID to inspect'),
22
+ workflowId: zod_1.z.string().min(1).regex(/^([a-z0-9_-]+|[a-z][a-z0-9_-]+\.[a-z][a-z0-9_-]+)$/, 'Workflow ID must be a valid legacy ID (e.g. my-workflow) or namespaced ID (e.g. wr.discovery)').describe('The workflow ID to inspect'),
22
23
  mode: zod_1.z.enum(['metadata', 'preview']).default('preview').describe('Detail level: metadata (name and description only) or preview (full step-by-step breakdown, default)'),
23
24
  workspacePath: workspacePathField.describe('Required. Absolute path to your current workspace directory (e.g. the "Workspace:" value from your system parameters). WorkRail uses this to resolve the correct project-scoped workflow variant for discovery-sensitive workflow inspection. Shared MCP servers cannot infer this safely.'),
24
25
  });
25
26
  exports.V2StartWorkflowInput = zod_1.z.object({
26
- workflowId: zod_1.z.string().min(1).regex(/^[A-Za-z0-9_-]+$/, 'Workflow ID must contain only letters, numbers, hyphens, and underscores').describe('The workflow ID to start'),
27
+ workflowId: zod_1.z.string().min(1).regex(/^([a-z0-9_-]+|[a-z][a-z0-9_-]+\.[a-z][a-z0-9_-]+)$/, 'Workflow ID must be a valid legacy ID (e.g. my-workflow) or namespaced ID (e.g. wr.discovery)').describe('The workflow ID to start'),
27
28
  workspacePath: workspacePathField.describe('Required. Absolute path to your current workspace directory (e.g. the "Workspace:" value from your system parameters). WorkRail uses this to resolve the correct project-scoped workflow variant and to anchor the session to the correct repo for future resume_session discovery. Shared MCP servers cannot infer this safely.'),
28
29
  });
29
30
  exports.V2ContinueWorkflowInputShape = zod_1.z.object({
@@ -12,6 +12,7 @@ export declare const MAX_VALIDATION_SUGGESTIONS_BYTES = 4096;
12
12
  export declare const MAX_CONTEXT_BYTES: number;
13
13
  export declare const MAX_CONTEXT_DEPTH = 64;
14
14
  export declare const MAX_OBSERVATION_SHORT_STRING_LENGTH = 80;
15
+ export declare const MAX_OBSERVATION_PATH_LENGTH = 512;
15
16
  export declare const SESSION_LOCK_RETRY_AFTER_MS = 1000;
16
17
  export declare const DEFAULT_RETRY_AFTER_MS = 1000;
17
18
  export declare const TRUNCATION_MARKER = "\n\n[TRUNCATED]";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AUTONOMY_MODE = exports.ADVANCE_INTENT = exports.MANIFEST_KIND = exports.EDGE_CAUSE = exports.ADVANCE_OUTCOME = exports.ENGINE_STATE = exports.EDGE_KIND = exports.PAYLOAD_KIND = exports.OUTPUT_CHANNEL = exports.EVENT_KIND = exports.DELIMITER_SAFE_ID_PATTERN = exports.SHA256_DIGEST_PATTERN = exports.MAX_RESUME_PREVIEW_BYTES = exports.RECOVERY_BUDGET_BYTES = exports.TRUNCATION_MARKER = exports.DEFAULT_RETRY_AFTER_MS = exports.SESSION_LOCK_RETRY_AFTER_MS = exports.MAX_OBSERVATION_SHORT_STRING_LENGTH = exports.MAX_CONTEXT_DEPTH = exports.MAX_CONTEXT_BYTES = exports.MAX_VALIDATION_SUGGESTIONS_BYTES = exports.MAX_VALIDATION_ISSUES_BYTES = exports.MAX_VALIDATION_SUGGESTION_ITEM_BYTES = exports.MAX_VALIDATION_ISSUE_ITEM_BYTES = exports.MAX_OUTPUT_NOTES_MARKDOWN_BYTES = exports.MAX_DECISION_TRACE_TOTAL_BYTES = exports.MAX_DECISION_TRACE_ENTRY_SUMMARY_BYTES = exports.MAX_DECISION_TRACE_ENTRIES = exports.MAX_BLOCKER_SUGGESTED_FIX_BYTES = exports.MAX_BLOCKER_MESSAGE_BYTES = exports.MAX_BLOCKERS = void 0;
3
+ exports.AUTONOMY_MODE = exports.ADVANCE_INTENT = exports.MANIFEST_KIND = exports.EDGE_CAUSE = exports.ADVANCE_OUTCOME = exports.ENGINE_STATE = exports.EDGE_KIND = exports.PAYLOAD_KIND = exports.OUTPUT_CHANNEL = exports.EVENT_KIND = exports.DELIMITER_SAFE_ID_PATTERN = exports.SHA256_DIGEST_PATTERN = exports.MAX_RESUME_PREVIEW_BYTES = exports.RECOVERY_BUDGET_BYTES = exports.TRUNCATION_MARKER = exports.DEFAULT_RETRY_AFTER_MS = exports.SESSION_LOCK_RETRY_AFTER_MS = exports.MAX_OBSERVATION_PATH_LENGTH = exports.MAX_OBSERVATION_SHORT_STRING_LENGTH = exports.MAX_CONTEXT_DEPTH = exports.MAX_CONTEXT_BYTES = exports.MAX_VALIDATION_SUGGESTIONS_BYTES = exports.MAX_VALIDATION_ISSUES_BYTES = exports.MAX_VALIDATION_SUGGESTION_ITEM_BYTES = exports.MAX_VALIDATION_ISSUE_ITEM_BYTES = exports.MAX_OUTPUT_NOTES_MARKDOWN_BYTES = exports.MAX_DECISION_TRACE_TOTAL_BYTES = exports.MAX_DECISION_TRACE_ENTRY_SUMMARY_BYTES = exports.MAX_DECISION_TRACE_ENTRIES = exports.MAX_BLOCKER_SUGGESTED_FIX_BYTES = exports.MAX_BLOCKER_MESSAGE_BYTES = exports.MAX_BLOCKERS = void 0;
4
4
  exports.MAX_BLOCKERS = 10;
5
5
  exports.MAX_BLOCKER_MESSAGE_BYTES = 512;
6
6
  exports.MAX_BLOCKER_SUGGESTED_FIX_BYTES = 1024;
@@ -15,6 +15,7 @@ exports.MAX_VALIDATION_SUGGESTIONS_BYTES = 4096;
15
15
  exports.MAX_CONTEXT_BYTES = 256 * 1024;
16
16
  exports.MAX_CONTEXT_DEPTH = 64;
17
17
  exports.MAX_OBSERVATION_SHORT_STRING_LENGTH = 80;
18
+ exports.MAX_OBSERVATION_PATH_LENGTH = 512;
18
19
  exports.SESSION_LOCK_RETRY_AFTER_MS = 1000;
19
20
  exports.DEFAULT_RETRY_AFTER_MS = 1000;
20
21
  exports.TRUNCATION_MARKER = '\n\n[TRUNCATED]';
@@ -1,6 +1,6 @@
1
1
  import type { WorkspaceAnchor } from '../../ports/workspace-anchor.port.js';
2
2
  export interface ObservationEventData {
3
- readonly key: 'git_branch' | 'git_head_sha' | 'repo_root_hash';
3
+ readonly key: 'git_branch' | 'git_head_sha' | 'repo_root_hash' | 'repo_root';
4
4
  readonly value: {
5
5
  readonly type: 'short_string';
6
6
  readonly value: string;
@@ -10,6 +10,9 @@ export interface ObservationEventData {
10
10
  } | {
11
11
  readonly type: 'sha256';
12
12
  readonly value: string;
13
+ } | {
14
+ readonly type: 'path';
15
+ readonly value: string;
13
16
  };
14
17
  readonly confidence: 'low' | 'med' | 'high';
15
18
  }
@@ -33,6 +33,15 @@ function anchorsToObservations(anchors) {
33
33
  confidence: 'high',
34
34
  });
35
35
  break;
36
+ case 'repo_root':
37
+ if (anchor.value.length > constants_js_1.MAX_OBSERVATION_PATH_LENGTH)
38
+ break;
39
+ observations.push({
40
+ key: 'repo_root',
41
+ value: { type: 'path', value: anchor.value },
42
+ confidence: 'high',
43
+ });
44
+ break;
36
45
  default: {
37
46
  const _exhaustive = anchor;
38
47
  return _exhaustive;
@@ -110,7 +110,7 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
110
110
  kind: z.ZodLiteral<"observation_recorded">;
111
111
  scope: z.ZodUndefined;
112
112
  data: z.ZodObject<{
113
- key: z.ZodEnum<["git_branch", "git_head_sha", "repo_root_hash"]>;
113
+ key: z.ZodEnum<["git_branch", "git_head_sha", "repo_root_hash", "repo_root"]>;
114
114
  value: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
115
115
  type: z.ZodLiteral<"short_string">;
116
116
  value: z.ZodString;
@@ -138,6 +138,15 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
138
138
  }, {
139
139
  value: string;
140
140
  type: "sha256";
141
+ }>, z.ZodObject<{
142
+ type: z.ZodLiteral<"path">;
143
+ value: z.ZodString;
144
+ }, "strip", z.ZodTypeAny, {
145
+ value: string;
146
+ type: "path";
147
+ }, {
148
+ value: string;
149
+ type: "path";
141
150
  }>]>;
142
151
  confidence: z.ZodEnum<["low", "med", "high"]>;
143
152
  }, "strip", z.ZodTypeAny, {
@@ -150,8 +159,11 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
150
159
  } | {
151
160
  value: string;
152
161
  type: "sha256";
162
+ } | {
163
+ value: string;
164
+ type: "path";
153
165
  };
154
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
166
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
155
167
  confidence: "high" | "low" | "med";
156
168
  }, {
157
169
  value: {
@@ -163,8 +175,11 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
163
175
  } | {
164
176
  value: string;
165
177
  type: "sha256";
178
+ } | {
179
+ value: string;
180
+ type: "path";
166
181
  };
167
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
182
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
168
183
  confidence: "high" | "low" | "med";
169
184
  }>;
170
185
  }, "strip", z.ZodTypeAny, {
@@ -180,8 +195,11 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
180
195
  } | {
181
196
  value: string;
182
197
  type: "sha256";
198
+ } | {
199
+ value: string;
200
+ type: "path";
183
201
  };
184
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
202
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
185
203
  confidence: "high" | "low" | "med";
186
204
  };
187
205
  v: 1;
@@ -202,8 +220,11 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
202
220
  } | {
203
221
  value: string;
204
222
  type: "sha256";
223
+ } | {
224
+ value: string;
225
+ type: "path";
205
226
  };
206
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
227
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
207
228
  confidence: "high" | "low" | "med";
208
229
  };
209
230
  v: 1;
@@ -4727,8 +4748,11 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
4727
4748
  } | {
4728
4749
  value: string;
4729
4750
  type: "sha256";
4751
+ } | {
4752
+ value: string;
4753
+ type: "path";
4730
4754
  };
4731
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
4755
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
4732
4756
  confidence: "high" | "low" | "med";
4733
4757
  };
4734
4758
  v: 1;
@@ -5300,8 +5324,11 @@ export declare const SessionContentsV1Schema: z.ZodObject<{
5300
5324
  } | {
5301
5325
  value: string;
5302
5326
  type: "sha256";
5327
+ } | {
5328
+ value: string;
5329
+ type: "path";
5303
5330
  };
5304
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
5331
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
5305
5332
  confidence: "high" | "low" | "med";
5306
5333
  };
5307
5334
  v: 1;
@@ -5791,7 +5818,7 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
5791
5818
  kind: z.ZodLiteral<"observation_recorded">;
5792
5819
  scope: z.ZodUndefined;
5793
5820
  data: z.ZodObject<{
5794
- key: z.ZodEnum<["git_branch", "git_head_sha", "repo_root_hash"]>;
5821
+ key: z.ZodEnum<["git_branch", "git_head_sha", "repo_root_hash", "repo_root"]>;
5795
5822
  value: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
5796
5823
  type: z.ZodLiteral<"short_string">;
5797
5824
  value: z.ZodString;
@@ -5819,6 +5846,15 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
5819
5846
  }, {
5820
5847
  value: string;
5821
5848
  type: "sha256";
5849
+ }>, z.ZodObject<{
5850
+ type: z.ZodLiteral<"path">;
5851
+ value: z.ZodString;
5852
+ }, "strip", z.ZodTypeAny, {
5853
+ value: string;
5854
+ type: "path";
5855
+ }, {
5856
+ value: string;
5857
+ type: "path";
5822
5858
  }>]>;
5823
5859
  confidence: z.ZodEnum<["low", "med", "high"]>;
5824
5860
  }, "strip", z.ZodTypeAny, {
@@ -5831,8 +5867,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
5831
5867
  } | {
5832
5868
  value: string;
5833
5869
  type: "sha256";
5870
+ } | {
5871
+ value: string;
5872
+ type: "path";
5834
5873
  };
5835
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
5874
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
5836
5875
  confidence: "high" | "low" | "med";
5837
5876
  }, {
5838
5877
  value: {
@@ -5844,8 +5883,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
5844
5883
  } | {
5845
5884
  value: string;
5846
5885
  type: "sha256";
5886
+ } | {
5887
+ value: string;
5888
+ type: "path";
5847
5889
  };
5848
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
5890
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
5849
5891
  confidence: "high" | "low" | "med";
5850
5892
  }>;
5851
5893
  }, "strip", z.ZodTypeAny, {
@@ -5861,8 +5903,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
5861
5903
  } | {
5862
5904
  value: string;
5863
5905
  type: "sha256";
5906
+ } | {
5907
+ value: string;
5908
+ type: "path";
5864
5909
  };
5865
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
5910
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
5866
5911
  confidence: "high" | "low" | "med";
5867
5912
  };
5868
5913
  v: 1;
@@ -5883,8 +5928,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
5883
5928
  } | {
5884
5929
  value: string;
5885
5930
  type: "sha256";
5931
+ } | {
5932
+ value: string;
5933
+ type: "path";
5886
5934
  };
5887
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
5935
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
5888
5936
  confidence: "high" | "low" | "med";
5889
5937
  };
5890
5938
  v: 1;
@@ -10408,8 +10456,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
10408
10456
  } | {
10409
10457
  value: string;
10410
10458
  type: "sha256";
10459
+ } | {
10460
+ value: string;
10461
+ type: "path";
10411
10462
  };
10412
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
10463
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
10413
10464
  confidence: "high" | "low" | "med";
10414
10465
  };
10415
10466
  v: 1;
@@ -10981,8 +11032,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
10981
11032
  } | {
10982
11033
  value: string;
10983
11034
  type: "sha256";
11035
+ } | {
11036
+ value: string;
11037
+ type: "path";
10984
11038
  };
10985
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
11039
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
10986
11040
  confidence: "high" | "low" | "med";
10987
11041
  };
10988
11042
  v: 1;
@@ -11583,8 +11637,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
11583
11637
  } | {
11584
11638
  value: string;
11585
11639
  type: "sha256";
11640
+ } | {
11641
+ value: string;
11642
+ type: "path";
11586
11643
  };
11587
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
11644
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
11588
11645
  confidence: "high" | "low" | "med";
11589
11646
  };
11590
11647
  v: 1;
@@ -12173,8 +12230,11 @@ export declare const ExportBundleV1Schema: z.ZodObject<{
12173
12230
  } | {
12174
12231
  value: string;
12175
12232
  type: "sha256";
12233
+ } | {
12234
+ value: string;
12235
+ type: "path";
12176
12236
  };
12177
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
12237
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
12178
12238
  confidence: "high" | "low" | "med";
12179
12239
  };
12180
12240
  v: 1;
@@ -95,7 +95,7 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
95
95
  kind: z.ZodLiteral<"observation_recorded">;
96
96
  scope: z.ZodUndefined;
97
97
  data: z.ZodObject<{
98
- key: z.ZodEnum<["git_branch", "git_head_sha", "repo_root_hash"]>;
98
+ key: z.ZodEnum<["git_branch", "git_head_sha", "repo_root_hash", "repo_root"]>;
99
99
  value: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
100
100
  type: z.ZodLiteral<"short_string">;
101
101
  value: z.ZodString;
@@ -123,6 +123,15 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
123
123
  }, {
124
124
  value: string;
125
125
  type: "sha256";
126
+ }>, z.ZodObject<{
127
+ type: z.ZodLiteral<"path">;
128
+ value: z.ZodString;
129
+ }, "strip", z.ZodTypeAny, {
130
+ value: string;
131
+ type: "path";
132
+ }, {
133
+ value: string;
134
+ type: "path";
126
135
  }>]>;
127
136
  confidence: z.ZodEnum<["low", "med", "high"]>;
128
137
  }, "strip", z.ZodTypeAny, {
@@ -135,8 +144,11 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
135
144
  } | {
136
145
  value: string;
137
146
  type: "sha256";
147
+ } | {
148
+ value: string;
149
+ type: "path";
138
150
  };
139
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
151
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
140
152
  confidence: "high" | "low" | "med";
141
153
  }, {
142
154
  value: {
@@ -148,8 +160,11 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
148
160
  } | {
149
161
  value: string;
150
162
  type: "sha256";
163
+ } | {
164
+ value: string;
165
+ type: "path";
151
166
  };
152
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
167
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
153
168
  confidence: "high" | "low" | "med";
154
169
  }>;
155
170
  }, "strip", z.ZodTypeAny, {
@@ -165,8 +180,11 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
165
180
  } | {
166
181
  value: string;
167
182
  type: "sha256";
183
+ } | {
184
+ value: string;
185
+ type: "path";
168
186
  };
169
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
187
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
170
188
  confidence: "high" | "low" | "med";
171
189
  };
172
190
  v: 1;
@@ -187,8 +205,11 @@ export declare const DomainEventV1Schema: z.ZodDiscriminatedUnion<"kind", [z.Zod
187
205
  } | {
188
206
  value: string;
189
207
  type: "sha256";
208
+ } | {
209
+ value: string;
210
+ type: "path";
190
211
  };
191
- key: "git_branch" | "git_head_sha" | "repo_root_hash";
212
+ key: "git_branch" | "git_head_sha" | "repo_root_hash" | "repo_root";
192
213
  confidence: "high" | "low" | "med";
193
214
  };
194
215
  v: 1;
@@ -107,11 +107,12 @@ exports.DomainEventV1Schema = zod_1.z.discriminatedUnion('kind', [
107
107
  kind: zod_1.z.literal('observation_recorded'),
108
108
  scope: zod_1.z.undefined(),
109
109
  data: zod_1.z.object({
110
- key: zod_1.z.enum(['git_branch', 'git_head_sha', 'repo_root_hash']),
110
+ key: zod_1.z.enum(['git_branch', 'git_head_sha', 'repo_root_hash', 'repo_root']),
111
111
  value: zod_1.z.discriminatedUnion('type', [
112
112
  zod_1.z.object({ type: zod_1.z.literal('short_string'), value: zod_1.z.string().min(1).max(constants_js_1.MAX_OBSERVATION_SHORT_STRING_LENGTH) }),
113
113
  zod_1.z.object({ type: zod_1.z.literal('git_sha1'), value: zod_1.z.string().regex(/^[0-9a-f]{40}$/) }),
114
114
  zod_1.z.object({ type: zod_1.z.literal('sha256'), value: sha256DigestSchema }),
115
+ zod_1.z.object({ type: zod_1.z.literal('path'), value: zod_1.z.string().min(1).max(constants_js_1.MAX_OBSERVATION_PATH_LENGTH) }),
115
116
  ]),
116
117
  confidence: zod_1.z.enum(['low', 'med', 'high']),
117
118
  }),
@@ -17,6 +17,7 @@ const EMPTY_OBSERVATIONS = {
17
17
  gitHeadSha: null,
18
18
  gitBranch: null,
19
19
  repoRootHash: null,
20
+ repoRoot: null,
20
21
  };
21
22
  const TITLE_CONTEXT_KEYS = ['goal', 'taskDescription', 'mrTitle', 'prTitle', 'ticketTitle', 'problem'];
22
23
  class LocalSessionSummaryProviderV2 {
@@ -131,6 +132,7 @@ function extractObservations(events) {
131
132
  case 'git_head_sha': return { ...acc, gitHeadSha: e.data.value.value };
132
133
  case 'git_branch': return { ...acc, gitBranch: e.data.value.value };
133
134
  case 'repo_root_hash': return { ...acc, repoRootHash: e.data.value.value };
135
+ case 'repo_root': return { ...acc, repoRoot: e.data.value.value };
134
136
  }
135
137
  }, EMPTY_OBSERVATIONS);
136
138
  }
@@ -40,6 +40,7 @@ class LocalWorkspaceAnchorV2 {
40
40
  if (repoRootHash) {
41
41
  anchors.push({ key: 'repo_root_hash', value: repoRootHash });
42
42
  }
43
+ anchors.push({ key: 'repo_root', value: repoRoot });
43
44
  const branch = await this.gitCommand('git rev-parse --abbrev-ref HEAD', cwd);
44
45
  if (branch && branch !== 'HEAD') {
45
46
  anchors.push({ key: 'git_branch', value: branch });
@@ -8,6 +8,9 @@ export type WorkspaceAnchor = {
8
8
  } | {
9
9
  readonly key: 'repo_root_hash';
10
10
  readonly value: string;
11
+ } | {
12
+ readonly key: 'repo_root';
13
+ readonly value: string;
11
14
  };
12
15
  export type WorkspaceAnchorError = {
13
16
  readonly code: 'ANCHOR_RESOLVE_FAILED';
@@ -8,6 +8,7 @@ export interface SessionObservations {
8
8
  readonly gitHeadSha: string | null;
9
9
  readonly gitBranch: string | null;
10
10
  readonly repoRootHash: string | null;
11
+ readonly repoRoot: string | null;
11
12
  }
12
13
  export type WorkflowIdentity = {
13
14
  readonly kind: 'unknown';
@@ -7,6 +7,7 @@ exports.mountConsoleRoutes = mountConsoleRoutes;
7
7
  const express_1 = __importDefault(require("express"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const fs_1 = __importDefault(require("fs"));
10
+ const worktree_service_js_1 = require("./worktree-service.js");
10
11
  function resolveConsoleDist() {
11
12
  const releasedDist = path_1.default.join(__dirname, '../../console');
12
13
  if (fs_1.default.existsSync(releasedDist))
@@ -24,6 +25,31 @@ function mountConsoleRoutes(app, consoleService) {
24
25
  const result = await consoleService.getSessionList();
25
26
  result.match((data) => res.json({ success: true, data }), (error) => res.status(500).json({ success: false, error: error.message }));
26
27
  });
28
+ let cwdRepoRootPromise = null;
29
+ const REPO_ROOTS_TTL_MS = 60000;
30
+ let cachedRepoRoots = [];
31
+ let repoRootsExpiresAt = 0;
32
+ app.get('/api/v2/worktrees', async (_req, res) => {
33
+ try {
34
+ const sessionResult = await consoleService.getSessionList();
35
+ const sessions = sessionResult.isOk() ? sessionResult.value.sessions : [];
36
+ const activeSessions = (0, worktree_service_js_1.buildActiveSessionCounts)(sessions);
37
+ if (Date.now() > repoRootsExpiresAt) {
38
+ cwdRepoRootPromise ?? (cwdRepoRootPromise = (0, worktree_service_js_1.resolveRepoRoot)(process.cwd()));
39
+ const cwdRoot = await cwdRepoRootPromise;
40
+ const repoRootSet = new Set(sessions.map(s => s.repoRoot).filter((r) => r !== null));
41
+ if (cwdRoot !== null)
42
+ repoRootSet.add(cwdRoot);
43
+ cachedRepoRoots = [...repoRootSet];
44
+ repoRootsExpiresAt = Date.now() + REPO_ROOTS_TTL_MS;
45
+ }
46
+ const data = await (0, worktree_service_js_1.getWorktreeList)(cachedRepoRoots, activeSessions);
47
+ res.json({ success: true, data });
48
+ }
49
+ catch (e) {
50
+ res.status(500).json({ success: false, error: e instanceof Error ? e.message : String(e) });
51
+ }
52
+ });
27
53
  app.get('/api/v2/sessions/:sessionId', async (req, res) => {
28
54
  const { sessionId } = req.params;
29
55
  const result = await consoleService.getSessionDetail(sessionId);
@@ -14,6 +14,7 @@ const run_context_js_1 = require("../projections/run-context.js");
14
14
  const constants_js_1 = require("../durable-core/constants.js");
15
15
  const index_js_1 = require("../durable-core/ids/index.js");
16
16
  const MAX_SESSIONS_TO_LOAD = 500;
17
+ const DORMANCY_THRESHOLD_MS = 3 * 24 * 60 * 60 * 1000;
17
18
  class ConsoleService {
18
19
  constructor(ports) {
19
20
  this.ports = ports;
@@ -91,13 +92,14 @@ class ConsoleService {
91
92
  });
92
93
  }
93
94
  collectSessionSummaries(sessionIds, mtimeBySessionId) {
94
- const tasks = sessionIds.map((id) => this.loadSessionSummary(id, mtimeBySessionId.get(id) ?? 0));
95
+ const nowMs = Date.now();
96
+ const tasks = sessionIds.map((id) => this.loadSessionSummary(id, mtimeBySessionId.get(id) ?? 0, nowMs));
95
97
  return neverthrow_1.ResultAsync.combine(tasks).map((results) => {
96
98
  const sessions = results.filter((s) => s !== null);
97
99
  return { sessions, totalCount: sessions.length };
98
100
  });
99
101
  }
100
- loadSessionSummary(sessionId, lastModifiedMs) {
102
+ loadSessionSummary(sessionId, lastModifiedMs, nowMs) {
101
103
  return this.ports.sessionStore
102
104
  .load(sessionId)
103
105
  .andThen((truth) => {
@@ -108,7 +110,7 @@ class ConsoleService {
108
110
  return neverthrow_1.ResultAsync.combine([
109
111
  resolveRunCompletion(truth.events, this.ports.snapshotStore),
110
112
  workflowNamesRA,
111
- ]).map(([completionMap, workflowNames]) => projectSessionSummary(sessionId, truth, completionMap, workflowNames, lastModifiedMs));
113
+ ]).map(([completionMap, workflowNames]) => projectSessionSummary(sessionId, truth, completionMap, workflowNames, lastModifiedMs, nowMs));
112
114
  })
113
115
  .orElse(() => (0, neverthrow_2.okAsync)(null));
114
116
  }
@@ -313,12 +315,22 @@ function extractGitBranch(events) {
313
315
  }
314
316
  return null;
315
317
  }
318
+ function extractRepoRoot(events) {
319
+ for (const e of events) {
320
+ if (e.kind !== constants_js_1.EVENT_KIND.OBSERVATION_RECORDED)
321
+ continue;
322
+ if (e.data.key === 'repo_root') {
323
+ return e.data.value.value;
324
+ }
325
+ }
326
+ return null;
327
+ }
316
328
  function truncateTitle(text, maxLen = 120) {
317
329
  if (text.length <= maxLen)
318
330
  return text;
319
331
  return text.slice(0, maxLen - 1) + '…';
320
332
  }
321
- function projectSessionSummary(sessionId, truth, completionByRunId, workflowNames, lastModifiedMs) {
333
+ function projectSessionSummary(sessionId, truth, completionByRunId, workflowNames, lastModifiedMs, nowMs) {
322
334
  const { events } = truth;
323
335
  const health = (0, session_health_js_1.projectSessionHealthV2)(truth);
324
336
  if (health.isErr())
@@ -332,9 +344,11 @@ function projectSessionSummary(sessionId, truth, completionByRunId, workflowName
332
344
  const gapsRes = (0, gaps_js_1.projectGapsV2)(events);
333
345
  const sessionTitle = deriveSessionTitle(events);
334
346
  const gitBranch = extractGitBranch(events);
347
+ const repoRoot = extractRepoRoot(events);
335
348
  const runs = Object.values(dag.runsById);
336
349
  const run = runs[0];
337
350
  if (!run) {
351
+ const noRunStatus = nowMs - lastModifiedMs > DORMANCY_THRESHOLD_MS ? 'dormant' : 'in_progress';
338
352
  return {
339
353
  sessionId,
340
354
  sessionTitle,
@@ -342,7 +356,7 @@ function projectSessionSummary(sessionId, truth, completionByRunId, workflowName
342
356
  workflowName: null,
343
357
  workflowHash: null,
344
358
  runId: null,
345
- status: 'in_progress',
359
+ status: noRunStatus,
346
360
  health: sessionHealth,
347
361
  nodeCount: 0,
348
362
  edgeCount: 0,
@@ -350,6 +364,7 @@ function projectSessionSummary(sessionId, truth, completionByRunId, workflowName
350
364
  hasUnresolvedGaps: false,
351
365
  recapSnippet: null,
352
366
  gitBranch,
367
+ repoRoot,
353
368
  lastModifiedMs,
354
369
  };
355
370
  }
@@ -358,7 +373,10 @@ function projectSessionSummary(sessionId, truth, completionByRunId, workflowName
358
373
  const workflowHash = workflow.kind === 'with_workflow' ? workflow.workflowHash : null;
359
374
  const workflowName = workflowHash ? (workflowNames[workflowHash] ?? null) : null;
360
375
  const statusSignals = statusRes.isOk() ? statusRes.value.byRunId[run.runId] : undefined;
361
- const status = deriveRunStatus(statusSignals?.isBlocked ?? false, statusSignals?.hasUnresolvedCriticalGaps ?? false, completionByRunId[run.runId] ?? false);
376
+ const runStatus = deriveRunStatus(statusSignals?.isBlocked ?? false, statusSignals?.hasUnresolvedCriticalGaps ?? false, completionByRunId[run.runId] ?? false);
377
+ const status = runStatus === 'in_progress' && nowMs - lastModifiedMs > DORMANCY_THRESHOLD_MS
378
+ ? 'dormant'
379
+ : runStatus;
362
380
  const hasUnresolvedGaps = gapsRes.isOk()
363
381
  ? Object.keys(gapsRes.value.unresolvedCriticalByRunId).length > 0
364
382
  : false;
@@ -389,6 +407,7 @@ function projectSessionSummary(sessionId, truth, completionByRunId, workflowName
389
407
  hasUnresolvedGaps,
390
408
  recapSnippet,
391
409
  gitBranch,
410
+ repoRoot,
392
411
  lastModifiedMs,
393
412
  };
394
413
  }
@@ -1,4 +1,5 @@
1
1
  export type ConsoleRunStatus = 'in_progress' | 'complete' | 'complete_with_gaps' | 'blocked';
2
+ export type ConsoleSessionStatus = ConsoleRunStatus | 'dormant';
2
3
  export type ConsoleSessionHealth = 'healthy' | 'corrupt';
3
4
  export interface ConsoleSessionSummary {
4
5
  readonly sessionId: string;
@@ -7,7 +8,7 @@ export interface ConsoleSessionSummary {
7
8
  readonly workflowName: string | null;
8
9
  readonly workflowHash: string | null;
9
10
  readonly runId: string | null;
10
- readonly status: ConsoleRunStatus;
11
+ readonly status: ConsoleSessionStatus;
11
12
  readonly health: ConsoleSessionHealth;
12
13
  readonly nodeCount: number;
13
14
  readonly edgeCount: number;
@@ -15,6 +16,7 @@ export interface ConsoleSessionSummary {
15
16
  readonly hasUnresolvedGaps: boolean;
16
17
  readonly recapSnippet: string | null;
17
18
  readonly gitBranch: string | null;
19
+ readonly repoRoot: string | null;
18
20
  readonly lastModifiedMs: number;
19
21
  }
20
22
  export interface ConsoleSessionListResponse {
@@ -81,6 +83,25 @@ export interface ConsoleArtifact {
81
83
  readonly byteLength: number;
82
84
  readonly content: unknown;
83
85
  }
86
+ export interface ConsoleWorktreeSummary {
87
+ readonly path: string;
88
+ readonly name: string;
89
+ readonly branch: string | null;
90
+ readonly headHash: string;
91
+ readonly headMessage: string;
92
+ readonly headTimestampMs: number;
93
+ readonly changedCount: number;
94
+ readonly aheadCount: number;
95
+ readonly activeSessionCount: number;
96
+ }
97
+ export interface ConsoleRepoWorktrees {
98
+ readonly repoName: string;
99
+ readonly repoRoot: string;
100
+ readonly worktrees: readonly ConsoleWorktreeSummary[];
101
+ }
102
+ export interface ConsoleWorktreeListResponse {
103
+ readonly repos: readonly ConsoleRepoWorktrees[];
104
+ }
84
105
  export interface ConsoleNodeDetail {
85
106
  readonly nodeId: string;
86
107
  readonly nodeKind: 'step' | 'checkpoint' | 'blocked_attempt';
@@ -0,0 +1,10 @@
1
+ import type { ConsoleWorktreeListResponse, ConsoleSessionStatus } from './console-types.js';
2
+ export declare function resolveRepoRoot(path: string): Promise<string | null>;
3
+ export interface ActiveSessionsByBranch {
4
+ readonly counts: ReadonlyMap<string, number>;
5
+ }
6
+ export declare function buildActiveSessionCounts(sessions: ReadonlyArray<{
7
+ gitBranch: string | null;
8
+ status: ConsoleSessionStatus;
9
+ }>): ActiveSessionsByBranch;
10
+ export declare function getWorktreeList(repoRoots: readonly string[], activeSessions: ActiveSessionsByBranch): Promise<ConsoleWorktreeListResponse>;