@exaudeus/workrail 3.8.2 → 3.9.1

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 (32) hide show
  1. package/README.md +1 -0
  2. package/dist/di/container.js +8 -0
  3. package/dist/di/tokens.d.ts +1 -0
  4. package/dist/di/tokens.js +1 -0
  5. package/dist/engine/engine-factory.js +2 -0
  6. package/dist/manifest.json +72 -40
  7. package/dist/mcp/handlers/shared/remembered-roots.d.ts +4 -0
  8. package/dist/mcp/handlers/shared/remembered-roots.js +54 -0
  9. package/dist/mcp/handlers/shared/request-workflow-reader.d.ts +4 -1
  10. package/dist/mcp/handlers/shared/request-workflow-reader.js +71 -2
  11. package/dist/mcp/handlers/shared/workflow-source-visibility.d.ts +33 -0
  12. package/dist/mcp/handlers/shared/workflow-source-visibility.js +109 -0
  13. package/dist/mcp/handlers/v2-execution/index.js +4 -0
  14. package/dist/mcp/handlers/v2-execution/start.js +2 -1
  15. package/dist/mcp/handlers/v2-resume.js +4 -0
  16. package/dist/mcp/handlers/v2-workflow.js +94 -53
  17. package/dist/mcp/output-schemas.d.ts +372 -0
  18. package/dist/mcp/output-schemas.js +19 -0
  19. package/dist/mcp/server.js +2 -0
  20. package/dist/mcp/tool-descriptions.js +9 -4
  21. package/dist/mcp/types.d.ts +2 -0
  22. package/dist/mcp/v2/tools.d.ts +6 -6
  23. package/dist/mcp/v2/tools.js +2 -2
  24. package/dist/v2/infra/local/data-dir/index.d.ts +2 -0
  25. package/dist/v2/infra/local/data-dir/index.js +6 -0
  26. package/dist/v2/infra/local/remembered-roots-store/index.d.ts +14 -0
  27. package/dist/v2/infra/local/remembered-roots-store/index.js +172 -0
  28. package/dist/v2/ports/data-dir.port.d.ts +2 -0
  29. package/dist/v2/ports/remembered-roots-store.port.d.ts +27 -0
  30. package/dist/v2/ports/remembered-roots-store.port.js +2 -0
  31. package/package.json +1 -1
  32. package/workflows/mr-review-workflow.agentic.v2.json +63 -84
package/README.md CHANGED
@@ -157,6 +157,7 @@ The agent will find the workflow, start at step 1, and proceed systematically.
157
157
  - **Lockfile is enforced**: `package-lock.json` is canonical and CI will fail if `npm ci` would modify it. Commit lockfile changes intentionally.
158
158
  - **Release authority**: releases are produced by **semantic-release** in GitHub Actions (don’t bump versions/tags locally).
159
159
  - **Major releases are approval-gated**: breaking changes become **minor by default** and only become **major** when `WORKRAIL_ALLOW_MAJOR_RELEASE=true`.
160
+ - **Release type comes from the commit on `main`**: for squash merges, the PR title / squash commit title controls whether the release is patch, minor, major, or untagged. See `docs/reference/releases.md`.
160
161
  - **Preview a release (dry-run)**:
161
162
  - **Locally**: `npx semantic-release --dry-run --no-ci`
162
163
  - **Locally (major allowed)**: `WORKRAIL_ALLOW_MAJOR_RELEASE=true npx semantic-release --dry-run --no-ci`
@@ -223,6 +223,7 @@ async function registerV2Services() {
223
223
  });
224
224
  const { LocalKeyringV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/keyring/index.js')));
225
225
  const { LocalTokenAliasStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/token-alias-store/index.js')));
226
+ const { LocalRememberedRootsStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/remembered-roots-store/index.js')));
226
227
  const { LocalSessionEventLogStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/session-store/index.js')));
227
228
  const { LocalSnapshotStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/snapshot-store/index.js')));
228
229
  const { LocalPinnedWorkflowStoreV2 } = await Promise.resolve().then(() => __importStar(require('../v2/infra/local/pinned-workflow-store/index.js')));
@@ -243,6 +244,13 @@ async function registerV2Services() {
243
244
  return new LocalTokenAliasStoreV2(dataDir, fs);
244
245
  }),
245
246
  });
247
+ tsyringe_1.container.register(tokens_js_1.DI.V2.RememberedRootsStore, {
248
+ useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
249
+ const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
250
+ const fs = c.resolve(tokens_js_1.DI.V2.FileSystem);
251
+ return new LocalRememberedRootsStoreV2(dataDir, fs);
252
+ }),
253
+ });
246
254
  tsyringe_1.container.register(tokens_js_1.DI.V2.SessionStore, {
247
255
  useFactory: (0, tsyringe_1.instanceCachingFactory)((c) => {
248
256
  const dataDir = c.resolve(tokens_js_1.DI.V2.DataDir);
@@ -39,6 +39,7 @@ export declare const DI: {
39
39
  readonly PinnedWorkflowStore: symbol;
40
40
  readonly SessionLock: symbol;
41
41
  readonly TokenAliasStore: symbol;
42
+ readonly RememberedRootsStore: symbol;
42
43
  readonly ExecutionGate: symbol;
43
44
  };
44
45
  readonly Runtime: {
package/dist/di/tokens.js CHANGED
@@ -42,6 +42,7 @@ exports.DI = {
42
42
  PinnedWorkflowStore: Symbol('V2.PinnedWorkflowStore'),
43
43
  SessionLock: Symbol('V2.SessionLock'),
44
44
  TokenAliasStore: Symbol('V2.TokenAliasStore'),
45
+ RememberedRootsStore: Symbol('V2.RememberedRootsStore'),
45
46
  ExecutionGate: Symbol('V2.ExecutionGate'),
46
47
  },
47
48
  Runtime: {
@@ -209,6 +209,7 @@ async function createWorkRailEngine(config = {}) {
209
209
  };
210
210
  const dataDir = container_js_1.container.resolve(tokens_js_1.DI.V2.DataDir);
211
211
  const tokenAliasStore = container_js_1.container.resolve(tokens_js_1.DI.V2.TokenAliasStore);
212
+ const rememberedRootsStore = container_js_1.container.resolve(tokens_js_1.DI.V2.RememberedRootsStore);
212
213
  const aliasLoadResult = await tokenAliasStore.loadIndex();
213
214
  if (aliasLoadResult.isErr()) {
214
215
  console.error(`[engine-factory] Token alias index load warning: ${aliasLoadResult.error.message}`);
@@ -224,6 +225,7 @@ async function createWorkRailEngine(config = {}) {
224
225
  idFactory,
225
226
  tokenCodecPorts,
226
227
  tokenAliasStore,
228
+ rememberedRootsStore,
227
229
  validationPipelineDeps,
228
230
  resolvedRootUris: [],
229
231
  dataDir,
@@ -386,16 +386,16 @@
386
386
  "bytes": 565
387
387
  },
388
388
  "di/container.js": {
389
- "sha256": "6ba1b310aee67ac495a873d8102a5f3453592128f41735fd540022eb1032370a",
390
- "bytes": 20016
389
+ "sha256": "adc19624cd812eecaad5b105f8521628dd19117493cd61d5c1ac2fb5ef51f18d",
390
+ "bytes": 20531
391
391
  },
392
392
  "di/tokens.d.ts": {
393
- "sha256": "15cb26fb2370ad60ff6fc39d1ccd7df133d0cd4088f7a2deceb30d4c00ca8aae",
394
- "bytes": 1991
393
+ "sha256": "258c404372076ed2f6c892daf546cec5da75a371d44acfadd1fa71e2918117a7",
394
+ "bytes": 2038
395
395
  },
396
396
  "di/tokens.js": {
397
- "sha256": "d41c9cd45ab4a9aea3e39cad8d6969a4e1e1407c862eb5093ebc01580f99b672",
398
- "bytes": 2458
397
+ "sha256": "68f7bb382faa412efc57b8aac618ca92527e3f48d729996a556dd34c0af19f03",
398
+ "bytes": 2523
399
399
  },
400
400
  "domain/execution/error.d.ts": {
401
401
  "sha256": "2eac85c42ec399a23724f868641eeadd0d196b4d324ee4caaff82a6b46155bd9",
@@ -450,8 +450,8 @@
450
450
  "bytes": 213
451
451
  },
452
452
  "engine/engine-factory.js": {
453
- "sha256": "5461827b470642059198115f5248e97463db9bfb012f63971a4c7f69acda5b7c",
454
- "bytes": 13841
453
+ "sha256": "fae2c23b55b51348f85182b47f6b411c9ff71e9eb61f10bca90320b725a8a814",
454
+ "bytes": 13978
455
455
  },
456
456
  "engine/index.d.ts": {
457
457
  "sha256": "91e12882c565e96a9809fdf43a0dc0a77fdffdfd562aaf43d2e86e6590ed0b16",
@@ -693,13 +693,21 @@
693
693
  "sha256": "83c9585400cc7c20cac3691b8771b9ce8cdc508ba7f5d083a96f7ed975aabc05",
694
694
  "bytes": 5967
695
695
  },
696
+ "mcp/handlers/shared/remembered-roots.d.ts": {
697
+ "sha256": "d98e4c6dbce6333248fd2e015efe482aab2882db61855e9b0576d50881bb0ea2",
698
+ "bytes": 521
699
+ },
700
+ "mcp/handlers/shared/remembered-roots.js": {
701
+ "sha256": "7f6da2812659bda447d91ef9e3c52d79e8874cddb2e8850f92f618aff0ba924a",
702
+ "bytes": 2785
703
+ },
696
704
  "mcp/handlers/shared/request-workflow-reader.d.ts": {
697
- "sha256": "39b3db1251f5db4da36a14a1b3dba76bdd0cefd5b24637e03307901a3994a12b",
698
- "bytes": 927
705
+ "sha256": "610456c2f26108e8903911a2d06083d7bc8cd4da0b0694d29b8b986998ba71d8",
706
+ "bytes": 1213
699
707
  },
700
708
  "mcp/handlers/shared/request-workflow-reader.js": {
701
- "sha256": "8e67f92fe93883e6620b4564a1c98c5402ac74f002049b8f8f136008778e2152",
702
- "bytes": 2297
709
+ "sha256": "4f92edc5ba378d6a0c069b2a29046de08809adf309790d15a3a6db7ce580d599",
710
+ "bytes": 5182
703
711
  },
704
712
  "mcp/handlers/shared/with-timeout.d.ts": {
705
713
  "sha256": "31ca3db008cb5cd439e0d1132bc4b29be0900c403c452931e3a24a50e45beb54",
@@ -709,6 +717,14 @@
709
717
  "sha256": "43ab43a505817e03817f2c0b4d1d9c4ec3f12bd120978f211dc0d554ab900139",
710
718
  "bytes": 405
711
719
  },
720
+ "mcp/handlers/shared/workflow-source-visibility.d.ts": {
721
+ "sha256": "5a72accff6d5fcddd3b8e908292a42eae4d8f3a86ecd65807fd586d33177ec79",
722
+ "bytes": 1552
723
+ },
724
+ "mcp/handlers/shared/workflow-source-visibility.js": {
725
+ "sha256": "3b3f02879704d02235360570c6839afe0c5d3b1475ee9c6a614ac9ed75ffe5e9",
726
+ "bytes": 4451
727
+ },
712
728
  "mcp/handlers/v2-advance-core.d.ts": {
713
729
  "sha256": "0a07ea15caf7b0a3123d3b0b44c2b85392d090dd140bf82370fd0e84d1aa5b0e",
714
730
  "bytes": 271
@@ -834,8 +850,8 @@
834
850
  "bytes": 1437
835
851
  },
836
852
  "mcp/handlers/v2-execution/index.js": {
837
- "sha256": "adb91a7f6ca0a8cbe07c32908019b012dc72365da900dda13cbbec2e76ee22e6",
838
- "bytes": 7239
853
+ "sha256": "628fb84477bea94a0f4b7ae236cd578bd735d101ed2f0a64ac188b088a053e6b",
854
+ "bytes": 7534
839
855
  },
840
856
  "mcp/handlers/v2-execution/replay.d.ts": {
841
857
  "sha256": "bbf97633d7e47af3574a9690a1976bb474ca49cecc08af1d5c07e90f9868036d",
@@ -850,8 +866,8 @@
850
866
  "bytes": 3422
851
867
  },
852
868
  "mcp/handlers/v2-execution/start.js": {
853
- "sha256": "e572eb50c6a0a30aa138620f32b04702727f07ce2570c29022d1027981c4bcef",
854
- "bytes": 18496
869
+ "sha256": "5287dda42c97dde95471ce52df74c2dc5aba9dc8485b01ebde00fef3a8e62e89",
870
+ "bytes": 18573
855
871
  },
856
872
  "mcp/handlers/v2-reference-resolver.d.ts": {
857
873
  "sha256": "0b1d2640306b6a0c1067ca66bb20e6ec35d291b33dc328a78d5bfa8006394234",
@@ -874,8 +890,8 @@
874
890
  "bytes": 471
875
891
  },
876
892
  "mcp/handlers/v2-resume.js": {
877
- "sha256": "536ba5db0c8fedf8a55c63a59eae4da5ad1cc793e4abf137a5ac9536e6514f68",
878
- "bytes": 4140
893
+ "sha256": "bd7b960ca913e7b7d88bae2d9fe2b6c685d0dd1908f7eb0c41d8f41b5a0b0c4e",
894
+ "bytes": 4424
879
895
  },
880
896
  "mcp/handlers/v2-state-conversion.d.ts": {
881
897
  "sha256": "94bd06904ef58dd210ff17ffb75c2492beea8937eb06d99749e5d860c0e0d96b",
@@ -898,8 +914,8 @@
898
914
  "bytes": 396
899
915
  },
900
916
  "mcp/handlers/v2-workflow.js": {
901
- "sha256": "d305d516bd450d409b8658857f7713cfa74299f9ccf85e103057f684fcd7008a",
902
- "bytes": 7185
917
+ "sha256": "b682d6ae85795519ba09c7aa00b517e7f4c3c7263e4fccd98c8b32182c1c0729",
918
+ "bytes": 9612
903
919
  },
904
920
  "mcp/handlers/v2-workspace-resolution.d.ts": {
905
921
  "sha256": "dd4de57b4918ebe749cf8c1df70c02bf1effc50932634bae60db53c9a157e872",
@@ -926,12 +942,12 @@
926
942
  "bytes": 7991
927
943
  },
928
944
  "mcp/output-schemas.d.ts": {
929
- "sha256": "191d696fd1c8d7dc8f1908c903f84117ceaed6c0aa323fb7606dd3566d739689",
930
- "bytes": 53246
945
+ "sha256": "37e72221eb151903529bc2203f05d0504584980c441086397a1a00f72d910501",
946
+ "bytes": 68013
931
947
  },
932
948
  "mcp/output-schemas.js": {
933
- "sha256": "28aa9b75ef6c21522b458bd4d05c55e28673510356f8f7e56eb1304544d35132",
934
- "bytes": 14384
949
+ "sha256": "f390f2c853580c63dbaa204354dd2199f282e47c0bcd162d2accf3ab27d7ec04",
950
+ "bytes": 15326
935
951
  },
936
952
  "mcp/render-envelope.d.ts": {
937
953
  "sha256": "22e83e1aba52968a7136cf289125a217b5f462a5a66a1eebe4669006e3326fdb",
@@ -954,8 +970,8 @@
954
970
  "bytes": 960
955
971
  },
956
972
  "mcp/server.js": {
957
- "sha256": "9689ba068eaac9f36f328fa5429b5abe84bb8bd0062f0a59b60dc361dea09cd2",
958
- "bytes": 11677
973
+ "sha256": "f1240cddafaa9b4a83a5e7e6b7176d1c628a63a92868626e420e5b44bc52e94a",
974
+ "bytes": 11830
959
975
  },
960
976
  "mcp/step-content-envelope.d.ts": {
961
977
  "sha256": "19bd63c4d4de1d5d93393d346625d28ffd1bebdc320d4ba4e694cb740ec97d3b",
@@ -978,8 +994,8 @@
978
994
  "bytes": 132
979
995
  },
980
996
  "mcp/tool-descriptions.js": {
981
- "sha256": "c1a517158ca5562ece07da0ebcb244bd275e4556518b2b8861f24c549d22c1cc",
982
- "bytes": 9579
997
+ "sha256": "5cd28a9bd851e64f2063743506d0a33c787803d1738141b23f557aa1e3240fff",
998
+ "bytes": 9921
983
999
  },
984
1000
  "mcp/tool-factory.d.ts": {
985
1001
  "sha256": "0fe3c6b863b2d7aef0c3d659ff54f3a9ee8a0a3c2005b6565d2f8ad517bc7211",
@@ -1038,8 +1054,8 @@
1038
1054
  "bytes": 747
1039
1055
  },
1040
1056
  "mcp/types.d.ts": {
1041
- "sha256": "4c53a0e1e9e34e1bc8be2f04730316f4f0623905b3d5c6dc72170b76ca51a0fa",
1042
- "bytes": 4792
1057
+ "sha256": "e9677621e0a54c1756e854837b80fe7bd8da58d056aef2db8ab906e817f99b1e",
1058
+ "bytes": 4950
1043
1059
  },
1044
1060
  "mcp/types.js": {
1045
1061
  "sha256": "d10c4070e4c3454d80f0fa9cdc0e978c69c53c13fd09baa8710fcd802fed8926",
@@ -1086,12 +1102,12 @@
1086
1102
  "bytes": 3276
1087
1103
  },
1088
1104
  "mcp/v2/tools.d.ts": {
1089
- "sha256": "93f1eac69e9d2be8ca354d714fbc64e3cea0e235e6669847677071d83a66450f",
1090
- "bytes": 7288
1105
+ "sha256": "0a1ffaec8cecdf042f78af53063f9f1288a1c410f59731f189532aa4101e71b9",
1106
+ "bytes": 7206
1091
1107
  },
1092
1108
  "mcp/v2/tools.js": {
1093
- "sha256": "2b5d12d4de385ca8c530532d12467f113d58a09a808fe075896d2b1c0caa52ae",
1094
- "bytes": 10071
1109
+ "sha256": "9fb084ad964296bf49e94327aacc8c55857822382f1636278143f8074572bc40",
1110
+ "bytes": 10661
1095
1111
  },
1096
1112
  "mcp/validation/bounded-json.d.ts": {
1097
1113
  "sha256": "82203ac6123d5c6989606c3b5405aaea99ab829c8958835f9ae3ba45b8bc8fd5",
@@ -2046,12 +2062,12 @@
2046
2062
  "bytes": 457
2047
2063
  },
2048
2064
  "v2/infra/local/data-dir/index.d.ts": {
2049
- "sha256": "fa6e659499c6c742be9f4182c0d705770d2086696956b02d5415b2c5a8c7a44f",
2050
- "bytes": 847
2065
+ "sha256": "fd652ef0dcf0db64b915c3592b1bd6b6cb272145dc0eed7c3a08bb635ddbe60a",
2066
+ "bytes": 921
2051
2067
  },
2052
2068
  "v2/infra/local/data-dir/index.js": {
2053
- "sha256": "29dd90a80d199cf16e79169efbc1c06a37ea63254416917cbac8f5e0a81c7f8c",
2054
- "bytes": 3128
2069
+ "sha256": "79cd5f9ee8ef7f7ca3d3db4868fc5f97b4bc8b002af514916905c412328f58a2",
2070
+ "bytes": 3368
2055
2071
  },
2056
2072
  "v2/infra/local/directory-listing/index.d.ts": {
2057
2073
  "sha256": "3139014cb738db3b0f10beca01a3a4a35b9ab8e72c8889b3bbff204fdbcb6b6c",
@@ -2109,6 +2125,14 @@
2109
2125
  "sha256": "dfacf7ed6ead595872b8523f113f7228f9ca3b7ab1a84700e874db61c6fad066",
2110
2126
  "bytes": 345
2111
2127
  },
2128
+ "v2/infra/local/remembered-roots-store/index.d.ts": {
2129
+ "sha256": "385164a9468d2fe3c4dd74c8363a0b1f003556bfcc7e9bd5e3046bb167a7231d",
2130
+ "bytes": 840
2131
+ },
2132
+ "v2/infra/local/remembered-roots-store/index.js": {
2133
+ "sha256": "9616940b66438ee85ae22fca2267b30072dfb2907abdab0b1e3ce03a5971f814",
2134
+ "bytes": 7198
2135
+ },
2112
2136
  "v2/infra/local/session-lock/index.d.ts": {
2113
2137
  "sha256": "3b381b372da6039df678dfddf377600547e1067cf921cc1b4cdf578e5df0a1bb",
2114
2138
  "bytes": 853
@@ -2206,8 +2230,8 @@
2206
2230
  "bytes": 77
2207
2231
  },
2208
2232
  "v2/ports/data-dir.port.d.ts": {
2209
- "sha256": "f3b73eb92fa52a657ef54eb9e3c62bdd2a6c265771fc2e23d6526f2ef558cf9c",
2210
- "bytes": 610
2233
+ "sha256": "f23993e2101651b738c43d7d02178e622b39c32033a36bf06699d6803b244c0e",
2234
+ "bytes": 684
2211
2235
  },
2212
2236
  "v2/ports/data-dir.port.js": {
2213
2237
  "sha256": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
@@ -2261,6 +2285,14 @@
2261
2285
  "sha256": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
2262
2286
  "bytes": 77
2263
2287
  },
2288
+ "v2/ports/remembered-roots-store.port.d.ts": {
2289
+ "sha256": "e05a1ae31b12d93c6c846cabaa0cf5b7c01a931b23f0c9ddebfdca6bad6e459c",
2290
+ "bytes": 965
2291
+ },
2292
+ "v2/ports/remembered-roots-store.port.js": {
2293
+ "sha256": "d43aa81f5bc89faa359e0f97c814ba25155591ff078fbb9bfd40f8c7c9683230",
2294
+ "bytes": 77
2295
+ },
2264
2296
  "v2/ports/session-event-log-store.port.d.ts": {
2265
2297
  "sha256": "97838c9212ba1f07d779cfe57591bc2dcbad05567aab99ce9714aba12a26ad26",
2266
2298
  "bytes": 2070
@@ -0,0 +1,4 @@
1
+ import { type ToolError } from '../../types.js';
2
+ import type { RememberedRootRecordV2, RememberedRootsStorePortV2 } from '../../../v2/ports/remembered-roots-store.port.js';
3
+ export declare function rememberExplicitWorkspaceRoot(workspacePath: string | undefined, rememberedRootsStore: RememberedRootsStorePortV2 | undefined): Promise<ToolError | null>;
4
+ export declare function listRememberedRootRecords(rememberedRootsStore: RememberedRootsStorePortV2 | undefined): Promise<readonly RememberedRootRecordV2[] | ToolError>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.rememberExplicitWorkspaceRoot = rememberExplicitWorkspaceRoot;
7
+ exports.listRememberedRootRecords = listRememberedRootRecords;
8
+ const path_1 = __importDefault(require("path"));
9
+ const types_js_1 = require("../../types.js");
10
+ async function rememberExplicitWorkspaceRoot(workspacePath, rememberedRootsStore) {
11
+ if (!workspacePath || !rememberedRootsStore)
12
+ return null;
13
+ if (!path_1.default.isAbsolute(workspacePath))
14
+ return null;
15
+ const result = await rememberedRootsStore.rememberRoot(workspacePath);
16
+ if (result.isErr()) {
17
+ const error = result.error;
18
+ if (error.code === 'REMEMBERED_ROOTS_BUSY') {
19
+ return (0, types_js_1.errRetryAfterMs)('INTERNAL_ERROR', 'WorkRail is temporarily busy updating remembered workflow roots.', error.retry.afterMs, {
20
+ suggestion: 'Wait a moment and retry this call. Another WorkRail process may be updating remembered workflow roots.',
21
+ });
22
+ }
23
+ return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', `WorkRail could not persist the workspace root for workflow-source setup.`, {
24
+ suggestion: 'Fix WorkRail local storage access and retry. Check that the ~/.workrail data directory exists and is writable.',
25
+ details: {
26
+ workspacePath,
27
+ rememberedRootsErrorCode: error.code,
28
+ rememberedRootsErrorMessage: error.message,
29
+ },
30
+ });
31
+ }
32
+ return null;
33
+ }
34
+ async function listRememberedRootRecords(rememberedRootsStore) {
35
+ if (!rememberedRootsStore)
36
+ return [];
37
+ const result = await rememberedRootsStore.listRootRecords();
38
+ if (result.isErr()) {
39
+ const error = result.error;
40
+ if (error.code === 'REMEMBERED_ROOTS_BUSY') {
41
+ return (0, types_js_1.errRetryAfterMs)('INTERNAL_ERROR', 'WorkRail is temporarily busy reading remembered workflow roots.', error.retry.afterMs, {
42
+ suggestion: 'Wait a moment and retry this call. Another WorkRail process may be updating remembered workflow roots.',
43
+ });
44
+ }
45
+ return (0, types_js_1.errNotRetryable)('INTERNAL_ERROR', 'WorkRail could not load remembered workflow roots for workflow-source visibility.', {
46
+ suggestion: 'Fix WorkRail local storage access and retry. Check that the ~/.workrail data directory exists and is writable.',
47
+ details: {
48
+ rememberedRootsErrorCode: error.code,
49
+ rememberedRootsErrorMessage: error.message,
50
+ },
51
+ });
52
+ }
53
+ return result.value;
54
+ }
@@ -1,10 +1,12 @@
1
1
  import type { IWorkflowReader } from '../../../types/storage.js';
2
2
  import type { IFeatureFlagProvider } from '../../../config/feature-flags.js';
3
+ import type { RememberedRootsStorePortV2 } from '../../../v2/ports/remembered-roots-store.port.js';
3
4
  export interface RequestWorkflowReaderOptions {
4
5
  readonly featureFlags: IFeatureFlagProvider;
5
6
  readonly workspacePath?: string;
6
7
  readonly resolvedRootUris?: readonly string[];
7
8
  readonly serverCwd?: string;
9
+ readonly rememberedRootsStore?: RememberedRootsStorePortV2;
8
10
  }
9
11
  export declare function hasRequestWorkspaceSignal(options: {
10
12
  readonly workspacePath?: string;
@@ -16,4 +18,5 @@ export declare function resolveRequestWorkspaceDirectory(options: {
16
18
  readonly serverCwd?: string;
17
19
  }): string;
18
20
  export declare function toProjectWorkflowDirectory(workspaceDirectory: string): string;
19
- export declare function createWorkflowReaderForRequest(options: RequestWorkflowReaderOptions): IWorkflowReader;
21
+ export declare function discoverRootedWorkflowDirectories(roots: readonly string[]): Promise<readonly string[]>;
22
+ export declare function createWorkflowReaderForRequest(options: RequestWorkflowReaderOptions): Promise<IWorkflowReader>;
@@ -6,7 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.hasRequestWorkspaceSignal = hasRequestWorkspaceSignal;
7
7
  exports.resolveRequestWorkspaceDirectory = resolveRequestWorkspaceDirectory;
8
8
  exports.toProjectWorkflowDirectory = toProjectWorkflowDirectory;
9
+ exports.discoverRootedWorkflowDirectories = discoverRootedWorkflowDirectories;
9
10
  exports.createWorkflowReaderForRequest = createWorkflowReaderForRequest;
11
+ const promises_1 = __importDefault(require("fs/promises"));
10
12
  const path_1 = __importDefault(require("path"));
11
13
  const url_1 = require("url");
12
14
  const enhanced_multi_source_workflow_storage_js_1 = require("../../../infrastructure/storage/enhanced-multi-source-workflow-storage.js");
@@ -32,12 +34,79 @@ function toProjectWorkflowDirectory(workspaceDirectory) {
32
34
  ? workspaceDirectory
33
35
  : path_1.default.join(workspaceDirectory, 'workflows');
34
36
  }
35
- function createWorkflowReaderForRequest(options) {
37
+ async function discoverRootedWorkflowDirectories(roots) {
38
+ const discoveredByPath = new Set();
39
+ const discoveredPaths = [];
40
+ for (const root of roots) {
41
+ const rootPath = path_1.default.resolve(root);
42
+ const nextPaths = await discoverWorkflowDirectoriesUnderRoot(rootPath);
43
+ for (const nextPath of nextPaths) {
44
+ const normalizedPath = path_1.default.resolve(nextPath);
45
+ if (discoveredByPath.has(normalizedPath))
46
+ continue;
47
+ discoveredByPath.add(normalizedPath);
48
+ discoveredPaths.push(normalizedPath);
49
+ }
50
+ }
51
+ return discoveredPaths;
52
+ }
53
+ async function createWorkflowReaderForRequest(options) {
36
54
  const workspaceDirectory = resolveRequestWorkspaceDirectory(options);
37
55
  const projectWorkflowDirectory = toProjectWorkflowDirectory(workspaceDirectory);
38
- const storage = new enhanced_multi_source_workflow_storage_js_1.EnhancedMultiSourceWorkflowStorage({ projectPath: projectWorkflowDirectory }, options.featureFlags);
56
+ const rememberedRoots = await listRememberedRoots(options.rememberedRootsStore);
57
+ const rootedWorkflowDirectories = await discoverRootedWorkflowDirectories(rememberedRoots);
58
+ const customPaths = rootedWorkflowDirectories.filter((directory) => directory !== projectWorkflowDirectory);
59
+ const storage = new enhanced_multi_source_workflow_storage_js_1.EnhancedMultiSourceWorkflowStorage({
60
+ customPaths,
61
+ projectPath: projectWorkflowDirectory,
62
+ }, options.featureFlags);
39
63
  return new schema_validating_workflow_storage_js_1.SchemaValidatingCompositeWorkflowStorage(storage);
40
64
  }
65
+ async function listRememberedRoots(rememberedRootsStore) {
66
+ if (!rememberedRootsStore)
67
+ return [];
68
+ const result = await rememberedRootsStore.listRoots();
69
+ if (result.isErr()) {
70
+ const error = result.error;
71
+ throw new Error(`Failed to load remembered workflow roots: ${error.code}: ${error.message}`);
72
+ }
73
+ return result.value.map((root) => path_1.default.resolve(root));
74
+ }
75
+ async function discoverWorkflowDirectoriesUnderRoot(rootPath) {
76
+ const discoveredPaths = [];
77
+ await walkForRootedWorkflowDirectories(rootPath, discoveredPaths);
78
+ return discoveredPaths;
79
+ }
80
+ async function walkForRootedWorkflowDirectories(currentDirectory, discoveredPaths) {
81
+ const entries = await promises_1.default.readdir(currentDirectory, { withFileTypes: true });
82
+ const sortedEntries = [...entries].sort((a, b) => a.name.localeCompare(b.name));
83
+ for (const entry of sortedEntries) {
84
+ if (!entry.isDirectory())
85
+ continue;
86
+ const entryPath = path_1.default.join(currentDirectory, entry.name);
87
+ if (shouldSkipDirectory(entry.name))
88
+ continue;
89
+ if (entry.name === '.workrail') {
90
+ const workflowsDirectory = path_1.default.join(entryPath, 'workflows');
91
+ if (await isDirectory(workflowsDirectory)) {
92
+ discoveredPaths.push(path_1.default.resolve(workflowsDirectory));
93
+ }
94
+ continue;
95
+ }
96
+ await walkForRootedWorkflowDirectories(entryPath, discoveredPaths);
97
+ }
98
+ }
99
+ function shouldSkipDirectory(name) {
100
+ return name === '.git' || name === 'node_modules';
101
+ }
102
+ async function isDirectory(targetPath) {
103
+ try {
104
+ return (await promises_1.default.stat(targetPath)).isDirectory();
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
41
110
  function fileUriToFsPath(uri) {
42
111
  if (!uri.startsWith('file://'))
43
112
  return null;
@@ -0,0 +1,33 @@
1
+ import type { Workflow, WorkflowSourceInfo } from '../../../types/workflow.js';
2
+ import type { IWorkflowReader } from '../../../types/storage.js';
3
+ import type { RememberedRootRecordV2 } from '../../../v2/ports/remembered-roots-store.port.js';
4
+ export interface PublicWorkflowSource {
5
+ readonly kind: WorkflowSourceInfo['kind'];
6
+ readonly displayName: string;
7
+ }
8
+ export interface RootedSharingContext {
9
+ readonly kind: 'remembered_root';
10
+ readonly rootPath: string;
11
+ readonly groupLabel: string;
12
+ }
13
+ export type WorkflowVisibilityCategory = 'built_in' | 'personal' | 'legacy_project' | 'rooted_sharing' | 'external';
14
+ export interface WorkflowMigrationGuidance {
15
+ readonly preferredSource: 'rooted_sharing';
16
+ readonly currentSource: 'legacy_project';
17
+ readonly reason: 'legacy_project_precedence';
18
+ readonly summary: string;
19
+ }
20
+ export interface WorkflowVisibility {
21
+ readonly category: WorkflowVisibilityCategory;
22
+ readonly source: PublicWorkflowSource;
23
+ readonly rootedSharing?: RootedSharingContext;
24
+ readonly migration?: WorkflowMigrationGuidance;
25
+ }
26
+ export declare function toWorkflowVisibility(workflow: Workflow, rememberedRoots: readonly RememberedRootRecordV2[], options?: {
27
+ readonly migration?: WorkflowMigrationGuidance;
28
+ }): WorkflowVisibility;
29
+ export declare function detectWorkflowMigrationGuidance(options: {
30
+ readonly workflow: Workflow;
31
+ readonly workflowReader: IWorkflowReader;
32
+ readonly rememberedRoots: readonly RememberedRootRecordV2[];
33
+ }): Promise<WorkflowMigrationGuidance | undefined>;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.toWorkflowVisibility = toWorkflowVisibility;
7
+ exports.detectWorkflowMigrationGuidance = detectWorkflowMigrationGuidance;
8
+ const path_1 = __importDefault(require("path"));
9
+ function toWorkflowVisibility(workflow, rememberedRoots, options = {}) {
10
+ const source = {
11
+ kind: workflow.source.kind,
12
+ displayName: workflow.source.kind === 'bundled'
13
+ ? 'Built-in'
14
+ : workflow.source.kind === 'user'
15
+ ? 'User Library'
16
+ : workflow.source.kind === 'project'
17
+ ? 'Project'
18
+ : workflow.source.kind === 'custom'
19
+ ? workflow.source.label || 'Custom'
20
+ : workflow.source.kind === 'git'
21
+ ? workflow.source.repositoryUrl
22
+ : workflow.source.kind === 'remote'
23
+ ? workflow.source.registryUrl
24
+ : workflow.source.pluginName,
25
+ };
26
+ const rootedSharing = deriveRootedSharingContext(workflow, rememberedRoots);
27
+ const category = deriveVisibilityCategory(workflow, rootedSharing);
28
+ return {
29
+ category,
30
+ source,
31
+ ...(rootedSharing ? { rootedSharing } : {}),
32
+ ...(options.migration ? { migration: options.migration } : {}),
33
+ };
34
+ }
35
+ async function detectWorkflowMigrationGuidance(options) {
36
+ const { workflow, workflowReader, rememberedRoots } = options;
37
+ if (workflow.source.kind !== 'project')
38
+ return undefined;
39
+ if (!isCompositeWorkflowReader(workflowReader))
40
+ return undefined;
41
+ for (const storage of workflowReader.getStorageInstances()) {
42
+ if (storage.source.kind !== 'custom')
43
+ continue;
44
+ const alternative = await storage.getWorkflowById(workflow.definition.id);
45
+ if (!alternative)
46
+ continue;
47
+ const rootedSharing = deriveRootedSharingContext(alternative, rememberedRoots);
48
+ if (!rootedSharing)
49
+ continue;
50
+ return {
51
+ preferredSource: 'rooted_sharing',
52
+ currentSource: 'legacy_project',
53
+ reason: 'legacy_project_precedence',
54
+ summary: 'Project-scoped ./workflows currently overrides rooted .workrail/workflows during migration. Prefer rooted sharing for new team-shared workflows.',
55
+ };
56
+ }
57
+ return undefined;
58
+ }
59
+ function isCompositeWorkflowReader(workflowReader) {
60
+ return (workflowReader.kind === 'composite' &&
61
+ typeof workflowReader.getStorageInstances === 'function');
62
+ }
63
+ function deriveRootedSharingContext(workflow, rememberedRoots) {
64
+ if (workflow.source.kind !== 'custom')
65
+ return undefined;
66
+ const sourcePath = path_1.default.resolve(workflow.source.directoryPath);
67
+ for (const record of rememberedRoots) {
68
+ const rootPath = path_1.default.resolve(record.path);
69
+ const relative = path_1.default.relative(rootPath, sourcePath);
70
+ const isUnderRoot = relative.length === 0 ||
71
+ (!relative.startsWith('..') && !path_1.default.isAbsolute(relative));
72
+ if (!isUnderRoot)
73
+ continue;
74
+ return {
75
+ kind: 'remembered_root',
76
+ rootPath,
77
+ groupLabel: deriveGroupLabel(rootPath, sourcePath),
78
+ };
79
+ }
80
+ return undefined;
81
+ }
82
+ function deriveVisibilityCategory(workflow, rootedSharing) {
83
+ switch (workflow.source.kind) {
84
+ case 'bundled':
85
+ return 'built_in';
86
+ case 'user':
87
+ return 'personal';
88
+ case 'project':
89
+ return 'legacy_project';
90
+ case 'custom':
91
+ return rootedSharing ? 'rooted_sharing' : 'external';
92
+ case 'git':
93
+ case 'remote':
94
+ case 'plugin':
95
+ return 'external';
96
+ }
97
+ }
98
+ function deriveGroupLabel(rootPath, sourcePath) {
99
+ const relative = path_1.default.relative(rootPath, sourcePath);
100
+ if (!relative || relative === '.workrail/workflows') {
101
+ return path_1.default.basename(rootPath);
102
+ }
103
+ const segments = relative.split(path_1.default.sep).filter(Boolean);
104
+ const workrailIndex = segments.indexOf('.workrail');
105
+ if (workrailIndex <= 0) {
106
+ return path_1.default.basename(rootPath);
107
+ }
108
+ return segments[workrailIndex - 1];
109
+ }
@@ -16,6 +16,7 @@ const start_js_1 = require("./start.js");
16
16
  const continue_rehydrate_js_1 = require("./continue-rehydrate.js");
17
17
  const continue_advance_js_1 = require("./continue-advance.js");
18
18
  const render_envelope_js_1 = require("../../render-envelope.js");
19
+ const remembered_roots_js_1 = require("../shared/remembered-roots.js");
19
20
  function buildNextCall(args) {
20
21
  if (args.isComplete && !args.pending)
21
22
  return null;
@@ -31,6 +32,9 @@ async function handleV2StartWorkflow(input, ctx) {
31
32
  const guard = (0, types_js_1.requireV2Context)(ctx);
32
33
  if (!guard.ok)
33
34
  return guard.error;
35
+ const rememberedRootFailure = await (0, remembered_roots_js_1.rememberExplicitWorkspaceRoot)(input.workspacePath, guard.ctx.v2.rememberedRootsStore);
36
+ if (rememberedRootFailure)
37
+ return rememberedRootFailure;
34
38
  return (0, start_js_1.executeStartWorkflow)(input, guard.ctx).match((result) => (0, types_js_1.success)((0, render_envelope_js_1.attachV2ExecutionRenderMetadata)({
35
39
  response: result.response,
36
40
  lifecycle: 'start',
@@ -219,10 +219,11 @@ function executeStartWorkflow(input, ctx) {
219
219
  const workflowReader = shouldUseRequestReader
220
220
  ? {
221
221
  getWorkflowById: async (workflowId) => {
222
- const requestReader = (0, request_workflow_reader_js_1.createWorkflowReaderForRequest)({
222
+ const requestReader = await (0, request_workflow_reader_js_1.createWorkflowReaderForRequest)({
223
223
  featureFlags: ctx.featureFlags,
224
224
  workspacePath: input.workspacePath,
225
225
  resolvedRootUris: ctx.v2.resolvedRootUris,
226
+ rememberedRootsStore: ctx.v2.rememberedRootsStore,
226
227
  });
227
228
  const requestResult = await requestReader.getWorkflowById(workflowId);
228
229
  if (requestResult != null)