@agent-relay/factory 0.1.19 → 0.1.21

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 (83) hide show
  1. package/README.md +42 -0
  2. package/dist/cli/fleet.d.ts +4 -0
  3. package/dist/cli/fleet.d.ts.map +1 -1
  4. package/dist/cli/fleet.js +186 -23
  5. package/dist/cli/fleet.js.map +1 -1
  6. package/dist/config/local-clone-paths.d.ts +18 -0
  7. package/dist/config/local-clone-paths.d.ts.map +1 -0
  8. package/dist/config/local-clone-paths.js +188 -0
  9. package/dist/config/local-clone-paths.js.map +1 -0
  10. package/dist/config/schema.d.ts +84 -2
  11. package/dist/config/schema.d.ts.map +1 -1
  12. package/dist/config/schema.js +71 -5
  13. package/dist/config/schema.js.map +1 -1
  14. package/dist/dispatch/templates.d.ts +18 -0
  15. package/dist/dispatch/templates.d.ts.map +1 -1
  16. package/dist/dispatch/templates.js +45 -11
  17. package/dist/dispatch/templates.js.map +1 -1
  18. package/dist/fleet/internal-fleet-client.d.ts +4 -0
  19. package/dist/fleet/internal-fleet-client.d.ts.map +1 -1
  20. package/dist/fleet/internal-fleet-client.js +4 -1
  21. package/dist/fleet/internal-fleet-client.js.map +1 -1
  22. package/dist/fleet/relay-fleet-client.d.ts +4 -0
  23. package/dist/fleet/relay-fleet-client.d.ts.map +1 -1
  24. package/dist/fleet/relay-fleet-client.js +14 -3
  25. package/dist/fleet/relay-fleet-client.js.map +1 -1
  26. package/dist/logging.d.ts +17 -0
  27. package/dist/logging.d.ts.map +1 -0
  28. package/dist/logging.js +347 -0
  29. package/dist/logging.js.map +1 -0
  30. package/dist/mount/relayfile-cloud-mount-client.js +10 -0
  31. package/dist/mount/relayfile-cloud-mount-client.js.map +1 -1
  32. package/dist/mount/relayfile-github-connection-write.d.ts.map +1 -1
  33. package/dist/mount/relayfile-github-connection-write.js +19 -7
  34. package/dist/mount/relayfile-github-connection-write.js.map +1 -1
  35. package/dist/orchestrator/batch-tracker.d.ts +3 -0
  36. package/dist/orchestrator/batch-tracker.d.ts.map +1 -1
  37. package/dist/orchestrator/batch-tracker.js +28 -0
  38. package/dist/orchestrator/batch-tracker.js.map +1 -1
  39. package/dist/orchestrator/factory.d.ts +2 -1
  40. package/dist/orchestrator/factory.d.ts.map +1 -1
  41. package/dist/orchestrator/factory.js +3295 -321
  42. package/dist/orchestrator/factory.js.map +1 -1
  43. package/dist/orchestrator/reaper.d.ts.map +1 -1
  44. package/dist/orchestrator/reaper.js +6 -4
  45. package/dist/orchestrator/reaper.js.map +1 -1
  46. package/dist/ports/fleet.d.ts +25 -0
  47. package/dist/ports/fleet.d.ts.map +1 -1
  48. package/dist/ports/mount.d.ts +7 -1
  49. package/dist/ports/mount.d.ts.map +1 -1
  50. package/dist/ports/state.d.ts +120 -0
  51. package/dist/ports/state.d.ts.map +1 -1
  52. package/dist/ports/writeback.d.ts +2 -0
  53. package/dist/ports/writeback.d.ts.map +1 -1
  54. package/dist/state/file-state-store.d.ts +33 -2
  55. package/dist/state/file-state-store.d.ts.map +1 -1
  56. package/dist/state/file-state-store.js +520 -10
  57. package/dist/state/file-state-store.js.map +1 -1
  58. package/dist/state/in-memory-state-store.d.ts +31 -1
  59. package/dist/state/in-memory-state-store.d.ts.map +1 -1
  60. package/dist/state/in-memory-state-store.js +245 -0
  61. package/dist/state/in-memory-state-store.js.map +1 -1
  62. package/dist/testing/fakes.d.ts +7 -0
  63. package/dist/testing/fakes.d.ts.map +1 -1
  64. package/dist/testing/fakes.js +1 -0
  65. package/dist/testing/fakes.js.map +1 -1
  66. package/dist/triage/agent-names.d.ts +13 -0
  67. package/dist/triage/agent-names.d.ts.map +1 -0
  68. package/dist/triage/agent-names.js +54 -0
  69. package/dist/triage/agent-names.js.map +1 -0
  70. package/dist/triage/heuristic.d.ts.map +1 -1
  71. package/dist/triage/heuristic.js +16 -23
  72. package/dist/triage/heuristic.js.map +1 -1
  73. package/dist/triage/llm.d.ts.map +1 -1
  74. package/dist/triage/llm.js +3 -2
  75. package/dist/triage/llm.js.map +1 -1
  76. package/dist/triage/schema.d.ts +26 -26
  77. package/dist/types.d.ts +2 -1
  78. package/dist/types.d.ts.map +1 -1
  79. package/dist/writeback/github.d.ts +1 -0
  80. package/dist/writeback/github.d.ts.map +1 -1
  81. package/dist/writeback/github.js +11 -0
  82. package/dist/writeback/github.js.map +1 -1
  83. package/package.json +1 -1
@@ -0,0 +1,188 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { realpath, stat } from 'node:fs/promises';
3
+ import { resolve } from 'node:path';
4
+ import { promisify } from 'node:util';
5
+ import { loadFactoryConfig } from './schema.js';
6
+ const execFileAsync = promisify(execFile);
7
+ /**
8
+ * Parse a factory config, then apply the environment-dependent conveniences
9
+ * that are appropriate only for a local CLI run.
10
+ */
11
+ export async function resolveLocalFactoryConfig(input, options = {}) {
12
+ const config = loadFactoryConfig(input).factoryConfig;
13
+ const git = options.git ?? runGit;
14
+ let resolved = config;
15
+ if (options.inferFromCwd !== false) {
16
+ const repoName = cwdInferenceRepoName(input);
17
+ if (repoName !== undefined) {
18
+ const repo = config.repos.byLabel[repoName];
19
+ if (!repo || !isQualifiedRepo(repo)) {
20
+ throw new Error(`[factory] cannot infer clonePath from cwd: resolved route for repos.names[0] must be owner/name (received ${JSON.stringify(repo ?? repoName)})`);
21
+ }
22
+ const checkoutRoot = await gitCheckoutRoot(options.cwd ?? process.cwd(), git, repo);
23
+ await assertMatchingGithubRemote(checkoutRoot, repo, git);
24
+ const clonePaths = { ...config.clonePaths, [repo]: checkoutRoot };
25
+ resolved = {
26
+ ...config,
27
+ clonePaths,
28
+ repos: { ...config.repos, clonePaths },
29
+ };
30
+ options.logger?.info?.(`[factory] clonePath inferred from cwd: ${checkoutRoot}`);
31
+ }
32
+ }
33
+ if (options.validateConfiguredCheckouts) {
34
+ await validateClonePaths(resolved.clonePaths, git);
35
+ }
36
+ return resolved;
37
+ }
38
+ async function gitCheckoutRoot(cwd, git, repo) {
39
+ let root;
40
+ try {
41
+ root = (await git(cwd, ['rev-parse', '--show-toplevel'])).trim();
42
+ }
43
+ catch {
44
+ throw new Error(`[factory] cannot infer clonePath for ${repo}: cwd is not inside a git checkout: ${resolve(cwd)}; configure cloneRoot or clonePaths explicitly`);
45
+ }
46
+ if (!root) {
47
+ throw new Error(`[factory] cannot infer clonePath for ${repo}: git returned no checkout root for cwd ${resolve(cwd)}; configure cloneRoot or clonePaths explicitly`);
48
+ }
49
+ return resolve(root);
50
+ }
51
+ async function assertMatchingGithubRemote(checkoutRoot, expectedRepo, git) {
52
+ const remoteNames = (await git(checkoutRoot, ['remote']))
53
+ .split(/\r?\n/u)
54
+ .map((name) => name.trim())
55
+ .filter(Boolean);
56
+ if (remoteNames.length === 0) {
57
+ throw new Error(`[factory] cannot infer clonePath for ${expectedRepo}: git checkout ${checkoutRoot} has no remotes; add a matching GitHub remote or configure cloneRoot/clonePaths explicitly`);
58
+ }
59
+ const remoteUrls = [];
60
+ for (const remote of remoteNames) {
61
+ try {
62
+ remoteUrls.push(...(await git(checkoutRoot, ['remote', 'get-url', '--all', remote]))
63
+ .split(/\r?\n/u)
64
+ .map((url) => url.trim())
65
+ .filter(Boolean));
66
+ }
67
+ catch {
68
+ // A concurrently removed/broken remote is treated like an unparseable
69
+ // remote below, yielding one actionable config error.
70
+ }
71
+ }
72
+ const parsedRepos = remoteUrls
73
+ .map(githubRepoFromRemoteUrl)
74
+ .filter((repo) => repo !== undefined);
75
+ if (parsedRepos.some((repo) => repo.toLowerCase() === expectedRepo.toLowerCase()))
76
+ return;
77
+ const found = [...new Set(parsedRepos)];
78
+ const detail = found.length > 0
79
+ ? `found GitHub remote${found.length === 1 ? '' : 's'} for ${found.join(', ')}`
80
+ : 'found no parseable GitHub remote URLs';
81
+ throw new Error(`[factory] cannot infer clonePath for ${expectedRepo}: ${detail} in ${checkoutRoot}; add a matching remote or configure cloneRoot/clonePaths explicitly`);
82
+ }
83
+ export function githubRepoFromRemoteUrl(remoteUrl) {
84
+ const value = remoteUrl.trim().replace(/^git\+/u, '');
85
+ let host;
86
+ let pathname;
87
+ try {
88
+ const url = new URL(value);
89
+ host = url.hostname;
90
+ pathname = url.pathname;
91
+ }
92
+ catch {
93
+ const scp = /^(?:[^@/\s]+@)?([^:/\s]+):(.+)$/u.exec(value);
94
+ if (scp) {
95
+ host = scp[1];
96
+ pathname = scp[2];
97
+ }
98
+ else {
99
+ const hostPath = /^([^/\s]+)\/(.+)$/u.exec(value);
100
+ if (!hostPath)
101
+ return undefined;
102
+ host = hostPath[1];
103
+ pathname = hostPath[2];
104
+ }
105
+ }
106
+ if (!['github.com', 'www.github.com'].includes(host.toLowerCase()))
107
+ return undefined;
108
+ const parts = pathname
109
+ .replace(/^\/+|\/+$/gu, '')
110
+ .replace(/\.git$/iu, '')
111
+ .split('/')
112
+ .filter(Boolean);
113
+ if (parts.length !== 2)
114
+ return undefined;
115
+ return `${parts[0]}/${parts[1]}`;
116
+ }
117
+ async function validateClonePaths(clonePaths, git) {
118
+ for (const [repo, clonePath] of Object.entries(clonePaths)) {
119
+ const path = resolve(clonePath);
120
+ let pathStat;
121
+ try {
122
+ pathStat = await stat(path);
123
+ }
124
+ catch {
125
+ throw new Error(`[factory] clonePath for ${repo} does not exist: ${path}`);
126
+ }
127
+ if (!pathStat.isDirectory()) {
128
+ throw new Error(`[factory] clonePath for ${repo} is not a directory: ${path}`);
129
+ }
130
+ let checkoutRoot;
131
+ try {
132
+ checkoutRoot = (await git(path, ['rev-parse', '--show-toplevel'])).trim();
133
+ }
134
+ catch {
135
+ throw new Error(`[factory] clonePath for ${repo} is not a git checkout: ${path}`);
136
+ }
137
+ if (!checkoutRoot) {
138
+ throw new Error(`[factory] clonePath for ${repo} is not a git checkout: ${path}`);
139
+ }
140
+ const [realPath, realRoot] = await Promise.all([realpath(path), realpath(resolve(checkoutRoot))]);
141
+ if (realPath !== realRoot) {
142
+ throw new Error(`[factory] clonePath for ${repo} points inside a git checkout (${path}); configure the checkout root ${resolve(checkoutRoot)}`);
143
+ }
144
+ }
145
+ }
146
+ async function runGit(cwd, args) {
147
+ const { stdout } = await execFileAsync('git', ['-C', cwd, ...args], {
148
+ encoding: 'utf8',
149
+ maxBuffer: 1024 * 1024,
150
+ });
151
+ return stdout;
152
+ }
153
+ function cwdInferenceRepoName(input) {
154
+ const inputRecord = record(input);
155
+ const factoryInput = record(inputRecord.factoryConfig ?? input);
156
+ if (hasOwn(factoryInput, 'workspaceConfig') || hasOwn(factoryInput, 'nodeConfig')) {
157
+ const workspace = record(factoryInput.workspaceConfig);
158
+ const node = record(factoryInput.nodeConfig);
159
+ const repos = record(workspace.repos);
160
+ if (hasClonePathConfig(workspace) || hasClonePathConfig(node) || hasClonePathConfig(repos))
161
+ return undefined;
162
+ return singleRepoName(repos.names);
163
+ }
164
+ const repos = record(factoryInput.repos);
165
+ if (hasClonePathConfig(factoryInput) || hasClonePathConfig(repos))
166
+ return undefined;
167
+ return singleRepoName(repos.names);
168
+ }
169
+ function hasClonePathConfig(value) {
170
+ return hasOwn(value, 'cloneRoot') || hasOwn(value, 'clonePaths');
171
+ }
172
+ function singleRepoName(value) {
173
+ return Array.isArray(value) && value.length === 1 && typeof value[0] === 'string'
174
+ ? value[0]
175
+ : undefined;
176
+ }
177
+ function isQualifiedRepo(repo) {
178
+ return /^[^/\s]+\/[^/\s]+$/u.test(repo);
179
+ }
180
+ function record(value) {
181
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
182
+ ? value
183
+ : {};
184
+ }
185
+ function hasOwn(value, key) {
186
+ return Object.prototype.hasOwnProperty.call(value, key);
187
+ }
188
+ //# sourceMappingURL=local-clone-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-clone-paths.js","sourceRoot":"","sources":["../../src/config/local-clone-paths.ts"],"names":[],"mappings":"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"}
@@ -134,20 +134,36 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
134
134
  triage?: string | undefined;
135
135
  babysitter?: string | undefined;
136
136
  }>>;
137
+ agentCapabilities: z.ZodDefault<z.ZodObject<{
138
+ implementer: z.ZodDefault<z.ZodEnum<["spawn:codex", "spawn:claude"]>>;
139
+ reviewer: z.ZodDefault<z.ZodEnum<["spawn:codex", "spawn:claude"]>>;
140
+ babysitter: z.ZodDefault<z.ZodEnum<["spawn:codex", "spawn:claude"]>>;
141
+ }, "strip", z.ZodTypeAny, {
142
+ implementer: "spawn:codex" | "spawn:claude";
143
+ reviewer: "spawn:codex" | "spawn:claude";
144
+ babysitter: "spawn:codex" | "spawn:claude";
145
+ }, {
146
+ implementer?: "spawn:codex" | "spawn:claude" | undefined;
147
+ reviewer?: "spawn:codex" | "spawn:claude" | undefined;
148
+ babysitter?: "spawn:codex" | "spawn:claude" | undefined;
149
+ }>>;
137
150
  slack: z.ZodOptional<z.ZodObject<{
138
151
  channel: z.ZodString;
139
152
  style: z.ZodDefault<z.ZodLiteral<"threaded-summarized">>;
140
153
  botUserId: z.ZodDefault<z.ZodString>;
154
+ stakeholderUserIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
141
155
  staleAfterMs: z.ZodDefault<z.ZodNumber>;
142
156
  }, "strip", z.ZodTypeAny, {
143
157
  channel: string;
144
158
  style: "threaded-summarized";
145
159
  botUserId: string;
160
+ stakeholderUserIds: string[];
146
161
  staleAfterMs: number;
147
162
  }, {
148
163
  channel: string;
149
164
  style?: "threaded-summarized" | undefined;
150
165
  botUserId?: string | undefined;
166
+ stakeholderUserIds?: string[] | undefined;
151
167
  staleAfterMs?: number | undefined;
152
168
  }>>;
153
169
  issueSource: z.ZodOptional<z.ZodEnum<["linear", "github"]>>;
@@ -316,6 +332,11 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
316
332
  reviewer?: string | undefined;
317
333
  triage?: string | undefined;
318
334
  };
335
+ agentCapabilities: {
336
+ implementer: "spawn:codex" | "spawn:claude";
337
+ reviewer: "spawn:codex" | "spawn:claude";
338
+ babysitter: "spawn:codex" | "spawn:claude";
339
+ };
319
340
  linear: {
320
341
  states: {
321
342
  readyForAgent?: string | undefined;
@@ -352,6 +373,7 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
352
373
  channel: string;
353
374
  style: "threaded-summarized";
354
375
  botUserId: string;
376
+ stakeholderUserIds: string[];
355
377
  staleAfterMs: number;
356
378
  } | undefined;
357
379
  issueSource?: "linear" | "github" | undefined;
@@ -407,10 +429,16 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
407
429
  triage?: string | undefined;
408
430
  babysitter?: string | undefined;
409
431
  } | undefined;
432
+ agentCapabilities?: {
433
+ implementer?: "spawn:codex" | "spawn:claude" | undefined;
434
+ reviewer?: "spawn:codex" | "spawn:claude" | undefined;
435
+ babysitter?: "spawn:codex" | "spawn:claude" | undefined;
436
+ } | undefined;
410
437
  slack?: {
411
438
  channel: string;
412
439
  style?: "threaded-summarized" | undefined;
413
440
  botUserId?: string | undefined;
441
+ stakeholderUserIds?: string[] | undefined;
414
442
  staleAfterMs?: number | undefined;
415
443
  } | undefined;
416
444
  linear?: {
@@ -496,6 +524,11 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
496
524
  reviewer?: string | undefined;
497
525
  triage?: string | undefined;
498
526
  };
527
+ agentCapabilities: {
528
+ implementer: "spawn:codex" | "spawn:claude";
529
+ reviewer: "spawn:codex" | "spawn:claude";
530
+ babysitter: "spawn:codex" | "spawn:claude";
531
+ };
499
532
  linear: {
500
533
  states: {
501
534
  readyForAgent?: string | undefined;
@@ -532,6 +565,7 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
532
565
  channel: string;
533
566
  style: "threaded-summarized";
534
567
  botUserId: string;
568
+ stakeholderUserIds: string[];
535
569
  staleAfterMs: number;
536
570
  } | undefined;
537
571
  issueSource?: "linear" | "github" | undefined;
@@ -587,10 +621,16 @@ export declare const WorkspaceConfigSchema: z.ZodEffects<z.ZodObject<{
587
621
  triage?: string | undefined;
588
622
  babysitter?: string | undefined;
589
623
  } | undefined;
624
+ agentCapabilities?: {
625
+ implementer?: "spawn:codex" | "spawn:claude" | undefined;
626
+ reviewer?: "spawn:codex" | "spawn:claude" | undefined;
627
+ babysitter?: "spawn:codex" | "spawn:claude" | undefined;
628
+ } | undefined;
590
629
  slack?: {
591
630
  channel: string;
592
631
  style?: "threaded-summarized" | undefined;
593
632
  botUserId?: string | undefined;
633
+ stakeholderUserIds?: string[] | undefined;
594
634
  staleAfterMs?: number | undefined;
595
635
  } | undefined;
596
636
  linear?: {
@@ -651,12 +691,12 @@ export declare const NodeConfigSchema: z.ZodEffects<z.ZodObject<{
651
691
  factoryLoopHeartbeatPath?: string | undefined;
652
692
  factoryLoopRegistryPath?: string | undefined;
653
693
  }>, {
694
+ cloneRoot: string | undefined;
695
+ clonePaths: Record<string, string>;
654
696
  factoryLoopHeartbeatPath: string | undefined;
655
697
  factoryLoopRegistryPath: string | undefined;
656
- clonePaths: Record<string, string>;
657
698
  capabilities: string[];
658
699
  dryRun: boolean;
659
- cloneRoot?: string | undefined;
660
700
  workspaceId?: string | undefined;
661
701
  }, {
662
702
  cloneRoot?: string | undefined;
@@ -799,20 +839,36 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
799
839
  triage?: string | undefined;
800
840
  babysitter?: string | undefined;
801
841
  }>>;
842
+ agentCapabilities: z.ZodDefault<z.ZodObject<{
843
+ implementer: z.ZodDefault<z.ZodEnum<["spawn:codex", "spawn:claude"]>>;
844
+ reviewer: z.ZodDefault<z.ZodEnum<["spawn:codex", "spawn:claude"]>>;
845
+ babysitter: z.ZodDefault<z.ZodEnum<["spawn:codex", "spawn:claude"]>>;
846
+ }, "strip", z.ZodTypeAny, {
847
+ implementer: "spawn:codex" | "spawn:claude";
848
+ reviewer: "spawn:codex" | "spawn:claude";
849
+ babysitter: "spawn:codex" | "spawn:claude";
850
+ }, {
851
+ implementer?: "spawn:codex" | "spawn:claude" | undefined;
852
+ reviewer?: "spawn:codex" | "spawn:claude" | undefined;
853
+ babysitter?: "spawn:codex" | "spawn:claude" | undefined;
854
+ }>>;
802
855
  slack: z.ZodOptional<z.ZodObject<{
803
856
  channel: z.ZodString;
804
857
  style: z.ZodDefault<z.ZodLiteral<"threaded-summarized">>;
805
858
  botUserId: z.ZodDefault<z.ZodString>;
859
+ stakeholderUserIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
806
860
  staleAfterMs: z.ZodDefault<z.ZodNumber>;
807
861
  }, "strip", z.ZodTypeAny, {
808
862
  channel: string;
809
863
  style: "threaded-summarized";
810
864
  botUserId: string;
865
+ stakeholderUserIds: string[];
811
866
  staleAfterMs: number;
812
867
  }, {
813
868
  channel: string;
814
869
  style?: "threaded-summarized" | undefined;
815
870
  botUserId?: string | undefined;
871
+ stakeholderUserIds?: string[] | undefined;
816
872
  staleAfterMs?: number | undefined;
817
873
  }>>;
818
874
  issueSource: z.ZodOptional<z.ZodEnum<["linear", "github"]>>;
@@ -990,6 +1046,11 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
990
1046
  reviewer?: string | undefined;
991
1047
  triage?: string | undefined;
992
1048
  };
1049
+ agentCapabilities: {
1050
+ implementer: "spawn:codex" | "spawn:claude";
1051
+ reviewer: "spawn:codex" | "spawn:claude";
1052
+ babysitter: "spawn:codex" | "spawn:claude";
1053
+ };
993
1054
  linear: {
994
1055
  states: {
995
1056
  readyForAgent?: string | undefined;
@@ -1029,6 +1090,7 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
1029
1090
  channel: string;
1030
1091
  style: "threaded-summarized";
1031
1092
  botUserId: string;
1093
+ stakeholderUserIds: string[];
1032
1094
  staleAfterMs: number;
1033
1095
  } | undefined;
1034
1096
  issueSource?: "linear" | "github" | undefined;
@@ -1088,10 +1150,16 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
1088
1150
  triage?: string | undefined;
1089
1151
  babysitter?: string | undefined;
1090
1152
  } | undefined;
1153
+ agentCapabilities?: {
1154
+ implementer?: "spawn:codex" | "spawn:claude" | undefined;
1155
+ reviewer?: "spawn:codex" | "spawn:claude" | undefined;
1156
+ babysitter?: "spawn:codex" | "spawn:claude" | undefined;
1157
+ } | undefined;
1091
1158
  slack?: {
1092
1159
  channel: string;
1093
1160
  style?: "threaded-summarized" | undefined;
1094
1161
  botUserId?: string | undefined;
1162
+ stakeholderUserIds?: string[] | undefined;
1095
1163
  staleAfterMs?: number | undefined;
1096
1164
  } | undefined;
1097
1165
  linear?: {
@@ -1188,6 +1256,11 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
1188
1256
  reviewer?: string | undefined;
1189
1257
  triage?: string | undefined;
1190
1258
  };
1259
+ agentCapabilities: {
1260
+ implementer: "spawn:codex" | "spawn:claude";
1261
+ reviewer: "spawn:codex" | "spawn:claude";
1262
+ babysitter: "spawn:codex" | "spawn:claude";
1263
+ };
1191
1264
  linear: {
1192
1265
  states: {
1193
1266
  readyForAgent?: string | undefined;
@@ -1226,6 +1299,7 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
1226
1299
  channel: string;
1227
1300
  style: "threaded-summarized";
1228
1301
  botUserId: string;
1302
+ stakeholderUserIds: string[];
1229
1303
  staleAfterMs: number;
1230
1304
  } | undefined;
1231
1305
  issueSource?: "linear" | "github" | undefined;
@@ -1283,10 +1357,16 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
1283
1357
  triage?: string | undefined;
1284
1358
  babysitter?: string | undefined;
1285
1359
  } | undefined;
1360
+ agentCapabilities?: {
1361
+ implementer?: "spawn:codex" | "spawn:claude" | undefined;
1362
+ reviewer?: "spawn:codex" | "spawn:claude" | undefined;
1363
+ babysitter?: "spawn:codex" | "spawn:claude" | undefined;
1364
+ } | undefined;
1286
1365
  slack?: {
1287
1366
  channel: string;
1288
1367
  style?: "threaded-summarized" | undefined;
1289
1368
  botUserId?: string | undefined;
1369
+ stakeholderUserIds?: string[] | undefined;
1290
1370
  staleAfterMs?: number | undefined;
1291
1371
  } | undefined;
1292
1372
  linear?: {
@@ -1326,6 +1406,8 @@ export declare const FactoryConfigSchema: z.ZodEffects<z.ZodObject<{
1326
1406
  factoryLoopHeartbeatPath?: string | undefined;
1327
1407
  factoryLoopRegistryPath?: string | undefined;
1328
1408
  }>;
1409
+ /** Expand the shell-like home shorthand that Node's filesystem APIs do not. */
1410
+ export declare function expandLeadingTilde(value: string, field?: string): string;
1329
1411
  export interface LoadedFactoryConfig {
1330
1412
  workspaceConfig: WorkspaceConfig;
1331
1413
  nodeConfig: NodeConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,eAAO,MAAM,mBAAmB,sFAMtB,CAAA;AACV,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAA;AAqLnE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAgF,CAAA;AAClH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAAsE,CAAA;AACnG,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAA4E,CAAA;AA6G5G,MAAM,WAAW,mBAAmB;IAClC,eAAe,EAAE,eAAe,CAAA;IAChC,UAAU,EAAE,UAAU,CAAA;IACtB,aAAa,EAAE,aAAa,CAAA;CAC7B;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,CAgBrE;AAuDD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAA;AACnE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAA;AACzD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAA"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/config/schema.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,5 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
1
3
  import { z } from 'zod';
2
4
  // The five workflow-state roles the factory drives an issue through. Each is
3
5
  // configured either by name (config.linear.states.<role>, resolved to a
@@ -76,10 +78,28 @@ const modelsSchema = z.object({
76
78
  // reviewer's unset (inherit) behavior.
77
79
  babysitter: z.string().default('sonnet'),
78
80
  }).default({});
81
+ // Which agent CLI backs each spawn role. `models.*` picks the model; this picks
82
+ // the CLI. Restricted to the two spawn capabilities actually wired into
83
+ // capabilityCli (internal-fleet-client.ts) — spawn:opencode / spawn:gemini would
84
+ // resolve to an undefined CLI and are intentionally excluded until mapped.
85
+ // Defaults preserve today's behavior (codex implements, claude reviews/babysits)
86
+ // so existing configs are unaffected unless a role is set explicitly. The map is
87
+ // named agentCapabilities rather than `capabilities` because the node config
88
+ // already owns a top-level `capabilities` (advertised node capability list).
89
+ const spawnCapabilitySchema = z.enum(['spawn:codex', 'spawn:claude']);
90
+ const agentCapabilitiesSchema = z.object({
91
+ implementer: spawnCapabilitySchema.default('spawn:codex'),
92
+ reviewer: spawnCapabilitySchema.default('spawn:claude'),
93
+ babysitter: spawnCapabilitySchema.default('spawn:claude'),
94
+ }).default({});
79
95
  const slackSchema = z.object({
80
96
  channel: z.string(),
81
97
  style: z.literal('threaded-summarized').default('threaded-summarized'),
82
98
  botUserId: z.string().default('U0B2596R7EZ'),
99
+ // Slack user IDs to mention when an agent needs a human decision. Keeping
100
+ // this explicit avoids guessing that a Linear assignee name is also a Slack
101
+ // identity, while still making parked questions immediately actionable.
102
+ stakeholderUserIds: z.array(z.string().min(1)).default([]),
83
103
  staleAfterMs: z.number().int().min(1_000).default(10 * 60_000),
84
104
  }).optional();
85
105
  const babysitterSchema = z.object({
@@ -127,6 +147,7 @@ const WorkspaceConfigObjectSchema = z.object({
127
147
  repos: workspaceReposSchema,
128
148
  batchSize: z.number().int().min(1).max(5).default(5),
129
149
  models: modelsSchema,
150
+ agentCapabilities: agentCapabilitiesSchema,
130
151
  slack: slackSchema,
131
152
  // Linear remains the default whenever its issue sub-root is connected. When
132
153
  // omitted, the orchestrator probes that sub-root once and falls back to
@@ -191,17 +212,28 @@ function normalizeWorkspaceConfig(cfg) {
191
212
  };
192
213
  }
193
214
  function normalizeNodeConfig(cfg) {
215
+ const cloneRoot = cfg.cloneRoot === undefined
216
+ ? undefined
217
+ : expandLeadingTilde(cfg.cloneRoot, 'cloneRoot');
194
218
  return {
195
219
  ...cfg,
220
+ cloneRoot,
221
+ clonePaths: expandClonePaths(cfg.clonePaths),
196
222
  factoryLoopHeartbeatPath: cfg.factoryLoopHeartbeatPath,
197
223
  factoryLoopRegistryPath: cfg.factoryLoopRegistryPath,
198
224
  };
199
225
  }
200
226
  function normalizeFactoryConfig(cfg) {
201
- const cloneRoot = cfg.cloneRoot ?? cfg.repos.cloneRoot;
227
+ const topLevelCloneRoot = cfg.cloneRoot === undefined
228
+ ? undefined
229
+ : expandLeadingTilde(cfg.cloneRoot, 'cloneRoot');
230
+ const legacyCloneRoot = cfg.repos.cloneRoot === undefined
231
+ ? undefined
232
+ : expandLeadingTilde(cfg.repos.cloneRoot, 'repos.cloneRoot');
233
+ const cloneRoot = topLevelCloneRoot ?? legacyCloneRoot;
202
234
  const explicitClonePaths = {
203
- ...cfg.repos.clonePaths,
204
- ...cfg.clonePaths,
235
+ ...expandClonePaths(cfg.repos.clonePaths, 'repos.clonePaths'),
236
+ ...expandClonePaths(cfg.clonePaths),
205
237
  };
206
238
  const resolved = resolveRepos(cfg.repos, cloneRoot, explicitClonePaths);
207
239
  const labels = resolveSubscriptionLabels(cfg.subscription.labels, cfg.repos.names ?? []);
@@ -244,13 +276,16 @@ function resolveRepos(repos, cloneRoot, explicitClonePaths = repos.clonePaths) {
244
276
  // Explicit clonePaths entries win.
245
277
  const derivedClonePaths = {};
246
278
  if (cloneRoot) {
247
- const root = cloneRoot.replace(/\/+$/u, '');
279
+ const root = expandLeadingTilde(cloneRoot, 'cloneRoot').replace(/\/+$/u, '');
248
280
  for (const repo of Object.values(resolvedByLabel)) {
249
281
  const repoName = repo.includes('/') ? repo.slice(repo.lastIndexOf('/') + 1) : repo;
250
282
  derivedClonePaths[repo] = `${root}/${repoName}`;
251
283
  }
252
284
  }
253
- const resolvedClonePaths = { ...derivedClonePaths, ...explicitClonePaths };
285
+ const resolvedClonePaths = {
286
+ ...derivedClonePaths,
287
+ ...expandClonePaths(explicitClonePaths),
288
+ };
254
289
  return {
255
290
  byLabel: resolvedByLabel,
256
291
  byProject,
@@ -259,6 +294,23 @@ function resolveRepos(repos, cloneRoot, explicitClonePaths = repos.clonePaths) {
259
294
  defaultRepo,
260
295
  };
261
296
  }
297
+ function expandClonePaths(clonePaths, field = 'clonePaths') {
298
+ return Object.fromEntries(Object.entries(clonePaths).map(([repo, clonePath]) => [
299
+ repo,
300
+ expandLeadingTilde(clonePath, `${field}[${JSON.stringify(repo)}]`),
301
+ ]));
302
+ }
303
+ /** Expand the shell-like home shorthand that Node's filesystem APIs do not. */
304
+ export function expandLeadingTilde(value, field = 'path') {
305
+ if (value === '~')
306
+ return homedir();
307
+ if (value.startsWith('~/'))
308
+ return join(homedir(), value.slice(2));
309
+ if (value.startsWith('~')) {
310
+ throw new Error(`${field} does not support ~user expansion; use ~ or ~/ instead`);
311
+ }
312
+ return value;
313
+ }
262
314
  function resolveSubscriptionLabels(labels, repoNames) {
263
315
  return labels.length > 0 ? labels : repoNames;
264
316
  }
@@ -296,6 +348,10 @@ function combineSplitConfigInput(workspaceInput, nodeInput) {
296
348
  const node = asConfigRecord(nodeInput);
297
349
  const workspaceRepos = asOptionalConfigRecord(workspace.repos);
298
350
  assertCompatibleWorkspaceIds(workspace.workspaceId, node.workspaceId);
351
+ // Validate tilde syntax in both split halves before node-local values take
352
+ // precedence, so an overridden ~user path is never silently accepted.
353
+ validateClonePathSyntax(workspaceRepos, 'workspaceConfig.repos');
354
+ validateClonePathSyntax(node, 'nodeConfig');
299
355
  return {
300
356
  ...workspace,
301
357
  ...node,
@@ -306,6 +362,16 @@ function combineSplitConfigInput(workspaceInput, nodeInput) {
306
362
  },
307
363
  };
308
364
  }
365
+ function validateClonePathSyntax(input, field) {
366
+ if (typeof input.cloneRoot === 'string')
367
+ expandLeadingTilde(input.cloneRoot, `${field}.cloneRoot`);
368
+ const clonePaths = asOptionalConfigRecord(input.clonePaths);
369
+ for (const [repo, clonePath] of Object.entries(clonePaths)) {
370
+ if (typeof clonePath === 'string') {
371
+ expandLeadingTilde(clonePath, `${field}.clonePaths[${JSON.stringify(repo)}]`);
372
+ }
373
+ }
374
+ }
309
375
  function assertCompatibleWorkspaceIds(workspaceId, nodeWorkspaceId) {
310
376
  if (typeof workspaceId === 'string' &&
311
377
  typeof nodeWorkspaceId === 'string' &&