paseo-follow-up 1.2.0-beta.3 → 1.2.0-beta.4
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/README.md +2 -2
- package/package.json +1 -1
- package/server/daemon.ts +16 -2
- package/server/discovery.ts +7 -2
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ The content comes from a new `needs_you` tool that every root agent gets while t
|
|
|
101
101
|
Install the beta:
|
|
102
102
|
|
|
103
103
|
```sh
|
|
104
|
-
paseo plugin update follow-through --version 1.2.0-beta.
|
|
104
|
+
paseo plugin update follow-through --version 1.2.0-beta.4
|
|
105
105
|
# or, for a fresh install:
|
|
106
106
|
paseo plugin install npm:paseo-follow-up@beta
|
|
107
107
|
```
|
|
@@ -117,7 +117,7 @@ Betas publish to npm's `beta` dist-tag, so `paseo plugin update` without `--vers
|
|
|
117
117
|
## How it works
|
|
118
118
|
|
|
119
119
|
- The workspace's main agent owns its PRs, tasks, questions, the spec link and the Done call. Other agents in the workspace can register PRs and check status.
|
|
120
|
-
- **Auto-send** is on for every workspace tracked for the first time (1.2.0-beta.
|
|
120
|
+
- **Auto-send** is on for every workspace tracked for the first time (1.2.0-beta.4), so review threads and red CI go to the agent without you. Workspaces tracked before keep their setting. Turn it off per workspace and each repair batch waits for your click; with it on, Follow-up sends batches itself once the agent is idle, after fresh permission and Session Guard checks.
|
|
121
121
|
- Follow-up never merges, turns on merge when ready, requests reviewers or archives on its own. **Merge** re-checks the stack, lists any PRs underneath that would merge first, and asks again.
|
|
122
122
|
- A merged PR is not the end: tasks such as production checks and open questions keep the workspace open until the main agent declares Done and everything has settled.
|
|
123
123
|
- Answers go out only when you click Send. Follow-up never answers for you.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paseo-follow-up",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "1.2.0-beta.
|
|
4
|
+
"version": "1.2.0-beta.4",
|
|
5
5
|
"description": "Follow every workspace PR stack in Paseo, answer agent questions, batch repairs for the main agent, and see what needs you.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
package/server/daemon.ts
CHANGED
|
@@ -137,9 +137,11 @@ export class PaseoGateway implements Gateway {
|
|
|
137
137
|
const candidates = agents.map(row => row.agent).filter(a => a.workspaceId === workspace.id && !a.archivedAt && !a.labels["follow-through.recovery"]);
|
|
138
138
|
const roots: Agent[] = [];
|
|
139
139
|
for (const agent of candidates) if (await isWorkspaceRoot(agent, findParent)) roots.push(agent);
|
|
140
|
-
const
|
|
140
|
+
const successor = successorMainAgent(roots, existing?.mainAgentId);
|
|
141
|
+
const main = successor ?? chooseMainAgent(roots, existing?.mainAgentId);
|
|
141
142
|
if (!main && !existing) continue;
|
|
142
|
-
const
|
|
143
|
+
const owner = successor?.id ?? existing?.mainAgentId ?? main!.id;
|
|
144
|
+
const row: DiscoveredWorkspace = { id: workspace.id, title: workspace.name || main?.title || existing!.title, mainAgentId: owner, ...(successor ? { replacesMainAgentId: existing!.mainAgentId } : {}), projectId: workspace.projectId, messageAt: main?.lastUserMessageAt ?? null, enable_default_tracking: defaults.has(workspace.projectId ?? ""), contributorAgentIds: roots.filter(a => a.id !== owner && a.id !== existing?.mainAgentId && a.id !== main?.id).map(a => a.id) };
|
|
143
145
|
// Preserve the designated owner even when it is no longer indexed. Other root
|
|
144
146
|
// sessions can still contribute their PRs without acquiring completion authority.
|
|
145
147
|
const project = catalog.projects.find(p => p.projectId === workspace.projectId);
|
|
@@ -364,3 +366,15 @@ export function chooseMainAgent(roots: Agent[], existingId?: string): Agent | un
|
|
|
364
366
|
if (existingId) return roots.find(a => a.id === existingId);
|
|
365
367
|
return [...roots].sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id))[0];
|
|
366
368
|
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* The recorded main agent is closed, archived or gone while the workspace still has a live root agent: the live
|
|
372
|
+
* one takes over (the one Tom wrote to last, then the newest). A live recorded main is never replaced.
|
|
373
|
+
*/
|
|
374
|
+
export function successorMainAgent(roots: Agent[], existingId: string | undefined): Agent | undefined {
|
|
375
|
+
if (!existingId) return undefined;
|
|
376
|
+
const current = roots.find(a => a.id === existingId);
|
|
377
|
+
if (current && current.status !== "closed") return undefined;
|
|
378
|
+
const live = roots.filter(a => a.id !== existingId && a.status !== "closed");
|
|
379
|
+
return live.sort((a, b) => (b.lastUserMessageAt ?? "").localeCompare(a.lastUserMessageAt ?? "") || b.createdAt.localeCompare(a.createdAt) || a.id.localeCompare(b.id))[0];
|
|
380
|
+
}
|
package/server/discovery.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { mergeArtifacts } from "./spec";
|
|
|
4
4
|
import type { State, Workspace } from "../shared/model";
|
|
5
5
|
|
|
6
6
|
export type History = { candidates: string[]; goal: string | null; complete: boolean; error: string | null };
|
|
7
|
-
export type DiscoveredWorkspace = { id: string; title: string; mainAgentId: string; messageAt: string | null; projectId?: string | null; enable_default_tracking?: boolean; history?: History; contributorAgentIds?: string[]; submissions?: Submission[]; artifacts?: Artifact[] };
|
|
7
|
+
export type DiscoveredWorkspace = { id: string; title: string; mainAgentId: string; /** Set when mainAgentId is a live successor to this closed or gone main agent. */ replacesMainAgentId?: string; messageAt: string | null; projectId?: string | null; enable_default_tracking?: boolean; history?: History; contributorAgentIds?: string[]; submissions?: Submission[]; artifacts?: Artifact[] };
|
|
8
8
|
|
|
9
9
|
export function extractHistory(items: readonly unknown[], complete = true, error: string | null = null): History {
|
|
10
10
|
const candidates = new Set<string>();
|
|
@@ -42,7 +42,12 @@ export function applyDiscovery(state: State, discovered: DiscoveredWorkspace[],
|
|
|
42
42
|
state.workspaces.push(w);
|
|
43
43
|
}
|
|
44
44
|
w.title = row.title;
|
|
45
|
-
//
|
|
45
|
+
// A closed or gone main agent hands the workspace to its live successor (discovery decides; see
|
|
46
|
+
// successorMainAgent). Any other mismatch keeps the recorded owner.
|
|
47
|
+
if (row.replacesMainAgentId && row.replacesMainAgentId === w.mainAgentId && !w.archived) {
|
|
48
|
+
w.mainAgentId = row.mainAgentId; w.runtimeReason = null;
|
|
49
|
+
for (const b of w.batches) if (b.state === "pending" && b.recipientAgentId === row.replacesMainAgentId) b.recipientAgentId = null;
|
|
50
|
+
}
|
|
46
51
|
if (w.mainAgentId !== row.mainAgentId || w.archived) continue;
|
|
47
52
|
if (row.contributorAgentIds) w.contributorAgentIds = row.contributorAgentIds;
|
|
48
53
|
if (row.enable_default_tracking && !w.enable_tracking && (!row.projectId || state.projectDefaults?.[row.projectId] !== false)) { w.enable_auto_sends = firstTracking(w); w.enable_tracking = true; }
|