@omercnet/paseo-agent-crew 0.3.0-next.99.1 → 1.0.0-next.150.1

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 CHANGED
@@ -8,16 +8,27 @@ parent-child relationships across workspace boundaries.
8
8
 
9
9
  ## Screenshots
10
10
 
11
- Names, workspaces, and task details in these screenshots are synthetic. The rendered page was
12
- rewritten before capture so no private project or session identifiers are published.
11
+ Agent, workspace, and task details come from an isolated synthetic workspace. Both PNGs were
12
+ captured at 2× pixel density after browser DevTools rewrote local paths and host labels and verified
13
+ that no private organization names remained in the rendered page.
13
14
 
14
15
  ### Crew overview
15
16
 
16
- ![Agent Crew descendant tree and status filters](docs/images/agent-crew-overview.png)
17
+ ![Agent Crew descendant tree and status filters](https://raw.githubusercontent.com/omercnet/paseo-plugins/main/agent-crew/docs/images/agent-crew-overview.png)
17
18
 
18
19
  ### Safe action confirmation
19
20
 
20
- ![Agent Crew nudge confirmation](docs/images/agent-crew-action.png)
21
+ ![Agent Crew nudge confirmation](https://raw.githubusercontent.com/omercnet/paseo-plugins/main/agent-crew/docs/images/agent-crew-action.png)
22
+
23
+ ## Auto open
24
+
25
+ When enabled in the plugin settings screen, Agent Crew opens the Explorer tab once for each new workspace observed on that host.
26
+
27
+ - Default: off.
28
+ - Existing workspaces are not opened when the setting is enabled.
29
+ - Disabling stops queued and future opens. Re-enabling resumes observation for workspaces created or updated afterward.
30
+ - Workspace claims are remembered on the daemon so reconnects and reloads do not reopen a workspace.
31
+ - A failed claim batch is retried once after two seconds, then dropped with a logged error.
21
32
 
22
33
  ## What it shows
23
34
 
@@ -99,46 +110,27 @@ From npm:
99
110
  paseo plugin install npm:@omercnet/paseo-agent-crew
100
111
  ```
101
112
 
102
- From GitHub:
103
-
104
- ```bash
105
- paseo plugin add omercnet/paseo-plugins:agent-crew
106
- ```
107
-
108
- Update a Git installation on either version, or an npm installation on Paseo 0.9, after reviewing
109
- the proposed revision:
113
+ Update the installed npm package after reviewing the proposed revision:
110
114
 
111
115
  ```bash
112
116
  paseo plugin update agent-crew
113
117
  ```
114
118
 
115
- From a local checkout on the Paseo daemon host, on Paseo 0.8 or 0.9:
116
-
117
- ```bash
118
- git clone https://github.com/omercnet/paseo-plugins.git
119
- cd paseo-plugins/agent-crew
120
- npm ci
121
- paseo plugin install "$PWD"
122
- ```
123
-
124
119
  Open a workspace, choose **New tab** in Explorer, then select **Agent Crew**. The **Open Agent Crew**
125
120
  Command Center action opens it directly.
126
121
 
127
122
  ## Develop
128
123
 
129
124
  ```bash
130
- npm ci
131
- npm run check
132
- npm test
133
- npm run test:coverage
134
- npm run typecheck
135
- paseo plugin install /absolute/path/to/paseo-agent-crew
136
- paseo plugin reload agent-crew
125
+ bun install
126
+ bun run check
127
+ bun run test
128
+ bun run test:coverage
129
+ bun run typecheck
137
130
  ```
138
131
 
139
- The manifest supports Paseo 0.8.x and the 0.9 beta line with
140
- `^0.8.0 || ^0.9.0-beta.1`. The project pins `@getpaseo/cli`, `@getpaseo/client`,
141
- `@getpaseo/plugin`, and `@getpaseo/protocol` to `0.9.0-beta.1` for development. React 19.1 and
132
+ The manifest requires Paseo `^0.9.0`. The project pins `@getpaseo/cli`, `@getpaseo/client`,
133
+ `@getpaseo/plugin`, and `@getpaseo/protocol` to `0.9.0` for development. React 19.1 and
142
134
  React Native 0.81 match the host.
143
135
 
144
136
  Release Please maintains versions, changelog entries, component tags, and GitHub releases from
@@ -0,0 +1,211 @@
1
+ import type { PluginClientContext } from "@getpaseo/plugin/client";
2
+ import { claimOpenedWorkspaces, MAX_AUTO_OPEN_CLAIM_BATCH } from "../shared/auto-open";
3
+ import {
4
+ agentCrewSettingsRpc,
5
+ agentCrewSettingsSchema,
6
+ DEFAULT_AUTO_OPEN_EXPLORER,
7
+ } from "../shared/settings";
8
+
9
+ const FLUSH_DELAY_MS = 400;
10
+ const RETRY_DELAY_MS = 2000;
11
+ const PAGE_LIMIT = 200;
12
+ const SETTINGS_POLL_MS = 15_000;
13
+
14
+ type ClaimJob = {
15
+ workspaceIds: string[];
16
+ retried: boolean;
17
+ };
18
+
19
+ type AutoOpenManager = {
20
+ setEnabled(enabled: boolean): void;
21
+ dispose(): void;
22
+ };
23
+
24
+ async function listWorkspaceIds(paseo: PluginClientContext["paseo"]): Promise<Set<string>> {
25
+ const workspaceIds = new Set<string>();
26
+ const seenCursors = new Set<string>();
27
+ let cursor: string | undefined;
28
+ while (true) {
29
+ const result = await paseo.workspaces.list({
30
+ page: { limit: PAGE_LIMIT, ...(cursor ? { cursor } : {}) },
31
+ });
32
+ for (const workspace of result.entries) workspaceIds.add(workspace.id);
33
+ if (!result.pageInfo.hasMore) return workspaceIds;
34
+ const nextCursor = result.pageInfo.nextCursor ?? undefined;
35
+ if (!nextCursor || seenCursors.has(nextCursor)) return workspaceIds;
36
+ seenCursors.add(nextCursor);
37
+ cursor = nextCursor;
38
+ }
39
+ }
40
+
41
+ function startAutoOpen(client: PluginClientContext): () => void {
42
+ const pending = new Set<string>();
43
+ const buffered = new Set<string>();
44
+ const jobs: ClaimJob[] = [];
45
+ let known: Set<string> | undefined;
46
+ let flushTimer: NodeJS.Timeout | undefined;
47
+ let removeObserver: (() => void) | undefined;
48
+ let releaseSubscription: (() => Promise<void>) | undefined;
49
+ let pumping = false;
50
+ let closed = false;
51
+
52
+ function schedule(delayMs: number) {
53
+ if (closed || pumping || flushTimer) return;
54
+ flushTimer = setTimeout(() => {
55
+ flushTimer = undefined;
56
+ void pump();
57
+ }, delayMs);
58
+ }
59
+
60
+ function enqueue(workspaceId: string) {
61
+ if (closed) return;
62
+ pending.add(workspaceId);
63
+ schedule(FLUSH_DELAY_MS);
64
+ }
65
+
66
+ function observe(workspaceId: string) {
67
+ if (!known) {
68
+ buffered.add(workspaceId);
69
+ return;
70
+ }
71
+ if (known.has(workspaceId)) return;
72
+ known.add(workspaceId);
73
+ enqueue(workspaceId);
74
+ }
75
+
76
+ function drainPending() {
77
+ const workspaceIds = [...pending];
78
+ pending.clear();
79
+ for (let index = 0; index < workspaceIds.length; index += MAX_AUTO_OPEN_CLAIM_BATCH) {
80
+ jobs.push({
81
+ workspaceIds: workspaceIds.slice(index, index + MAX_AUTO_OPEN_CLAIM_BATCH),
82
+ retried: false,
83
+ });
84
+ }
85
+ }
86
+
87
+ async function pump() {
88
+ if (closed || pumping) return;
89
+ pumping = true;
90
+ drainPending();
91
+ while (!closed && jobs.length > 0) {
92
+ const job = jobs.shift();
93
+ if (!job) break;
94
+ try {
95
+ const { claimed } = await client.rpc(claimOpenedWorkspaces, {
96
+ workspaceIds: job.workspaceIds,
97
+ });
98
+ for (const workspaceId of claimed) {
99
+ try {
100
+ client.openPanel("crew", { workspaceId, location: "explorer" });
101
+ } catch (error) {
102
+ console.error(
103
+ `Agent Crew auto-open could not open the panel for workspace ${workspaceId}`,
104
+ error,
105
+ );
106
+ }
107
+ }
108
+ } catch (error) {
109
+ console.error("Agent Crew auto-open claim failed", error);
110
+ if (!job.retried) {
111
+ jobs.unshift({ ...job, retried: true });
112
+ pumping = false;
113
+ schedule(RETRY_DELAY_MS);
114
+ return;
115
+ }
116
+ }
117
+ }
118
+ pumping = false;
119
+ if (!closed && pending.size > 0) schedule(FLUSH_DELAY_MS);
120
+ }
121
+
122
+ void client.paseo.workspaces
123
+ .list({ subscribe: {} })
124
+ .then(({ subscription }) => {
125
+ if (closed) return subscription.release();
126
+ releaseSubscription = () => subscription.release();
127
+ removeObserver = subscription.subscribe({
128
+ snapshot() {},
129
+ update(message) {
130
+ if (message.type !== "workspace_update" || message.payload.kind !== "upsert") return;
131
+ observe(message.payload.workspace.id);
132
+ },
133
+ });
134
+ return listWorkspaceIds(client.paseo).then((workspaceIds) => {
135
+ if (closed) return;
136
+ known = workspaceIds;
137
+ for (const workspaceId of buffered) observe(workspaceId);
138
+ buffered.clear();
139
+ });
140
+ })
141
+ .catch((error: unknown) => {
142
+ if (!closed) console.error("Agent Crew auto-open observation failed", error);
143
+ });
144
+
145
+ return () => {
146
+ closed = true;
147
+ pending.clear();
148
+ buffered.clear();
149
+ jobs.length = 0;
150
+ clearTimeout(flushTimer);
151
+ removeObserver?.();
152
+ void releaseSubscription?.();
153
+ releaseSubscription = undefined;
154
+ };
155
+ }
156
+
157
+ export function createAutoOpenManager(client: PluginClientContext): AutoOpenManager {
158
+ let enabled = DEFAULT_AUTO_OPEN_EXPLORER;
159
+ let activeCleanup: (() => void) | undefined;
160
+ let disposed = false;
161
+ let refreshRunning = false;
162
+ let generation = 0;
163
+ const pollTimer = setInterval(() => {
164
+ void refreshEnabled();
165
+ }, SETTINGS_POLL_MS);
166
+
167
+ function syncEnabled(nextEnabled: boolean) {
168
+ if (disposed || nextEnabled === enabled) return;
169
+ enabled = nextEnabled;
170
+ activeCleanup?.();
171
+ activeCleanup = enabled ? startAutoOpen(client) : undefined;
172
+ }
173
+
174
+ async function refreshEnabled() {
175
+ if (disposed || refreshRunning) return;
176
+ refreshRunning = true;
177
+ const requestGeneration = generation;
178
+ try {
179
+ const result = await client.rpc(agentCrewSettingsRpc.read, {});
180
+ if (disposed || requestGeneration !== generation) return;
181
+ if (result.status !== "ready") {
182
+ syncEnabled(DEFAULT_AUTO_OPEN_EXPLORER);
183
+ return;
184
+ }
185
+ const parsed = agentCrewSettingsSchema.safeParse(result.values);
186
+ syncEnabled(parsed.success ? parsed.data.autoOpenExplorer : DEFAULT_AUTO_OPEN_EXPLORER);
187
+ } catch (error) {
188
+ if (disposed || requestGeneration !== generation) return;
189
+ console.error("Agent Crew auto-open settings read failed", error);
190
+ syncEnabled(DEFAULT_AUTO_OPEN_EXPLORER);
191
+ } finally {
192
+ refreshRunning = false;
193
+ }
194
+ }
195
+
196
+ void refreshEnabled();
197
+
198
+ return {
199
+ setEnabled(nextEnabled) {
200
+ generation += 1;
201
+ syncEnabled(nextEnabled);
202
+ },
203
+ dispose() {
204
+ disposed = true;
205
+ generation += 1;
206
+ clearInterval(pollTimer);
207
+ activeCleanup?.();
208
+ activeCleanup = undefined;
209
+ },
210
+ };
211
+ }
package/client/crew.ts CHANGED
@@ -6,11 +6,46 @@ export type AgentEntry = Awaited<ReturnType<PaseoApi["agents"]["list"]>>["entrie
6
6
  type AgentSnapshot = AgentEntry["agent"];
7
7
 
8
8
  export function listenToCrewDirectory(paseo: PaseoApi, invalidate: () => void): () => void {
9
- const unsubscribeAgents = paseo.agents.subscribe(invalidate);
10
- const unsubscribeWorkspaces = paseo.workspaces.subscribe(invalidate);
9
+ let closed = false;
10
+ let removeAgentObserver: (() => void) | undefined;
11
+ let removeWorkspaceObserver: (() => void) | undefined;
12
+ let releaseAgentSubscription: (() => Promise<void>) | undefined;
13
+ let releaseWorkspaceSubscription: (() => Promise<void>) | undefined;
14
+
15
+ void paseo.agents
16
+ .list({ subscribe: {} })
17
+ .then(({ subscription }) => {
18
+ if (closed) return subscription.release();
19
+ releaseAgentSubscription = () => subscription.release();
20
+ removeAgentObserver = subscription.subscribe({
21
+ snapshot: invalidate,
22
+ update: invalidate,
23
+ });
24
+ })
25
+ .catch((error: unknown) => {
26
+ if (!closed) console.error("Agent Crew agent observation failed", error);
27
+ });
28
+
29
+ void paseo.workspaces
30
+ .list({ subscribe: {} })
31
+ .then(({ subscription }) => {
32
+ if (closed) return subscription.release();
33
+ releaseWorkspaceSubscription = () => subscription.release();
34
+ removeWorkspaceObserver = subscription.subscribe({
35
+ snapshot: invalidate,
36
+ update: invalidate,
37
+ });
38
+ })
39
+ .catch((error: unknown) => {
40
+ if (!closed) console.error("Agent Crew workspace observation failed", error);
41
+ });
42
+
11
43
  return () => {
12
- unsubscribeAgents();
13
- unsubscribeWorkspaces();
44
+ closed = true;
45
+ removeAgentObserver?.();
46
+ removeWorkspaceObserver?.();
47
+ void releaseAgentSubscription?.();
48
+ void releaseWorkspaceSubscription?.();
14
49
  };
15
50
  }
16
51
 
@@ -0,0 +1,78 @@
1
+ import { type PluginSurfaceProps, useSettings } from "@getpaseo/plugin/client";
2
+ import {
3
+ SettingsAction,
4
+ SettingsCard,
5
+ SettingsSection,
6
+ SettingsSwitch,
7
+ } from "@getpaseo/plugin/client/ui";
8
+ import { useMemo } from "react";
9
+ import { Text } from "react-native";
10
+ import { agentCrewSettings } from "../shared/settings";
11
+
12
+ export interface AgentCrewSettingsScreenProps extends PluginSurfaceProps {
13
+ onAutoOpenChange(enabled: boolean): void;
14
+ }
15
+
16
+ export function AgentCrewSettingsScreen({ onAutoOpenChange, theme }: AgentCrewSettingsScreenProps) {
17
+ const settings = useSettings(agentCrewSettings);
18
+ const styles = useMemo(
19
+ () => ({
20
+ text: { color: theme.colors.foreground },
21
+ error: { color: theme.colors.statusDanger },
22
+ }),
23
+ [theme],
24
+ );
25
+
26
+ if (settings.status === "loading") return <Text style={styles.text}>Loading settings…</Text>;
27
+ if (settings.status !== "ready") {
28
+ return (
29
+ <SettingsSection title="Agent Crew settings">
30
+ <Text accessibilityRole="alert" style={styles.error}>
31
+ {settings.error}
32
+ </Text>
33
+ <SettingsCard>
34
+ <SettingsAction
35
+ label="Read settings again"
36
+ actionLabel="Reload"
37
+ onPress={settings.reload}
38
+ />
39
+ {settings.status === "invalid" ? (
40
+ <SettingsAction
41
+ label="Replace invalid data with defaults"
42
+ actionLabel="Reset"
43
+ disabled={settings.saving}
44
+ onPress={async () => {
45
+ if (await settings.reset()) onAutoOpenChange(false);
46
+ }}
47
+ />
48
+ ) : null}
49
+ </SettingsCard>
50
+ </SettingsSection>
51
+ );
52
+ }
53
+
54
+ return (
55
+ <SettingsSection title="Explorer">
56
+ <SettingsCard>
57
+ <SettingsSwitch
58
+ label="Open Agent Crew automatically"
59
+ hint="Open the Explorer panel once for each new workspace observed while enabled"
60
+ value={settings.values.autoOpenExplorer}
61
+ disabled={settings.saving}
62
+ onValueChange={async (autoOpenExplorer) => {
63
+ const saved = await settings.save(
64
+ { ...settings.values, autoOpenExplorer },
65
+ settings.revision,
66
+ );
67
+ if (saved) onAutoOpenChange(autoOpenExplorer);
68
+ }}
69
+ />
70
+ </SettingsCard>
71
+ {settings.saveError ? (
72
+ <Text accessibilityRole="alert" style={styles.error}>
73
+ {settings.saveError}
74
+ </Text>
75
+ ) : null}
76
+ </SettingsSection>
77
+ );
78
+ }
package/index.client.tsx CHANGED
@@ -1,8 +1,23 @@
1
- import type { PluginClientContext } from "@getpaseo/plugin/client";
1
+ import type { PluginClientContext, PluginSurfaceProps } from "@getpaseo/plugin/client";
2
+ import { createAutoOpenManager } from "./client/auto-open";
2
3
  import { AgentCrew } from "./client/main";
4
+ import { AgentCrewSettingsScreen } from "./client/settings-screen";
5
+ import { agentCrewSettings } from "./shared/settings";
3
6
 
4
7
  export default function contribute(client: PluginClientContext) {
5
- client.addWorkspacePanel({
8
+ const autoOpen = createAutoOpenManager(client);
9
+
10
+ function SettingsSurface(props: PluginSurfaceProps) {
11
+ return <AgentCrewSettingsScreen {...props} onAutoOpenChange={autoOpen.setEnabled} />;
12
+ }
13
+
14
+ const removeSettings = client.addSettingsScreen({
15
+ id: agentCrewSettings.id,
16
+ title: "Agent Crew settings",
17
+ icon: "Settings",
18
+ Component: SettingsSurface,
19
+ });
20
+ const removeWorkspacePanel = client.addWorkspacePanel({
6
21
  id: "crew",
7
22
  title: "Agent Crew",
8
23
  icon: "Network",
@@ -10,7 +25,7 @@ export default function contribute(client: PluginClientContext) {
10
25
  locations: ["explorer"],
11
26
  Component: AgentCrew,
12
27
  });
13
- client.addCommandCenterItem({
28
+ const removeOpenCrew = client.addCommandCenterItem({
14
29
  id: "open-crew",
15
30
  title: "Open Agent Crew",
16
31
  icon: "Network",
@@ -20,5 +35,11 @@ export default function contribute(client: PluginClientContext) {
20
35
  openPanel("crew", { location: "explorer" });
21
36
  },
22
37
  });
23
- return () => {};
38
+
39
+ return () => {
40
+ removeOpenCrew();
41
+ removeWorkspacePanel();
42
+ removeSettings();
43
+ autoOpen.dispose();
44
+ };
24
45
  }
@@ -0,0 +1,10 @@
1
+ import type { PluginServerContext } from "@getpaseo/plugin/server";
2
+ import { createAutoOpenClaimHandler } from "./server/auto-open";
3
+ import { claimOpenedWorkspaces } from "./shared/auto-open";
4
+ import { agentCrewSettings } from "./shared/settings";
5
+
6
+ export default function contribute(server: PluginServerContext) {
7
+ server.registerSettings(agentCrewSettings);
8
+ server.handle(claimOpenedWorkspaces, createAutoOpenClaimHandler());
9
+ return () => {};
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-agent-crew",
3
- "version": "0.3.0-next.99.1",
3
+ "version": "1.0.0-next.150.1",
4
4
  "type": "module",
5
5
  "description": "A Paseo Explorer panel for visualizing and controlling every managed agent crew in a workspace.",
6
6
  "license": "MIT",
@@ -16,7 +16,7 @@
16
16
  "publishConfig": {
17
17
  "access": "public"
18
18
  },
19
- "packageManager": "npm@11.19.1",
19
+ "packageManager": "bun@1.4.2",
20
20
  "engines": {
21
21
  "node": ">=24"
22
22
  },
@@ -29,39 +29,37 @@
29
29
  "orchestration"
30
30
  ],
31
31
  "files": [
32
- "CHANGELOG.md",
33
32
  "LICENSE",
34
33
  "README.md",
35
34
  "client",
36
- "docs",
35
+ "shared",
36
+ "server",
37
37
  "index.client.tsx",
38
- "paseo-plugin.json",
39
- "package.json",
40
- "tsconfig.json"
38
+ "index.server.ts",
39
+ "paseo-plugin.json"
41
40
  ],
42
41
  "scripts": {
43
42
  "check": "biome check .",
44
43
  "check:write": "biome check --write .",
45
44
  "test": "vitest run",
46
- "package:release": "node scripts/package-release.ts",
47
45
  "test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=lcov --coverage.thresholds.lines=90 --coverage.thresholds.functions=85",
48
46
  "typecheck": "tsc --noEmit"
49
47
  },
50
48
  "devDependencies": {
51
49
  "@biomejs/biome": "^2.5.10",
52
- "@getpaseo/cli": "0.9.0-beta.1",
53
- "@getpaseo/client": "0.9.0-beta.1",
54
- "@getpaseo/plugin": "0.9.0-beta.1",
55
- "@getpaseo/protocol": "0.9.0-beta.1",
50
+ "@getpaseo/cli": "0.9.0",
51
+ "@getpaseo/client": "0.9.0",
52
+ "@getpaseo/plugin": "0.9.0",
53
+ "@getpaseo/protocol": "0.9.0",
56
54
  "@tanstack/react-query": "^5.102.3",
57
- "@types/node": "^24.5.2",
58
- "@types/react": "~19.2.0",
55
+ "@types/node": "^24.10.1",
56
+ "@types/react": "^19.2.18",
59
57
  "@vitest/coverage-v8": "^5.0.0",
60
58
  "fflate": "^0.8.3",
61
59
  "react": "19.1.0",
62
60
  "react-native": "0.81.5",
63
- "typescript": "^7.0.0",
61
+ "typescript": "^7.0.2",
64
62
  "vitest": "^5.0.0",
65
- "zod": "^4.4.3"
63
+ "zod": "4.4.3"
66
64
  }
67
65
  }
package/paseo-plugin.json CHANGED
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "id": "agent-crew",
3
- "requirements": {
4
- "paseo": "^0.8.0 || ^0.9.0-beta.1"
5
- }
3
+ "description": "A Paseo Explorer panel for visualizing and controlling every managed agent crew in a workspace.",
4
+ "requirements": { "paseo": "^0.9.0" }
6
5
  }
@@ -0,0 +1,86 @@
1
+ import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join } from "node:path";
4
+ import type { RpcInput } from "@getpaseo/plugin";
5
+ import { type claimOpenedWorkspaces, claimUnclaimedWorkspaces } from "../shared/auto-open";
6
+
7
+ export interface AutoOpenStore {
8
+ load(): Promise<ReadonlySet<string>>;
9
+ persist(next: ReadonlySet<string>): Promise<void>;
10
+ }
11
+
12
+ export function autoOpenDataFilePath(): string {
13
+ const home = process.env.PASEO_HOME ?? join(homedir(), ".paseo");
14
+ return join(home, "plugin-data", "agent-crew", "auto-open.json");
15
+ }
16
+
17
+ function parseWorkspaceIds(raw: string): Set<string> {
18
+ const parsed: unknown = JSON.parse(raw);
19
+ if (!Array.isArray(parsed)) {
20
+ throw new Error("auto-open store must contain an array of workspace IDs");
21
+ }
22
+ const values = new Set<string>();
23
+ for (const value of parsed) {
24
+ if (typeof value !== "string" || value.length === 0) {
25
+ throw new Error("auto-open store must contain only non-empty string workspace IDs");
26
+ }
27
+ values.add(value);
28
+ }
29
+ return values;
30
+ }
31
+
32
+ export function createFileAutoOpenStore(filePath: string = autoOpenDataFilePath()): AutoOpenStore {
33
+ return {
34
+ async load() {
35
+ let raw: string;
36
+ try {
37
+ raw = await readFile(filePath, "utf8");
38
+ } catch (error) {
39
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return new Set();
40
+ throw error;
41
+ }
42
+ try {
43
+ return parseWorkspaceIds(raw);
44
+ } catch (error) {
45
+ console.error("Agent Crew auto-open store load failed", { filePath, error });
46
+ throw error;
47
+ }
48
+ },
49
+ async persist(next) {
50
+ await mkdir(dirname(filePath), { recursive: true });
51
+ const temporary = `${filePath}.${process.pid}.tmp`;
52
+ await writeFile(temporary, `${JSON.stringify([...next], null, 2)}\n`, "utf8");
53
+ await rename(temporary, filePath);
54
+ },
55
+ };
56
+ }
57
+
58
+ export function createMemoryAutoOpenStore(initial: Iterable<string> = []): AutoOpenStore {
59
+ let values = new Set(initial);
60
+ return {
61
+ async load() {
62
+ return new Set(values);
63
+ },
64
+ async persist(next) {
65
+ values = new Set(next);
66
+ },
67
+ };
68
+ }
69
+
70
+ export function createAutoOpenClaimHandler(store: AutoOpenStore = createFileAutoOpenStore()) {
71
+ let chain: Promise<unknown> = Promise.resolve();
72
+ return async function handleClaim({ workspaceIds }: RpcInput<typeof claimOpenedWorkspaces>) {
73
+ const run = async () => {
74
+ const opened = await store.load();
75
+ const claimed = claimUnclaimedWorkspaces(opened, workspaceIds);
76
+ if (claimed.length === 0) return { claimed };
77
+ const next = new Set(opened);
78
+ for (const workspaceId of claimed) next.add(workspaceId);
79
+ await store.persist(next);
80
+ return { claimed };
81
+ };
82
+ const result = chain.then(run, run);
83
+ chain = result.catch(() => {});
84
+ return result;
85
+ };
86
+ }
@@ -0,0 +1,24 @@
1
+ import { defineRpc } from "@getpaseo/plugin";
2
+ import { z } from "zod";
3
+
4
+ export const MAX_AUTO_OPEN_CLAIM_BATCH = 1000;
5
+
6
+ export const claimOpenedWorkspaces = defineRpc({
7
+ name: "agent-crew.auto-open.claim",
8
+ input: z.object({
9
+ workspaceIds: z.array(z.string().min(1)).min(1).max(MAX_AUTO_OPEN_CLAIM_BATCH),
10
+ }),
11
+ output: z.object({ claimed: z.array(z.string()) }),
12
+ });
13
+
14
+ export function claimUnclaimedWorkspaces(
15
+ opened: ReadonlySet<string>,
16
+ candidates: readonly string[],
17
+ ): string[] {
18
+ const claimed: string[] = [];
19
+ for (const workspaceId of candidates) {
20
+ if (opened.has(workspaceId) || claimed.includes(workspaceId)) continue;
21
+ claimed.push(workspaceId);
22
+ }
23
+ return claimed;
24
+ }
@@ -0,0 +1,17 @@
1
+ import { defineSettings, settingsRpc } from "@getpaseo/plugin";
2
+ import { z } from "zod";
3
+
4
+ export const DEFAULT_AUTO_OPEN_EXPLORER = false;
5
+
6
+ export const agentCrewSettingsSchema = z.object({
7
+ autoOpenExplorer: z.boolean().default(DEFAULT_AUTO_OPEN_EXPLORER),
8
+ });
9
+
10
+ export const agentCrewSettings = defineSettings({
11
+ id: "agent-crew-preferences",
12
+ scope: "host",
13
+ version: 1,
14
+ schema: agentCrewSettingsSchema,
15
+ });
16
+
17
+ export const agentCrewSettingsRpc = settingsRpc(agentCrewSettings.id);
package/CHANGELOG.md DELETED
@@ -1,60 +0,0 @@
1
- # Changelog
2
-
3
- ## Unreleased
4
-
5
- ### Migration
6
-
7
- - migrate the plugin to the Paseo 0.8 runtime-entry layout
8
- - move client modules under `client/` and shared code under `shared/`
9
- - switch to first-class `parentAgentId` handling with bounded cycles
10
- - add an explicit pending-permission modal with Allow and Deny actions
11
- - pin Paseo plugin packages to `0.8.0-beta.1`
12
-
13
- ## [0.3.0](https://github.com/omercnet/paseo-plugins/compare/agent-crew-v0.2.3...agent-crew-v0.3.0) (2026-09-18)
14
-
15
-
16
- ### Features
17
-
18
- * **release:** publish plugins to npm ([#80](https://github.com/omercnet/paseo-plugins/issues/80)) ([3c93048](https://github.com/omercnet/paseo-plugins/commit/3c93048cfefda97d8c2bc1631e3428fb64bdad09))
19
-
20
-
21
- ### Bug Fixes
22
-
23
- * **agent-crew:** support Paseo 0.9 beta ([#90](https://github.com/omercnet/paseo-plugins/issues/90)) ([d2d3114](https://github.com/omercnet/paseo-plugins/commit/d2d3114bfcca63e0c184e9be4843a941a1fb56b6))
24
-
25
- ## [0.2.3](https://github.com/omercnet/paseo-plugins/compare/agent-crew-v0.2.2...agent-crew-v0.2.3) (2026-09-10)
26
-
27
-
28
- ### Bug Fixes
29
-
30
- * upgrade plugins to Paseo 0.8.0 ([c6b4e40](https://github.com/omercnet/paseo-plugins/commit/c6b4e4081fc9527de0ca6183a3903ff5ec4f93c4))
31
- * upgrade plugins to Paseo 0.8.0 ([d7ad454](https://github.com/omercnet/paseo-plugins/commit/d7ad4540d2e1875bdf4a4d21f76ded25cac2f3dc))
32
-
33
- ## [0.2.2](https://github.com/omercnet/paseo-agent-crew/compare/v0.2.1...v0.2.2) (2026-09-08)
34
-
35
-
36
- ### Bug Fixes
37
-
38
- * derive client API types from host SDK ([#7](https://github.com/omercnet/paseo-agent-crew/issues/7)) ([ffdfacf](https://github.com/omercnet/paseo-agent-crew/commit/ffdfacf9e4f0bd43f84e973176b2b2ffd7fe6350))
39
-
40
- ## [0.2.1](https://github.com/omercnet/paseo-agent-crew/compare/v0.2.0...v0.2.1) (2026-09-08)
41
-
42
-
43
- ### Bug Fixes
44
-
45
- * clean up Paseo 0.8 migration ([#5](https://github.com/omercnet/paseo-agent-crew/issues/5)) ([02912fb](https://github.com/omercnet/paseo-agent-crew/commit/02912fbb73956420ae8315bceea7025e71dd7a71))
46
-
47
- ## [0.2.0](https://github.com/omercnet/paseo-agent-crew/compare/v0.1.0...v0.2.0) (2026-09-08)
48
-
49
-
50
- ### Features
51
-
52
- * migrate agent crew to paseo 0.8 ([#3](https://github.com/omercnet/paseo-agent-crew/issues/3)) ([3014f1c](https://github.com/omercnet/paseo-agent-crew/commit/3014f1c2f1afb8187cb2ac0e86f2b442bb805911))
53
-
54
- ## 0.1.0 (2026-09-02)
55
-
56
- ### Features
57
-
58
- - add the workspace-wide Agent Crew Explorer panel
59
- - organize managed agents into collapsible cross-workspace delegation trees
60
- - add status filters, search, navigation, and guarded agent controls
Binary file
Binary file
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2024",
4
- "module": "ESNext",
5
- "moduleResolution": "Bundler",
6
- "lib": ["ES2024"],
7
- "jsx": "react-jsx",
8
- "strict": true,
9
- "skipLibCheck": true,
10
- "noEmit": true,
11
- "esModuleInterop": true,
12
- "allowSyntheticDefaultImports": true,
13
- "types": ["node"]
14
- },
15
- "include": ["**/*.ts", "**/*.tsx"]
16
- }