paseo-bm-plugin 0.0.0-placeholder.0
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/LICENSE +21 -0
- package/README.md +53 -0
- package/client/agent-tree.ts +308 -0
- package/client/answer-state.ts +62 -0
- package/client/bead-chips.tsx +147 -0
- package/client/beads-header-button.ts +108 -0
- package/client/beads-model.ts +581 -0
- package/client/beads-screen.tsx +516 -0
- package/client/beads-tab.tsx +58 -0
- package/client/chat-card.tsx +636 -0
- package/client/chat-cards.ts +1038 -0
- package/client/dashboard-actions.tsx +255 -0
- package/client/dashboard-model.ts +947 -0
- package/client/dashboard-view.ts +215 -0
- package/client/dashboard.tsx +318 -0
- package/client/launch-manager.ts +323 -0
- package/client/launcher.tsx +516 -0
- package/client/markdown-view.tsx +112 -0
- package/client/markdown.ts +145 -0
- package/client/settings.tsx +104 -0
- package/client/setup-model.ts +552 -0
- package/client/setup-screen.tsx +913 -0
- package/client/slot.ts +47 -0
- package/client/tree.tsx +204 -0
- package/client/ui.tsx +262 -0
- package/client/waiting-pills-model.ts +156 -0
- package/client/waiting-pills.tsx +201 -0
- package/index.client.tsx +232 -0
- package/index.server.ts +168 -0
- package/package.json +35 -0
- package/paseo-plugin.json +6 -0
- package/roles/manager.md +181 -0
- package/roles/reviewer.md +160 -0
- package/roles/worker.md +407 -0
- package/server/agent-labels.ts +194 -0
- package/server/agent-role.ts +102 -0
- package/server/answer-marks.ts +120 -0
- package/server/bead-actions.ts +88 -0
- package/server/bead-work.ts +80 -0
- package/server/beads-store.ts +342 -0
- package/server/bm-report.ts +433 -0
- package/server/chat-peers.ts +65 -0
- package/server/chat-rpc.ts +122 -0
- package/server/chat-waiting.ts +182 -0
- package/server/collector.ts +629 -0
- package/server/config-writer.ts +222 -0
- package/server/cost.ts +88 -0
- package/server/dashboard-rpc.ts +662 -0
- package/server/fallback-detect.ts +183 -0
- package/server/fallback-handover.ts +365 -0
- package/server/fallback-manager.ts +170 -0
- package/server/fallback-reviewer.ts +198 -0
- package/server/fallback-rpc.ts +306 -0
- package/server/fallback-settings.ts +322 -0
- package/server/fallback-state.ts +518 -0
- package/server/fallback-switch.ts +191 -0
- package/server/fallback-wait.ts +188 -0
- package/server/format-check.ts +352 -0
- package/server/install-home.ts +187 -0
- package/server/live-timeline.ts +129 -0
- package/server/manager-instructions.ts +9 -0
- package/server/manager.ts +647 -0
- package/server/model-costs.ts +238 -0
- package/server/notice-queue.ts +315 -0
- package/server/notices.ts +81 -0
- package/server/paseo-cli.ts +115 -0
- package/server/provider-id.ts +12 -0
- package/server/review-budget.ts +208 -0
- package/server/reviewer-instructions.ts +9 -0
- package/server/role-choices.ts +161 -0
- package/server/role-extras.ts +270 -0
- package/server/role-hook.ts +347 -0
- package/server/role-mode.ts +397 -0
- package/server/role-settings-rpc.ts +325 -0
- package/server/roles.ts +96 -0
- package/server/settings-notices.ts +112 -0
- package/server/setup-rpc.ts +70 -0
- package/server/setup-skills.ts +121 -0
- package/server/setup-tools.ts +162 -0
- package/server/shell.ts +68 -0
- package/server/stop-propagation.ts +365 -0
- package/server/tools-check.ts +118 -0
- package/server/trace-store.ts +1137 -0
- package/server/traces.ts +1356 -0
- package/server/worker-instructions.ts +9 -0
- package/server/workflow-steps.ts +422 -0
- package/shared/bead-ids.ts +25 -0
- package/shared/bm-fallback.ts +91 -0
- package/shared/bm-format.ts +424 -0
- package/shared/bm-questions.ts +213 -0
- package/shared/bm-report.ts +433 -0
- package/shared/contracts.ts +1371 -0
- package/shared/fallback-patterns.ts +201 -0
- package/shared/fallback.ts +46 -0
- package/shared/new-request.ts +20 -0
- package/shared/order.ts +22 -0
- package/shared/prices.ts +65 -0
- package/shared/settings.ts +57 -0
- package/shared/sole-worker.ts +20 -0
- package/shared/version.ts +6 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chat.waiting`: which Workers wait for the user's answer, per Manager
|
|
3
|
+
* (delta 20260918d-card-replies §4.8, REQ-059 j), and which fallback
|
|
4
|
+
* incidents wait for the user's decision (delta 20260921 §4.4.6). Read-only.
|
|
5
|
+
*
|
|
6
|
+
* The Workers come from each Manager's live timeline, not the trace store: a
|
|
7
|
+
* report reaches the store only when the Manager's turn ends, and the pill
|
|
8
|
+
* should appear as soon as the question does. The incidents come from the
|
|
9
|
+
* incidents file, not the timeline, so their pill is right even when the
|
|
10
|
+
* Manager's own turn failed on the same plan.
|
|
11
|
+
*/
|
|
12
|
+
import { parseQuestions } from "../shared/bm-questions";
|
|
13
|
+
import { parseReports } from "../shared/bm-report";
|
|
14
|
+
import type { FallbackIncident, WaitingFallback, WaitingWorker } from "../shared/contracts";
|
|
15
|
+
import { soleWorkerOf } from "../shared/sole-worker";
|
|
16
|
+
import { peersOfWorkspace, workspaceRecordsReader } from "./chat-peers";
|
|
17
|
+
import { bmAgentsOf, type DashboardPaseo } from "./dashboard-rpc";
|
|
18
|
+
import { readIncidents, replacementsOf } from "./fallback-state";
|
|
19
|
+
import { readTimelinePages } from "./live-timeline";
|
|
20
|
+
import { installHomeOf } from "./role-extras";
|
|
21
|
+
import { TIMED_OUT, withTimeout } from "./role-mode";
|
|
22
|
+
|
|
23
|
+
/** How much of a Manager's timeline is read per call: one page, newest first. */
|
|
24
|
+
export const WAITING_TIMELINE_PAGES = 1;
|
|
25
|
+
export const WAITING_TIMELINE_LIMIT = 200;
|
|
26
|
+
|
|
27
|
+
const REQUEST_ID = /^req-\d{8}T\d{6}Z$/;
|
|
28
|
+
|
|
29
|
+
/** A timeline entry as Paseo returns it. */
|
|
30
|
+
export interface WaitingEntry {
|
|
31
|
+
item?: unknown;
|
|
32
|
+
timestamp?: unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** An agent as `waitingOf` needs it: a chat peer and its workspace. */
|
|
36
|
+
export interface WaitingCandidate {
|
|
37
|
+
id: string;
|
|
38
|
+
workspaceId: string | null;
|
|
39
|
+
role: string;
|
|
40
|
+
title: string | null;
|
|
41
|
+
status: string;
|
|
42
|
+
requestId: string | null;
|
|
43
|
+
archived: boolean;
|
|
44
|
+
/** A fallback Worker replaced this one (delta 20260921 §4.4.8). */
|
|
45
|
+
replaced?: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The Workers waiting on one Manager. `entries` are that Manager's timeline,
|
|
50
|
+
* NEWEST FIRST. A message counts only when another agent sent it (no
|
|
51
|
+
* `clientMessageId`); the newest report of each request decides, so a later
|
|
52
|
+
* `received` or `finished` hides an earlier `blocked`. A Worker is waiting
|
|
53
|
+
* when that newest report is `blocked` with at least one question for its own
|
|
54
|
+
* request, and the single Worker of that request is idle (or errored): a
|
|
55
|
+
* running Worker already has its answer.
|
|
56
|
+
*/
|
|
57
|
+
export function waitingOf(
|
|
58
|
+
manager: { id: string; workspaceId: string },
|
|
59
|
+
entries: readonly WaitingEntry[],
|
|
60
|
+
workers: readonly WaitingCandidate[],
|
|
61
|
+
): WaitingWorker[] {
|
|
62
|
+
const newest = new Map<string, { phase: string | null; text: string; at: string | null }>();
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
const item = entry.item as { type?: unknown; text?: unknown; clientMessageId?: unknown } | null | undefined;
|
|
65
|
+
if (item === null || item === undefined || item.type !== "user_message") continue;
|
|
66
|
+
if (typeof item.clientMessageId === "string" || typeof item.text !== "string") continue;
|
|
67
|
+
const at = typeof entry.timestamp === "string" ? entry.timestamp : null;
|
|
68
|
+
const report = parseReports(item.text, { agentId: manager.id, at: at ?? "" })
|
|
69
|
+
.filter((block) => block.requestId !== null && REQUEST_ID.test(block.requestId))
|
|
70
|
+
.at(-1);
|
|
71
|
+
if (report === undefined || newest.has(report.requestId!)) continue;
|
|
72
|
+
newest.set(report.requestId!, { phase: report.phase, text: item.text, at });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const out: WaitingWorker[] = [];
|
|
76
|
+
for (const [requestId, report] of newest) {
|
|
77
|
+
if (report.phase !== "blocked") continue;
|
|
78
|
+
const asked = parseQuestions(report.text);
|
|
79
|
+
if (asked === null || asked.questions.length === 0) continue;
|
|
80
|
+
if (asked.requestId !== null && asked.requestId !== requestId) continue;
|
|
81
|
+
// The same rule the card uses to pick where a reply goes (delta 20260918f F12).
|
|
82
|
+
const worker = soleWorkerOf(
|
|
83
|
+
workers.filter((candidate) => candidate.workspaceId === manager.workspaceId),
|
|
84
|
+
requestId,
|
|
85
|
+
);
|
|
86
|
+
if (worker === null) continue;
|
|
87
|
+
if (worker.status !== "idle" && worker.status !== "error") continue;
|
|
88
|
+
out.push({
|
|
89
|
+
managerId: manager.id,
|
|
90
|
+
workspaceId: manager.workspaceId,
|
|
91
|
+
workerId: worker.id,
|
|
92
|
+
workerTitle: worker.title,
|
|
93
|
+
requestId,
|
|
94
|
+
text: report.text,
|
|
95
|
+
at: report.at,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The fallback incidents the pills count: `pending`, with a `managerId` that is
|
|
103
|
+
* one of `managers` (live ones: only a live chat shows a pill). Each carries
|
|
104
|
+
* its Manager's workspace, where the pill goes. In the order given (the
|
|
105
|
+
* incidents file keeps them oldest first).
|
|
106
|
+
*/
|
|
107
|
+
export function pendingFallbackOf(
|
|
108
|
+
managers: ReadonlyArray<{ id: string; workspaceId: string }>,
|
|
109
|
+
incidents: readonly FallbackIncident[],
|
|
110
|
+
): WaitingFallback[] {
|
|
111
|
+
const workspaceOf = new Map(managers.map((manager) => [manager.id, manager.workspaceId]));
|
|
112
|
+
return incidents.flatMap((incident) => {
|
|
113
|
+
const workspaceId = incident.status === "pending" && incident.managerId !== null ? workspaceOf.get(incident.managerId) : undefined;
|
|
114
|
+
return workspaceId === undefined ? [] : [{ managerId: incident.managerId!, workspaceId, incident }];
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ChatWaitingDeps {
|
|
119
|
+
homedir?: () => string;
|
|
120
|
+
/** The install home; the handler looks it up (within the lookup budget), tests pass one. */
|
|
121
|
+
home?: string | null;
|
|
122
|
+
log?: (message: string) => void;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/** Every recorded incident; none when the install home or the file cannot be read. Never throws. */
|
|
126
|
+
async function incidentsOf(paseo: DashboardPaseo, deps: ChatWaitingDeps): Promise<FallbackIncident[]> {
|
|
127
|
+
try {
|
|
128
|
+
const found = deps.home !== undefined ? deps.home : await withTimeout(installHomeOf(paseo, { homedir: deps.homedir }));
|
|
129
|
+
if (found === null || found === TIMED_OUT) return [];
|
|
130
|
+
return readIncidents(found, deps.log).incidents;
|
|
131
|
+
} catch {
|
|
132
|
+
return [];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* `chat.waiting` handler: every live Manager's waiting Workers and pending
|
|
138
|
+
* fallback incidents. Never throws for one Manager.
|
|
139
|
+
*/
|
|
140
|
+
export async function handleChatWaiting(
|
|
141
|
+
paseo: DashboardPaseo,
|
|
142
|
+
deps: ChatWaitingDeps = {},
|
|
143
|
+
): Promise<{ waiting: WaitingWorker[]; fallback: WaitingFallback[] }> {
|
|
144
|
+
const all = await bmAgentsOf(paseo);
|
|
145
|
+
const managers = all.filter(
|
|
146
|
+
(entry) => entry.facts.role === "manager" && !entry.facts.archived && entry.facts.status !== "closed" && entry.workspaceId !== null,
|
|
147
|
+
);
|
|
148
|
+
if (managers.length === 0) return { waiting: [], fallback: [] };
|
|
149
|
+
|
|
150
|
+
// Only a live Manager's chat shows pills, so agents elsewhere are never
|
|
151
|
+
// looked at and their workspace's trace store never read (delta 20260918f
|
|
152
|
+
// F11). The peers come from the helper `chat.peers` uses, so a pill and its
|
|
153
|
+
// card see the same Workers (F12).
|
|
154
|
+
const managerWorkspaces = new Set(managers.map((manager) => manager.workspaceId!));
|
|
155
|
+
const incidents = await incidentsOf(paseo, deps);
|
|
156
|
+
const replacements = replacementsOf(incidents);
|
|
157
|
+
const workers: WaitingCandidate[] = [];
|
|
158
|
+
for (const workspaceId of managerWorkspaces) {
|
|
159
|
+
for (const peer of await peersOfWorkspace(all, workspaceId, workspaceRecordsReader(paseo, workspaceId, deps), replacements)) {
|
|
160
|
+
if (peer.role === "worker") workers.push({ ...peer, workspaceId });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const waiting: WaitingWorker[] = [];
|
|
165
|
+
for (const manager of managers) {
|
|
166
|
+
const entries: WaitingEntry[] = [];
|
|
167
|
+
try {
|
|
168
|
+
await readTimelinePages(paseo, manager.facts.id, { pages: WAITING_TIMELINE_PAGES, limit: WAITING_TIMELINE_LIMIT }, (page) => {
|
|
169
|
+
// Pages come oldest first; `waitingOf` wants newest first.
|
|
170
|
+
entries.push(...[...page].reverse());
|
|
171
|
+
});
|
|
172
|
+
} catch {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
waiting.push(...waitingOf({ id: manager.facts.id, workspaceId: manager.workspaceId! }, entries, workers));
|
|
176
|
+
}
|
|
177
|
+
const fallback = pendingFallbackOf(
|
|
178
|
+
managers.map((manager) => ({ id: manager.facts.id, workspaceId: manager.workspaceId! })),
|
|
179
|
+
incidents,
|
|
180
|
+
);
|
|
181
|
+
return { waiting, fallback };
|
|
182
|
+
}
|