@omercnet/paseo-omp 5.1.1 → 5.2.0-next.362.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.
@@ -0,0 +1,391 @@
1
+ import {
2
+ type PluginHostProps,
3
+ type PluginTimelineItemProps,
4
+ useRpc,
5
+ } from "@getpaseo/plugin/client";
6
+ import { Icon, TextInput, useToast } from "@getpaseo/plugin/client/ui";
7
+ import { useEffect, useMemo, useReducer, useState } from "react";
8
+ import { Pressable, Text, View } from "react-native";
9
+ import {
10
+ cancelOmpSubagent,
11
+ OMP_SUBAGENT_STEER_MAX_LENGTH,
12
+ type OmpSubagentStrip as OmpSubagentStripData,
13
+ steerOmpSubagent,
14
+ } from "../shared/subagents";
15
+ import {
16
+ activeSubagent,
17
+ formatElapsed,
18
+ INITIAL_SUBAGENT_STRIP_STATE,
19
+ isSendableDraft,
20
+ isSubagentRequestPending,
21
+ type OmpSubagentRow,
22
+ requestedSubmission,
23
+ type SubagentStripAction,
24
+ type SubagentStripState,
25
+ stopOutcomeMessage,
26
+ subagentRequestError,
27
+ subagentStripReducer,
28
+ } from "./subagents-state";
29
+
30
+ const ROW_HEIGHT = 28;
31
+
32
+ function createStyles(theme: PluginHostProps["theme"], compact: boolean) {
33
+ return {
34
+ root: { gap: 2, paddingVertical: 2 },
35
+ row: {
36
+ flexDirection: "row" as const,
37
+ alignItems: "center" as const,
38
+ gap: 8,
39
+ height: ROW_HEIGHT,
40
+ paddingLeft: compact ? 2 : 4,
41
+ },
42
+ dot: { width: 6, height: 6, borderRadius: 3, backgroundColor: theme.colors.accent },
43
+ dotFinished: { backgroundColor: theme.colors.foregroundMuted },
44
+ agent: { color: theme.colors.foreground, fontSize: 13, flexShrink: 0 },
45
+ description: { color: theme.colors.foregroundMuted, fontSize: 13, flexShrink: 1, minWidth: 0 },
46
+ elapsed: {
47
+ color: theme.colors.foregroundMuted,
48
+ fontSize: 12,
49
+ fontVariant: ["tabular-nums" as const],
50
+ },
51
+ actions: { flexDirection: "row" as const, alignItems: "center" as const, gap: 2 },
52
+ iconButton: {
53
+ width: 24,
54
+ height: 24,
55
+ borderRadius: 6,
56
+ alignItems: "center" as const,
57
+ justifyContent: "center" as const,
58
+ },
59
+ textButton: {
60
+ height: 24,
61
+ paddingHorizontal: 8,
62
+ borderRadius: 6,
63
+ justifyContent: "center" as const,
64
+ },
65
+ hovered: { backgroundColor: theme.colors.surface2 },
66
+ disabled: { opacity: 0.5 },
67
+ stopLabel: { color: theme.colors.statusDanger, fontSize: 12 },
68
+ keepLabel: { color: theme.colors.foregroundMuted, fontSize: 12 },
69
+ tooltip: {
70
+ position: "absolute" as const,
71
+ bottom: 28,
72
+ right: 0,
73
+ paddingHorizontal: 6,
74
+ paddingVertical: 2,
75
+ borderRadius: 6,
76
+ backgroundColor: theme.colors.surface2,
77
+ borderWidth: 1,
78
+ borderColor: theme.colors.border,
79
+ },
80
+ tooltipText: { color: theme.colors.foreground, fontSize: 11 },
81
+ input: {
82
+ flexBasis: 360,
83
+ flexShrink: 1,
84
+ minWidth: 0,
85
+ height: 24,
86
+ paddingHorizontal: 8,
87
+ borderRadius: 6,
88
+ borderWidth: 1,
89
+ borderColor: theme.colors.border,
90
+ backgroundColor: theme.colors.surface1,
91
+ color: theme.colors.foreground,
92
+ fontSize: 13,
93
+ },
94
+ };
95
+ }
96
+
97
+ type Styles = ReturnType<typeof createStyles>;
98
+
99
+ /** Re-renders once per second while `active`, for elapsed times. */
100
+ function useSecondTick(active: boolean): number {
101
+ const [now, setNow] = useState(() => Date.now());
102
+ useEffect(() => {
103
+ if (!active) return;
104
+ const timer = setInterval(() => setNow(Date.now()), 1_000);
105
+ return () => clearInterval(timer);
106
+ }, [active]);
107
+ return now;
108
+ }
109
+
110
+ /**
111
+ * Running OMP subagents of one `task` call, published by the provider below its tool card. Paseo
112
+ * shows provider subagents read-only, so the plugin adds steer and stop here.
113
+ */
114
+ export function OmpSubagentStrip({
115
+ agentId,
116
+ item,
117
+ theme,
118
+ layout,
119
+ }: PluginTimelineItemProps<OmpSubagentStripData>) {
120
+ const cancel = useRpc(cancelOmpSubagent);
121
+ const steer = useRpc(steerOmpSubagent);
122
+ const toast = useToast();
123
+ const styles = useMemo(() => createStyles(theme, layout.compact), [theme, layout.compact]);
124
+ const [state, dispatch] = useReducer(subagentStripReducer, INITIAL_SUBAGENT_STRIP_STATE);
125
+ const listed = item.data.subagents;
126
+ const active = activeSubagent(state);
127
+ const unlisted = active && !listed.some((row) => row.id === active.id) ? active : null;
128
+ const now = useSecondTick(listed.length > 0);
129
+
130
+ async function submit(): Promise<void> {
131
+ const request = requestedSubmission(state);
132
+ dispatch({ type: "submit" });
133
+ if (!request) return;
134
+ try {
135
+ if (request.kind === "stopping") {
136
+ const result = await cancel({ agentId, subagentId: request.subagent.id });
137
+ toast.show(stopOutcomeMessage(result.cancelled), {
138
+ variant: result.cancelled ? "success" : "default",
139
+ });
140
+ } else {
141
+ await steer({ agentId, subagentId: request.subagent.id, message: request.draft });
142
+ toast.show("Message sent to the subagent", { variant: "success" });
143
+ }
144
+ dispatch({ type: "settled" });
145
+ } catch (cause) {
146
+ toast.error(subagentRequestError(cause));
147
+ dispatch({ type: "failed" });
148
+ }
149
+ }
150
+
151
+ if (listed.length === 0 && !unlisted) return null;
152
+ return (
153
+ <View style={styles.root}>
154
+ {listed.map((row) => (
155
+ <SubagentRow
156
+ key={row.id}
157
+ row={row}
158
+ listed
159
+ now={now}
160
+ state={state}
161
+ styles={styles}
162
+ theme={theme}
163
+ dispatch={dispatch}
164
+ submit={submit}
165
+ />
166
+ ))}
167
+ {unlisted ? (
168
+ <SubagentRow
169
+ row={unlisted}
170
+ listed={false}
171
+ now={now}
172
+ state={state}
173
+ styles={styles}
174
+ theme={theme}
175
+ dispatch={dispatch}
176
+ submit={submit}
177
+ />
178
+ ) : null}
179
+ </View>
180
+ );
181
+ }
182
+
183
+ interface RowProps {
184
+ row: OmpSubagentRow;
185
+ /** False keeps the row that owns a draft visible after its subagent leaves the strip. */
186
+ listed: boolean;
187
+ now: number;
188
+ state: SubagentStripState;
189
+ styles: Styles;
190
+ theme: PluginHostProps["theme"];
191
+ dispatch: (action: SubagentStripAction) => void;
192
+ submit(): Promise<void>;
193
+ }
194
+
195
+ function SubagentRow(props: RowProps) {
196
+ const { row, listed, now, state, styles } = props;
197
+ const owner = activeSubagent(state)?.id === row.id ? state : null;
198
+ const steering = owner?.kind === "steering" || owner?.kind === "sending";
199
+ return (
200
+ <View style={styles.row}>
201
+ <View style={[styles.dot, listed ? null : styles.dotFinished]} />
202
+ <Text numberOfLines={1} style={styles.agent}>
203
+ {row.agent}
204
+ </Text>
205
+ {steering ? (
206
+ <SteerControls {...props} />
207
+ ) : (
208
+ <>
209
+ <Text numberOfLines={1} style={styles.description}>
210
+ {row.description ?? ""}
211
+ </Text>
212
+ {owner?.kind === "confirming-stop" || owner?.kind === "stopping" ? (
213
+ <StopConfirmation {...props} />
214
+ ) : (
215
+ <>
216
+ <Text style={styles.elapsed}>
217
+ {listed ? formatElapsed(now - row.runningSince) : "finished"}
218
+ </Text>
219
+ {listed ? <RowActions {...props} /> : null}
220
+ </>
221
+ )}
222
+ </>
223
+ )}
224
+ </View>
225
+ );
226
+ }
227
+
228
+ function RowActions({ row, state, styles, theme, dispatch }: RowProps) {
229
+ const pending = isSubagentRequestPending(state);
230
+ return (
231
+ <View style={styles.actions}>
232
+ <IconButton
233
+ icon="MessageSquare"
234
+ label="Steer: send this subagent a message at its next step"
235
+ disabled={pending}
236
+ styles={styles}
237
+ theme={theme}
238
+ onPress={() => dispatch({ type: "start-steer", subagent: row })}
239
+ />
240
+ <IconButton
241
+ icon="CircleStop"
242
+ label="Stop this subagent and its nested subagents; the parent keeps running"
243
+ disabled={pending}
244
+ styles={styles}
245
+ theme={theme}
246
+ onPress={() => dispatch({ type: "request-stop", subagent: row })}
247
+ />
248
+ </View>
249
+ );
250
+ }
251
+
252
+ function StopConfirmation({ state, styles, dispatch, submit }: RowProps) {
253
+ const stopping = state.kind === "stopping";
254
+ return (
255
+ <View style={styles.actions}>
256
+ <TextButton
257
+ label={stopping ? "Stopping..." : "Stop"}
258
+ accessibilityLabel="Confirm stopping this subagent"
259
+ labelStyle={styles.stopLabel}
260
+ disabled={stopping}
261
+ styles={styles}
262
+ onPress={() => void submit()}
263
+ />
264
+ <TextButton
265
+ label="Keep"
266
+ accessibilityLabel="Keep this subagent running"
267
+ labelStyle={styles.keepLabel}
268
+ disabled={stopping}
269
+ styles={styles}
270
+ onPress={() => dispatch({ type: "dismiss" })}
271
+ />
272
+ </View>
273
+ );
274
+ }
275
+
276
+ function SteerControls({ row, state, styles, theme, dispatch, submit }: RowProps) {
277
+ const sending = state.kind === "sending";
278
+ const draft = state.kind === "steering" || state.kind === "sending" ? state.draft : "";
279
+ return (
280
+ <>
281
+ <TextInput
282
+ accessibilityLabel={`Message for ${row.agent}`}
283
+ autoFocus
284
+ editable={!sending}
285
+ maxLength={OMP_SUBAGENT_STEER_MAX_LENGTH}
286
+ onChangeText={(text) => dispatch({ type: "edit-draft", draft: text })}
287
+ onKeyPress={(event) => {
288
+ if (event.nativeEvent.key === "Escape") dispatch({ type: "dismiss" });
289
+ }}
290
+ onSubmitEditing={() => void submit()}
291
+ placeholder="Message the subagent"
292
+ placeholderTextColor={theme.colors.foregroundMuted}
293
+ style={styles.input}
294
+ value={draft}
295
+ />
296
+ <View style={styles.actions}>
297
+ <IconButton
298
+ icon="SendHorizontal"
299
+ label="Send to the subagent (Enter)"
300
+ disabled={sending || !isSendableDraft(draft)}
301
+ styles={styles}
302
+ theme={theme}
303
+ onPress={() => void submit()}
304
+ />
305
+ <IconButton
306
+ icon="X"
307
+ label="Close (Esc)"
308
+ disabled={sending}
309
+ styles={styles}
310
+ theme={theme}
311
+ onPress={() => dispatch({ type: "dismiss" })}
312
+ />
313
+ </View>
314
+ </>
315
+ );
316
+ }
317
+
318
+ function IconButton({
319
+ icon,
320
+ label,
321
+ disabled,
322
+ styles,
323
+ theme,
324
+ onPress,
325
+ }: {
326
+ icon: string;
327
+ label: string;
328
+ disabled: boolean;
329
+ styles: Styles;
330
+ theme: PluginHostProps["theme"];
331
+ onPress(): void;
332
+ }) {
333
+ const [hovered, setHovered] = useState(false);
334
+ return (
335
+ <View>
336
+ <Pressable
337
+ accessibilityRole="button"
338
+ accessibilityLabel={label}
339
+ disabled={disabled}
340
+ onHoverIn={() => setHovered(true)}
341
+ onHoverOut={() => setHovered(false)}
342
+ onPress={onPress}
343
+ style={[
344
+ styles.iconButton,
345
+ hovered && !disabled ? styles.hovered : null,
346
+ disabled ? styles.disabled : null,
347
+ ]}
348
+ >
349
+ <Icon name={icon} size={14} color={theme.colors.foregroundMuted} />
350
+ </Pressable>
351
+ {hovered && !disabled ? (
352
+ <View pointerEvents="none" style={styles.tooltip}>
353
+ <Text numberOfLines={1} style={styles.tooltipText}>
354
+ {label}
355
+ </Text>
356
+ </View>
357
+ ) : null}
358
+ </View>
359
+ );
360
+ }
361
+
362
+ function TextButton({
363
+ label,
364
+ accessibilityLabel,
365
+ labelStyle,
366
+ disabled,
367
+ styles,
368
+ onPress,
369
+ }: {
370
+ label: string;
371
+ accessibilityLabel: string;
372
+ labelStyle: Styles["stopLabel"];
373
+ disabled: boolean;
374
+ styles: Styles;
375
+ onPress(): void;
376
+ }) {
377
+ const [hovered, setHovered] = useState(false);
378
+ return (
379
+ <Pressable
380
+ accessibilityRole="button"
381
+ accessibilityLabel={accessibilityLabel}
382
+ disabled={disabled}
383
+ onHoverIn={() => setHovered(true)}
384
+ onHoverOut={() => setHovered(false)}
385
+ onPress={onPress}
386
+ style={[styles.textButton, hovered && !disabled ? styles.hovered : null]}
387
+ >
388
+ <Text style={labelStyle}>{label}</Text>
389
+ </Pressable>
390
+ );
391
+ }
package/index.client.tsx CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  HUB_SIDEBAR_ITEM_ID,
39
39
  loadHubSnapshot,
40
40
  } from "./client/sidebar-compat";
41
+ import { OmpSubagentStrip } from "./client/subagents-strip";
41
42
  import {
42
43
  type ComposerPillSettings,
43
44
  composerPillSettings,
@@ -48,6 +49,7 @@ import { OMP_MCP_AUTH_TIMELINE_KIND, ompMcpAuthorizationTimelineSchema } from ".
48
49
  import { type OmpStore, storeForProvider } from "./shared/omp-store";
49
50
  import { ompImageTimelineSchema, transformOmpImageToolItem } from "./shared/provider-image";
50
51
  import { listOmpQuotas } from "./shared/quota";
52
+ import { OMP_SUBAGENT_STRIP_KIND, ompSubagentStripSchema } from "./shared/subagents";
51
53
 
52
54
  const PAGE_LIMIT = 200;
53
55
  const MAX_PAGES = 10;
@@ -237,6 +239,12 @@ export default function contribute(client: PluginClientContext) {
237
239
  schema: ompMcpAuthorizationTimelineSchema,
238
240
  Component: OmpMcpAuthorizationCard,
239
241
  });
242
+ const removeSubagentStripRenderer = client.addTimelineRenderer({
243
+ kind: OMP_SUBAGENT_STRIP_KIND,
244
+ version: 1,
245
+ schema: ompSubagentStripSchema,
246
+ Component: OmpSubagentStrip,
247
+ });
240
248
  const removeImageTransformer = client.addTimelineTransformer({
241
249
  id: "omp-images",
242
250
  query: { itemType: "tool_call" },
@@ -544,6 +552,7 @@ export default function contribute(client: PluginClientContext) {
544
552
  pills.clear();
545
553
  removeImageRenderer();
546
554
  removeMcpAuthorizationRenderer();
555
+ removeSubagentStripRenderer();
547
556
  removeImageTransformer();
548
557
  removeOpenConfig();
549
558
  removeConfigEntry();
package/index.server.ts CHANGED
@@ -25,9 +25,15 @@ import {
25
25
  } from "./server/provider/profile-providers";
26
26
  import { createOmpProvider } from "./server/provider/registration";
27
27
  import { OmpPublicError } from "./server/provider/security";
28
+ import { OmpSessionLimit } from "./server/provider/session-limit";
28
29
  import { resolveGetOmpProviderHealth } from "./server/provider-diagnostics";
29
30
  import { resolveListOmpQuotas } from "./server/quota";
30
31
  import { resolveListOmpSessions } from "./server/sessions";
32
+ import {
33
+ OmpSubagentControlRegistry,
34
+ resolveCancelOmpSubagent,
35
+ resolveSteerOmpSubagent,
36
+ } from "./server/subagents";
31
37
  import { resolveGetOmpSupportReport } from "./server/support-diagnostics";
32
38
  import { composerPillSettings } from "./shared/composer-pill-settings";
33
39
  import { listHubProcesses, tailHubLog } from "./shared/hub";
@@ -47,6 +53,7 @@ import { getOmpProviderHealth } from "./shared/provider-diagnostics";
47
53
  import { providerLaunchSettings } from "./shared/provider-launch-settings";
48
54
  import { listOmpQuotas } from "./shared/quota";
49
55
  import { listOmpSessions } from "./shared/sessions";
56
+ import { cancelOmpSubagent, steerOmpSubagent } from "./shared/subagents";
50
57
  import { getOmpSupportReport } from "./shared/support-diagnostics";
51
58
 
52
59
  function scoped<T extends { store?: OmpStore }, R>(handler: (input: T) => R) {
@@ -62,6 +69,12 @@ export default function contribute(server: PluginServerContext) {
62
69
  throw new OmpPublicError("OMP host launch settings are unavailable");
63
70
  };
64
71
  const browserAuthorizationRegistry = new OmpBrowserAuthorizationRegistry();
72
+ const sessionLimit = new OmpSessionLimit(async () => {
73
+ const current = await launchSettings.read();
74
+ return current.status === "ready" ? current.values.maxSessions : undefined;
75
+ });
76
+ const removeSessionLimitListener = launchSettings.subscribe(() => sessionLimit.refresh());
77
+ const subagentControlRegistry = new OmpSubagentControlRegistry();
65
78
  const protocolViolations = new OmpProtocolViolationCollector();
66
79
  const operationalFailures = new OmpOperationalFailureCollector();
67
80
  const profiles = discoverOmpProfilesSync();
@@ -70,6 +83,8 @@ export default function contribute(server: PluginServerContext) {
70
83
  server.registerProvider(
71
84
  createProfileOmpProvider(profile, {
72
85
  browserAuthorizationRegistry,
86
+ sessionLimit,
87
+ subagentControlRegistry,
73
88
  reportProtocolViolation: protocolViolations.report,
74
89
  reportOperationalFailure: operationalFailures.report,
75
90
  resolveHostInheritEnv,
@@ -97,6 +112,12 @@ export default function contribute(server: PluginServerContext) {
97
112
  server.handle(openOmpMcpAuthorizationInPaseoBrowser, (input) =>
98
113
  resolveOpenOmpMcpAuthorizationInPaseoBrowser(input, browserAuthorizationRegistry),
99
114
  );
115
+ server.handle(cancelOmpSubagent, (input) =>
116
+ resolveCancelOmpSubagent(input, subagentControlRegistry),
117
+ );
118
+ server.handle(steerOmpSubagent, (input) =>
119
+ resolveSteerOmpSubagent(input, subagentControlRegistry),
120
+ );
100
121
  const removeSessionOpenHook = server.before("agent.session_open", ({ request }) => {
101
122
  if (request.provider !== "omp-plugin" && !request.provider.startsWith("omp-plugin-")) return;
102
123
  return withOmpSessionOpenEnv(request);
@@ -104,6 +125,8 @@ export default function contribute(server: PluginServerContext) {
104
125
  server.registerProvider(
105
126
  createOmpProvider({
106
127
  browserAuthorizationRegistry,
128
+ sessionLimit,
129
+ subagentControlRegistry,
107
130
  reportProtocolViolation: protocolViolations.report,
108
131
  reportOperationalFailure: operationalFailures.report,
109
132
  resolveHostInheritEnv,
@@ -111,6 +134,8 @@ export default function contribute(server: PluginServerContext) {
111
134
  );
112
135
  return () => {
113
136
  browserAuthorizationRegistry.clear();
137
+ removeSessionLimitListener();
138
+ subagentControlRegistry.clear();
114
139
  removeSessionOpenHook();
115
140
  };
116
141
  }
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-omp",
3
- "version": "5.1.1",
3
+ "version": "5.2.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@omercnet/paseo-omp",
9
- "version": "5.1.1",
9
+ "version": "5.2.0",
10
10
  "dependencies": {
11
11
  "@getpaseo/protocol": "0.14.0",
12
- "@modelcontextprotocol/sdk": "1.30.0",
12
+ "@modelcontextprotocol/sdk": "1.32.0",
13
13
  "yaml": "^2.9.0"
14
14
  }
15
15
  },
@@ -35,9 +35,9 @@
35
35
  }
36
36
  },
37
37
  "node_modules/@modelcontextprotocol/sdk": {
38
- "version": "1.30.0",
39
- "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.30.0.tgz",
40
- "integrity": "sha512-xKd8OIzlqNzcqcNumGAa6g+PW2kjD5vrpcKOnfldAUPP3j7lnqMPwlTXQm8gF+UwH72z0lqaRbjr9hqGz0eITA==",
38
+ "version": "1.32.0",
39
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.32.0.tgz",
40
+ "integrity": "sha512-8BviX/hK4Gd2eL1KdTwm0gfl4d3wdoErW0Jd00h6nomUAtk6IJk5vwQJc6kfWtJTzN5OEV/lgIiJKI8fiDGAlA==",
41
41
  "license": "MIT",
42
42
  "dependencies": {
43
43
  "@hono/node-server": "^1.19.9 || ^2.0.5",
@@ -435,9 +435,9 @@
435
435
  }
436
436
  },
437
437
  "node_modules/express-rate-limit": {
438
- "version": "8.7.1",
439
- "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.1.tgz",
440
- "integrity": "sha512-2FXbqkygil3mZiXzEtM0IjE/UBmBNot0J1GEVrJT4JbrvwOSoEtqtO+wHbiMQELWe1eNvHoYRg2dkjOid+6JkA==",
438
+ "version": "8.7.2",
439
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.2.tgz",
440
+ "integrity": "sha512-U1ScocxDD9WI09LaBVypvNkOtNR5yWAmZLkuKJdAzIchA4YKFYF6GT5IKER7epG58oXtjvGlV0/6ii0F83EyZg==",
441
441
  "license": "MIT",
442
442
  "dependencies": {
443
443
  "debug": "^4.4.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omercnet/paseo-omp",
3
- "version": "5.1.1",
3
+ "version": "5.2.0-next.362.1",
4
4
  "type": "module",
5
5
  "description": "Paseo integration for OMP, including its direct provider and workspace tooling.",
6
6
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@getpaseo/protocol": "0.14.0",
58
- "@modelcontextprotocol/sdk": "1.30.0",
58
+ "@modelcontextprotocol/sdk": "1.32.0",
59
59
  "yaml": "^2.9.0"
60
60
  },
61
61
  "devDependencies": {
@@ -64,22 +64,19 @@
64
64
  "@getpaseo/client": "0.14.0",
65
65
  "@getpaseo/plugin": "0.14.0",
66
66
  "@getpaseo/server": "0.14.0",
67
- "@oh-my-pi/pi-agent-core": "18.12.6",
68
- "@oh-my-pi/pi-coding-agent": "18.12.6",
67
+ "@oh-my-pi/pi-agent-core": "18.14.0",
68
+ "@oh-my-pi/pi-coding-agent": "18.14.0",
69
69
  "@tanstack/react-query": "^5.102.3",
70
70
  "@types/node": "^24.10.1",
71
71
  "@types/react": "^19.2.18",
72
72
  "@vitest/coverage-v8": "^5.0.0",
73
73
  "esbuild": "0.28.2",
74
+ "pino": "10.4.0",
74
75
  "react": "19.1.0",
75
76
  "react-native": "0.81.5",
76
- "pino": "10.3.1",
77
77
  "tsx": "^4.20.6",
78
78
  "typescript": "^7.0.2",
79
79
  "vitest": "^5.0.0",
80
- "zod": "4.4.3"
81
- },
82
- "overrides": {
83
- "qs": "6.16.1"
80
+ "zod": "4.6.7"
84
81
  }
85
82
  }
@@ -1,9 +1,11 @@
1
- import { execFileSync } from "node:child_process";
1
+ import { execSync } from "node:child_process";
2
2
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
3
  import { dirname, join } from "node:path";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
5
 
6
6
  const projectRoot = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ // Run through the shell so Windows resolves npm.cmd: Node refuses to spawn .cmd files without one.
8
+ const INSTALL_COMMAND = "npm ci --omit=dev --ignore-scripts --workspaces=false";
7
9
 
8
10
  function loadRootCatalog(root) {
9
11
  for (let directory = root; ; directory = dirname(directory)) {
@@ -27,6 +29,8 @@ function stageManifest(root, catalog, install) {
27
29
  manifest.dependencies[name] = catalog[name];
28
30
  }
29
31
  delete manifest.devDependencies;
32
+ // npm-shrinkwrap.json is generated without overrides, so npm ci rejects a manifest that keeps them.
33
+ delete manifest.overrides;
30
34
 
31
35
  writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
32
36
  try {
@@ -36,18 +40,11 @@ function stageManifest(root, catalog, install) {
36
40
  }
37
41
  }
38
42
 
39
- export function prepareDependencies(root = projectRoot, execute = execFileSync) {
43
+ export function prepareDependencies(root = projectRoot, execute = execSync) {
40
44
  const catalog = loadRootCatalog(root);
41
45
  if (!catalog) return false;
42
46
  stageManifest(root, catalog, () => {
43
- execute(
44
- process.platform === "win32" ? "npm.cmd" : "npm",
45
- ["ci", "--omit=dev", "--ignore-scripts", "--workspaces=false"],
46
- {
47
- cwd: root,
48
- stdio: "inherit",
49
- },
50
- );
47
+ execute(INSTALL_COMMAND, { cwd: root, stdio: "inherit" });
51
48
  });
52
49
  return true;
53
50
  }
@@ -1,9 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import type { RpcInput } from "@getpaseo/plugin";
3
3
  import type { openOmpMcpAuthorizationInPaseoBrowser } from "../shared/mcp";
4
+ import { MAX_SESSIONS_UPPER_BOUND } from "../shared/provider-launch-settings";
4
5
  import { OmpPublicError } from "./provider/security";
5
6
 
6
- const MAX_REGISTERED_SESSIONS = 32;
7
+ // One registration per live session. The shared session limit refuses sessions first.
8
+ const MAX_REGISTERED_SESSIONS = MAX_SESSIONS_UPPER_BOUND;
7
9
  const MAX_AUTHORIZATIONS_PER_SESSION = 16;
8
10
 
9
11
  type BrowserAuthorizationOpener = (url: string) => Promise<void>;
@@ -1,2 +1,2 @@
1
1
  /** Release Please keeps this bundle-safe constant aligned with package.json. */
2
- export const PASEO_OMP_PACKAGE_VERSION = "5.1.1"; // x-release-please-version
2
+ export const PASEO_OMP_PACKAGE_VERSION = "5.2.0-next.362.1"; // x-release-please-version