@omercnet/paseo-agent-crew 0.3.0-next.129.1 → 0.3.0-next.137.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
@@ -20,6 +20,16 @@ that no private organization names remained in the rendered page.
20
20
 
21
21
  ![Agent Crew nudge confirmation](https://raw.githubusercontent.com/omercnet/paseo-plugins/main/agent-crew/docs/images/agent-crew-action.png)
22
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.
32
+
23
33
  ## What it shows
24
34
 
25
35
  - Every non-archived managed agent in the current workspace, organized into orchestration trees.
@@ -100,46 +110,27 @@ From npm:
100
110
  paseo plugin install npm:@omercnet/paseo-agent-crew
101
111
  ```
102
112
 
103
- From GitHub:
104
-
105
- ```bash
106
- paseo plugin add omercnet/paseo-plugins:agent-crew
107
- ```
108
-
109
- Update a Git installation on either version, or an npm installation on Paseo 0.9, after reviewing
110
- the proposed revision:
113
+ Update the installed npm package after reviewing the proposed revision:
111
114
 
112
115
  ```bash
113
116
  paseo plugin update agent-crew
114
117
  ```
115
118
 
116
- From a local checkout on the Paseo daemon host, on Paseo 0.8 or 0.9:
117
-
118
- ```bash
119
- git clone https://github.com/omercnet/paseo-plugins.git
120
- cd paseo-plugins/agent-crew
121
- npm ci
122
- paseo plugin install "$PWD"
123
- ```
124
-
125
119
  Open a workspace, choose **New tab** in Explorer, then select **Agent Crew**. The **Open Agent Crew**
126
120
  Command Center action opens it directly.
127
121
 
128
122
  ## Develop
129
123
 
130
124
  ```bash
131
- npm ci
132
- npm run check
133
- npm test
134
- npm run test:coverage
135
- npm run typecheck
136
- paseo plugin install /absolute/path/to/paseo-agent-crew
137
- 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
138
130
  ```
139
131
 
140
- The manifest supports Paseo 0.8.x and the 0.9 beta line with
141
- `^0.8.0 || ^0.9.0-beta.1`. The project pins `@getpaseo/cli`, `@getpaseo/client`,
142
- `@getpaseo/plugin`, and `@getpaseo/protocol` to `0.9.0-beta.1` for development. React 19.1 and
132
+ The manifest supports Paseo `>=0.8.0 <0.10.0`. The project pins `@getpaseo/cli`, `@getpaseo/client`,
133
+ `@getpaseo/plugin`, and `@getpaseo/protocol` to `0.9.0` for development. React 19.1 and
143
134
  React Native 0.81 match the host.
144
135
 
145
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
+ }
@@ -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.129.1",
3
+ "version": "0.3.0-next.137.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
  },
@@ -32,36 +32,34 @@
32
32
  "LICENSE",
33
33
  "README.md",
34
34
  "client",
35
+ "shared",
36
+ "server",
35
37
  "index.client.tsx",
38
+ "index.server.ts",
36
39
  "paseo-plugin.json"
37
40
  ],
38
41
  "scripts": {
39
- "check": "biome check --error-on-warnings .",
42
+ "check": "biome check .",
40
43
  "check:write": "biome check --write .",
41
- "test": "run-s test:ci:*",
42
- "package:release": "node scripts/package-release.ts",
44
+ "test": "vitest run",
43
45
  "test:coverage": "vitest run --coverage --coverage.reporter=text --coverage.reporter=lcov --coverage.thresholds.lines=90 --coverage.thresholds.functions=85",
44
- "typecheck": "tsc --noEmit",
45
- "build": "node ../.github/scripts/build-plugin.mjs",
46
- "test:ci:unit": "vitest run"
46
+ "typecheck": "tsc --noEmit"
47
47
  },
48
48
  "devDependencies": {
49
- "@biomejs/biome": "^2.5.14",
50
- "@getpaseo/cli": "0.9.0-beta.1",
51
- "@getpaseo/client": "0.9.0-beta.1",
52
- "@getpaseo/plugin": "0.9.0-beta.1",
53
- "@getpaseo/protocol": "0.9.0-beta.1",
54
- "@getpaseo/server": "0.9.0-beta.1",
49
+ "@biomejs/biome": "^2.5.10",
50
+ "@getpaseo/cli": "0.9.0",
51
+ "@getpaseo/client": "0.9.0",
52
+ "@getpaseo/plugin": "0.9.0",
53
+ "@getpaseo/protocol": "0.9.0",
55
54
  "@tanstack/react-query": "^5.102.3",
56
- "@types/node": "^24.5.2",
57
- "@types/react": "~19.2.0",
55
+ "@types/node": "^24.10.1",
56
+ "@types/react": "^19.2.18",
58
57
  "@vitest/coverage-v8": "^5.0.0",
59
58
  "fflate": "^0.8.3",
60
- "npm-run-all2": "^9.0.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,6 @@
1
1
  {
2
2
  "id": "agent-crew",
3
3
  "requirements": {
4
- "paseo": "^0.8.0 || ^0.9.0-beta.1"
4
+ "paseo": ">=0.8.0 <0.10.0"
5
5
  }
6
6
  }
@@ -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);