@hyperdrive.bot/paseo-server 0.3.41 → 0.3.43
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/server/server/agent/agent-manager.d.ts +15 -0
- package/dist/server/server/agent/agent-manager.js +142 -25
- package/dist/server/server/agent/mcp-server.js +6 -1
- package/dist/server/server/agent/providers/claude/background-task-tracker.d.ts +38 -1
- package/dist/server/server/agent/providers/claude/background-task-tracker.js +114 -9
- package/dist/server/server/agent/providers/claude/background-work-kinds.d.ts +95 -0
- package/dist/server/server/agent/providers/claude/background-work-kinds.js +73 -0
- package/dist/server/server/agent/providers/claude/pty-session-launcher.js +3 -0
- package/dist/server/server/agent/providers/claude/transport/pty.d.ts +17 -0
- package/dist/server/server/agent/providers/claude/transport/pty.js +51 -1
- package/dist/server/server/agent/providers/claude/transport/tmux.d.ts +74 -0
- package/dist/server/server/agent/providers/claude/transport/tmux.js +157 -0
- package/dist/server/server/agent/providers/claude/transport/types.d.ts +6 -0
- package/dist/server/server/agent/providers/opencode-agent.d.ts +7 -0
- package/dist/server/server/agent/providers/opencode-agent.js +51 -1
- package/dist/server/server/bootstrap.js +1 -0
- package/dist/server/server/session.js +1 -0
- package/dist/server/server/workflow/workflow-agent-resolution.d.ts +82 -0
- package/dist/server/server/workflow/workflow-agent-resolution.js +105 -0
- package/dist/server/server/workflow/workflow-manager.d.ts +155 -20
- package/dist/server/server/workflow/workflow-manager.js +439 -31
- package/dist/server/server/workflow/workflow-progress.d.ts +53 -0
- package/dist/server/server/workflow/workflow-progress.js +96 -0
- package/dist/server/server/workspace-directory.js +32 -14
- package/dist/server/web-ui/_expo/static/js/web/{index-cb251ddad56c08c3021036af3a43fc0f.js → index-2ff48a7009ad2309c577d5a43b925f69.js} +18 -18
- package/dist/server/web-ui/_expo/static/js/web/index-2ff48a7009ad2309c577d5a43b925f69.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-2ff48a7009ad2309c577d5a43b925f69.js.gz +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-cb251ddad56c08c3021036af3a43fc0f.js.map.br → index-2ff48a7009ad2309c577d5a43b925f69.js.map.br} +0 -0
- package/dist/server/web-ui/_expo/static/js/web/{index-cb251ddad56c08c3021036af3a43fc0f.js.map.gz → index-2ff48a7009ad2309c577d5a43b925f69.js.map.gz} +0 -0
- package/dist/server/web-ui/index.html +1 -1
- package/dist/server/web-ui/index.html.br +0 -0
- package/dist/server/web-ui/index.html.gz +0 -0
- package/package.json +6 -6
- package/dist/server/web-ui/_expo/static/js/web/index-cb251ddad56c08c3021036af3a43fc0f.js.br +0 -0
- package/dist/server/web-ui/_expo/static/js/web/index-cb251ddad56c08c3021036af3a43fc0f.js.gz +0 -0
|
@@ -86,10 +86,63 @@ export declare class WorkflowCycleError extends Error {
|
|
|
86
86
|
readonly unresolved: string[];
|
|
87
87
|
constructor(unresolved: string[]);
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* A graph that is malformed *before* dependencies are even considered: it declares no
|
|
91
|
+
* tasks at all, or it declares the same task id twice.
|
|
92
|
+
*
|
|
93
|
+
* Kept separate from {@link WorkflowCycleError} because neither defect is a dependency
|
|
94
|
+
* problem, and both callers (`session.ts` RPC, `mcp-server.ts` tool) forward
|
|
95
|
+
* `error.message` verbatim to a human - telling that human "dependency cycle" when the
|
|
96
|
+
* real defect is "you sent zero tasks" is the kind of message that costs an hour.
|
|
97
|
+
* `defect` is the stable discriminator; the message is the human-facing half.
|
|
98
|
+
*/
|
|
99
|
+
export declare class WorkflowInvalidGraphError extends Error {
|
|
100
|
+
readonly defect: "empty-graph" | "duplicate-task-id";
|
|
101
|
+
constructor(defect: "empty-graph" | "duplicate-task-id", message: string);
|
|
102
|
+
}
|
|
89
103
|
/**
|
|
90
104
|
* Resolve the layered execution order for a graph. Prefer the graph's declared
|
|
91
105
|
* `metadata.executionLayers`; otherwise compute layers from task dependencies.
|
|
92
106
|
*/
|
|
93
107
|
export declare function resolveExecutionLayers(graph: TaskGraph): string[][];
|
|
108
|
+
/**
|
|
109
|
+
* Assert that a task graph is schedulable: it declares at least one task, no task id is
|
|
110
|
+
* declared twice, every declared dependency names a task that exists in the graph, and
|
|
111
|
+
* the dependency relation is acyclic. Throws {@link WorkflowInvalidGraphError} for the
|
|
112
|
+
* two shape defects and {@link WorkflowCycleError} for the two dependency defects, on
|
|
113
|
+
* the first violation; returns `void` on success.
|
|
114
|
+
*
|
|
115
|
+
* WHY the shape defects are rejected here rather than tolerated: both produce a workflow
|
|
116
|
+
* that can never reach a terminal status, which is the exact failure this validator
|
|
117
|
+
* exists to prevent. An empty graph makes `startWorkflow` flip to `running`, spawn zero
|
|
118
|
+
* children and return - and `settleWorkflow` is only ever reached from a child terminal
|
|
119
|
+
* event, so with no children no event ever fires and the workflow sits `running`
|
|
120
|
+
* forever. Duplicate ids are worse than useless: two children spawn for one task id and
|
|
121
|
+
* the first of them to close settles the whole workflow to `completed` while its twin is
|
|
122
|
+
* still live, recording intent as fact. Neither is stopped downstream - the wire schema
|
|
123
|
+
* (`WorkflowTaskGraphSchema.tasks`) has no `.min(1)` and no uniqueness constraint - so
|
|
124
|
+
* submission time is the only place they can be stopped. The definition layer already
|
|
125
|
+
* rejects both (`steps: z.array(...).min(1)` and `collectDuplicateStepIds` in
|
|
126
|
+
* `protocol/src/workflow/definition.ts`); this closes the same two holes on the
|
|
127
|
+
* raw-graph path, which is the one the wire and the CLI can still reach directly.
|
|
128
|
+
*
|
|
129
|
+
* Pure: no I/O, no snapshot reads, no `Date` API (see the determinism rules above).
|
|
130
|
+
*
|
|
131
|
+
* WHY this cannot delegate to `resolveExecutionLayers`: that function (above) returns
|
|
132
|
+
* `graph.metadata.executionLayers` verbatim and unvalidated whenever it is non-empty,
|
|
133
|
+
* and `WorkflowTaskGraphSchema.metadata.executionLayers` is a REQUIRED wire field
|
|
134
|
+
* (`packages/protocol/src/messages.ts`), so every real graph carries declared layers and
|
|
135
|
+
* takes that branch. The Kahn check inside `computeLayersFromDependencies` therefore
|
|
136
|
+
* never runs for real traffic. This validator reads `TaskNode.dependencies` only and
|
|
137
|
+
* ignores the declared layering entirely, so a graph that *claims* to be layerable is
|
|
138
|
+
* still rejected when its dependencies say otherwise.
|
|
139
|
+
*
|
|
140
|
+
* It also deliberately does NOT share `computeLayersFromDependencies`' escape hatch
|
|
141
|
+
* (`|| !byId.has(dep)`, which treats an out-of-graph dependency as already satisfied).
|
|
142
|
+
* That clause is correct for the projection, which must keep layering graphs stored
|
|
143
|
+
* before this validator existed, and wrong for scheduling, where an out-of-graph
|
|
144
|
+
* dependency can never be met. Rejecting is done here, one level up, instead.
|
|
145
|
+
*/
|
|
146
|
+
export declare function assertSchedulableGraph(graph: TaskGraph): void;
|
|
94
147
|
export {};
|
|
95
148
|
//# sourceMappingURL=workflow-progress.d.ts.map
|
|
@@ -138,6 +138,23 @@ export class WorkflowCycleError extends Error {
|
|
|
138
138
|
this.name = "WorkflowCycleError";
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* A graph that is malformed *before* dependencies are even considered: it declares no
|
|
143
|
+
* tasks at all, or it declares the same task id twice.
|
|
144
|
+
*
|
|
145
|
+
* Kept separate from {@link WorkflowCycleError} because neither defect is a dependency
|
|
146
|
+
* problem, and both callers (`session.ts` RPC, `mcp-server.ts` tool) forward
|
|
147
|
+
* `error.message` verbatim to a human - telling that human "dependency cycle" when the
|
|
148
|
+
* real defect is "you sent zero tasks" is the kind of message that costs an hour.
|
|
149
|
+
* `defect` is the stable discriminator; the message is the human-facing half.
|
|
150
|
+
*/
|
|
151
|
+
export class WorkflowInvalidGraphError extends Error {
|
|
152
|
+
constructor(defect, message) {
|
|
153
|
+
super(message);
|
|
154
|
+
this.defect = defect;
|
|
155
|
+
this.name = "WorkflowInvalidGraphError";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
141
158
|
/**
|
|
142
159
|
* Resolve the layered execution order for a graph. Prefer the graph's declared
|
|
143
160
|
* `metadata.executionLayers`; otherwise compute layers from task dependencies.
|
|
@@ -175,4 +192,83 @@ function computeLayersFromDependencies(tasks) {
|
|
|
175
192
|
}
|
|
176
193
|
return layers;
|
|
177
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Assert that a task graph is schedulable: it declares at least one task, no task id is
|
|
197
|
+
* declared twice, every declared dependency names a task that exists in the graph, and
|
|
198
|
+
* the dependency relation is acyclic. Throws {@link WorkflowInvalidGraphError} for the
|
|
199
|
+
* two shape defects and {@link WorkflowCycleError} for the two dependency defects, on
|
|
200
|
+
* the first violation; returns `void` on success.
|
|
201
|
+
*
|
|
202
|
+
* WHY the shape defects are rejected here rather than tolerated: both produce a workflow
|
|
203
|
+
* that can never reach a terminal status, which is the exact failure this validator
|
|
204
|
+
* exists to prevent. An empty graph makes `startWorkflow` flip to `running`, spawn zero
|
|
205
|
+
* children and return - and `settleWorkflow` is only ever reached from a child terminal
|
|
206
|
+
* event, so with no children no event ever fires and the workflow sits `running`
|
|
207
|
+
* forever. Duplicate ids are worse than useless: two children spawn for one task id and
|
|
208
|
+
* the first of them to close settles the whole workflow to `completed` while its twin is
|
|
209
|
+
* still live, recording intent as fact. Neither is stopped downstream - the wire schema
|
|
210
|
+
* (`WorkflowTaskGraphSchema.tasks`) has no `.min(1)` and no uniqueness constraint - so
|
|
211
|
+
* submission time is the only place they can be stopped. The definition layer already
|
|
212
|
+
* rejects both (`steps: z.array(...).min(1)` and `collectDuplicateStepIds` in
|
|
213
|
+
* `protocol/src/workflow/definition.ts`); this closes the same two holes on the
|
|
214
|
+
* raw-graph path, which is the one the wire and the CLI can still reach directly.
|
|
215
|
+
*
|
|
216
|
+
* Pure: no I/O, no snapshot reads, no `Date` API (see the determinism rules above).
|
|
217
|
+
*
|
|
218
|
+
* WHY this cannot delegate to `resolveExecutionLayers`: that function (above) returns
|
|
219
|
+
* `graph.metadata.executionLayers` verbatim and unvalidated whenever it is non-empty,
|
|
220
|
+
* and `WorkflowTaskGraphSchema.metadata.executionLayers` is a REQUIRED wire field
|
|
221
|
+
* (`packages/protocol/src/messages.ts`), so every real graph carries declared layers and
|
|
222
|
+
* takes that branch. The Kahn check inside `computeLayersFromDependencies` therefore
|
|
223
|
+
* never runs for real traffic. This validator reads `TaskNode.dependencies` only and
|
|
224
|
+
* ignores the declared layering entirely, so a graph that *claims* to be layerable is
|
|
225
|
+
* still rejected when its dependencies say otherwise.
|
|
226
|
+
*
|
|
227
|
+
* It also deliberately does NOT share `computeLayersFromDependencies`' escape hatch
|
|
228
|
+
* (`|| !byId.has(dep)`, which treats an out-of-graph dependency as already satisfied).
|
|
229
|
+
* That clause is correct for the projection, which must keep layering graphs stored
|
|
230
|
+
* before this validator existed, and wrong for scheduling, where an out-of-graph
|
|
231
|
+
* dependency can never be met. Rejecting is done here, one level up, instead.
|
|
232
|
+
*/
|
|
233
|
+
export function assertSchedulableGraph(graph) {
|
|
234
|
+
// Pass 0 - shape. Runs before anything reads `dependencies`, because a graph this
|
|
235
|
+
// malformed has no dependency verdict worth reporting.
|
|
236
|
+
if (graph.tasks.length === 0) {
|
|
237
|
+
throw new WorkflowInvalidGraphError("empty-graph", "Workflow task graph declares no tasks: a workflow must declare at least one task.");
|
|
238
|
+
}
|
|
239
|
+
// Built by hand rather than `new Map(graph.tasks.map(...))` precisely so the collision
|
|
240
|
+
// is observable: the Map constructor silently keeps the LAST entry for a repeated key,
|
|
241
|
+
// which is how a duplicate id survives every downstream check.
|
|
242
|
+
const byId = new Map();
|
|
243
|
+
for (const task of graph.tasks) {
|
|
244
|
+
if (byId.has(task.id)) {
|
|
245
|
+
throw new WorkflowInvalidGraphError("duplicate-task-id", `Workflow task graph declares duplicate task id: ${task.id}`);
|
|
246
|
+
}
|
|
247
|
+
byId.set(task.id, task);
|
|
248
|
+
}
|
|
249
|
+
// Pass 1 - dangling dependencies. Runs FIRST so a dependency on a task that does not
|
|
250
|
+
// exist is never misreported as a cycle. First offender in graph order, for a
|
|
251
|
+
// deterministic message.
|
|
252
|
+
for (const task of graph.tasks) {
|
|
253
|
+
for (const dep of task.dependencies) {
|
|
254
|
+
if (!byId.has(dep)) {
|
|
255
|
+
throw new WorkflowCycleError([`${task.id} -> ${dep}`]);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Pass 2 - cycles. Kahn-style sweep over `dependencies` only; no out-of-graph escape,
|
|
260
|
+
// which is safe precisely because pass 1 proved every dependency is in-graph.
|
|
261
|
+
const resolved = new Set();
|
|
262
|
+
let remaining = graph.tasks;
|
|
263
|
+
while (remaining.length > 0) {
|
|
264
|
+
const layer = remaining.filter((task) => task.dependencies.every((dep) => resolved.has(dep)));
|
|
265
|
+
if (layer.length === 0) {
|
|
266
|
+
throw new WorkflowCycleError(remaining.map((task) => task.id));
|
|
267
|
+
}
|
|
268
|
+
for (const task of layer) {
|
|
269
|
+
resolved.add(task.id);
|
|
270
|
+
}
|
|
271
|
+
remaining = remaining.filter((task) => !resolved.has(task.id));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
178
274
|
//# sourceMappingURL=workflow-progress.js.map
|
|
@@ -44,6 +44,28 @@ export function workspaceIdsOnCheckout(workspaces, cwd) {
|
|
|
44
44
|
.filter((workspace) => !workspace.archivedAt && resolve(workspace.cwd) === resolvedCwd)
|
|
45
45
|
.map((workspace) => workspace.workspaceId);
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Build the bucket-derivation input for one agent snapshot.
|
|
49
|
+
*
|
|
50
|
+
* Exists so every call site in this file feeds `deriveAgentStateBucket` the
|
|
51
|
+
* SAME shape. It previously did not: two call sites hand-built the object and
|
|
52
|
+
* both omitted `activeChildCount`, so server-derived workspace buckets could
|
|
53
|
+
* never see armed background work while the app-side derivation could. The same
|
|
54
|
+
* agent then bucketed as "done" in the workspace list and "running" on its
|
|
55
|
+
* kanban card. One helper, one shape, no drift.
|
|
56
|
+
*/
|
|
57
|
+
function toBucketInput(agent) {
|
|
58
|
+
return {
|
|
59
|
+
status: agent.status,
|
|
60
|
+
pendingPermissionCount: agent.pendingPermissions?.length ?? 0,
|
|
61
|
+
requiresAttention: agent.requiresAttention,
|
|
62
|
+
attentionReason: agent.attentionReason ?? null,
|
|
63
|
+
// Monitors, crons and backgrounded shells still alive on this agent. Live
|
|
64
|
+
// SUBAGENTS are not counted here — they arrive as their own snapshots and
|
|
65
|
+
// are attributed to the delegation root in applyAgentBucketContributions.
|
|
66
|
+
activeChildCount: agent.activeBackgroundTaskCount ?? 0,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
47
69
|
export class WorkspaceDirectory {
|
|
48
70
|
constructor(deps) {
|
|
49
71
|
this.deps = deps;
|
|
@@ -166,15 +188,13 @@ export class WorkspaceDirectory {
|
|
|
166
188
|
continue;
|
|
167
189
|
}
|
|
168
190
|
workspaceAgent = parentAgent;
|
|
169
|
-
|
|
191
|
+
// A live subagent means the ROOT's own turn is not in flight — it is
|
|
192
|
+
// waiting on delegated work that will hand control back by itself.
|
|
193
|
+
// That is "pending", not "running"; see agent-state-bucket.ts.
|
|
194
|
+
bucket = "pending";
|
|
170
195
|
}
|
|
171
196
|
else {
|
|
172
|
-
bucket = deriveAgentStateBucket(
|
|
173
|
-
status: agent.status,
|
|
174
|
-
pendingPermissionCount: agent.pendingPermissions?.length ?? 0,
|
|
175
|
-
requiresAttention: agent.requiresAttention,
|
|
176
|
-
attentionReason: agent.attentionReason ?? null,
|
|
177
|
-
});
|
|
197
|
+
bucket = deriveAgentStateBucket(toBucketInput(agent));
|
|
178
198
|
}
|
|
179
199
|
const workspaceId = workspaceAgent.workspaceId;
|
|
180
200
|
if (!workspaceId) {
|
|
@@ -210,7 +230,10 @@ export class WorkspaceDirectory {
|
|
|
210
230
|
existing.status = bucket;
|
|
211
231
|
}
|
|
212
232
|
const entries = terminalEntriesByWorkspaceId.get(workspaceId) ?? [];
|
|
213
|
-
entries.push({
|
|
233
|
+
entries.push({
|
|
234
|
+
bucket,
|
|
235
|
+
changedAtIso: new Date(activity.changedAt).toISOString(),
|
|
236
|
+
});
|
|
214
237
|
terminalEntriesByWorkspaceId.set(workspaceId, entries);
|
|
215
238
|
}
|
|
216
239
|
return terminalEntriesByWorkspaceId;
|
|
@@ -271,12 +294,7 @@ export class WorkspaceDirectory {
|
|
|
271
294
|
findNewestTimestampInBucket(contributingAgents, terminalEntries, winningBucket) {
|
|
272
295
|
const agentTimestamps = contributingAgents
|
|
273
296
|
.filter((agent) => {
|
|
274
|
-
const derived = deriveAgentStateBucket(
|
|
275
|
-
status: agent.status,
|
|
276
|
-
pendingPermissionCount: agent.pendingPermissions?.length ?? 0,
|
|
277
|
-
requiresAttention: agent.requiresAttention,
|
|
278
|
-
attentionReason: agent.attentionReason ?? null,
|
|
279
|
-
});
|
|
297
|
+
const derived = deriveAgentStateBucket(toBucketInput(agent));
|
|
280
298
|
return derived === winningBucket;
|
|
281
299
|
})
|
|
282
300
|
.map((agent) => {
|