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,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Destructive Dashboard flows: delete and reassign
|
|
3
|
+
* (WP-211.2.2; REQ-054, REQ-055b, REQ-057d).
|
|
4
|
+
*
|
|
5
|
+
* These live in their own file because they are the only irreversible thing the
|
|
6
|
+
* Dashboard can do, and they have their own review and evidence boundary.
|
|
7
|
+
*
|
|
8
|
+
* The shape is always the same three steps, and none can be skipped:
|
|
9
|
+
*
|
|
10
|
+
* 1. ask the server what would be lost (`dryRun: true`);
|
|
11
|
+
* 2. show that answer and wait — the confirmation gate has no default "Yes",
|
|
12
|
+
* it simply has nothing pending until step 1 returned;
|
|
13
|
+
* 3. only then call the real RPC.
|
|
14
|
+
*
|
|
15
|
+
* Nothing here runs on mount, on a timer, or on a refresh: every path starts
|
|
16
|
+
* with a press (REQ-054f).
|
|
17
|
+
*
|
|
18
|
+
* Client rules: React Native primitives only, colours from `theme.colors`, no
|
|
19
|
+
* Node import, no `server/` import.
|
|
20
|
+
*/
|
|
21
|
+
import { type PluginSurfaceProps, useRpc, usePaseo } from "@getpaseo/plugin/client";
|
|
22
|
+
import { useQuery } from "@tanstack/react-query";
|
|
23
|
+
import { useMemo, useState, useSyncExternalStore } from "react";
|
|
24
|
+
import { ActivityIndicator, Pressable, Text, View } from "react-native";
|
|
25
|
+
import { tracesDeleteRpc, tracesReassignRpc, type WorkspaceState } from "../shared/contracts";
|
|
26
|
+
import type { dashboardStyles } from "./dashboard-model";
|
|
27
|
+
import {
|
|
28
|
+
OLDER_THAN_DAYS,
|
|
29
|
+
createConfirmationGate,
|
|
30
|
+
describeAction,
|
|
31
|
+
olderThanCutoff,
|
|
32
|
+
toneColor,
|
|
33
|
+
type DeleteScope,
|
|
34
|
+
} from "./dashboard-model";
|
|
35
|
+
import { errorMessageOf } from "./launch-manager";
|
|
36
|
+
|
|
37
|
+
export interface TraceActionsProps {
|
|
38
|
+
theme: PluginSurfaceProps["theme"];
|
|
39
|
+
layout: PluginSurfaceProps["layout"];
|
|
40
|
+
styles: ReturnType<typeof dashboardStyles>;
|
|
41
|
+
workspaceId: string;
|
|
42
|
+
scope: "workspace" | "trace";
|
|
43
|
+
traceId?: string;
|
|
44
|
+
/** Reassignment is only offered for a workspace Paseo no longer lists. */
|
|
45
|
+
workspaceState?: WorkspaceState;
|
|
46
|
+
onDone: () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function TraceActions({
|
|
50
|
+
theme,
|
|
51
|
+
layout,
|
|
52
|
+
styles,
|
|
53
|
+
workspaceId,
|
|
54
|
+
scope,
|
|
55
|
+
traceId,
|
|
56
|
+
workspaceState,
|
|
57
|
+
onDone,
|
|
58
|
+
}: TraceActionsProps) {
|
|
59
|
+
const deleteTraces = useRpc(tracesDeleteRpc);
|
|
60
|
+
const reassign = useRpc(tracesReassignRpc);
|
|
61
|
+
const paseo = usePaseo();
|
|
62
|
+
const gate = useMemo(() => createConfirmationGate(), []);
|
|
63
|
+
const pending = useSyncExternalStore(gate.subscribe, gate.getPending, gate.getPending);
|
|
64
|
+
const [busy, setBusy] = useState(false);
|
|
65
|
+
const [error, setError] = useState<string | null>(null);
|
|
66
|
+
|
|
67
|
+
const targets = useQuery({
|
|
68
|
+
queryKey: ["paseo-bm", "reassign-targets"],
|
|
69
|
+
queryFn: async () => {
|
|
70
|
+
const result = await paseo.workspaces.list({ sort: [{ key: "activity_at", direction: "desc" }] });
|
|
71
|
+
return result.entries
|
|
72
|
+
.filter((workspace) => !workspace.archivingAt && workspace.id !== workspaceId)
|
|
73
|
+
.map((workspace) => ({ id: workspace.id, label: workspace.title || workspace.name }));
|
|
74
|
+
},
|
|
75
|
+
enabled: workspaceState === "orphaned",
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/** Step 1: ask what would be lost, then hand it to the gate. */
|
|
79
|
+
const preview = async (deleteScope: DeleteScope) => {
|
|
80
|
+
setError(null);
|
|
81
|
+
setBusy(true);
|
|
82
|
+
try {
|
|
83
|
+
const result = await deleteTraces({ workspaceId, scope: deleteScope, dryRun: true });
|
|
84
|
+
gate.request({
|
|
85
|
+
kind: "delete",
|
|
86
|
+
scope: deleteScope,
|
|
87
|
+
preview: result.deleted,
|
|
88
|
+
running: result.deleted.running,
|
|
89
|
+
});
|
|
90
|
+
} catch (failure) {
|
|
91
|
+
setError(errorMessageOf(failure));
|
|
92
|
+
} finally {
|
|
93
|
+
setBusy(false);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const previewReassign = async (toWorkspaceId: string) => {
|
|
98
|
+
setError(null);
|
|
99
|
+
setBusy(true);
|
|
100
|
+
try {
|
|
101
|
+
const result = await reassign({ fromWorkspaceId: workspaceId, toWorkspaceId, dryRun: true });
|
|
102
|
+
gate.request({ kind: "reassign", fromWorkspaceId: workspaceId, toWorkspaceId, preview: result.moved });
|
|
103
|
+
} catch (failure) {
|
|
104
|
+
setError(errorMessageOf(failure));
|
|
105
|
+
} finally {
|
|
106
|
+
setBusy(false);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** Step 3: the only place either destructive RPC is called for real. */
|
|
111
|
+
const runPending = async () => {
|
|
112
|
+
setError(null);
|
|
113
|
+
setBusy(true);
|
|
114
|
+
try {
|
|
115
|
+
await gate.confirm(async (action) => {
|
|
116
|
+
if (action.kind === "delete") {
|
|
117
|
+
await deleteTraces({ workspaceId, scope: action.scope });
|
|
118
|
+
} else {
|
|
119
|
+
await reassign({ fromWorkspaceId: action.fromWorkspaceId, toWorkspaceId: action.toWorkspaceId });
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
onDone();
|
|
123
|
+
} catch (failure) {
|
|
124
|
+
setError(errorMessageOf(failure));
|
|
125
|
+
} finally {
|
|
126
|
+
setBusy(false);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const described = pending === null ? null : describeAction(pending);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<View style={{ gap: layout.compact ? 6 : 8 }}>
|
|
134
|
+
{error === null ? null : (
|
|
135
|
+
<Text style={[styles.body, { color: toneColor(theme, "danger") }]} accessibilityLiveRegion="polite">
|
|
136
|
+
{error}
|
|
137
|
+
</Text>
|
|
138
|
+
)}
|
|
139
|
+
|
|
140
|
+
{described === null ? (
|
|
141
|
+
<View style={{ flexDirection: layout.compact ? "column" : "row", gap: 8 }}>
|
|
142
|
+
{scope === "trace" && traceId !== undefined ? (
|
|
143
|
+
<Pressable
|
|
144
|
+
accessibilityRole="button"
|
|
145
|
+
accessibilityLabel="Delete this trace"
|
|
146
|
+
accessibilityState={{ disabled: busy }}
|
|
147
|
+
disabled={busy}
|
|
148
|
+
onPress={() => {
|
|
149
|
+
void preview({ traceId });
|
|
150
|
+
}}
|
|
151
|
+
style={styles.dangerButton}
|
|
152
|
+
>
|
|
153
|
+
<Text style={styles.dangerButtonText}>Delete this trace</Text>
|
|
154
|
+
</Pressable>
|
|
155
|
+
) : null}
|
|
156
|
+
|
|
157
|
+
{scope === "workspace" ? (
|
|
158
|
+
<>
|
|
159
|
+
<Pressable
|
|
160
|
+
accessibilityRole="button"
|
|
161
|
+
accessibilityLabel={`Delete traces older than ${OLDER_THAN_DAYS} days`}
|
|
162
|
+
accessibilityState={{ disabled: busy }}
|
|
163
|
+
disabled={busy}
|
|
164
|
+
onPress={() => {
|
|
165
|
+
void preview({ before: olderThanCutoff(new Date()) });
|
|
166
|
+
}}
|
|
167
|
+
style={styles.dangerButton}
|
|
168
|
+
>
|
|
169
|
+
<Text style={styles.dangerButtonText}>{`Delete traces older than ${OLDER_THAN_DAYS} days`}</Text>
|
|
170
|
+
</Pressable>
|
|
171
|
+
<Pressable
|
|
172
|
+
accessibilityRole="button"
|
|
173
|
+
accessibilityLabel="Delete every trace of this workspace"
|
|
174
|
+
accessibilityState={{ disabled: busy }}
|
|
175
|
+
disabled={busy}
|
|
176
|
+
onPress={() => {
|
|
177
|
+
void preview({ allOfWorkspace: true });
|
|
178
|
+
}}
|
|
179
|
+
style={styles.dangerButton}
|
|
180
|
+
>
|
|
181
|
+
<Text style={styles.dangerButtonText}>Delete all traces here</Text>
|
|
182
|
+
</Pressable>
|
|
183
|
+
</>
|
|
184
|
+
) : null}
|
|
185
|
+
|
|
186
|
+
{busy ? <ActivityIndicator color={styles.spinner.color} /> : null}
|
|
187
|
+
</View>
|
|
188
|
+
) : (
|
|
189
|
+
<View style={styles.card}>
|
|
190
|
+
<Text style={styles.sectionTitle}>{described.title}</Text>
|
|
191
|
+
{described.body.map((line) => (
|
|
192
|
+
<Text key={line} style={styles.body}>
|
|
193
|
+
{line}
|
|
194
|
+
</Text>
|
|
195
|
+
))}
|
|
196
|
+
<View style={{ flexDirection: layout.compact ? "column" : "row", gap: 8 }}>
|
|
197
|
+
{/* Cancel first, so the safe choice is the one under the thumb. */}
|
|
198
|
+
<Pressable
|
|
199
|
+
accessibilityRole="button"
|
|
200
|
+
accessibilityLabel="Cancel and keep the traces"
|
|
201
|
+
onPress={() => {
|
|
202
|
+
setError(null);
|
|
203
|
+
gate.cancel();
|
|
204
|
+
}}
|
|
205
|
+
style={styles.secondaryButton}
|
|
206
|
+
>
|
|
207
|
+
<Text style={styles.secondaryButtonText}>No, keep them</Text>
|
|
208
|
+
</Pressable>
|
|
209
|
+
<Pressable
|
|
210
|
+
accessibilityRole="button"
|
|
211
|
+
accessibilityLabel={described.confirmLabel}
|
|
212
|
+
accessibilityState={{ disabled: busy }}
|
|
213
|
+
disabled={busy}
|
|
214
|
+
onPress={() => {
|
|
215
|
+
void runPending();
|
|
216
|
+
}}
|
|
217
|
+
style={styles.dangerButton}
|
|
218
|
+
>
|
|
219
|
+
<Text style={styles.dangerButtonText}>{busy ? "Working…" : described.confirmLabel}</Text>
|
|
220
|
+
</Pressable>
|
|
221
|
+
</View>
|
|
222
|
+
</View>
|
|
223
|
+
)}
|
|
224
|
+
|
|
225
|
+
{/* Reassignment: only for a workspace Paseo no longer lists (REQ-057d). */}
|
|
226
|
+
{described === null && scope === "workspace" && workspaceState === "orphaned" ? (
|
|
227
|
+
<View style={{ gap: 6 }}>
|
|
228
|
+
<Text style={styles.body}>
|
|
229
|
+
Move these traces onto a workspace that exists. They keep the workspace they were
|
|
230
|
+
recorded under; only the grouping changes.
|
|
231
|
+
</Text>
|
|
232
|
+
{targets.isPending ? <ActivityIndicator color={styles.spinner.color} /> : null}
|
|
233
|
+
{targets.data?.length === 0 ? (
|
|
234
|
+
<Text style={styles.body}>There is no other workspace to move them to.</Text>
|
|
235
|
+
) : null}
|
|
236
|
+
{targets.data?.map((target) => (
|
|
237
|
+
<Pressable
|
|
238
|
+
key={target.id}
|
|
239
|
+
accessibilityRole="button"
|
|
240
|
+
accessibilityLabel={`Reassign these traces to ${target.label}`}
|
|
241
|
+
accessibilityState={{ disabled: busy }}
|
|
242
|
+
disabled={busy}
|
|
243
|
+
onPress={() => {
|
|
244
|
+
void previewReassign(target.id);
|
|
245
|
+
}}
|
|
246
|
+
style={styles.secondaryButton}
|
|
247
|
+
>
|
|
248
|
+
<Text style={styles.secondaryButtonText}>{`Reassign to ${target.label}`}</Text>
|
|
249
|
+
</Pressable>
|
|
250
|
+
))}
|
|
251
|
+
</View>
|
|
252
|
+
) : null}
|
|
253
|
+
</View>
|
|
254
|
+
);
|
|
255
|
+
}
|