@h-rig/core 0.0.6-alpha.18 → 0.0.6-alpha.181

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 (105) hide show
  1. package/dist/src/agent-role-registry.d.ts +4 -0
  2. package/dist/src/agent-role-registry.js +27 -0
  3. package/dist/src/authority-paths.d.ts +15 -0
  4. package/dist/src/authority-paths.js +80 -0
  5. package/dist/src/baked-secrets.d.ts +3 -0
  6. package/dist/src/baked-secrets.js +63 -0
  7. package/dist/src/build-time-config.d.ts +12 -0
  8. package/dist/src/build-time-config.js +25 -0
  9. package/dist/src/build-time-config.macro.d.ts +1 -0
  10. package/dist/src/capability-loaders.d.ts +51 -0
  11. package/dist/src/capability-loaders.js +870 -0
  12. package/dist/src/capability.d.ts +79 -0
  13. package/dist/src/capability.js +63 -0
  14. package/dist/src/checkout-root.d.ts +1 -0
  15. package/dist/src/checkout-root.js +30 -0
  16. package/dist/src/config-env.d.ts +4 -0
  17. package/dist/src/config-env.js +23 -0
  18. package/dist/src/config.d.ts +3 -0
  19. package/dist/src/config.js +44 -0
  20. package/dist/src/declarative-config.d.ts +14 -0
  21. package/dist/src/declarative-config.js +85 -0
  22. package/dist/src/default-kernel.d.ts +1 -0
  23. package/dist/src/default-kernel.js +12 -0
  24. package/dist/src/define-config.d.ts +20 -0
  25. package/dist/src/define-config.js +28 -15
  26. package/dist/src/define-plugin.d.ts +13 -0
  27. package/dist/src/define-plugin.js +4 -43
  28. package/dist/src/embedded-plugins.d.ts +59 -0
  29. package/dist/src/embedded-plugins.js +22 -0
  30. package/dist/src/exec.d.ts +13 -0
  31. package/dist/src/exec.js +101 -0
  32. package/dist/src/harness-paths.d.ts +9 -0
  33. package/dist/src/harness-paths.js +126 -0
  34. package/dist/src/hook-materializer.d.ts +21 -0
  35. package/dist/src/hook-materializer.js +152 -0
  36. package/dist/src/hook-protocol.d.ts +2 -0
  37. package/dist/src/hook-protocol.js +432 -0
  38. package/dist/src/hook-runner.d.ts +48 -0
  39. package/dist/src/hook-runner.js +868 -0
  40. package/dist/src/hook-runtime.d.ts +52 -0
  41. package/dist/src/hook-runtime.js +432 -0
  42. package/dist/src/index.d.ts +8 -0
  43. package/dist/src/index.js +210 -2499
  44. package/dist/src/json-files.d.ts +9 -0
  45. package/dist/src/json-files.js +124 -0
  46. package/dist/src/kernel-boot.d.ts +2 -0
  47. package/dist/src/kernel-boot.js +10 -0
  48. package/dist/src/kernel-entrypoint.d.ts +22 -0
  49. package/dist/src/kernel-entrypoint.js +660 -0
  50. package/dist/src/kernel-plugin-abi.d.ts +1 -0
  51. package/dist/src/kernel-plugin-abi.js +1 -0
  52. package/dist/src/kernel-resolver.d.ts +2 -0
  53. package/dist/src/kernel-resolver.js +6 -0
  54. package/dist/src/layout.d.ts +10 -0
  55. package/dist/src/layout.js +138 -0
  56. package/dist/src/load-config.d.ts +2 -0
  57. package/dist/src/load-config.js +535 -30
  58. package/dist/src/placement.d.ts +58 -0
  59. package/dist/src/placement.js +53 -0
  60. package/dist/src/plugin-host-context.d.ts +65 -0
  61. package/dist/src/plugin-host-context.js +1171 -0
  62. package/dist/src/plugin-host-registries.d.ts +31 -0
  63. package/dist/src/plugin-host-registries.js +79 -0
  64. package/dist/src/plugin-host.d.ts +77 -0
  65. package/dist/src/plugin-host.js +127 -63
  66. package/dist/src/plugin-runtime.d.ts +173 -0
  67. package/dist/src/project-plugins.d.ts +63 -0
  68. package/dist/src/project-plugins.js +905 -0
  69. package/dist/src/remote-config.d.ts +125 -0
  70. package/dist/src/remote-config.js +85 -0
  71. package/dist/src/root-resolver.d.ts +5 -0
  72. package/dist/src/root-resolver.js +68 -0
  73. package/dist/src/run-provisioning.d.ts +37 -0
  74. package/dist/src/run-provisioning.js +35 -0
  75. package/dist/src/runtime-context.d.ts +20 -0
  76. package/dist/src/runtime-context.js +257 -0
  77. package/dist/src/runtime-events.d.ts +44 -0
  78. package/dist/src/runtime-events.js +208 -0
  79. package/dist/src/runtime-overlay.d.ts +11 -0
  80. package/dist/src/runtime-overlay.js +69 -0
  81. package/dist/src/runtime-paths.d.ts +21 -0
  82. package/dist/src/runtime-paths.js +181 -0
  83. package/dist/src/runtime-provisioning-env.d.ts +5 -0
  84. package/dist/src/runtime-provisioning-env.js +217 -0
  85. package/dist/src/runtime-runner-context.d.ts +12 -0
  86. package/dist/src/runtime-runner-context.js +1 -0
  87. package/dist/src/safe-identifiers.d.ts +44 -0
  88. package/dist/src/safe-identifiers.js +96 -0
  89. package/dist/src/scope-rules.d.ts +4 -0
  90. package/dist/src/scope-rules.js +21 -0
  91. package/dist/src/server-paths.d.ts +22 -0
  92. package/dist/src/server-paths.js +219 -0
  93. package/dist/src/setup-version.d.ts +3 -0
  94. package/dist/src/setup-version.js +14 -0
  95. package/dist/src/task-record-reader.d.ts +3 -0
  96. package/dist/src/task-record-reader.js +9 -0
  97. package/dist/src/validator-registry.d.ts +27 -0
  98. package/dist/src/validator-registry.js +64 -0
  99. package/package.json +162 -10
  100. package/dist/src/engineReadModelReducer.js +0 -1780
  101. package/dist/src/rig-init-builder.js +0 -57
  102. package/dist/src/rigSelectors.js +0 -293
  103. package/dist/src/taskGraph.js +0 -64
  104. package/dist/src/taskGraphCodes.js +0 -26
  105. package/dist/src/taskGraphLayout.js +0 -374
package/dist/src/index.js CHANGED
@@ -1,69 +1,42 @@
1
1
  // @bun
2
2
  // packages/core/src/define-plugin.ts
3
3
  import { Schema } from "effect";
4
- import { RigPlugin } from "@rig/contracts";
5
- function definePlugin(meta, runtime) {
6
- const validated = Schema.decodeUnknownSync(RigPlugin)(meta);
7
- if (!runtime) {
8
- return validated;
9
- }
10
- const declaredValidators = new Map((validated.contributes?.validators ?? []).map((v) => [v.id, v]));
11
- const runtimeValidators = new Map((runtime.validators ?? []).map((v) => [v.id, v]));
12
- for (const v of runtimeValidators.values()) {
13
- const metadata = declaredValidators.get(v.id);
14
- if (!metadata) {
15
- throw new Error(`definePlugin(${validated.name}): executable validator "${v.id}" has no matching metadata entry in contributes.validators`);
16
- }
17
- if (metadata.category !== v.category) {
18
- throw new Error(`definePlugin(${validated.name}): executable validator "${v.id}" category "${v.category}" does not match metadata category "${metadata.category}"`);
19
- }
20
- }
21
- if (runtime.validators) {
22
- for (const v of declaredValidators.values()) {
23
- if (!runtimeValidators.has(v.id)) {
24
- throw new Error(`definePlugin(${validated.name}): validator metadata "${v.id}" has no runtime implementation`);
25
- }
26
- }
27
- }
28
- const declaredSources = new Map((validated.contributes?.taskSources ?? []).map((s) => [s.id, s]));
29
- const runtimeSources = new Map((runtime.taskSources ?? []).map((s) => [s.id, s]));
30
- for (const s of runtimeSources.values()) {
31
- const metadata = declaredSources.get(s.id);
32
- if (!metadata) {
33
- throw new Error(`definePlugin(${validated.name}): executable task source "${s.id}" has no matching metadata entry in contributes.taskSources`);
34
- }
35
- if (metadata.kind !== s.kind) {
36
- throw new Error(`definePlugin(${validated.name}): executable task source "${s.id}" kind "${s.kind}" does not match metadata kind "${metadata.kind}"`);
37
- }
38
- }
39
- if (runtime.taskSources) {
40
- for (const s of declaredSources.values()) {
41
- if (!runtimeSources.has(s.id)) {
42
- throw new Error(`definePlugin(${validated.name}): task source metadata "${s.id}" has no runtime implementation`);
43
- }
44
- }
45
- }
46
- return { ...validated, __runtime: runtime };
4
+ import { RigPlugin as RigPluginManifest } from "@rig/contracts";
5
+ function definePlugin(plugin) {
6
+ Schema.decodeUnknownSync(RigPluginManifest)(plugin);
7
+ return plugin;
47
8
  }
48
9
  // packages/core/src/define-config.ts
49
10
  import { Schema as Schema2 } from "effect";
50
11
  import { RigConfig } from "@rig/contracts";
12
+ function normalizeWorkspaceConfig(raw) {
13
+ const workspace = raw && typeof raw === "object" && !Array.isArray(raw) ? { ...raw } : {};
14
+ if (workspace.mainRepo === undefined)
15
+ workspace.mainRepo = ".";
16
+ if (workspace.checkout === undefined && workspace.isolation !== undefined)
17
+ workspace.checkout = workspace.isolation;
18
+ if (workspace.isolation === undefined && workspace.checkout !== undefined)
19
+ workspace.isolation = workspace.checkout;
20
+ return workspace;
21
+ }
22
+ function applyConfigDefaults(raw) {
23
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
24
+ return raw;
25
+ const record = raw;
26
+ return {
27
+ ...record,
28
+ plugins: Array.isArray(record.plugins) ? record.plugins.flat() : [],
29
+ workspace: normalizeWorkspaceConfig(record.workspace)
30
+ };
31
+ }
51
32
  function defineConfig(cfg) {
52
- const runtimeByName = new Map;
53
- const plugins = cfg.plugins ?? [];
54
- for (const plugin of plugins) {
55
- if (plugin?.__runtime) {
56
- runtimeByName.set(plugin.name, plugin.__runtime);
57
- }
58
- }
59
- const decoded = Schema2.decodeUnknownSync(RigConfig)(cfg);
60
- const decodedPlugins = decoded.plugins.map((p) => {
61
- const runtime = runtimeByName.get(p.name);
62
- if (!runtime)
63
- return p;
64
- return { ...p, __runtime: runtime };
33
+ const withDefaults = applyConfigDefaults(cfg);
34
+ const explicitPlugins = Array.isArray(withDefaults.plugins) ? [...withDefaults.plugins] : [];
35
+ const decoded = Schema2.decodeUnknownSync(RigConfig)({
36
+ ...withDefaults,
37
+ plugins: explicitPlugins
65
38
  });
66
- return { ...decoded, plugins: decodedPlugins };
39
+ return { ...decoded, plugins: explicitPlugins };
67
40
  }
68
41
  // packages/core/src/plugin-host.ts
69
42
  function indexById(contributions, kind) {
@@ -87,49 +60,8 @@ function assertUniquePluginNames(plugins) {
87
60
  seen.add(plugin.name);
88
61
  }
89
62
  }
90
- function assertRuntimeMatchesMetadata(plugin) {
91
- const declaredValidators = new Map((plugin.contributes?.validators ?? []).map((validator) => [validator.id, validator]));
92
- const runtimeValidators = new Map((plugin.__runtime?.validators ?? []).map((validator) => [validator.id, validator]));
93
- for (const validator of runtimeValidators.values()) {
94
- const metadata = declaredValidators.get(validator.id);
95
- if (!metadata) {
96
- throw new Error(`plugin "${plugin.name}" executable validator "${validator.id}" has no matching metadata entry in contributes.validators`);
97
- }
98
- if (metadata.category !== validator.category) {
99
- throw new Error(`plugin "${plugin.name}" executable validator "${validator.id}" category "${validator.category}" does not match metadata category "${metadata.category}"`);
100
- }
101
- }
102
- if (plugin.__runtime?.validators) {
103
- for (const validator of declaredValidators.values()) {
104
- if (!runtimeValidators.has(validator.id)) {
105
- throw new Error(`plugin "${plugin.name}" validator metadata "${validator.id}" has no runtime implementation`);
106
- }
107
- }
108
- }
109
- const declaredTaskSources = new Map((plugin.contributes?.taskSources ?? []).map((source) => [source.id, source]));
110
- const runtimeTaskSources = new Map((plugin.__runtime?.taskSources ?? []).map((source) => [source.id, source]));
111
- for (const source of runtimeTaskSources.values()) {
112
- const metadata = declaredTaskSources.get(source.id);
113
- if (!metadata) {
114
- throw new Error(`plugin "${plugin.name}" executable task source "${source.id}" has no matching metadata entry in contributes.taskSources`);
115
- }
116
- if (metadata.kind !== source.kind) {
117
- throw new Error(`plugin "${plugin.name}" executable task source "${source.id}" kind "${source.kind}" does not match metadata kind "${metadata.kind}"`);
118
- }
119
- }
120
- if (plugin.__runtime?.taskSources) {
121
- for (const source of declaredTaskSources.values()) {
122
- if (!runtimeTaskSources.has(source.id)) {
123
- throw new Error(`plugin "${plugin.name}" task source metadata "${source.id}" has no runtime implementation`);
124
- }
125
- }
126
- }
127
- }
128
63
  function createPluginHost(plugins) {
129
64
  assertUniquePluginNames(plugins);
130
- for (const plugin of plugins) {
131
- assertRuntimeMatchesMetadata(plugin);
132
- }
133
65
  const validators = [];
134
66
  const hooks = [];
135
67
  const skills = [];
@@ -138,19 +70,19 @@ function createPluginHost(plugins) {
138
70
  const taskFieldExtensions = [];
139
71
  const taskSources = [];
140
72
  const cliCommands = [];
141
- const executableValidators = [];
142
- const executableTaskSources = [];
73
+ const capabilities = [];
74
+ const panels = [];
75
+ const blockerClassifiers = [];
76
+ const sessionExtensions = [];
77
+ const seedEntrypoints = [];
78
+ const stages = [];
79
+ const stageMutations = [];
80
+ const stageExecutors = {};
143
81
  for (const plugin of plugins) {
144
82
  const c = plugin.contributes;
145
83
  if (!c)
146
84
  continue;
147
85
  const pluginName = plugin.name;
148
- if (plugin.__runtime?.validators) {
149
- executableValidators.push(...plugin.__runtime.validators.map((item) => ({ item, pluginName })));
150
- }
151
- if (plugin.__runtime?.taskSources) {
152
- executableTaskSources.push(...plugin.__runtime.taskSources.map((item) => ({ item, pluginName })));
153
- }
154
86
  if (c.validators)
155
87
  validators.push(...c.validators.map((item) => ({ item, pluginName })));
156
88
  if (c.hooks)
@@ -167,17 +99,25 @@ function createPluginHost(plugins) {
167
99
  taskSources.push(...c.taskSources.map((item) => ({ item, pluginName })));
168
100
  if (c.cliCommands)
169
101
  cliCommands.push(...c.cliCommands.map((item) => ({ item, pluginName })));
170
- }
171
- indexById(executableValidators, "executableValidator");
172
- indexById(executableTaskSources, "executableTaskSource");
173
- const taskSourceFactoryByKind = new Map;
174
- const taskSourceKindRegistrant = new Map;
175
- for (const { item, pluginName } of executableTaskSources) {
176
- if (taskSourceFactoryByKind.has(item.kind)) {
177
- throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
102
+ if (c.capabilities)
103
+ capabilities.push(...c.capabilities.map((item) => ({ item, pluginName })));
104
+ if (c.panels)
105
+ panels.push(...c.panels.map((item) => ({ item, pluginName })));
106
+ if (c.blockerClassifiers)
107
+ blockerClassifiers.push(...c.blockerClassifiers.map((item) => ({ item, pluginName })));
108
+ if (c.sessionExtensions)
109
+ sessionExtensions.push(...c.sessionExtensions.map((item) => ({ item, pluginName })));
110
+ if (c.seedEntrypoints)
111
+ seedEntrypoints.push(...c.seedEntrypoints.map((item) => ({ item, pluginName })));
112
+ if (c.stageMutations)
113
+ stageMutations.push(...c.stageMutations.map((item) => ({ item, pluginName })));
114
+ if (c.stages) {
115
+ for (const stage of c.stages) {
116
+ stages.push({ item: stage, pluginName });
117
+ if (stage.run)
118
+ stageExecutors[stage.id] = stage.run;
119
+ }
178
120
  }
179
- taskSourceFactoryByKind.set(item.kind, item);
180
- taskSourceKindRegistrant.set(item.kind, pluginName);
181
121
  }
182
122
  const validatorMap = indexById(validators, "validator");
183
123
  const hookMap = indexById(hooks, "hook");
@@ -187,6 +127,52 @@ function createPluginHost(plugins) {
187
127
  const taskFieldExtMap = indexById(taskFieldExtensions, "taskFieldExtension");
188
128
  const taskSourceMap = indexById(taskSources, "taskSource");
189
129
  const cliCommandMap = indexById(cliCommands, "cliCommand");
130
+ const capabilityMap = indexById(capabilities, "capability");
131
+ const panelMap = indexById(panels, "panel");
132
+ const blockerClassifierMap = indexById(blockerClassifiers, "blockerClassifier");
133
+ indexById(sessionExtensions, "sessionExtension");
134
+ indexById(seedEntrypoints, "seedEntrypoint");
135
+ const taskSourceFactoryByKind = new Map;
136
+ const taskSourceKindRegistrant = new Map;
137
+ for (const { item, pluginName } of taskSources) {
138
+ if (taskSourceFactoryByKind.has(item.kind)) {
139
+ throw new Error(`duplicate task source kind "${item.kind}": registered by plugins "${taskSourceKindRegistrant.get(item.kind)}" and "${pluginName}"`);
140
+ }
141
+ taskSourceFactoryByKind.set(item.kind, item);
142
+ taskSourceKindRegistrant.set(item.kind, pluginName);
143
+ }
144
+ const seedEntrypointByBasename = new Map;
145
+ const seedEntrypointByWorkerArg = new Map;
146
+ let insidePtySeedEntrypoint;
147
+ const registerSeedEntrypointSelector = (map, selectorKind, selector, contribution) => {
148
+ const existing = map.get(selector);
149
+ if (existing) {
150
+ throw new Error(`duplicate seed entrypoint ${selectorKind} "${selector}": registered by entrypoint "${existing.item.id}" from plugin "${existing.pluginName}" and entrypoint "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
151
+ }
152
+ map.set(selector, contribution);
153
+ };
154
+ for (const contribution of seedEntrypoints) {
155
+ const entrypoint = contribution.item;
156
+ let selectorCount = 0;
157
+ if (entrypoint.basename) {
158
+ selectorCount += 1;
159
+ registerSeedEntrypointSelector(seedEntrypointByBasename, "basename", entrypoint.basename, contribution);
160
+ }
161
+ if (entrypoint.workerArg) {
162
+ selectorCount += 1;
163
+ registerSeedEntrypointSelector(seedEntrypointByWorkerArg, "workerArg", entrypoint.workerArg, contribution);
164
+ }
165
+ if (entrypoint.insidePty) {
166
+ selectorCount += 1;
167
+ if (insidePtySeedEntrypoint) {
168
+ throw new Error(`duplicate seed entrypoint inside-pty selector: registered by entrypoint "${insidePtySeedEntrypoint.item.id}" from plugin "${insidePtySeedEntrypoint.pluginName}" and entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}"`);
169
+ }
170
+ insidePtySeedEntrypoint = contribution;
171
+ }
172
+ if (selectorCount === 0) {
173
+ throw new Error(`seed entrypoint "${entrypoint.id}" from plugin "${contribution.pluginName}" must declare at least one selector (basename, workerArg, or insidePty)`);
174
+ }
175
+ }
190
176
  const allValidators = validators.map((c) => c.item);
191
177
  const allHooks = hooks.map((c) => c.item);
192
178
  const allSkills = skills.map((c) => c.item);
@@ -195,8 +181,39 @@ function createPluginHost(plugins) {
195
181
  const allTaskFieldExtensions = taskFieldExtensions.map((c) => c.item);
196
182
  const allTaskSources = taskSources.map((c) => c.item);
197
183
  const allCliCommands = cliCommands.map((c) => c.item);
198
- const allExecutableValidators = executableValidators.map((c) => c.item);
199
- const allExecutableTaskSources = executableTaskSources.map((c) => c.item);
184
+ const allStageMutations = stageMutations.map((c) => c.item);
185
+ const allStages = stages.map((c) => c.item);
186
+ const allCapabilities = capabilities.map((c) => c.item);
187
+ const allPanels = panels.map((c) => c.item);
188
+ const allBlockerClassifiers = blockerClassifiers.map((c) => c.item);
189
+ const allSessionExtensions = sessionExtensions.map((c) => c.item);
190
+ const allSeedEntrypoints = seedEntrypoints.map((c) => c.item);
191
+ const executableCliCommandByName = new Map;
192
+ const registerExecutableCliCommandSelector = (selector, contribution) => {
193
+ const existing = executableCliCommandByName.get(selector);
194
+ if (existing && existing.item.id !== contribution.item.id) {
195
+ throw new Error(`duplicate executable CLI selector "${selector}" registered by command "${existing.item.id}" from plugin "${existing.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
196
+ }
197
+ executableCliCommandByName.set(selector, contribution);
198
+ };
199
+ for (const contribution of cliCommands) {
200
+ const command = contribution.item;
201
+ const family = command.family ?? command.id;
202
+ registerExecutableCliCommandSelector(command.id, contribution);
203
+ registerExecutableCliCommandSelector(family, contribution);
204
+ for (const alias of command.aliases ?? []) {
205
+ registerExecutableCliCommandSelector(alias, contribution);
206
+ }
207
+ }
208
+ let defaultRootCliCommand;
209
+ for (const contribution of cliCommands) {
210
+ if (!contribution.item.rootDefault)
211
+ continue;
212
+ if (defaultRootCliCommand) {
213
+ throw new Error(`duplicate default root CLI command registered by command "${defaultRootCliCommand.item.id}" from plugin "${defaultRootCliCommand.pluginName}" and command "${contribution.item.id}" from plugin "${contribution.pluginName}"`);
214
+ }
215
+ defaultRootCliCommand = contribution;
216
+ }
200
217
  return {
201
218
  getValidator: (id) => validatorMap.get(id),
202
219
  getHook: (id) => hookMap.get(id),
@@ -206,6 +223,9 @@ function createPluginHost(plugins) {
206
223
  getTaskFieldExtension: (id) => taskFieldExtMap.get(id),
207
224
  getTaskSource: (id) => taskSourceMap.get(id),
208
225
  getCliCommand: (id) => cliCommandMap.get(id),
226
+ getCapability: (id) => capabilityMap.get(id),
227
+ getPanel: (id) => panelMap.get(id),
228
+ getBlockerClassifier: (id) => blockerClassifierMap.get(id),
209
229
  listValidators: () => allValidators,
210
230
  listHooks: () => allHooks,
211
231
  listSkills: () => allSkills,
@@ -214,2403 +234,94 @@ function createPluginHost(plugins) {
214
234
  listTaskFieldExtensions: () => allTaskFieldExtensions,
215
235
  listTaskSources: () => allTaskSources,
216
236
  listCliCommands: () => allCliCommands,
217
- listExecutableValidators: () => allExecutableValidators,
218
- listExecutableTaskSources: () => allExecutableTaskSources,
237
+ listCapabilities: () => allCapabilities,
238
+ listPanels: () => allPanels,
239
+ listBlockerClassifiers: () => allBlockerClassifiers,
240
+ listStages: () => allStages,
241
+ listStageMutations: () => allStageMutations,
242
+ listStageExecutors: () => stageExecutors,
243
+ listExecutableValidators: () => allValidators.filter((v) => typeof v.run === "function"),
244
+ listExecutableTaskSources: () => allTaskSources,
245
+ listExecutableCapabilities: () => allCapabilities.filter((c) => typeof c.run === "function"),
246
+ listExecutablePanels: () => allPanels.filter((p) => typeof p.produce === "function"),
247
+ listExecutableBlockerClassifiers: () => allBlockerClassifiers,
248
+ listExecutableCliCommands: () => allCliCommands,
249
+ listSessionExtensions: () => allSessionExtensions,
250
+ listSeedEntrypoints: () => allSeedEntrypoints,
251
+ resolveSeedEntrypointByBasename: (value) => seedEntrypointByBasename.get(value)?.item,
252
+ resolveSeedEntrypointByWorkerArg: (value) => seedEntrypointByWorkerArg.get(value)?.item,
253
+ resolveInsidePtySeedEntrypoint: () => insidePtySeedEntrypoint?.item,
254
+ resolveExecutableCliCommand: (requested) => executableCliCommandByName.get(requested)?.item,
255
+ resolveDefaultRootCliCommand: () => defaultRootCliCommand?.item,
219
256
  resolveTaskSourceFactoryByKind: (kind) => taskSourceFactoryByKind.get(kind)
220
257
  };
221
258
  }
222
- // packages/core/src/rig-init-builder.ts
223
- function buildRigInitConfigSource(input) {
224
- const lines = [`import { defineConfig } from "@rig/core";`];
225
- if (input.useStandardPlugin && input.taskSource.kind === "github-issues") {
226
- lines.push(`import standard, { createStateGitHubCredentialProvider } from "@rig/standard-plugin";`);
227
- } else if (input.useStandardPlugin) {
228
- lines.push(`import standard from "@rig/standard-plugin";`);
229
- }
230
- lines.push(``, `export default defineConfig({`);
231
- const projectRepo = input.projectRepo ?? (input.taskSource.kind === "github-issues" ? `${input.taskSource.owner}/${input.taskSource.repo}` : undefined);
232
- lines.push(projectRepo ? ` project: { name: ${JSON.stringify(input.projectName)}, repo: ${JSON.stringify(projectRepo)} },` : ` project: { name: ${JSON.stringify(input.projectName)} },`);
233
- if (input.useStandardPlugin && input.taskSource.kind === "github-issues") {
234
- lines.push(` plugins: [standard({`);
235
- lines.push(` githubCredentialProvider: createStateGitHubCredentialProvider(),`);
236
- lines.push(` githubWorkspaceId: ${JSON.stringify(`${input.taskSource.owner}/${input.taskSource.repo}`)},`);
237
- lines.push(` githubUserId: process.env.RIG_GITHUB_USER_ID ?? "server-selected-user",`);
238
- lines.push(` })],`);
239
- } else {
240
- lines.push(` plugins: [${input.useStandardPlugin ? "standard()" : ""}],`);
241
- }
242
- if (input.taskSource.kind === "github-issues") {
243
- lines.push(` taskSource: {`);
244
- lines.push(` kind: "github-issues",`);
245
- lines.push(` owner: ${JSON.stringify(input.taskSource.owner)},`);
246
- lines.push(` repo: ${JSON.stringify(input.taskSource.repo)},`);
247
- lines.push(` // labels: ["task"], // uncomment to filter by labels`);
248
- lines.push(` state: "open",`);
249
- if (input.taskSource.assignee?.trim()) {
250
- lines.push(` options: { assignee: ${JSON.stringify(input.taskSource.assignee.trim())} },`);
251
- }
252
- lines.push(` },`);
253
- } else {
254
- lines.push(` taskSource: {`);
255
- lines.push(` kind: "files",`);
256
- lines.push(` path: ${JSON.stringify(input.taskSource.path)},`);
257
- lines.push(` },`);
258
- }
259
- lines.push(` workspace: { mainRepo: ".", isolation: "worktree" },`);
260
- lines.push(` runtime: { harness: "pi", mode: "yolo" },`);
261
- lines.push(` planning: { mode: "auto" },`);
262
- lines.push(` github: {`);
263
- lines.push(` issueUpdates: "lifecycle",`);
264
- lines.push(` projects: { enabled: false },`);
265
- lines.push(` },`);
266
- lines.push(` automation: { maxValidationAttempts: 30, maxPrFixIterations: 100500 },`);
267
- lines.push(` pr: { mode: "auto", watchChecks: true, autoFixChecks: true, autoFixReview: true },`);
268
- lines.push(` merge: { mode: "auto", method: "repo-default", deleteBranch: "repo-default", bypass: false },`);
269
- lines.push(` issueAnalysis: { enabled: true, harness: "pi", mode: "continuous" },`);
270
- lines.push(`});`);
271
- lines.push(``);
272
- return lines.join(`
273
- `);
274
- }
275
- // packages/core/src/engineReadModelReducer.ts
276
- import {
277
- ActionId,
278
- ConversationId,
279
- EngineRuntimeId,
280
- MessageId,
281
- RunId,
282
- TaskId,
283
- WorkspaceId,
284
- WorktreeId
285
- } from "@rig/contracts";
286
- function isRecord(value) {
287
- return typeof value === "object" && value !== null;
288
- }
289
- function readString(payload, key) {
290
- const value = payload[key];
291
- return typeof value === "string" ? value : undefined;
292
- }
293
- function readNullableString(payload, key) {
294
- const value = payload[key];
295
- return typeof value === "string" ? value : value === null ? null : undefined;
296
- }
297
- function readRecord(payload, key) {
298
- const value = payload[key];
299
- return isRecord(value) ? value : undefined;
300
- }
301
- function asWorkspaceId(value) {
302
- return WorkspaceId.makeUnsafe(value);
303
- }
304
- function asTaskId(value) {
305
- return TaskId.makeUnsafe(value);
306
- }
307
- function asRunId(value) {
308
- return RunId.makeUnsafe(value);
309
- }
310
- function conversationIdFromRunId(value) {
311
- return ConversationId.makeUnsafe(value);
312
- }
313
- function asActionId(value) {
314
- return ActionId.makeUnsafe(value);
315
- }
316
- function runtimeIdFromRunId(value) {
317
- return EngineRuntimeId.makeUnsafe(value);
318
- }
319
- function worktreeIdFromRunId(value) {
320
- return WorktreeId.makeUnsafe(value);
321
- }
322
- function asMessageId(value) {
323
- return MessageId.makeUnsafe(value);
324
- }
325
- function upsertById(items, entry) {
326
- const index = items.findIndex((candidate) => candidate.id === entry.id);
327
- if (index < 0) {
328
- return [...items, entry];
329
- }
330
- const next = items.slice();
331
- next[index] = entry;
332
- return next;
333
- }
334
- function removeById(items, id) {
335
- return items.filter((candidate) => candidate.id !== id);
336
- }
337
- function patchById(items, id, updater) {
338
- const index = items.findIndex((candidate) => candidate.id === id);
339
- if (index < 0) {
340
- return items.slice();
341
- }
342
- const next = items.slice();
343
- next[index] = updater(next[index]);
344
- return next;
345
- }
346
- function upsertByKey(items, entry, key) {
347
- const index = items.findIndex((candidate) => candidate[key] === entry[key]);
348
- if (index < 0) {
349
- return [...items, entry];
350
- }
351
- const next = items.slice();
352
- next[index] = entry;
353
- return next;
354
- }
355
- function patchByKey(items, keyValue, key, updater) {
356
- const index = items.findIndex((candidate) => candidate[key] === keyValue);
357
- if (index < 0) {
358
- return items.slice();
359
- }
360
- const next = items.slice();
361
- next[index] = updater(next[index]);
362
- return next;
363
- }
364
- function mergeById(items, incoming) {
365
- return incoming.reduce((acc, item) => upsertById(acc, item), [...items]);
366
- }
367
- function replaceWorkspaceSlice(items, workspaceId, incoming) {
368
- return [...items.filter((item) => item.workspaceId !== workspaceId), ...incoming];
369
- }
370
- function withQueuePositions(items) {
371
- return items.map((item, index) => Object.assign({}, item, {
372
- position: index
373
- }));
374
- }
375
- function maxIsoDate(left, right) {
376
- return left.localeCompare(right) >= 0 ? left : right;
377
- }
378
- var REMOTE_HOST_STATUS_ONLINE = new Set([
379
- "ready",
380
- "busy",
381
- "degraded",
382
- "draining"
383
- ]);
384
- function deriveRemoteFleetStatus(hosts, warnings) {
385
- if (hosts.length === 0)
386
- return "empty";
387
- if (warnings.length > 0)
388
- return "degraded";
389
- if (hosts.some((host) => host.status === "degraded" || host.status === "quarantined")) {
390
- return "degraded";
391
- }
392
- const onlineHostCount = hosts.filter((host) => REMOTE_HOST_STATUS_ONLINE.has(host.status)).length;
393
- return onlineHostCount > 0 ? "ready" : "degraded";
394
- }
395
- function buildRemoteFleetSummary(input) {
396
- const warnings = input.fleet?.warnings ?? [];
397
- const manifestCount = input.fleet?.manifestCount ?? 0;
398
- const onlineHostCount = input.hosts.filter((host) => REMOTE_HOST_STATUS_ONLINE.has(host.status)).length;
399
- return {
400
- updatedAt: input.updatedAt,
401
- status: deriveRemoteFleetStatus(input.hosts, warnings),
402
- manifestCount,
403
- hostCount: input.hosts.length,
404
- onlineHostCount,
405
- hosts: [...input.hosts].sort((left, right) => left.name.localeCompare(right.name)),
406
- warnings
407
- };
408
- }
409
- function toRunMode(interactionMode) {
410
- return interactionMode === "plan" ? "supervised" : "interactive";
411
- }
412
- function mapLegacySessionStatusToRunStatus(status) {
413
- switch (status) {
414
- case "starting":
415
- return "preparing";
416
- case "running":
417
- return "running";
418
- case "ready":
419
- return "completed";
420
- case "interrupted":
421
- return "paused";
422
- case "error":
423
- return "failed";
424
- case "stopped":
425
- return "cancelled";
426
- default:
427
- return "created";
428
- }
429
- }
430
- function mapLegacySessionStatusToRuntimeStatus(status) {
431
- switch (status) {
432
- case "starting":
433
- return "starting";
434
- case "running":
435
- return "running";
436
- case "interrupted":
437
- return "interrupted";
438
- case "error":
439
- return "failed";
440
- default:
441
- return "exited";
442
- }
443
- }
444
- function mapTaskStatusFromRunStatus(status, fallback) {
445
- if (fallback === "blocked" || fallback === "cancelled" || fallback === "completed" || fallback === "closed" || fallback === "unknown") {
446
- return fallback === "closed" ? "completed" : fallback;
447
- }
448
- switch (status) {
449
- case "created":
450
- return fallback;
451
- case "queued":
452
- return "queued";
453
- case "preparing":
454
- case "running":
455
- case "waiting-approval":
456
- case "waiting-user-input":
457
- case "validating":
458
- case "paused":
459
- return "in_progress";
460
- case "reviewing":
461
- return "under_review";
462
- case "completed":
463
- return "completed";
464
- case "failed":
465
- return "ready";
466
- case "cancelled":
467
- return "cancelled";
468
- }
469
- }
470
- function withSnapshotMetadata(snapshot, event, next) {
471
- return {
472
- ...snapshot,
473
- ...next,
474
- snapshotSequence: Math.max(snapshot.snapshotSequence, event.sequence),
475
- updatedAt: maxIsoDate(snapshot.updatedAt, event.createdAt)
476
- };
477
- }
478
- function ensureConversation(snapshot, run) {
479
- const conversationId = conversationIdFromRunId(run.id);
480
- if (snapshot.conversations.some((conversation) => conversation.id === conversationId)) {
481
- return snapshot;
482
- }
483
- return {
484
- ...snapshot,
485
- conversations: [
486
- ...snapshot.conversations,
487
- {
488
- id: conversationId,
489
- runId: run.id,
490
- title: run.title,
491
- createdAt: run.createdAt,
492
- updatedAt: run.updatedAt
493
- }
494
- ]
495
- };
496
- }
497
- function updateTaskFromRun(snapshot, run) {
498
- if (!run.taskId) {
499
- return snapshot;
500
- }
501
- return {
502
- ...snapshot,
503
- tasks: patchById(snapshot.tasks, run.taskId, (task) => ({
504
- ...task,
505
- status: mapTaskStatusFromRunStatus(run.status, task.status),
506
- updatedAt: maxIsoDate(task.updatedAt, run.updatedAt)
507
- }))
508
- };
509
- }
510
- function applyRun(snapshot, run) {
511
- const withConversation = ensureConversation(snapshot, run);
512
- const nextRuns = upsertById(withConversation.runs, run);
513
- return updateTaskFromRun({ ...withConversation, runs: nextRuns }, run);
514
- }
515
- function applyRuntime(snapshot, runtime) {
516
- return {
517
- ...snapshot,
518
- runtimes: upsertById(snapshot.runtimes, runtime)
519
- };
520
- }
521
- function applyWorktree(snapshot, worktree) {
522
- return {
523
- ...snapshot,
524
- worktrees: upsertById(snapshot.worktrees, worktree)
525
- };
526
- }
527
- function countPendingApprovals(approvals, runId) {
528
- return approvals.filter((approval) => approval.runId === runId && approval.status === "pending").length;
529
- }
530
- function countPendingUserInputs(userInputs, actions, runId) {
531
- const persistedCount = (userInputs ?? []).filter((request) => request.runId === runId && request.status === "pending").length;
532
- if (persistedCount > 0) {
533
- return persistedCount;
534
- }
535
- const openRequestIds = new Set;
536
- for (const action of actions) {
537
- if (action.runId !== runId || !isRecord(action.payload)) {
538
- continue;
539
- }
540
- const requestId = readString(action.payload, "requestId");
541
- if (!requestId) {
542
- continue;
543
- }
544
- if (action.actionType === "user-input.requested") {
545
- openRequestIds.add(requestId);
546
- }
547
- if (action.actionType === "user-input.resolved") {
548
- openRequestIds.delete(requestId);
549
- }
550
- }
551
- return openRequestIds.size;
552
- }
553
- function reconcileRunCounts(snapshot, runId, statusOverride) {
554
- return {
555
- ...snapshot,
556
- runs: patchById(snapshot.runs, runId, (run) => {
557
- const pendingApprovalCount = countPendingApprovals(snapshot.approvals, runId);
558
- const pendingUserInputCount = countPendingUserInputs(snapshot.userInputs, snapshot.actions, runId);
559
- const nextStatus = statusOverride ?? (pendingApprovalCount > 0 ? "waiting-approval" : pendingUserInputCount > 0 ? "waiting-user-input" : run.status === "waiting-approval" || run.status === "waiting-user-input" ? "running" : run.status);
560
- return {
561
- ...run,
562
- pendingApprovalCount,
563
- pendingUserInputCount,
564
- status: nextStatus
565
- };
566
- })
567
- };
568
- }
569
- function applyApprovalActivity(approvals, runId, action) {
570
- if (!isRecord(action.payload)) {
571
- return approvals.slice();
572
- }
573
- const requestId = readString(action.payload, "requestId");
574
- if (!requestId) {
575
- return approvals.slice();
576
- }
577
- if (action.actionType === "approval.requested") {
578
- const requestKind = readString(action.payload, "requestKind") ?? "command";
579
- return upsertById(approvals, {
580
- id: requestId,
581
- runId,
582
- actionId: action.id,
583
- requestKind,
584
- status: "pending",
585
- payload: action.payload,
586
- createdAt: action.startedAt,
587
- resolvedAt: null
588
- });
589
- }
590
- const shouldResolve = action.actionType === "approval.resolved" || action.actionType === "provider.approval.respond.failed" && ((readString(action.payload, "detail") ?? "").includes("Unknown pending permission request") || (readString(action.payload, "message") ?? "").includes("Unknown pending permission request"));
591
- if (!shouldResolve) {
592
- return approvals.slice();
593
- }
594
- return approvals.map((approval) => approval.id === requestId ? {
595
- ...approval,
596
- status: "resolved",
597
- resolvedAt: action.completedAt ?? action.startedAt
598
- } : approval);
599
- }
600
- function makeRuntimeFromRun(run, occurredAt) {
601
- return {
602
- id: run.activeRuntimeId ?? runtimeIdFromRunId(run.id),
603
- workspaceId: run.workspaceId,
604
- runId: run.id,
605
- adapterKind: run.runtimeAdapter,
606
- executionTarget: run.executionTarget ?? "local",
607
- remoteHostId: run.remoteHostId ?? null,
608
- status: run.status === "failed" ? "failed" : run.status === "paused" ? "interrupted" : "starting",
609
- sandboxMode: "danger-full-access",
610
- isolationMode: run.worktreePath ? "worktree" : "env",
611
- workspaceDir: run.worktreePath,
612
- homeDir: null,
613
- tmpDir: null,
614
- cacheDir: null,
615
- logsDir: null,
616
- stateDir: null,
617
- sessionDir: null,
618
- sessionLogPath: null,
619
- pid: null,
620
- startedAt: run.startedAt ?? occurredAt,
621
- updatedAt: occurredAt,
622
- exitedAt: run.completedAt
623
- };
624
- }
625
- function applySyntheticRuntimePreparation(snapshot, event) {
626
- if (!isRecord(event.payload)) {
627
- return { status: "ignored", snapshot };
628
- }
629
- const runId = readString(event.payload, "runId") ?? event.aggregateId;
630
- const workspaceId = readString(event.payload, "workspaceId");
631
- const taskId = readString(event.payload, "taskId") ?? null;
632
- const workspaceDir = readNullableString(event.payload, "workspaceDir");
633
- const homeDir = readNullableString(event.payload, "homeDir");
634
- const tmpDir = readNullableString(event.payload, "tmpDir");
635
- const cacheDir = readNullableString(event.payload, "cacheDir");
636
- const branchName = readString(event.payload, "taskExternalId") ?? snapshot.runs.find((run) => run.id === runId)?.branch ?? "task";
637
- const failed = event.type.endsWith(".failed");
638
- const finished = event.type.endsWith(".finished");
639
- const existingRun = snapshot.runs.find((run) => run.id === runId);
640
- if (!existingRun || !workspaceId) {
641
- return { status: "ignored", snapshot };
642
- }
643
- const runtimeRunId = asRunId(runId);
644
- const nextTaskId = taskId ? asTaskId(taskId) : null;
645
- const nextWorkspaceId = asWorkspaceId(workspaceId);
646
- const nextRun = {
647
- ...existingRun,
648
- taskId: existingRun.taskId ?? nextTaskId,
649
- runKind: existingRun.runKind === "adhoc" && nextTaskId ? "task" : existingRun.runKind,
650
- runtimeAdapter: "rig-native",
651
- initialPrompt: existingRun.initialPrompt ?? null,
652
- executionTarget: existingRun.executionTarget ?? "local",
653
- remoteHostId: existingRun.remoteHostId ?? null,
654
- activeRuntimeId: existingRun.activeRuntimeId ?? runtimeIdFromRunId(runtimeRunId),
655
- worktreePath: workspaceDir ?? existingRun.worktreePath,
656
- status: failed ? "failed" : finished ? "preparing" : "preparing",
657
- errorText: failed ? readString(event.payload, "message") ?? existingRun.errorText : existingRun.errorText,
658
- updatedAt: event.createdAt,
659
- completedAt: failed ? event.createdAt : existingRun.completedAt
660
- };
661
- const runtimeBase = snapshot.runtimes.find((runtime) => runtime.runId === runId) ?? makeRuntimeFromRun(nextRun, event.createdAt);
662
- const nextRuntime = {
663
- ...runtimeBase,
664
- workspaceId: nextWorkspaceId,
665
- runId: runtimeRunId,
666
- adapterKind: "rig-native",
667
- executionTarget: existingRun.executionTarget ?? "local",
668
- remoteHostId: existingRun.remoteHostId ?? null,
669
- status: failed ? "failed" : finished ? "prepared" : "starting",
670
- isolationMode: "worktree",
671
- workspaceDir: workspaceDir ?? runtimeBase.workspaceDir,
672
- homeDir: homeDir ?? runtimeBase.homeDir,
673
- tmpDir: tmpDir ?? runtimeBase.tmpDir,
674
- cacheDir: cacheDir ?? runtimeBase.cacheDir,
675
- updatedAt: event.createdAt,
676
- exitedAt: failed ? event.createdAt : runtimeBase.exitedAt
677
- };
678
- let nextSnapshot = applyRun(snapshot, nextRun);
679
- nextSnapshot = applyRuntime(nextSnapshot, nextRuntime);
680
- if (workspaceDir) {
681
- nextSnapshot = applyWorktree(nextSnapshot, {
682
- id: worktreeIdFromRunId(runtimeRunId),
683
- workspaceId: nextWorkspaceId,
684
- runId: runtimeRunId,
685
- taskId: nextRun.taskId,
686
- branchName,
687
- path: workspaceDir,
688
- status: failed ? "failed" : finished ? "prepared" : "preparing",
689
- createdAt: existingRun.createdAt,
690
- cleanedAt: null
691
- });
692
- }
693
- return {
694
- status: "applied",
695
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
696
- };
697
- }
698
- function applySyntheticRuntimePrepared(snapshot, event) {
699
- if (!isRecord(event.payload)) {
700
- return { status: "ignored", snapshot };
701
- }
702
- const runId = readString(event.payload, "runId") ?? event.aggregateId;
703
- const workspaceId = readString(event.payload, "workspaceId");
704
- const worktreePath = readString(event.payload, "worktreePath");
705
- const existingRun = snapshot.runs.find((run) => run.id === runId);
706
- if (!existingRun || !workspaceId || !worktreePath) {
707
- return { status: "ignored", snapshot };
708
- }
709
- const runtimeRunId = asRunId(runId);
710
- const nextWorkspaceId = asWorkspaceId(workspaceId);
711
- let nextSnapshot = applyRuntime(snapshot, {
712
- ...snapshot.runtimes.find((runtime) => runtime.runId === runId) ?? makeRuntimeFromRun(existingRun, event.createdAt),
713
- workspaceId: nextWorkspaceId,
714
- runId: runtimeRunId,
715
- adapterKind: "rig-native",
716
- executionTarget: existingRun.executionTarget ?? "local",
717
- remoteHostId: existingRun.remoteHostId ?? null,
718
- status: "prepared",
719
- isolationMode: "worktree",
720
- workspaceDir: worktreePath,
721
- updatedAt: event.createdAt
722
- });
723
- nextSnapshot = applyWorktree(nextSnapshot, {
724
- id: worktreeIdFromRunId(runtimeRunId),
725
- workspaceId: nextWorkspaceId,
726
- runId: runtimeRunId,
727
- taskId: existingRun.taskId,
728
- branchName: existingRun.branch ?? "task",
729
- path: worktreePath,
730
- status: "prepared",
731
- createdAt: existingRun.createdAt,
732
- cleanedAt: null
733
- });
734
- return {
735
- status: "applied",
736
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
737
- };
738
- }
739
- function applyLegacyProjectEvent(snapshot, event) {
740
- if (!isRecord(event.payload)) {
741
- return { status: "ignored", snapshot };
742
- }
743
- const payload = event.payload;
744
- if (event.type === "legacy.project.created") {
745
- const workspaceId = readString(payload, "projectId");
746
- const title = readString(payload, "title");
747
- const rootPath = readString(payload, "workspaceRoot");
748
- const createdAt = readString(payload, "createdAt");
749
- const updatedAt = readString(payload, "updatedAt");
750
- if (!workspaceId || !title || !rootPath || !createdAt || !updatedAt) {
751
- return { status: "ignored", snapshot };
752
- }
753
- const workspace = {
754
- id: asWorkspaceId(workspaceId),
755
- title,
756
- rootPath,
757
- sourceKind: "native",
758
- defaultRuntimeAdapter: "codex-app-server",
759
- defaultModel: readNullableString(payload, "defaultModel") ?? null,
760
- createdAt,
761
- updatedAt
762
- };
763
- return {
764
- status: "applied",
765
- snapshot: withSnapshotMetadata(snapshot, event, {
766
- workspaces: upsertById(snapshot.workspaces, workspace)
767
- })
768
- };
769
- }
770
- if (event.type === "legacy.project.meta-updated") {
771
- const workspaceId = readString(payload, "projectId");
772
- if (!workspaceId) {
773
- return { status: "ignored", snapshot };
774
- }
775
- return {
776
- status: "applied",
777
- snapshot: withSnapshotMetadata(snapshot, event, {
778
- workspaces: patchById(snapshot.workspaces, workspaceId, (workspace) => ({
779
- ...workspace,
780
- title: readString(payload, "title") ?? workspace.title,
781
- rootPath: readString(payload, "workspaceRoot") ?? workspace.rootPath,
782
- defaultModel: readNullableString(payload, "defaultModel") ?? workspace.defaultModel,
783
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
784
- }))
785
- })
786
- };
787
- }
788
- if (event.type === "legacy.project.deleted") {
789
- const workspaceId = readString(payload, "projectId");
790
- if (!workspaceId) {
791
- return { status: "ignored", snapshot };
792
- }
793
- return {
794
- status: "applied",
795
- snapshot: withSnapshotMetadata(snapshot, event, {
796
- workspaces: removeById(snapshot.workspaces, workspaceId),
797
- graphs: snapshot.graphs.filter((graph) => graph.workspaceId !== workspaceId),
798
- tasks: snapshot.tasks.filter((task) => task.workspaceId !== workspaceId),
799
- runs: snapshot.runs.filter((run) => run.workspaceId !== workspaceId),
800
- runtimes: snapshot.runtimes.filter((runtime) => runtime.workspaceId !== workspaceId),
801
- conversations: snapshot.conversations.filter((conversation) => {
802
- const run = snapshot.runs.find((candidate) => candidate.id === conversation.runId);
803
- return run?.workspaceId !== workspaceId;
804
- }),
805
- messages: snapshot.messages.filter((message) => {
806
- const conversation = snapshot.conversations.find((candidate) => candidate.id === message.conversationId);
807
- const run = snapshot.runs.find((candidate) => candidate.id === conversation?.runId);
808
- return run?.workspaceId !== workspaceId;
809
- }),
810
- actions: snapshot.actions.filter((action) => {
811
- const run = snapshot.runs.find((candidate) => candidate.id === action.runId);
812
- return run?.workspaceId !== workspaceId;
813
- }),
814
- approvals: snapshot.approvals.filter((approval) => {
815
- const run = snapshot.runs.find((candidate) => candidate.id === approval.runId);
816
- return run?.workspaceId !== workspaceId;
817
- }),
818
- queue: snapshot.queue.filter((entry) => {
819
- const task = snapshot.tasks.find((candidate) => candidate.id === entry.taskId);
820
- return task?.workspaceId !== workspaceId;
821
- }),
822
- worktrees: snapshot.worktrees.filter((worktree) => worktree.workspaceId !== workspaceId)
823
- })
824
- };
825
- }
826
- return { status: "ignored", snapshot };
827
- }
828
- function applyLegacyThreadEvent(snapshot, event) {
829
- if (!isRecord(event.payload)) {
830
- return { status: "ignored", snapshot };
831
- }
832
- const payload = event.payload;
833
- if (event.type === "legacy.thread.created") {
834
- const runId = readString(payload, "threadId");
835
- const workspaceId = readString(payload, "projectId");
836
- const title = readString(payload, "title");
837
- const model = readString(payload, "model");
838
- const createdAt = readString(payload, "createdAt");
839
- const updatedAt = readString(payload, "updatedAt");
840
- if (!runId || !workspaceId || !title || !model || !createdAt || !updatedAt) {
841
- return { status: "ignored", snapshot };
842
- }
843
- const run = {
844
- id: asRunId(runId),
845
- workspaceId: asWorkspaceId(workspaceId),
846
- taskId: null,
847
- title,
848
- runKind: "adhoc",
849
- mode: toRunMode(readString(payload, "interactionMode")),
850
- runtimeMode: "full-access",
851
- interactionMode: "default",
852
- status: "created",
853
- runtimeAdapter: "codex-app-server",
854
- model,
855
- initialPrompt: null,
856
- activeRuntimeId: null,
857
- latestMessageId: null,
858
- pendingApprovalCount: 0,
859
- pendingUserInputCount: 0,
860
- branch: readNullableString(payload, "branch") ?? null,
861
- worktreePath: readNullableString(payload, "worktreePath") ?? null,
862
- errorText: null,
863
- createdAt,
864
- updatedAt,
865
- startedAt: null,
866
- completedAt: null
867
- };
868
- let nextSnapshot = applyRun(snapshot, run);
869
- if (run.branch && run.worktreePath) {
870
- nextSnapshot = applyWorktree(nextSnapshot, {
871
- id: worktreeIdFromRunId(asRunId(runId)),
872
- workspaceId: asWorkspaceId(workspaceId),
873
- runId: asRunId(runId),
874
- taskId: null,
875
- branchName: run.branch,
876
- path: run.worktreePath,
877
- status: "active",
878
- createdAt,
879
- cleanedAt: null
880
- });
881
- }
882
- return {
883
- status: "applied",
884
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
885
- };
886
- }
887
- if (event.type === "legacy.thread.deleted") {
888
- const runId = readString(payload, "threadId");
889
- if (!runId) {
890
- return { status: "ignored", snapshot };
891
- }
892
- return {
893
- status: "applied",
894
- snapshot: withSnapshotMetadata(snapshot, event, {
895
- runs: removeById(snapshot.runs, runId),
896
- runtimes: snapshot.runtimes.filter((runtime) => runtime.runId !== runId),
897
- conversations: snapshot.conversations.filter((conversation) => conversation.runId !== runId),
898
- messages: snapshot.messages.filter((message) => message.conversationId !== runId),
899
- actions: snapshot.actions.filter((action) => action.runId !== runId),
900
- approvals: snapshot.approvals.filter((approval) => approval.runId !== runId),
901
- worktrees: snapshot.worktrees.filter((worktree) => worktree.runId !== runId)
902
- })
903
- };
904
- }
905
- if (event.type === "legacy.thread.meta-updated") {
906
- const runId = readString(payload, "threadId");
907
- if (!runId) {
908
- return { status: "ignored", snapshot };
909
- }
910
- let nextSnapshot = {
911
- ...snapshot,
912
- runs: patchById(snapshot.runs, runId, (run2) => ({
913
- ...run2,
914
- title: readString(payload, "title") ?? run2.title,
915
- model: readString(payload, "model") ?? run2.model,
916
- branch: readNullableString(payload, "branch") === undefined ? run2.branch : readNullableString(payload, "branch") ?? null,
917
- worktreePath: readNullableString(payload, "worktreePath") === undefined ? run2.worktreePath : readNullableString(payload, "worktreePath") ?? null,
918
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
919
- })),
920
- conversations: patchById(snapshot.conversations, runId, (conversation) => ({
921
- ...conversation,
922
- title: readString(payload, "title") ?? conversation.title,
923
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
924
- }))
925
- };
926
- const run = nextSnapshot.runs.find((candidate) => candidate.id === runId);
927
- if (run?.worktreePath && run.branch) {
928
- nextSnapshot = applyWorktree(nextSnapshot, {
929
- id: worktreeIdFromRunId(run.id),
930
- workspaceId: run.workspaceId,
931
- runId: run.id,
932
- taskId: run.taskId,
933
- branchName: run.branch,
934
- path: run.worktreePath,
935
- status: "active",
936
- createdAt: run.createdAt,
937
- cleanedAt: null
938
- });
939
- }
940
- if (run && run.worktreePath === null) {
941
- nextSnapshot = {
942
- ...nextSnapshot,
943
- worktrees: nextSnapshot.worktrees.filter((worktree) => worktree.runId !== runId)
944
- };
945
- }
946
- return {
947
- status: "applied",
948
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
949
- };
950
- }
951
- if (event.type === "legacy.thread.interaction-mode-set") {
952
- const runId = readString(payload, "threadId");
953
- if (!runId) {
954
- return { status: "ignored", snapshot };
955
- }
956
- return {
957
- status: "applied",
958
- snapshot: withSnapshotMetadata(snapshot, event, {
959
- runs: patchById(snapshot.runs, runId, (run) => ({
960
- ...run,
961
- mode: toRunMode(readString(payload, "interactionMode")),
962
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
963
- }))
964
- })
965
- };
966
- }
967
- if (event.type === "legacy.thread.runtime-mode-set") {
968
- const runId = readString(payload, "threadId");
969
- if (!runId) {
970
- return { status: "ignored", snapshot };
971
- }
972
- const runtimeMode = readString(payload, "runtimeMode");
973
- return {
974
- status: "applied",
975
- snapshot: withSnapshotMetadata(snapshot, event, {
976
- runtimes: patchById(snapshot.runtimes, runId, (runtime) => ({
977
- ...runtime,
978
- sandboxMode: runtimeMode === "approval-required" ? "workspace-write" : "danger-full-access",
979
- updatedAt: readString(payload, "updatedAt") ?? event.createdAt
980
- }))
981
- })
982
- };
983
- }
984
- if (event.type === "legacy.thread.message-sent") {
985
- const runId = readString(payload, "threadId");
986
- const messageId = readString(payload, "messageId");
987
- const role = readString(payload, "role");
988
- const text = readString(payload, "text");
989
- const createdAt = readString(payload, "createdAt");
990
- const updatedAt = readString(payload, "updatedAt");
991
- if (!runId || !messageId || !role || text === undefined || !createdAt || !updatedAt) {
992
- return { status: "ignored", snapshot };
993
- }
994
- const attachments = Array.isArray(payload.attachments) ? payload.attachments : [];
995
- const streaming = payload.streaming === true;
996
- const message = {
997
- id: asMessageId(messageId),
998
- conversationId: conversationIdFromRunId(asRunId(runId)),
999
- role: role === "assistant" || role === "system" ? role : "user",
1000
- text,
1001
- attachments,
1002
- state: streaming ? "streaming" : "completed",
1003
- createdAt,
1004
- completedAt: streaming ? null : updatedAt
1005
- };
1006
- const existingRun = snapshot.runs.find((run) => run.id === runId);
1007
- if (!existingRun) {
1008
- return { status: "ignored", snapshot };
1009
- }
1010
- return {
1011
- status: "applied",
1012
- snapshot: withSnapshotMetadata(ensureConversation({
1013
- ...snapshot,
1014
- messages: upsertById(snapshot.messages, message),
1015
- runs: patchById(snapshot.runs, runId, (run) => ({
1016
- ...run,
1017
- latestMessageId: asMessageId(messageId),
1018
- updatedAt
1019
- }))
1020
- }, existingRun), event, {})
1021
- };
1022
- }
1023
- if (event.type === "legacy.thread.session-set") {
1024
- const runId = readString(event.payload, "threadId");
1025
- const session = readRecord(event.payload, "session");
1026
- if (!runId || !session) {
1027
- return { status: "ignored", snapshot };
1028
- }
1029
- const existingRun = snapshot.runs.find((run) => run.id === runId);
1030
- if (!existingRun) {
1031
- return { status: "ignored", snapshot };
1032
- }
1033
- const sessionUpdatedAt = readString(session, "updatedAt") ?? event.createdAt;
1034
- const providerName = readNullableString(session, "providerName");
1035
- const nextRun = {
1036
- ...existingRun,
1037
- runtimeAdapter: providerName ?? existingRun.runtimeAdapter,
1038
- initialPrompt: existingRun.initialPrompt ?? null,
1039
- activeRuntimeId: runtimeIdFromRunId(asRunId(runId)),
1040
- status: mapLegacySessionStatusToRunStatus(readString(session, "status")),
1041
- errorText: readNullableString(session, "lastError") ?? existingRun.errorText,
1042
- updatedAt: sessionUpdatedAt,
1043
- completedAt: readString(session, "status") === "ready" || readString(session, "status") === "stopped" || readString(session, "status") === "error" ? sessionUpdatedAt : existingRun.completedAt
1044
- };
1045
- let nextSnapshot = applyRun(snapshot, nextRun);
1046
- nextSnapshot = applyRuntime(nextSnapshot, {
1047
- ...snapshot.runtimes.find((runtime) => runtime.runId === runId) ?? makeRuntimeFromRun(nextRun, sessionUpdatedAt),
1048
- workspaceId: existingRun.workspaceId,
1049
- runId: asRunId(runId),
1050
- adapterKind: providerName ?? existingRun.runtimeAdapter,
1051
- status: mapLegacySessionStatusToRuntimeStatus(readString(session, "status")),
1052
- workspaceDir: existingRun.worktreePath,
1053
- isolationMode: existingRun.worktreePath ? "worktree" : "env",
1054
- updatedAt: sessionUpdatedAt,
1055
- exitedAt: readString(session, "status") === "ready" || readString(session, "status") === "stopped" || readString(session, "status") === "error" ? sessionUpdatedAt : null
1056
- });
1057
- return {
1058
- status: "applied",
1059
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
1060
- };
1061
- }
1062
- if (event.type === "legacy.thread.activity-appended") {
1063
- const runId = readString(event.payload, "threadId");
1064
- const activity = readRecord(event.payload, "activity");
1065
- if (!runId || !activity) {
1066
- return { status: "ignored", snapshot };
1067
- }
1068
- const actionId = readString(activity, "id");
1069
- const actionType = readString(activity, "kind");
1070
- const title = readString(activity, "summary");
1071
- const startedAt = readString(activity, "createdAt");
1072
- if (!actionId || !actionType || !title || !startedAt) {
1073
- return { status: "ignored", snapshot };
1074
- }
1075
- const payload2 = readRecord(activity, "payload") ?? {};
1076
- const detail = readString(payload2, "detail") ?? null;
1077
- const action = {
1078
- id: asActionId(actionId),
1079
- runId: asRunId(runId),
1080
- messageId: null,
1081
- actionType,
1082
- title,
1083
- detail,
1084
- state: "completed",
1085
- payload: payload2,
1086
- startedAt,
1087
- completedAt: startedAt
1088
- };
1089
- let nextSnapshot = {
1090
- ...snapshot,
1091
- actions: upsertById(snapshot.actions, action),
1092
- approvals: applyApprovalActivity(snapshot.approvals, asRunId(runId), action),
1093
- runs: patchById(snapshot.runs, runId, (run) => ({
1094
- ...run,
1095
- updatedAt: maxIsoDate(run.updatedAt, startedAt),
1096
- status: actionType === "engine.runtime.ready" ? "running" : actionType === "engine.runtime.failed" ? "failed" : run.status,
1097
- errorText: actionType === "engine.runtime.failed" && detail ? detail : run.errorText,
1098
- completedAt: actionType === "engine.runtime.failed" ? startedAt : run.completedAt
1099
- }))
1100
- };
1101
- nextSnapshot = reconcileRunCounts(nextSnapshot, asRunId(runId));
1102
- return {
1103
- status: "applied",
1104
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
1105
- };
1106
- }
1107
- if (event.type === "legacy.thread.reverted") {
1108
- return { status: "requires-resync", snapshot };
1109
- }
1110
- return { status: "ignored", snapshot };
1111
- }
1112
- function applyEngineEvent(snapshot, event) {
1113
- if (event.sequence <= snapshot.snapshotSequence) {
1114
- return { status: "ignored", snapshot };
1115
- }
1116
- if (event.sequence > snapshot.snapshotSequence + 1) {
1117
- return { status: "requires-resync", snapshot };
1118
- }
1119
- const base = {
1120
- ...snapshot,
1121
- snapshotSequence: event.sequence,
1122
- updatedAt: event.createdAt
1123
- };
1124
- const payload = event.payload && typeof event.payload === "object" ? event.payload : {};
1125
- switch (event.type) {
1126
- case "WorkspaceRegistered":
1127
- case "RigWorkspaceImported": {
1128
- const workspace = {
1129
- id: payload.workspaceId,
1130
- title: payload.title,
1131
- rootPath: payload.rootPath,
1132
- sourceKind: payload.sourceKind,
1133
- defaultModel: payload.defaultModel ?? null,
1134
- topology: undefined,
1135
- remoteFleet: undefined,
1136
- serviceFabric: undefined,
1137
- createdAt: payload.createdAt,
1138
- updatedAt: payload.createdAt
1139
- };
1140
- return {
1141
- status: "applied",
1142
- snapshot: {
1143
- ...base,
1144
- workspaces: upsertById(base.workspaces, workspace)
1145
- }
1146
- };
1147
- }
1148
- case "RigStateHydrated": {
1149
- const workspaceRunIds = new Set([
1150
- ...base.runs.filter((run) => run.workspaceId === payload.workspaceId).map((run) => run.id),
1151
- ...(payload.runs ?? []).map((run) => run.id)
1152
- ]);
1153
- const withoutWorkspaceRunData = (items) => items.filter((item) => !workspaceRunIds.has(item.runId));
1154
- const workspaces = payload.rootPath ? patchById(base.workspaces, payload.workspaceId, (workspace) => ({
1155
- ...workspace,
1156
- rootPath: payload.rootPath,
1157
- updatedAt: payload.createdAt
1158
- })) : base.workspaces;
1159
- return {
1160
- status: "applied",
1161
- snapshot: {
1162
- ...base,
1163
- workspaces,
1164
- graphs: upsertById(base.graphs, payload.graph),
1165
- tasks: replaceWorkspaceSlice(base.tasks, payload.workspaceId, payload.tasks ?? []),
1166
- runs: replaceWorkspaceSlice(base.runs, payload.workspaceId, payload.runs ?? []),
1167
- runtimes: replaceWorkspaceSlice(base.runtimes, payload.workspaceId, payload.runtimes ?? []),
1168
- actions: mergeById(withoutWorkspaceRunData(base.actions), payload.actions ?? []),
1169
- logs: mergeById(withoutWorkspaceRunData(base.logs), payload.logs ?? []),
1170
- userInputs: (base.userInputs ?? []).filter((request) => !workspaceRunIds.has(request.runId)),
1171
- validations: mergeById(withoutWorkspaceRunData(base.validations), payload.validations ?? []),
1172
- reviews: mergeById(withoutWorkspaceRunData(base.reviews), payload.reviews ?? []),
1173
- artifacts: mergeById(withoutWorkspaceRunData(base.artifacts), payload.artifacts ?? []),
1174
- policyDecisions: mergeById(withoutWorkspaceRunData(base.policyDecisions), payload.policyDecisions ?? []),
1175
- queue: [...payload.queue ?? []]
1176
- }
1177
- };
1178
- }
1179
- case "WorkspaceTopologyCompiled": {
1180
- return {
1181
- status: "applied",
1182
- snapshot: {
1183
- ...base,
1184
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => ({
1185
- ...workspace,
1186
- rootPath: payload.rootPath ?? workspace.rootPath,
1187
- topology: payload.topology,
1188
- updatedAt: payload.createdAt
1189
- }))
1190
- }
1191
- };
1192
- }
1193
- case "WorkspaceRemoteFleetSynced": {
1194
- return {
1195
- status: "applied",
1196
- snapshot: {
1197
- ...base,
1198
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => ({
1199
- ...workspace,
1200
- rootPath: payload.rootPath ?? workspace.rootPath,
1201
- remoteFleet: payload.remoteFleet,
1202
- updatedAt: payload.createdAt
1203
- }))
1204
- }
1205
- };
1206
- }
1207
- case "WorkspaceServiceFabricSynced": {
1208
- return {
1209
- status: "applied",
1210
- snapshot: {
1211
- ...base,
1212
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => ({
1213
- ...workspace,
1214
- rootPath: payload.rootPath ?? workspace.rootPath,
1215
- serviceFabric: payload.serviceFabric,
1216
- updatedAt: payload.createdAt
1217
- }))
1218
- }
1219
- };
1220
- }
1221
- case "WorkspaceRemoteHostRegistered": {
1222
- return {
1223
- status: "applied",
1224
- snapshot: {
1225
- ...base,
1226
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => {
1227
- const withoutExisting = (workspace.remoteFleet?.hosts ?? []).filter((host) => host.id !== payload.host.id);
1228
- return {
1229
- ...workspace,
1230
- remoteFleet: buildRemoteFleetSummary({
1231
- fleet: workspace.remoteFleet,
1232
- hosts: [...withoutExisting, payload.host],
1233
- updatedAt: payload.createdAt
1234
- }),
1235
- updatedAt: payload.createdAt
1236
- };
1237
- })
1238
- }
1239
- };
1240
- }
1241
- case "WorkspaceRemoteHostStatusUpdated": {
1242
- return {
1243
- status: "applied",
1244
- snapshot: {
1245
- ...base,
1246
- workspaces: patchById(base.workspaces, payload.workspaceId, (workspace) => {
1247
- const fleet = workspace.remoteFleet;
1248
- if (!fleet) {
1249
- return {
1250
- ...workspace,
1251
- updatedAt: payload.createdAt
1252
- };
1253
- }
1254
- const hosts = fleet.hosts.map((host) => host.id !== payload.hostId ? host : {
1255
- ...host,
1256
- status: payload.status,
1257
- currentLeaseCount: typeof payload.currentLeaseCount === "number" ? payload.currentLeaseCount : host.currentLeaseCount,
1258
- lastHeartbeatAt: payload.lastHeartbeatAt ?? host.lastHeartbeatAt
1259
- });
1260
- return {
1261
- ...workspace,
1262
- remoteFleet: buildRemoteFleetSummary({
1263
- fleet,
1264
- hosts,
1265
- updatedAt: payload.createdAt
1266
- }),
1267
- updatedAt: payload.createdAt
1268
- };
1269
- })
1270
- }
1271
- };
1272
- }
1273
- case "RunCreated": {
1274
- const run = {
1275
- id: payload.runId,
1276
- workspaceId: payload.workspaceId,
1277
- taskId: payload.taskId ?? null,
1278
- title: payload.title,
1279
- runKind: payload.runKind,
1280
- mode: payload.mode,
1281
- runtimeMode: payload.runtimeMode,
1282
- interactionMode: payload.interactionMode,
1283
- status: "created",
1284
- runtimeAdapter: payload.runtimeAdapter,
1285
- model: payload.model ?? null,
1286
- initialPrompt: payload.initialPrompt ?? null,
1287
- executionTarget: payload.executionTarget ?? (payload.remoteHostId ? "remote" : "local"),
1288
- remoteHostId: payload.remoteHostId ?? null,
1289
- remoteLeaseId: null,
1290
- remoteLeaseClaimedAt: null,
1291
- activeRuntimeId: null,
1292
- latestMessageId: null,
1293
- pendingApprovalCount: 0,
1294
- pendingUserInputCount: 0,
1295
- branch: null,
1296
- worktreePath: null,
1297
- errorText: null,
1298
- createdAt: payload.createdAt,
1299
- updatedAt: payload.createdAt,
1300
- startedAt: null,
1301
- completedAt: null
1302
- };
1303
- return {
1304
- status: "applied",
1305
- snapshot: { ...base, runs: upsertById(base.runs, run) }
1306
- };
1307
- }
1308
- case "RunInterrupted":
1309
- case "RunCancelled":
1310
- return {
1311
- status: "applied",
1312
- snapshot: {
1313
- ...base,
1314
- runs: patchById(base.runs, payload.runId, (run) => ({
1315
- ...run,
1316
- status: "cancelled",
1317
- updatedAt: payload.createdAt,
1318
- completedAt: payload.createdAt
1319
- }))
1320
- }
1321
- };
1322
- case "RunCompleted":
1323
- return {
1324
- status: "applied",
1325
- snapshot: {
1326
- ...base,
1327
- runs: patchById(base.runs, payload.runId, (run) => ({
1328
- ...run,
1329
- status: "completed",
1330
- updatedAt: payload.createdAt,
1331
- completedAt: payload.createdAt
1332
- }))
1333
- }
1334
- };
1335
- case "RunStatusSet":
1336
- return {
1337
- status: "applied",
1338
- snapshot: {
1339
- ...base,
1340
- runs: patchById(base.runs, payload.runId, (run) => ({
1341
- ...run,
1342
- status: payload.status,
1343
- updatedAt: payload.createdAt,
1344
- completedAt: null
1345
- }))
1346
- }
1347
- };
1348
- case "RunFailed":
1349
- return {
1350
- status: "applied",
1351
- snapshot: {
1352
- ...base,
1353
- runs: patchById(base.runs, payload.runId, (run) => ({
1354
- ...run,
1355
- status: "failed",
1356
- errorText: payload.errorText ?? null,
1357
- updatedAt: payload.createdAt,
1358
- completedAt: payload.createdAt
1359
- }))
1360
- }
1361
- };
1362
- case "RuntimeModeSet":
1363
- return {
1364
- status: "applied",
1365
- snapshot: {
1366
- ...base,
1367
- runs: patchById(base.runs, payload.runId, (run) => ({
1368
- ...run,
1369
- runtimeMode: payload.runtimeMode,
1370
- updatedAt: payload.createdAt
1371
- }))
1372
- }
1373
- };
1374
- case "InteractionModeSet":
1375
- return {
1376
- status: "applied",
1377
- snapshot: {
1378
- ...base,
1379
- runs: patchById(base.runs, payload.runId, (run) => ({
1380
- ...run,
1381
- interactionMode: payload.interactionMode,
1382
- updatedAt: payload.createdAt
1383
- }))
1384
- }
1385
- };
1386
- case "ConversationAttached": {
1387
- const conversation = {
1388
- id: payload.conversationId,
1389
- runId: payload.runId,
1390
- title: payload.title,
1391
- createdAt: payload.createdAt,
1392
- updatedAt: payload.createdAt
1393
- };
1394
- return {
1395
- status: "applied",
1396
- snapshot: {
1397
- ...base,
1398
- conversations: upsertById(base.conversations, conversation)
1399
- }
1400
- };
1401
- }
1402
- case "MessageAppended": {
1403
- const conversation = base.conversations.find((item) => item.runId === payload.runId);
1404
- if (!conversation) {
1405
- return { status: "ignored", snapshot: base };
1406
- }
1407
- const conversationId = conversation.id;
1408
- const message = {
1409
- id: payload.messageId,
1410
- conversationId,
1411
- role: payload.role,
1412
- text: payload.text,
1413
- attachments: payload.attachments ?? [],
1414
- state: payload.state ?? "completed",
1415
- createdAt: payload.createdAt,
1416
- completedAt: payload.completedAt ?? payload.createdAt
1417
- };
1418
- return {
1419
- status: "applied",
1420
- snapshot: {
1421
- ...base,
1422
- messages: upsertById(base.messages, message),
1423
- runs: patchById(base.runs, payload.runId, (run) => ({
1424
- ...run,
1425
- latestMessageId: payload.messageId,
1426
- updatedAt: payload.createdAt
1427
- }))
1428
- }
1429
- };
1430
- }
1431
- case "ActionStarted": {
1432
- const action = {
1433
- id: payload.actionId,
1434
- runId: payload.runId,
1435
- messageId: payload.messageId ?? null,
1436
- actionType: payload.actionType,
1437
- title: payload.title,
1438
- detail: payload.detail ?? null,
1439
- state: payload.state ?? "running",
1440
- payload: payload.payload ?? null,
1441
- startedAt: payload.startedAt,
1442
- completedAt: null
1443
- };
1444
- return {
1445
- status: "applied",
1446
- snapshot: {
1447
- ...base,
1448
- actions: upsertById(base.actions, action)
1449
- }
1450
- };
1451
- }
1452
- case "ActionCompleted":
1453
- return {
1454
- status: "applied",
1455
- snapshot: {
1456
- ...base,
1457
- actions: patchById(base.actions, payload.actionId, (action) => ({
1458
- ...action,
1459
- state: payload.state,
1460
- detail: payload.detail ?? null,
1461
- payload: payload.payload,
1462
- completedAt: payload.completedAt
1463
- }))
1464
- }
1465
- };
1466
- case "RunLogAppended": {
1467
- const log = {
1468
- id: payload.logId,
1469
- runId: payload.runId,
1470
- title: payload.title,
1471
- detail: payload.detail ?? null,
1472
- tone: payload.tone,
1473
- status: payload.status ?? null,
1474
- payload: payload.payload ?? null,
1475
- createdAt: payload.createdAt
1476
- };
1477
- return {
1478
- status: "applied",
1479
- snapshot: {
1480
- ...base,
1481
- logs: upsertById(base.logs, log),
1482
- runs: patchById(base.runs, payload.runId, (run) => ({
1483
- ...run,
1484
- updatedAt: payload.createdAt
1485
- }))
1486
- }
1487
- };
1488
- }
1489
- case "ValidationRecorded":
1490
- return {
1491
- status: "applied",
1492
- snapshot: {
1493
- ...base,
1494
- validations: upsertById(base.validations, {
1495
- id: payload.id,
1496
- runId: payload.runId,
1497
- taskId: payload.taskId ?? null,
1498
- validatorKey: payload.validatorKey,
1499
- status: payload.status,
1500
- output: payload.output,
1501
- startedAt: payload.startedAt,
1502
- completedAt: payload.completedAt ?? null
1503
- })
1504
- }
1505
- };
1506
- case "ReviewRecorded":
1507
- return {
1508
- status: "applied",
1509
- snapshot: {
1510
- ...base,
1511
- reviews: upsertById(base.reviews, {
1512
- id: payload.id,
1513
- runId: payload.runId,
1514
- taskId: payload.taskId ?? null,
1515
- provider: payload.provider,
1516
- mode: payload.mode,
1517
- status: payload.status,
1518
- summary: payload.summary ?? null,
1519
- output: payload.output,
1520
- createdAt: payload.createdAt,
1521
- completedAt: payload.completedAt ?? null
1522
- })
1523
- }
1524
- };
1525
- case "ArtifactRegistered":
1526
- return {
1527
- status: "applied",
1528
- snapshot: {
1529
- ...base,
1530
- artifacts: upsertById(base.artifacts, {
1531
- id: payload.id,
1532
- runId: payload.runId,
1533
- taskId: payload.taskId ?? null,
1534
- kind: payload.kind,
1535
- label: payload.label,
1536
- path: payload.path ?? null,
1537
- url: payload.url ?? null,
1538
- metadata: payload.metadata ?? {},
1539
- createdAt: payload.createdAt
1540
- })
1541
- }
1542
- };
1543
- case "ApprovalRequested": {
1544
- const run = base.runs.find((item) => item.id === payload.runId);
1545
- const approval = {
1546
- id: payload.requestId,
1547
- runId: payload.runId,
1548
- actionId: payload.actionId ?? null,
1549
- requestKind: payload.requestKind,
1550
- status: "pending",
1551
- payload: payload.payload ?? null,
1552
- createdAt: payload.createdAt,
1553
- resolvedAt: null
1554
- };
1555
- return {
1556
- status: "applied",
1557
- snapshot: {
1558
- ...base,
1559
- approvals: upsertById(base.approvals, approval),
1560
- runs: patchById(base.runs, payload.runId, (item) => ({
1561
- ...item,
1562
- pendingApprovalCount: (run?.pendingApprovalCount ?? 0) + 1,
1563
- status: "waiting-approval",
1564
- updatedAt: payload.createdAt
1565
- }))
1566
- }
1567
- };
1568
- }
1569
- case "ApprovalResolved": {
1570
- const approval = base.approvals.find((item) => item.id === payload.requestId && item.runId === payload.runId);
1571
- if (!approval) {
1572
- return { status: "applied", snapshot: base };
1573
- }
1574
- const run = base.runs.find((item) => item.id === payload.runId);
1575
- const pendingApprovalCount = run ? Math.max(0, Number(run.pendingApprovalCount) - 1) : 0;
1576
- const nextStatus = run?.status === "waiting-approval" && pendingApprovalCount === 0 ? "running" : run?.status;
1577
- return {
1578
- status: "applied",
1579
- snapshot: {
1580
- ...base,
1581
- approvals: patchById(base.approvals, payload.requestId, (item) => ({
1582
- ...item,
1583
- status: "resolved",
1584
- resolvedAt: payload.createdAt
1585
- })),
1586
- runs: patchById(base.runs, payload.runId, (item) => ({
1587
- ...item,
1588
- pendingApprovalCount,
1589
- ...nextStatus ? { status: nextStatus } : {},
1590
- updatedAt: payload.createdAt
1591
- }))
1592
- }
1593
- };
1594
- }
1595
- case "UserInputRequested": {
1596
- const run = base.runs.find((item) => item.id === payload.runId);
1597
- return {
1598
- status: "applied",
1599
- snapshot: {
1600
- ...base,
1601
- userInputs: upsertById(base.userInputs ?? [], {
1602
- id: payload.requestId,
1603
- runId: payload.runId,
1604
- status: "pending",
1605
- payload: payload.payload,
1606
- createdAt: payload.createdAt,
1607
- resolvedAt: null
1608
- }),
1609
- runs: patchById(base.runs, payload.runId, (item) => ({
1610
- ...item,
1611
- pendingUserInputCount: (run?.pendingUserInputCount ?? 0) + 1,
1612
- status: "waiting-user-input",
1613
- updatedAt: payload.createdAt
1614
- }))
1615
- }
1616
- };
1617
- }
1618
- case "UserInputResolved": {
1619
- const run = base.runs.find((item) => item.id === payload.runId);
1620
- const pendingUserInputCount = run ? Math.max(0, Number(run.pendingUserInputCount) - 1) : 0;
1621
- const nextStatus = run?.status === "waiting-user-input" && pendingUserInputCount === 0 ? "running" : run?.status;
1622
- return {
1623
- status: "applied",
1624
- snapshot: {
1625
- ...base,
1626
- userInputs: patchById(base.userInputs ?? [], payload.requestId, (item) => ({
1627
- ...item,
1628
- status: "resolved",
1629
- resolvedAt: payload.createdAt
1630
- })),
1631
- runs: patchById(base.runs, payload.runId, (item) => ({
1632
- ...item,
1633
- pendingUserInputCount,
1634
- ...nextStatus ? { status: nextStatus } : {},
1635
- updatedAt: payload.createdAt
1636
- }))
1637
- }
1638
- };
1639
- }
1640
- case "RuntimePrepared": {
1641
- const runtime = {
1642
- id: payload.runtimeId,
1643
- workspaceId: payload.workspaceId,
1644
- runId: payload.runId,
1645
- adapterKind: payload.adapter,
1646
- executionTarget: payload.executionTarget ?? "local",
1647
- remoteHostId: payload.remoteHostId ?? null,
1648
- status: "prepared",
1649
- sandboxMode: "read-only",
1650
- isolationMode: "none",
1651
- workspaceDir: null,
1652
- homeDir: null,
1653
- tmpDir: null,
1654
- cacheDir: null,
1655
- logsDir: null,
1656
- stateDir: null,
1657
- sessionDir: null,
1658
- sessionLogPath: null,
1659
- pid: null,
1660
- startedAt: null,
1661
- updatedAt: payload.createdAt,
1662
- exitedAt: null
1663
- };
1664
- return {
1665
- status: "applied",
1666
- snapshot: {
1667
- ...base,
1668
- runtimes: upsertById(base.runtimes, runtime),
1669
- runs: patchById(base.runs, payload.runId, (run) => ({
1670
- ...run,
1671
- status: "preparing",
1672
- executionTarget: payload.executionTarget ?? run.executionTarget ?? "local",
1673
- remoteHostId: payload.remoteHostId ?? run.remoteHostId ?? null,
1674
- updatedAt: payload.createdAt
1675
- }))
1676
- }
1677
- };
1678
- }
1679
- case "RunRemoteLeaseClaimed":
1680
- return {
1681
- status: "applied",
1682
- snapshot: {
1683
- ...base,
1684
- runs: patchById(base.runs, payload.runId, (run) => ({
1685
- ...run,
1686
- remoteHostId: payload.hostId,
1687
- remoteLeaseId: payload.leaseId,
1688
- remoteLeaseClaimedAt: payload.createdAt,
1689
- updatedAt: payload.createdAt
1690
- }))
1691
- }
1692
- };
1693
- case "RunRemoteLeaseReleased":
1694
- return {
1695
- status: "applied",
1696
- snapshot: {
1697
- ...base,
1698
- runs: patchById(base.runs, payload.runId, (run) => ({
1699
- ...run,
1700
- remoteLeaseId: null,
1701
- remoteLeaseClaimedAt: null,
1702
- updatedAt: payload.createdAt
1703
- }))
1704
- }
1705
- };
1706
- case "RuntimeAttached": {
1707
- const tasks = payload.taskId == null ? base.tasks : patchById(base.tasks, payload.taskId, (task) => ({
1708
- ...task,
1709
- status: "running",
1710
- updatedAt: payload.createdAt
1711
- }));
1712
- return {
1713
- status: "applied",
1714
- snapshot: {
1715
- ...base,
1716
- runs: patchById(base.runs, payload.runId, (run) => ({
1717
- ...run,
1718
- activeRuntimeId: payload.runtimeId,
1719
- status: "running",
1720
- startedAt: payload.createdAt,
1721
- updatedAt: payload.createdAt
1722
- })),
1723
- runtimes: patchById(base.runtimes, payload.runtimeId, (runtime) => ({
1724
- ...runtime,
1725
- status: "running",
1726
- startedAt: payload.createdAt,
1727
- updatedAt: payload.createdAt
1728
- })),
1729
- tasks
1730
- }
1731
- };
1732
- }
1733
- case "RuntimeDetached":
1734
- return {
1735
- status: "applied",
1736
- snapshot: {
1737
- ...base,
1738
- runs: patchById(base.runs, payload.runId, (run) => ({
1739
- ...run,
1740
- activeRuntimeId: null,
1741
- updatedAt: payload.createdAt
1742
- })),
1743
- runtimes: patchById(base.runtimes, payload.runtimeId, (runtime) => ({
1744
- ...runtime,
1745
- status: "exited",
1746
- exitedAt: payload.createdAt,
1747
- updatedAt: payload.createdAt
1748
- }))
1749
- }
1750
- };
1751
- case "RuntimeMetadataUpdated":
1752
- return {
1753
- status: "applied",
1754
- snapshot: {
1755
- ...base,
1756
- runs: patchById(base.runs, payload.runId, (run) => ({
1757
- ...run,
1758
- ...payload.branch !== undefined ? { branch: payload.branch } : {},
1759
- ...payload.worktreePath !== undefined ? { worktreePath: payload.worktreePath } : {},
1760
- updatedAt: payload.createdAt
1761
- })),
1762
- runtimes: patchById(base.runtimes, payload.runtimeId, (runtime) => ({
1763
- ...runtime,
1764
- ...payload.sandboxMode !== undefined ? { sandboxMode: payload.sandboxMode } : {},
1765
- ...payload.isolationMode !== undefined ? { isolationMode: payload.isolationMode } : {},
1766
- ...payload.workspaceDir !== undefined ? { workspaceDir: payload.workspaceDir } : {},
1767
- ...payload.homeDir !== undefined ? { homeDir: payload.homeDir } : {},
1768
- ...payload.tmpDir !== undefined ? { tmpDir: payload.tmpDir } : {},
1769
- ...payload.cacheDir !== undefined ? { cacheDir: payload.cacheDir } : {},
1770
- ...payload.logsDir !== undefined ? { logsDir: payload.logsDir } : {},
1771
- ...payload.stateDir !== undefined ? { stateDir: payload.stateDir } : {},
1772
- ...payload.sessionDir !== undefined ? { sessionDir: payload.sessionDir } : {},
1773
- ...payload.sessionLogPath !== undefined ? { sessionLogPath: payload.sessionLogPath } : {},
1774
- ...payload.pid !== undefined ? { pid: payload.pid } : {},
1775
- updatedAt: payload.createdAt
1776
- }))
1777
- }
1778
- };
1779
- case "TaskStatusChanged": {
1780
- const queue = payload.status === "queued" ? base.queue : withQueuePositions(base.queue.filter((item) => item.taskId !== payload.taskId));
1781
- return {
1782
- status: "applied",
1783
- snapshot: {
1784
- ...base,
1785
- tasks: patchById(base.tasks, payload.taskId, (task) => ({
1786
- ...task,
1787
- status: payload.status,
1788
- updatedAt: payload.createdAt
1789
- })),
1790
- queue
1791
- }
1792
- };
1793
- }
1794
- case "TaskEnqueued": {
1795
- const entry = {
1796
- taskId: payload.taskId,
1797
- score: payload.score,
1798
- unblockCount: 0,
1799
- position: base.queue.length
1800
- };
1801
- const positionedQueue = withQueuePositions([
1802
- ...base.queue.filter((item) => item.taskId !== payload.taskId),
1803
- entry
1804
- ]);
1805
- return {
1806
- status: "applied",
1807
- snapshot: {
1808
- ...base,
1809
- tasks: patchById(base.tasks, payload.taskId, (task) => ({
1810
- ...task,
1811
- status: "queued",
1812
- updatedAt: payload.createdAt
1813
- })),
1814
- queue: positionedQueue
1815
- }
1816
- };
1817
- }
1818
- case "RemoteEndpointRegistered": {
1819
- const endpoint = payload.endpoint;
1820
- if (!endpoint || !endpoint.id) {
1821
- return { status: "ignored", snapshot: base };
1822
- }
1823
- return {
1824
- status: "applied",
1825
- snapshot: {
1826
- ...base,
1827
- remoteEndpoints: upsertById(base.remoteEndpoints, endpoint)
1828
- }
1829
- };
1830
- }
1831
- case "RemoteEndpointRemoved": {
1832
- const endpointId = payload.endpointId;
1833
- if (!endpointId) {
1834
- return { status: "ignored", snapshot: base };
1835
- }
1836
- return {
1837
- status: "applied",
1838
- snapshot: {
1839
- ...base,
1840
- remoteEndpoints: removeById(base.remoteEndpoints, endpointId),
1841
- remoteConnections: base.remoteConnections.filter((conn) => conn.endpointId !== endpointId),
1842
- remoteOrchestrations: base.remoteOrchestrations.filter((orch) => orch.endpointId !== endpointId)
1843
- }
1844
- };
1845
- }
1846
- case "RemoteEndpointUpdated": {
1847
- const endpointId = payload.endpointId;
1848
- const updates = payload.updates;
1849
- if (!endpointId || !updates) {
1850
- return { status: "ignored", snapshot: base };
1851
- }
1852
- return {
1853
- status: "applied",
1854
- snapshot: {
1855
- ...base,
1856
- remoteEndpoints: patchById(base.remoteEndpoints, endpointId, (endpoint) => ({
1857
- ...endpoint,
1858
- ...updates
1859
- }))
1860
- }
1861
- };
1862
- }
1863
- case "RemoteConnectionChanged": {
1864
- const endpointId = payload.endpointId;
1865
- const status = payload.status;
1866
- if (!endpointId || !status) {
1867
- return { status: "ignored", snapshot: base };
1868
- }
1869
- const existing = base.remoteConnections.find((conn) => conn.endpointId === endpointId);
1870
- const connection = {
1871
- ...existing ?? {
1872
- endpointId,
1873
- status,
1874
- error: null,
1875
- connectedAt: null,
1876
- tokenExpiresAt: null,
1877
- latencyMs: null,
1878
- subscribedEvents: []
1879
- },
1880
- endpointId,
1881
- status,
1882
- ...payload.error !== undefined ? { error: payload.error ?? null } : {},
1883
- ...status === "connected" ? { connectedAt: event.createdAt } : {}
1884
- };
1885
- return {
1886
- status: "applied",
1887
- snapshot: {
1888
- ...base,
1889
- remoteConnections: upsertByKey(base.remoteConnections, connection, "endpointId")
1890
- }
1891
- };
1892
- }
1893
- case "RemoteWorkspaceHydrated": {
1894
- const endpointId = payload.endpointId;
1895
- const workspace = payload.workspace;
1896
- const tasks = payload.tasks;
1897
- const graph = payload.graph;
1898
- if (!endpointId || !workspace) {
1899
- return { status: "ignored", snapshot: base };
1900
- }
1901
- return {
1902
- status: "applied",
1903
- snapshot: {
1904
- ...base,
1905
- workspaces: upsertById(base.workspaces, workspace),
1906
- tasks: Array.isArray(tasks) ? replaceWorkspaceSlice(base.tasks, workspace.id, tasks) : base.tasks,
1907
- graphs: graph ? upsertById(base.graphs, graph) : base.graphs
1908
- }
1909
- };
1910
- }
1911
- case "RemoteStateRefreshed": {
1912
- const endpointId = payload.endpointId;
1913
- const tasks = payload.tasks;
1914
- if (!endpointId) {
1915
- return { status: "ignored", snapshot: base };
1916
- }
1917
- const remoteWorkspace = base.workspaces.find((ws) => ws.id === `remote-workspace:${endpointId}`);
1918
- return {
1919
- status: "applied",
1920
- snapshot: {
1921
- ...base,
1922
- tasks: Array.isArray(tasks) && remoteWorkspace ? replaceWorkspaceSlice(base.tasks, remoteWorkspace.id, tasks) : base.tasks
1923
- }
1924
- };
1925
- }
1926
- case "RemoteEventReceived":
1927
- return { status: "applied", snapshot: base };
1928
- case "RemoteOrchestrationStarted": {
1929
- const orchestration = payload.orchestration;
1930
- if (!orchestration || !orchestration.orchestrationId) {
1931
- return { status: "ignored", snapshot: base };
1932
- }
1933
- return {
1934
- status: "applied",
1935
- snapshot: {
1936
- ...base,
1937
- remoteOrchestrations: upsertByKey(base.remoteOrchestrations, orchestration, "orchestrationId")
1938
- }
1939
- };
1940
- }
1941
- case "RemoteOrchestrationUpdated": {
1942
- const orchestrationId = payload.orchestrationId;
1943
- const state = payload.state;
1944
- if (!orchestrationId) {
1945
- return { status: "ignored", snapshot: base };
1946
- }
1947
- return {
1948
- status: "applied",
1949
- snapshot: {
1950
- ...base,
1951
- remoteOrchestrations: patchByKey(base.remoteOrchestrations, orchestrationId, "orchestrationId", (orch) => ({
1952
- ...orch,
1953
- ...isRecord(state) ? state : {}
1954
- }))
1955
- }
1956
- };
1957
- }
1958
- }
1959
- if (event.type === "workspace.imported") {
1960
- return { status: "requires-resync", snapshot };
1961
- }
1962
- if (event.type === "task.run-linked") {
1963
- const payload2 = isRecord(event.payload) ? event.payload : {};
1964
- const runId = readString(payload2, "runId");
1965
- const taskId = readString(payload2, "taskId");
1966
- const workspaceId = readString(payload2, "workspaceId");
1967
- if (!runId || !taskId || !workspaceId) {
1968
- return { status: "ignored", snapshot };
1969
- }
1970
- const baseRun = snapshot.runs.find((run) => run.id === runId) ?? {
1971
- id: asRunId(runId),
1972
- workspaceId: asWorkspaceId(workspaceId),
1973
- taskId: asTaskId(taskId),
1974
- title: "Task run",
1975
- runKind: "task",
1976
- mode: "interactive",
1977
- runtimeMode: "full-access",
1978
- interactionMode: "default",
1979
- status: "created",
1980
- runtimeAdapter: "rig-native",
1981
- model: null,
1982
- initialPrompt: null,
1983
- activeRuntimeId: runtimeIdFromRunId(asRunId(runId)),
1984
- latestMessageId: null,
1985
- pendingApprovalCount: 0,
1986
- pendingUserInputCount: 0,
1987
- branch: null,
1988
- worktreePath: null,
1989
- errorText: null,
1990
- createdAt: event.createdAt,
1991
- updatedAt: event.createdAt,
1992
- startedAt: event.createdAt,
1993
- completedAt: null
1994
- };
1995
- const nextSnapshot = applyRun(snapshot, {
1996
- ...baseRun,
1997
- workspaceId: asWorkspaceId(workspaceId),
1998
- taskId: asTaskId(taskId),
1999
- runKind: "task",
2000
- runtimeAdapter: "rig-native",
2001
- activeRuntimeId: runtimeIdFromRunId(asRunId(runId)),
2002
- updatedAt: event.createdAt
2003
- });
2004
- return {
2005
- status: "applied",
2006
- snapshot: withSnapshotMetadata(nextSnapshot, event, {})
2007
- };
2008
- }
2009
- if (event.type.startsWith("runtime.prepare.")) {
2010
- return applySyntheticRuntimePreparation(snapshot, event);
2011
- }
2012
- if (event.type === "runtime.prepared") {
2013
- return applySyntheticRuntimePrepared(snapshot, event);
2014
- }
2015
- if (event.type.startsWith("legacy.project.")) {
2016
- return applyLegacyProjectEvent(snapshot, event);
2017
- }
2018
- if (event.type.startsWith("legacy.thread.")) {
2019
- return applyLegacyThreadEvent(snapshot, event);
2020
- }
2021
- return { status: "ignored", snapshot: base };
2022
- }
2023
- function applyEngineEvents(snapshot, events) {
2024
- let nextSnapshot = snapshot;
2025
- let requiresResync = false;
2026
- let applied = false;
2027
- for (const event of events) {
2028
- const result = applyEngineEvent(nextSnapshot, event);
2029
- nextSnapshot = result.snapshot;
2030
- if (result.status === "requires-resync") {
2031
- requiresResync = true;
2032
- }
2033
- if (result.status === "applied") {
2034
- applied = true;
2035
- }
259
+ // packages/core/src/capability.ts
260
+ import { resolveCapability } from "@rig/kernel-seed";
261
+
262
+ class CapabilityProviderMissingError extends Error {
263
+ capabilityId;
264
+ name = "CapabilityProviderMissingError";
265
+ constructor(capabilityId) {
266
+ super(`No provider resolved for capability "${capabilityId}"`);
267
+ this.capabilityId = capabilityId;
268
+ }
269
+ }
270
+ function isFactory(impl) {
271
+ return typeof impl === "function";
272
+ }
273
+ function defineCapability(id) {
274
+ const idString = String(id);
275
+ const tag = idString;
276
+ async function resolve(host) {
277
+ const providers = host.listExecutableCapabilities().filter((c) => c.id === idString && typeof c.run === "function").map((c) => ({
278
+ name: c.id,
279
+ provides: [c.id],
280
+ capabilityProviders: { [c.id]: c.run }
281
+ }));
282
+ const resolution = resolveCapability(providers, tag);
283
+ const run = resolution?.value;
284
+ if (!run)
285
+ return null;
286
+ return await run(undefined);
2036
287
  }
2037
288
  return {
2038
- status: requiresResync ? "requires-resync" : applied ? "applied" : "ignored",
2039
- snapshot: nextSnapshot
2040
- };
2041
- }
2042
- function pruneQueueEntries(snapshot) {
2043
- return snapshot.queue.filter((entry) => {
2044
- const task = snapshot.tasks.find((candidate) => candidate.id === entry.taskId);
2045
- return task?.status !== "running" && task?.status !== "in_progress" && task?.status !== "under_review";
2046
- });
2047
- }
2048
- // packages/core/src/rigSelectors.ts
2049
- function selectWorkspaces(snapshot) {
2050
- return snapshot?.workspaces ?? [];
2051
- }
2052
- function selectPrimaryWorkspace(snapshot) {
2053
- const workspaces = selectWorkspaces(snapshot);
2054
- return workspaces.find((workspace) => workspace.sourceKind === "rig-import") ?? workspaces[0] ?? null;
2055
- }
2056
- function pickDefaultWorkspaceId(snapshot) {
2057
- return selectPrimaryWorkspace(snapshot)?.id ?? null;
2058
- }
2059
- function selectWorkspace(snapshot, workspaceId) {
2060
- if (!workspaceId)
2061
- return null;
2062
- return snapshot?.workspaces.find((workspace) => workspace.id === workspaceId) ?? null;
2063
- }
2064
- function selectTask(snapshot, taskId) {
2065
- if (!taskId)
2066
- return null;
2067
- return snapshot?.tasks.find((task) => task.id === taskId) ?? null;
2068
- }
2069
- function selectTasksByWorkspace(snapshot, workspaceId) {
2070
- if (!workspaceId)
2071
- return [];
2072
- return snapshot?.tasks.filter((task) => task.workspaceId === workspaceId) ?? [];
2073
- }
2074
- var selectTasksForWorkspace = selectTasksByWorkspace;
2075
- function selectTasksByStatus(snapshot, status) {
2076
- return snapshot?.tasks.filter((task) => task.status === status) ?? [];
2077
- }
2078
- function projectTaskStatusForGrouping(status) {
2079
- switch (status) {
2080
- case "failed":
2081
- return "ready";
2082
- case "closed":
2083
- return "completed";
2084
- case "running":
2085
- case "in_progress":
2086
- case "under_review":
2087
- return "running";
2088
- default:
2089
- return status;
2090
- }
2091
- }
2092
- var STATUS_PRIORITY = [
2093
- "running",
2094
- "blocked",
2095
- "queued",
2096
- "ready",
2097
- "open",
2098
- "draft",
2099
- "unknown",
2100
- "cancelled",
2101
- "completed"
2102
- ];
2103
- function selectTasksGroupedByStatus(snapshot, workspaceId) {
2104
- const tasks = selectTasksByWorkspace(snapshot, workspaceId);
2105
- const byStatus = new Map;
2106
- for (const task of tasks) {
2107
- const projectedStatus = projectTaskStatusForGrouping(task.status);
2108
- const group = byStatus.get(projectedStatus);
2109
- if (group) {
2110
- group.push(task);
2111
- } else {
2112
- byStatus.set(projectedStatus, [task]);
2113
- }
2114
- }
2115
- const result = [];
2116
- for (const status of STATUS_PRIORITY) {
2117
- const group = byStatus.get(status);
2118
- if (group && group.length > 0) {
2119
- result.push({ status, tasks: group });
289
+ id,
290
+ provide(impl, opts) {
291
+ return {
292
+ id: idString,
293
+ title: opts?.title ?? idString,
294
+ ...opts?.description !== undefined ? { description: opts.description } : {},
295
+ run: isFactory(impl) ? () => impl() : () => impl
296
+ };
297
+ },
298
+ resolve,
299
+ async require(host) {
300
+ const value = await resolve(host);
301
+ if (value === null)
302
+ throw new CapabilityProviderMissingError(idString);
303
+ return value;
2120
304
  }
2121
- }
2122
- const overflowStatuses = Array.from(byStatus.keys()).filter((status) => !STATUS_PRIORITY.includes(status)).sort();
2123
- for (const status of overflowStatuses) {
2124
- const group = byStatus.get(status);
2125
- if (group && group.length > 0) {
2126
- result.push({ status, tasks: group });
2127
- }
2128
- }
2129
- return result;
2130
- }
2131
- function selectRun(snapshot, runId) {
2132
- if (!runId)
2133
- return null;
2134
- return snapshot?.runs.find((run) => run.id === runId) ?? null;
2135
- }
2136
- function selectRunsByTask(snapshot, taskId) {
2137
- if (!taskId)
2138
- return [];
2139
- return snapshot?.runs.filter((run) => run.taskId === taskId) ?? [];
2140
- }
2141
- var selectRunsForTask = selectRunsByTask;
2142
- function selectRunsForWorkspace(snapshot, workspaceId) {
2143
- if (!workspaceId)
2144
- return [];
2145
- return snapshot?.runs.filter((run) => run.workspaceId === workspaceId) ?? [];
2146
- }
2147
- function selectAdhocRuns(snapshot) {
2148
- return snapshot?.runs.filter((run) => run.taskId === null) ?? [];
2149
- }
2150
- function selectAdhocRunsForWorkspace(snapshot, workspaceId) {
2151
- if (!workspaceId)
2152
- return [];
2153
- return snapshot?.runs.filter((run) => run.taskId === null && run.workspaceId === workspaceId) ?? [];
2154
- }
2155
- function selectGraphsForWorkspace(snapshot, workspaceId) {
2156
- if (!workspaceId)
2157
- return [];
2158
- return snapshot?.graphs.filter((graph) => graph.workspaceId === workspaceId) ?? [];
2159
- }
2160
- function selectQueueForWorkspace(snapshot, workspaceId) {
2161
- if (!snapshot || !workspaceId)
2162
- return [];
2163
- const taskIds = new Set(selectTasksByWorkspace(snapshot, workspaceId).map((task) => task.id));
2164
- return snapshot.queue.filter((entry) => taskIds.has(entry.taskId));
2165
- }
2166
- function selectPendingApprovals(snapshot) {
2167
- return snapshot?.approvals.filter((approval) => approval.status === "pending") ?? [];
2168
- }
2169
- function selectApprovalsForRun(snapshot, runId) {
2170
- if (!runId)
2171
- return [];
2172
- return snapshot?.approvals.filter((approval) => approval.runId === runId) ?? [];
2173
- }
2174
- function selectApprovalsForWorkspace(snapshot, workspaceId) {
2175
- if (!snapshot || !workspaceId)
2176
- return [];
2177
- const runIds = new Set(selectRunsForWorkspace(snapshot, workspaceId).map((run) => run.id));
2178
- return snapshot.approvals.filter((approval) => runIds.has(approval.runId));
2179
- }
2180
- function selectUserInputsForRun(snapshot, runId) {
2181
- if (!runId)
2182
- return [];
2183
- return (snapshot?.userInputs ?? []).filter((request) => request.runId === runId);
2184
- }
2185
- function selectUserInputsForWorkspace(snapshot, workspaceId) {
2186
- if (!snapshot || !workspaceId)
2187
- return [];
2188
- const runIds = new Set(selectRunsForWorkspace(snapshot, workspaceId).map((run) => run.id));
2189
- return (snapshot.userInputs ?? []).filter((request) => runIds.has(request.runId));
2190
- }
2191
- // packages/core/src/taskGraph.ts
2192
- function isObjectRecord(value) {
2193
- return typeof value === "object" && value !== null && !Array.isArray(value);
2194
- }
2195
- function readTaskMetadataStringList(task, key) {
2196
- const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
2197
- const value = metadata?.[key];
2198
- return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
2199
- }
2200
- function readTaskSourceIssueId(task) {
2201
- const metadata = isObjectRecord(task.metadata) ? task.metadata : null;
2202
- return typeof metadata?.sourceIssueId === "string" && metadata.sourceIssueId.length > 0 ? metadata.sourceIssueId : null;
2203
- }
2204
- function resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId) {
2205
- if (tasksById.has(ref))
2206
- return ref;
2207
- return taskIdBySourceIssueId.get(ref) ?? taskIdByExternalRef.get(ref) ?? null;
2208
- }
2209
- function buildTaskReferenceIndex(tasks) {
2210
- return {
2211
- tasksById: new Map(tasks.map((task) => [task.id, task])),
2212
- taskIdByExternalRef: new Map(tasks.flatMap((task) => task.externalId ? [[task.externalId, task.id]] : [])),
2213
- taskIdBySourceIssueId: new Map(tasks.flatMap((task) => {
2214
- const sourceIssueId = readTaskSourceIssueId(task);
2215
- return sourceIssueId ? [[sourceIssueId, task.id]] : [];
2216
- }))
2217
- };
2218
- }
2219
- function computeTaskBlockingDepths(tasks) {
2220
- const { tasksById, taskIdByExternalRef, taskIdBySourceIssueId } = buildTaskReferenceIndex(tasks);
2221
- const memo = new Map;
2222
- const visit = (taskId, stack) => {
2223
- const cached = memo.get(taskId);
2224
- if (cached !== undefined)
2225
- return cached;
2226
- if (stack.has(taskId))
2227
- return 0;
2228
- const task = tasksById.get(taskId);
2229
- if (!task)
2230
- return 0;
2231
- stack.add(taskId);
2232
- const refs = [
2233
- ...readTaskMetadataStringList(task, "dependencies"),
2234
- ...readTaskMetadataStringList(task, "parentChildDeps")
2235
- ];
2236
- const blockers = refs.map((ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId)).filter((ref) => ref !== null && ref !== taskId);
2237
- const depth = blockers.length === 0 ? 0 : Math.max(...blockers.map((blockerId) => visit(blockerId, stack) + 1));
2238
- stack.delete(taskId);
2239
- memo.set(taskId, depth);
2240
- return depth;
2241
- };
2242
- for (const task of tasks) {
2243
- visit(task.id, new Set);
2244
- }
2245
- return memo;
2246
- }
2247
- // packages/core/src/taskGraphCodes.ts
2248
- var TASK_CODE_RE = /^\[([A-Z0-9]+(?:-[A-Z0-9]+)*)\]\s*/;
2249
- function extractTaskCode(title) {
2250
- const match = title.match(TASK_CODE_RE);
2251
- return match?.[1] ?? null;
2252
- }
2253
- function extractTaskGroupKey(title) {
2254
- const code = extractTaskCode(title);
2255
- if (!code)
2256
- return null;
2257
- const parts = code.split("-");
2258
- const suffix = parts.at(-1) ?? "";
2259
- if (/^\d+$/.test(suffix)) {
2260
- return parts.slice(0, -1).join("-");
2261
- }
2262
- return parts[0] ?? code;
2263
- }
2264
- function stripTaskCode(label) {
2265
- return label.replace(TASK_CODE_RE, "");
2266
- }
2267
- // packages/core/src/taskGraphLayout.ts
2268
- var CARD_WIDTH = 200;
2269
- var CARD_HEIGHT = 110;
2270
- var CELL_V_PAD = 12;
2271
- var CELL_H_PAD = 12;
2272
- var ROW_GAP = 28;
2273
- var COL_GAP = 40;
2274
- var LANE_LABEL_W = 120;
2275
- var STAGE_HDR_H = 32;
2276
- var PALETTE = [
2277
- { bg: "#3a2d12", border: "#8d6b19", edge: "#d6a11d" },
2278
- { bg: "#102642", border: "#245fbf", edge: "#66a2ff" },
2279
- { bg: "#2c173f", border: "#7b39d4", edge: "#a76df5" },
2280
- { bg: "#112d1c", border: "#2d8f4e", edge: "#62d882" },
2281
- { bg: "#3a2314", border: "#c86d1c", edge: "#e69654" },
2282
- { bg: "#31152b", border: "#bf3d88", edge: "#f07ebb" },
2283
- { bg: "#132c35", border: "#1783a6", edge: "#53c4e5" },
2284
- { bg: "#26310f", border: "#6d9a19", edge: "#a7da42" }
2285
- ];
2286
- function isObjectRecord2(value) {
2287
- return typeof value === "object" && value !== null && !Array.isArray(value);
2288
- }
2289
- function readIssueType(task) {
2290
- const metadata = isObjectRecord2(task.metadata) ? task.metadata : null;
2291
- return typeof metadata?.issueType === "string" ? metadata.issueType : null;
2292
- }
2293
- function isGraphTask(task) {
2294
- return readIssueType(task) !== "epic";
2295
- }
2296
- function findEpicAncestor(task, resolve, tasksById) {
2297
- const visited = new Set;
2298
- let current = task;
2299
- let epic = null;
2300
- while (!visited.has(current.id)) {
2301
- visited.add(current.id);
2302
- const parentRef = readTaskMetadataStringList(current, "parentChildDeps")[0];
2303
- if (!parentRef)
2304
- break;
2305
- const parentId = resolve(parentRef);
2306
- if (!parentId)
2307
- break;
2308
- const parent = tasksById.get(parentId);
2309
- if (!parent)
2310
- break;
2311
- if (readIssueType(parent) === "epic") {
2312
- epic = parent;
2313
- }
2314
- current = parent;
2315
- }
2316
- return epic;
2317
- }
2318
- function getRowKey(task, resolve, tasksById) {
2319
- const epic = findEpicAncestor(task, resolve, tasksById);
2320
- if (epic) {
2321
- return `group:${epic.id}`;
2322
- }
2323
- const codeGroup = extractTaskGroupKey(task.title);
2324
- if (codeGroup) {
2325
- return `code:${codeGroup}`;
2326
- }
2327
- return `type:${readIssueType(task) ?? "task"}`;
2328
- }
2329
- function getRowLabel(task, rowKey, resolve, tasksById) {
2330
- if (rowKey.startsWith("group:")) {
2331
- const groupId = rowKey.slice("group:".length);
2332
- return tasksById.get(groupId)?.title ?? groupId;
2333
- }
2334
- if (rowKey.startsWith("code:")) {
2335
- return rowKey.slice("code:".length);
2336
- }
2337
- const code = extractTaskCode(task.title);
2338
- if (code)
2339
- return code;
2340
- const issueType = readIssueType(task);
2341
- if (issueType === "task")
2342
- return "Tasks";
2343
- if (issueType)
2344
- return `${issueType[0]?.toUpperCase() ?? ""}${issueType.slice(1)}`;
2345
- const parentRef = readTaskMetadataStringList(task, "parentChildDeps")[0];
2346
- if (parentRef) {
2347
- const parentId = resolve(parentRef);
2348
- if (parentId) {
2349
- return tasksById.get(parentId)?.title ?? "Tasks";
2350
- }
2351
- }
2352
- return "Tasks";
2353
- }
2354
- function computeDepths(ids, edges) {
2355
- const blockers = new Map;
2356
- for (const edge of edges) {
2357
- if (!ids.has(edge.source) || !ids.has(edge.target))
2358
- continue;
2359
- const current = blockers.get(edge.target) ?? [];
2360
- current.push(edge.source);
2361
- blockers.set(edge.target, current);
2362
- }
2363
- const memo = new Map;
2364
- const visit = (id, stack) => {
2365
- const cached = memo.get(id);
2366
- if (cached !== undefined)
2367
- return cached;
2368
- if (stack.has(id))
2369
- return 0;
2370
- stack.add(id);
2371
- const deps = blockers.get(id);
2372
- if (!deps || deps.length === 0) {
2373
- memo.set(id, 0);
2374
- return 0;
2375
- }
2376
- let maxDepth = 0;
2377
- for (const dep of deps) {
2378
- maxDepth = Math.max(maxDepth, visit(dep, stack) + 1);
2379
- }
2380
- stack.delete(id);
2381
- memo.set(id, maxDepth);
2382
- return maxDepth;
2383
305
  };
2384
- for (const id of ids) {
2385
- visit(id, new Set);
2386
- }
2387
- return memo;
2388
306
  }
2389
- function buildTaskGraphLayout(snapshot, tasks, options) {
2390
- const showParentChild = options?.showParentChild ?? false;
2391
- const graphTasks = tasks.filter(isGraphTask);
2392
- if (graphTasks.length === 0) {
2393
- return {
2394
- lanes: [],
2395
- stages: [],
2396
- nodes: [],
2397
- edges: [],
2398
- totalWidth: 0,
2399
- totalHeight: 0,
2400
- taskCount: 0
2401
- };
2402
- }
2403
- const { tasksById, taskIdByExternalRef, taskIdBySourceIssueId } = buildTaskReferenceIndex(tasks);
2404
- const resolve = (ref) => resolveTaskReference(ref, tasksById, taskIdByExternalRef, taskIdBySourceIssueId);
2405
- const rows = new Map;
2406
- const rowLabelByKey = new Map;
2407
- for (const task of graphTasks) {
2408
- const rowKey = getRowKey(task, resolve, tasksById);
2409
- const current = rows.get(rowKey) ?? [];
2410
- current.push(task);
2411
- rows.set(rowKey, current);
2412
- if (!rowLabelByKey.has(rowKey)) {
2413
- rowLabelByKey.set(rowKey, getRowLabel(task, rowKey, resolve, tasksById));
2414
- }
2415
- }
2416
- const orderedRows = [...rows.entries()].sort((left, right) => {
2417
- if (left[1].length !== right[1].length)
2418
- return right[1].length - left[1].length;
2419
- return (rowLabelByKey.get(left[0]) ?? left[0]).localeCompare(rowLabelByKey.get(right[0]) ?? right[0]);
2420
- });
2421
- const rowIndexByKey = new Map(orderedRows.map(([key], index) => [key, index]));
2422
- const rowIndexByTaskId = new Map;
2423
- for (const task of graphTasks) {
2424
- rowIndexByTaskId.set(task.id, rowIndexByKey.get(getRowKey(task, resolve, tasksById)) ?? 0);
2425
- }
2426
- const blockingEdgesRaw = [];
2427
- const depsIn = new Map;
2428
- const depsOut = new Map;
2429
- const edges = [];
2430
- for (const task of graphTasks) {
2431
- for (const ref of readTaskMetadataStringList(task, "dependencies")) {
2432
- const sourceId = resolve(ref);
2433
- if (!sourceId)
2434
- continue;
2435
- blockingEdgesRaw.push({ source: sourceId, target: task.id });
2436
- depsOut.set(sourceId, (depsOut.get(sourceId) ?? 0) + 1);
2437
- depsIn.set(task.id, (depsIn.get(task.id) ?? 0) + 1);
2438
- const color = PALETTE[(rowIndexByTaskId.get(sourceId) ?? 0) % PALETTE.length].edge;
2439
- edges.push({
2440
- id: `blocking:${sourceId}:${task.id}`,
2441
- sourceId,
2442
- targetId: task.id,
2443
- color,
2444
- kind: "blocking"
2445
- });
2446
- }
2447
- if (!showParentChild)
2448
- continue;
2449
- for (const ref of readTaskMetadataStringList(task, "parentChildDeps")) {
2450
- const sourceId = resolve(ref);
2451
- if (!sourceId)
2452
- continue;
2453
- edges.push({
2454
- id: `parent:${sourceId}:${task.id}`,
2455
- sourceId,
2456
- targetId: task.id,
2457
- color: "#5b6472",
2458
- kind: "parent-child"
2459
- });
2460
- }
2461
- }
2462
- const graphTaskIds = new Set(graphTasks.map((task) => task.id));
2463
- const depths = computeDepths(graphTaskIds, blockingEdgesRaw);
2464
- const maxStage = Math.max(0, ...depths.values());
2465
- const rowMaxStack = new Array(orderedRows.length).fill(0);
2466
- const cells = new Map;
2467
- for (const task of graphTasks) {
2468
- const rowIndex = rowIndexByTaskId.get(task.id) ?? 0;
2469
- const stage = depths.get(task.id) ?? 0;
2470
- const key = `${rowIndex}:${stage}`;
2471
- const current = cells.get(key) ?? [];
2472
- current.push(task);
2473
- cells.set(key, current);
2474
- }
2475
- for (const [cellKey, cellTasks] of cells) {
2476
- const [rowIndexText] = cellKey.split(":");
2477
- const rowIndex = Number.parseInt(rowIndexText ?? "0", 10) || 0;
2478
- cellTasks.sort((left, right) => {
2479
- const leftFanout = depsOut.get(left.id) ?? 0;
2480
- const rightFanout = depsOut.get(right.id) ?? 0;
2481
- if (leftFanout !== rightFanout)
2482
- return rightFanout - leftFanout;
2483
- return left.title.localeCompare(right.title);
2484
- });
2485
- rowMaxStack[rowIndex] = Math.max(rowMaxStack[rowIndex] ?? 0, cellTasks.length);
2486
- }
2487
- const rowHeights = rowMaxStack.map((count) => Math.max(count, 1) * (CARD_HEIGHT + CELL_V_PAD) - CELL_V_PAD + CELL_V_PAD * 2);
2488
- const colWidths = new Array(maxStage + 1).fill(CARD_WIDTH + CELL_H_PAD * 2);
2489
- const colX = [];
2490
- let currentX = LANE_LABEL_W;
2491
- for (let index = 0;index <= maxStage; index += 1) {
2492
- colX.push(currentX);
2493
- currentX += (colWidths[index] ?? 0) + COL_GAP;
2494
- }
2495
- const totalWidth = currentX - COL_GAP;
2496
- const rowY = [];
2497
- let currentY = STAGE_HDR_H;
2498
- for (let rowIndex = 0;rowIndex < orderedRows.length; rowIndex += 1) {
2499
- rowY.push(currentY);
2500
- currentY += (rowHeights[rowIndex] ?? 0) + ROW_GAP;
2501
- }
2502
- const totalHeight = currentY - ROW_GAP;
2503
- const lanes = orderedRows.map(([rowKey, rowTasks], rowIndex) => ({
2504
- key: rowKey,
2505
- label: rowLabelByKey.get(rowKey) ?? rowKey,
2506
- rowIndex,
2507
- x: LANE_LABEL_W - 6,
2508
- y: (rowY[rowIndex] ?? 0) - 6,
2509
- width: totalWidth - LANE_LABEL_W + 12,
2510
- height: (rowHeights[rowIndex] ?? 0) + 12,
2511
- color: PALETTE[rowIndex % PALETTE.length].border,
2512
- taskCount: rowTasks.length
2513
- }));
2514
- const stages = Array.from({ length: maxStage + 1 }, (_, index) => ({
2515
- index,
2516
- label: index === 0 ? "Roots" : `Stage ${index}`,
2517
- x: (colX[index] ?? 0) + CELL_H_PAD,
2518
- width: CARD_WIDTH
2519
- }));
2520
- const pendingApprovalRunIds = new Set(selectPendingApprovals(snapshot).map((approval) => approval.runId));
2521
- const nodes = [];
2522
- for (const [rowIndex, [rowKey]] of orderedRows.entries()) {
2523
- for (let stage = 0;stage <= maxStage; stage += 1) {
2524
- const cellTasks = cells.get(`${rowIndex}:${stage}`) ?? [];
2525
- const baseX = (colX[stage] ?? 0) + CELL_H_PAD;
2526
- const baseY = (rowY[rowIndex] ?? 0) + CELL_V_PAD;
2527
- const palette = PALETTE[rowIndex % PALETTE.length];
2528
- for (const [stackIndex, task] of cellTasks.entries()) {
2529
- const runs = selectRunsByTask(snapshot, task.id);
2530
- const runIds = new Set(runs.map((run) => run.id));
2531
- const hasApprovals = runs.some((run) => pendingApprovalRunIds.has(run.id));
2532
- const hasPendingUserInput = runs.some((run) => selectUserInputsForRun(snapshot, run.id).some((request) => request.status === "pending"));
2533
- const hasRejectedReview = (snapshot?.reviews ?? []).some((review) => runIds.has(review.runId) && review.status === "rejected");
2534
- const hasFailedValidations = (snapshot?.validations ?? []).some((validation) => runIds.has(validation.runId) && validation.status === "failed");
2535
- const artifactCount = (snapshot?.artifacts ?? []).filter((artifact) => runIds.has(artifact.runId)).length;
2536
- nodes.push({
2537
- id: task.id,
2538
- task,
2539
- rowKey,
2540
- rowLabel: rowLabelByKey.get(rowKey) ?? rowKey,
2541
- rowIndex,
2542
- stage,
2543
- x: baseX,
2544
- y: baseY + stackIndex * (CARD_HEIGHT + CELL_V_PAD),
2545
- width: CARD_WIDTH,
2546
- height: CARD_HEIGHT,
2547
- color: palette.border,
2548
- taskCode: extractTaskCode(task.title),
2549
- strippedTitle: stripTaskCode(task.title),
2550
- depsIn: depsIn.get(task.id) ?? 0,
2551
- depsOut: depsOut.get(task.id) ?? 0,
2552
- runCount: runs.length,
2553
- hasApprovals,
2554
- hasPendingUserInput,
2555
- hasRejectedReview,
2556
- hasFailedValidations,
2557
- artifactCount
2558
- });
2559
- }
2560
- }
2561
- }
307
+ function defineServiceCapability(id) {
308
+ const capability = defineCapability(id);
2562
309
  return {
2563
- lanes,
2564
- stages,
2565
- nodes,
2566
- edges,
2567
- totalWidth,
2568
- totalHeight,
2569
- taskCount: graphTasks.length
310
+ ...capability,
311
+ provideService: capability.provide,
312
+ resolveService: capability.resolve,
313
+ requireService: capability.require
2570
314
  };
2571
315
  }
2572
316
 
2573
317
  // packages/core/src/index.ts
2574
318
  var RIG_CORE_PACKAGE = "@rig/core";
2575
319
  export {
2576
- stripTaskCode,
2577
- selectWorkspaces,
2578
- selectWorkspace,
2579
- selectUserInputsForWorkspace,
2580
- selectUserInputsForRun,
2581
- selectTasksGroupedByStatus,
2582
- selectTasksForWorkspace,
2583
- selectTasksByWorkspace,
2584
- selectTasksByStatus,
2585
- selectTask,
2586
- selectRunsForWorkspace,
2587
- selectRunsForTask,
2588
- selectRunsByTask,
2589
- selectRun,
2590
- selectQueueForWorkspace,
2591
- selectPrimaryWorkspace,
2592
- selectPendingApprovals,
2593
- selectGraphsForWorkspace,
2594
- selectApprovalsForWorkspace,
2595
- selectApprovalsForRun,
2596
- selectAdhocRunsForWorkspace,
2597
- selectAdhocRuns,
2598
- resolveTaskReference,
2599
- readTaskSourceIssueId,
2600
- readTaskMetadataStringList,
2601
- pruneQueueEntries,
2602
- pickDefaultWorkspaceId,
2603
- extractTaskGroupKey,
2604
- extractTaskCode,
320
+ defineServiceCapability,
2605
321
  definePlugin,
2606
322
  defineConfig,
323
+ defineCapability,
2607
324
  createPluginHost,
2608
- computeTaskBlockingDepths,
2609
- buildTaskReferenceIndex,
2610
- buildTaskGraphLayout,
2611
- buildRigInitConfigSource,
2612
- applyEngineEvent as applyRigEvent,
2613
- applyEngineEvents,
2614
- applyEngineEvent,
2615
- RIG_CORE_PACKAGE
325
+ RIG_CORE_PACKAGE,
326
+ CapabilityProviderMissingError
2616
327
  };