@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.
- package/dist/src/agent-role-registry.d.ts +4 -0
- package/dist/src/agent-role-registry.js +27 -0
- package/dist/src/authority-paths.d.ts +15 -0
- package/dist/src/authority-paths.js +80 -0
- package/dist/src/baked-secrets.d.ts +3 -0
- package/dist/src/baked-secrets.js +63 -0
- package/dist/src/build-time-config.d.ts +12 -0
- package/dist/src/build-time-config.js +25 -0
- package/dist/src/build-time-config.macro.d.ts +1 -0
- package/dist/src/capability-loaders.d.ts +51 -0
- package/dist/src/capability-loaders.js +870 -0
- package/dist/src/capability.d.ts +79 -0
- package/dist/src/capability.js +63 -0
- package/dist/src/checkout-root.d.ts +1 -0
- package/dist/src/checkout-root.js +30 -0
- package/dist/src/config-env.d.ts +4 -0
- package/dist/src/config-env.js +23 -0
- package/dist/src/config.d.ts +3 -0
- package/dist/src/config.js +44 -0
- package/dist/src/declarative-config.d.ts +14 -0
- package/dist/src/declarative-config.js +85 -0
- package/dist/src/default-kernel.d.ts +1 -0
- package/dist/src/default-kernel.js +12 -0
- package/dist/src/define-config.d.ts +20 -0
- package/dist/src/define-config.js +28 -15
- package/dist/src/define-plugin.d.ts +13 -0
- package/dist/src/define-plugin.js +4 -43
- package/dist/src/embedded-plugins.d.ts +59 -0
- package/dist/src/embedded-plugins.js +22 -0
- package/dist/src/exec.d.ts +13 -0
- package/dist/src/exec.js +101 -0
- package/dist/src/harness-paths.d.ts +9 -0
- package/dist/src/harness-paths.js +126 -0
- package/dist/src/hook-materializer.d.ts +21 -0
- package/dist/src/hook-materializer.js +152 -0
- package/dist/src/hook-protocol.d.ts +2 -0
- package/dist/src/hook-protocol.js +432 -0
- package/dist/src/hook-runner.d.ts +48 -0
- package/dist/src/hook-runner.js +868 -0
- package/dist/src/hook-runtime.d.ts +52 -0
- package/dist/src/hook-runtime.js +432 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +210 -2499
- package/dist/src/json-files.d.ts +9 -0
- package/dist/src/json-files.js +124 -0
- package/dist/src/kernel-boot.d.ts +2 -0
- package/dist/src/kernel-boot.js +10 -0
- package/dist/src/kernel-entrypoint.d.ts +22 -0
- package/dist/src/kernel-entrypoint.js +660 -0
- package/dist/src/kernel-plugin-abi.d.ts +1 -0
- package/dist/src/kernel-plugin-abi.js +1 -0
- package/dist/src/kernel-resolver.d.ts +2 -0
- package/dist/src/kernel-resolver.js +6 -0
- package/dist/src/layout.d.ts +10 -0
- package/dist/src/layout.js +138 -0
- package/dist/src/load-config.d.ts +2 -0
- package/dist/src/load-config.js +535 -30
- package/dist/src/placement.d.ts +58 -0
- package/dist/src/placement.js +53 -0
- package/dist/src/plugin-host-context.d.ts +65 -0
- package/dist/src/plugin-host-context.js +1171 -0
- package/dist/src/plugin-host-registries.d.ts +31 -0
- package/dist/src/plugin-host-registries.js +79 -0
- package/dist/src/plugin-host.d.ts +77 -0
- package/dist/src/plugin-host.js +127 -63
- package/dist/src/plugin-runtime.d.ts +173 -0
- package/dist/src/project-plugins.d.ts +63 -0
- package/dist/src/project-plugins.js +905 -0
- package/dist/src/remote-config.d.ts +125 -0
- package/dist/src/remote-config.js +85 -0
- package/dist/src/root-resolver.d.ts +5 -0
- package/dist/src/root-resolver.js +68 -0
- package/dist/src/run-provisioning.d.ts +37 -0
- package/dist/src/run-provisioning.js +35 -0
- package/dist/src/runtime-context.d.ts +20 -0
- package/dist/src/runtime-context.js +257 -0
- package/dist/src/runtime-events.d.ts +44 -0
- package/dist/src/runtime-events.js +208 -0
- package/dist/src/runtime-overlay.d.ts +11 -0
- package/dist/src/runtime-overlay.js +69 -0
- package/dist/src/runtime-paths.d.ts +21 -0
- package/dist/src/runtime-paths.js +181 -0
- package/dist/src/runtime-provisioning-env.d.ts +5 -0
- package/dist/src/runtime-provisioning-env.js +217 -0
- package/dist/src/runtime-runner-context.d.ts +12 -0
- package/dist/src/runtime-runner-context.js +1 -0
- package/dist/src/safe-identifiers.d.ts +44 -0
- package/dist/src/safe-identifiers.js +96 -0
- package/dist/src/scope-rules.d.ts +4 -0
- package/dist/src/scope-rules.js +21 -0
- package/dist/src/server-paths.d.ts +22 -0
- package/dist/src/server-paths.js +219 -0
- package/dist/src/setup-version.d.ts +3 -0
- package/dist/src/setup-version.js +14 -0
- package/dist/src/task-record-reader.d.ts +3 -0
- package/dist/src/task-record-reader.js +9 -0
- package/dist/src/validator-registry.d.ts +27 -0
- package/dist/src/validator-registry.js +64 -0
- package/package.json +162 -10
- package/dist/src/engineReadModelReducer.js +0 -1780
- package/dist/src/rig-init-builder.js +0 -57
- package/dist/src/rigSelectors.js +0 -293
- package/dist/src/taskGraph.js +0 -64
- package/dist/src/taskGraphCodes.js +0 -26
- 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(
|
|
6
|
-
|
|
7
|
-
|
|
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
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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:
|
|
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
|
|
142
|
-
const
|
|
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
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (
|
|
177
|
-
|
|
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
|
|
199
|
-
const
|
|
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
|
-
|
|
218
|
-
|
|
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/
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
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
|
|
2390
|
-
const
|
|
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
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2609
|
-
|
|
2610
|
-
buildTaskGraphLayout,
|
|
2611
|
-
buildRigInitConfigSource,
|
|
2612
|
-
applyEngineEvent as applyRigEvent,
|
|
2613
|
-
applyEngineEvents,
|
|
2614
|
-
applyEngineEvent,
|
|
2615
|
-
RIG_CORE_PACKAGE
|
|
325
|
+
RIG_CORE_PACKAGE,
|
|
326
|
+
CapabilityProviderMissingError
|
|
2616
327
|
};
|