@omercnet/paseo-omp 1.3.3 → 2.0.0-next.245.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/OVERVIEW.md +1 -1
- package/README.md +8 -7
- package/client/hub-sidebar.tsx +5 -42
- package/client/sidebar-compat.ts +1 -31
- package/index.client.tsx +13 -36
- package/npm-shrinkwrap.json +2 -2
- package/package.json +4 -4
- package/paseo-plugin.json +1 -1
- package/server/package-version.ts +1 -1
- package/server/provider/omp-rpc-protocol.ts +32 -0
- package/server/provider/profile-providers.ts +15 -13
- package/server/provider/registration.ts +74 -20
- package/server/provider/session.ts +4 -1
- package/server/provider/timeline-projector.ts +3 -0
- package/server/quota.ts +8 -2
package/OVERVIEW.md
CHANGED
|
@@ -2,7 +2,7 @@ OMP adds an agent provider for the OMP coding agent, plus an OMP sidebar and wor
|
|
|
2
2
|
|
|
3
3
|
## Setup
|
|
4
4
|
|
|
5
|
-
- Paseo 0.
|
|
5
|
+
- Paseo ^0.11.0.
|
|
6
6
|
- OMP 18.1.15 or newer on the daemon host, speaking RPC protocol v2. Older runtimes are rejected.
|
|
7
7
|
- Provider options let you change the OMP command, set environment values, choose an output redaction mode and set a session directory and timeout. Named OMP profiles appear as separate `OMP · <profile>` providers.
|
|
8
8
|
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ before capture.
|
|
|
24
24
|
|
|
25
25
|
## Quick start
|
|
26
26
|
|
|
27
|
-
Requirements: Paseo `^0.
|
|
27
|
+
Requirements: Paseo `^0.11.0`, OMP `18.1.15` or newer, and OMP RPC protocol v2. The config screen and Hub row use the 0.11 screen and sidebar APIs, so Paseo 0.9 and 0.10 hosts are no longer supported.
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
30
|
paseo plugin install npm:@omercnet/paseo-omp@<version>
|
|
@@ -40,7 +40,7 @@ Open the **OMP** sidebar to review the provider-profile contract, choose which c
|
|
|
40
40
|
- [Core-provider issue and parity audit](docs/core-provider-issue-audit.md)
|
|
41
41
|
- [Release checklist](docs/release-checklist.md)
|
|
42
42
|
|
|
43
|
-
The plugin uses public Paseo 0.
|
|
43
|
+
The plugin uses public Paseo 0.11 provider and client contracts and registers distinct `omp-plugin` and `omp-plugin-<profile>` identities; it does not modify the bundled `omp` provider.
|
|
44
44
|
|
|
45
45
|
## Named OMP profiles
|
|
46
46
|
|
|
@@ -55,7 +55,7 @@ Profile selection is request-local: concurrent clients cannot change each other'
|
|
|
55
55
|
## Paseo provider SDK coverage
|
|
56
56
|
|
|
57
57
|
The capability inventory is pinned to `@getpaseo/plugin` 0.11.1, with nightly compatibility checks
|
|
58
|
-
against
|
|
58
|
+
against the latest and beta channels. Provider capability scoring covers
|
|
59
59
|
all 17 entries in the SDK's `PROVIDER_CAPABILITIES`; the separate provider/client SDK table below
|
|
60
60
|
also tracks the 0.11 registration, screen, and sidebar APIs. This measures strict SDK surface
|
|
61
61
|
coverage, not general product quality.
|
|
@@ -95,7 +95,7 @@ Provider functionality outside the capability flags is tracked separately:
|
|
|
95
95
|
| --- | ---: | --- |
|
|
96
96
|
| Registration metadata and sanitized SVG icon | **100%** | Stable `omp-plugin` identity, label, description, and bundled icon. |
|
|
97
97
|
| Strict `providerOptionsSchema` | **100%** | Command, literal environment, names-only profile inherited environment, output-redaction mode, session directory, RPC timeout, and role-model options are validated and normalized. Host-wide inherited names are separately persisted and merged at launch. |
|
|
98
|
-
| Availability diagnostics | **50%** | The plugin implements a `checkAvailability` hook with bounded checks that distinguish missing, unrunnable, incompatible, and available OMP runtimes, but Paseo 0.
|
|
98
|
+
| Availability diagnostics | **50%** | The plugin implements a `checkAvailability` hook with bounded checks that distinguish missing, unrunnable, incompatible, and available OMP runtimes, but Paseo 0.11.1 does not call it. The host uses the `status()` hook below instead, which reuses this probe for the default launch. |
|
|
99
99
|
| Catalog cache identity | **100%** | Hash includes effective options, merged host/profile inherited-environment names, settings, scope, cwd, and default command, but never resolves or fingerprints inherited values. |
|
|
100
100
|
| Models, modes, and thinking catalog | **100%** | Native catalog is mapped to opaque public model IDs with committed defaults and permission-gated modes. |
|
|
101
101
|
| Connection `send` / `onEvent` / `close` lifecycle | **100%** | Request correlation, multi-session ownership, process recovery, teardown, and provider reload/removal are covered. |
|
|
@@ -105,8 +105,9 @@ Provider functionality outside the capability flags is tracked separately:
|
|
|
105
105
|
| Commands and committed session state events | **100%** | Publishes `session.commands`, `session.opened`, `session.config`, `session.ready`, and request completion in protocol order. |
|
|
106
106
|
| Built-in timeline snapshots | **100%** | Assistant, reasoning, tools, todos, notifications, errors, compaction, and friendly MCP labels use stable IDs and complete snapshots. OMP emits client-safe PNG/JPEG when possible; validated legacy WebP images render on capable clients with an explicit per-image fallback elsewhere. |
|
|
107
107
|
| Usage reporting | **100%** | Periodic, post-compaction, fallback, terminal, timeout, and recovered-runtime samples publish `session.usage`. |
|
|
108
|
-
| 0.11 provider `status()` | **
|
|
109
|
-
| 0.11
|
|
108
|
+
| 0.11 provider `status()` | **75%** | Registered for the plugin-owned default launch (`OMP_COMMAND` or `omp`, plus the fixed `--profile` wrapper for profile providers), using the bounded `--version`/`--help` probe. Only a missing executable reports `available: false`; slow, failing, or unrecognized probes stay available with a diagnostic. An unexpected internal probe error is not caught here, so the host reports it as unavailable. Available results are shared for 30 seconds; an unavailable result is never remembered, so installing `omp` takes effect on the next host call. The request carries only an optional daemon `launch` and never `providerOptions`, so a per-agent `providerOptions.command` or `env` is invisible. **Breaking on 0.11 hosts:** the host gates create, resume, and draft commands/features on this status, so a missing default executable blocks them all, including agents configured with an otherwise valid custom command. Any request `launch` is ignored. |
|
|
109
|
+
| 0.11 provider `command` and daemon launch | **0%** | Deliberately not declared. Declaring `command` makes the daemon resolve it against its own `PATH` and reject every `connect` when the default binary is missing. The resulting `launch.env` is the daemon's entire process environment plus overrides, indistinguishable from configured values, so adopting it would defeat names-only `inheritEnv` and the fixed profile store environment. The host never supplies `launch` without a declared `command`; `connect` and `getCatalogCacheKey` do not read it, each pinned by `tests/provider-catalog.test.ts`. Revisit if the host stops gating on the default binary or exposes configured `env` separately from the daemon environment. |
|
|
110
|
+
| 0.11 screens and sidebar items | **100%** | The global config surface migrates to `addScreen({ id: "config" })`, preserving legacy links and encoding profile selection in screen params. The Hub status row uses `addSidebarHeaderItem` + `SidebarRow`, lists processes and tails logs in a reusable popover. The legacy static config row and surface were removed with the 0.11 floor. |
|
|
110
111
|
| 0.11 usage source | **0%** | Not registered. `server/quota.ts` reads OMP's recorded `usage_history`, not live quota, so there is no live fetcher to register. Upstream Claude and Codex sources fetch live quota for the Anthropic and Codex OAuth credentials in the daemon's default or `OMP_PROFILE` store; other providers and named profiles remain covered only by this plugin's historical quota pill. |
|
|
111
112
|
|
|
112
113
|
Tracking rules:
|
|
@@ -124,7 +125,7 @@ Existing agents whose provider is `omp` remain owned by the bundled provider. Ne
|
|
|
124
125
|
|
|
125
126
|
OMP `18.1.15` is the oldest version tested end to end. The direct provider's hard compatibility gate is `rpc-ui` protocol v2: metadata-free legacy ready frames and v1-only runtimes are rejected before a provider session opens because they cannot support the advertised persistence and conversation-rewind capabilities. Typed tool approvals remain capability-gated and fall back as described above. For terminal completion, a matching `agent_end.requestId` is authoritative and mismatches are ignored before state changes. Released OMP 18.2.x builds that omit that field use an ordered fallback requiring a fresh correlated native user entry followed by current-turn assistant activity, an idle non-compacting runtime, and no conflicting permission, tool, steer, or child-session work. Ambiguous events while OMP is active are ignored; unresolved ambiguity after confirmed idle fails only the Paseo turn. This trades a bounded residual same-agent stale-event risk for compatibility with released OMP instead of terminating and lazily restarting the runtime after every later prompt. Paseo clients, including mobile clients inside reconnect grace, do not own or terminate the daemon-managed OMP session.
|
|
126
127
|
|
|
127
|
-
Paseo 0.
|
|
128
|
+
Paseo 0.11 is the minimum supported release. The controlled Docker canary is pinned to official 0.11.1; nightly SDK checks cover the latest and beta channels. The canary preserves nested provider-subagent ancestry and spawning-tool links, and validates npm-managed plugin sources, platform-owned external URL opening, and whole-item timeline transforms before Overview grouping.
|
|
128
129
|
|
|
129
130
|
Install, update, disable, or remove `paseo-omp` independently of the bundled provider. Verify the provider snapshot contains `omp-plugin` after installation; a bundled `omp` entry may remain present and is not modified by this plugin.
|
|
130
131
|
|
package/client/hub-sidebar.tsx
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
// 0.11-only sidebar items. Typed locally so older SDK typechecks still compile.
|
|
4
|
-
import * as pluginUi from "@getpaseo/plugin/client/ui";
|
|
1
|
+
import type { PluginPopoverProps, PluginSidebarItemProps } from "@getpaseo/plugin/client";
|
|
2
|
+
import { SidebarRow } from "@getpaseo/plugin/client/ui";
|
|
5
3
|
import { useQuery } from "@tanstack/react-query";
|
|
6
|
-
import type { ComponentType, ReactNode } from "react";
|
|
7
4
|
import { Text, View } from "react-native";
|
|
8
5
|
import { HubProcessList } from "./hub-popover";
|
|
9
6
|
import { CONFIG_SCREEN_ID, type HubSnapshot, hubTrailing } from "./sidebar-compat";
|
|
@@ -12,40 +9,7 @@ import { CONFIG_SCREEN_ID, type HubSnapshot, hubTrailing } from "./sidebar-compa
|
|
|
12
9
|
const HUB_POLL_MS = 15_000;
|
|
13
10
|
const HUB_SIDEBAR_QUERY_KEY = ["paseo-omp", "hub-sidebar"] as const;
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
screenId: string;
|
|
17
|
-
params?: Record<string, string>;
|
|
18
|
-
}
|
|
19
|
-
type HostProps = Pick<PluginHostProps, "theme" | "layout">;
|
|
20
|
-
interface PopoverProps extends HostProps {
|
|
21
|
-
close(): void;
|
|
22
|
-
openScreen(input: OpenScreenInput): void;
|
|
23
|
-
}
|
|
24
|
-
interface SidebarItemProps extends HostProps {
|
|
25
|
-
currentScreen: { screenId: string; params: Record<string, string> } | null;
|
|
26
|
-
openScreen(input: OpenScreenInput): void;
|
|
27
|
-
openPopover(Content: ComponentType<PopoverProps>): void;
|
|
28
|
-
}
|
|
29
|
-
type SidebarRowComponent = ComponentType<{
|
|
30
|
-
id?: string;
|
|
31
|
-
icon?: string | ComponentType<{ size: number; color: string }>;
|
|
32
|
-
label?: string;
|
|
33
|
-
onPress(): void;
|
|
34
|
-
active?: boolean;
|
|
35
|
-
trailing?: ReactNode;
|
|
36
|
-
}>;
|
|
37
|
-
|
|
38
|
-
const uiModule: object = pluginUi;
|
|
39
|
-
// Present on every 0.11 host, the only hosts that render these items. The guard and cast keep
|
|
40
|
-
// older-SDK typechecks (which lack SidebarRow) compiling; presence check only, because host
|
|
41
|
-
// components may be memo/forwardRef objects rather than plain functions.
|
|
42
|
-
const SidebarRow: SidebarRowComponent | undefined =
|
|
43
|
-
"SidebarRow" in uiModule && uiModule.SidebarRow
|
|
44
|
-
? (uiModule.SidebarRow as SidebarRowComponent)
|
|
45
|
-
: undefined;
|
|
46
|
-
|
|
47
|
-
export function ConfigSidebarItem({ currentScreen, openScreen }: SidebarItemProps) {
|
|
48
|
-
if (!SidebarRow) return null;
|
|
12
|
+
export function ConfigSidebarItem({ currentScreen, openScreen }: PluginSidebarItemProps) {
|
|
49
13
|
return (
|
|
50
14
|
<SidebarRow
|
|
51
15
|
icon="Settings"
|
|
@@ -64,7 +28,7 @@ export function createHubSidebar(loadSnapshot: () => Promise<HubSnapshot>) {
|
|
|
64
28
|
refetchInterval: HUB_POLL_MS,
|
|
65
29
|
});
|
|
66
30
|
|
|
67
|
-
function HubSidebarPopover({ theme, layout }:
|
|
31
|
+
function HubSidebarPopover({ theme, layout }: PluginPopoverProps) {
|
|
68
32
|
const snapshot = useHubSnapshot();
|
|
69
33
|
const muted = { color: theme.colors.foregroundMuted, fontSize: 13 };
|
|
70
34
|
if (snapshot.isLoading) return <Text style={muted}>Loading hub processes…</Text>;
|
|
@@ -102,9 +66,8 @@ export function createHubSidebar(loadSnapshot: () => Promise<HubSnapshot>) {
|
|
|
102
66
|
);
|
|
103
67
|
}
|
|
104
68
|
|
|
105
|
-
function HubSidebarItem({ theme, openPopover }:
|
|
69
|
+
function HubSidebarItem({ theme, openPopover }: PluginSidebarItemProps) {
|
|
106
70
|
const snapshot = useHubSnapshot();
|
|
107
|
-
if (!SidebarRow) return null;
|
|
108
71
|
const state = hubTrailing(snapshot.data, snapshot.error !== null);
|
|
109
72
|
const trailing = state ? (
|
|
110
73
|
<View
|
package/client/sidebar-compat.ts
CHANGED
|
@@ -2,42 +2,12 @@ import type { HubProcess } from "../shared/hub";
|
|
|
2
2
|
import { isOmpProfileName, type OmpStore } from "../shared/omp-store";
|
|
3
3
|
import { hubProcessTone } from "./hub-status";
|
|
4
4
|
|
|
5
|
-
/** The
|
|
5
|
+
/** The config screen keeps the id of the retired 0.10 sidebar item so saved links work. */
|
|
6
6
|
export const CONFIG_SCREEN_ID = "config";
|
|
7
7
|
export const HUB_SIDEBAR_ITEM_ID = "hub";
|
|
8
8
|
|
|
9
9
|
type ScreenParams = Readonly<Record<string, string>>;
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* The 0.11 screen/sidebar-item API, typed structurally so 0.9/0.10 SDK typechecks still compile.
|
|
13
|
-
* On 0.10 hosts these members are absent at runtime.
|
|
14
|
-
*/
|
|
15
|
-
export interface ScreenCapableClient {
|
|
16
|
-
addScreen(contribution: {
|
|
17
|
-
id: string;
|
|
18
|
-
title: string | ((params: ScreenParams) => string);
|
|
19
|
-
Component: (props: never) => unknown;
|
|
20
|
-
}): () => void;
|
|
21
|
-
addSidebarHeaderItem(contribution: {
|
|
22
|
-
id: string;
|
|
23
|
-
title: string;
|
|
24
|
-
Component: (props: never) => unknown;
|
|
25
|
-
}): () => void;
|
|
26
|
-
openScreen(input: { screenId: string; params?: Record<string, string> }): void;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** True only on Paseo 0.11+ hosts, which provide screens, header items, and `openScreen`. */
|
|
30
|
-
export function supportsScreens<Client extends object>(
|
|
31
|
-
client: Client,
|
|
32
|
-
): client is Client & ScreenCapableClient {
|
|
33
|
-
const candidate = client as Partial<Record<keyof ScreenCapableClient, unknown>>;
|
|
34
|
-
return (
|
|
35
|
-
typeof candidate.addScreen === "function" &&
|
|
36
|
-
typeof candidate.addSidebarHeaderItem === "function" &&
|
|
37
|
-
typeof candidate.openScreen === "function"
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
11
|
/** A `profile` screen param selects that store; anything invalid falls back to the default. */
|
|
42
12
|
export function configStoreFromParams(params: ScreenParams | undefined): OmpStore | undefined {
|
|
43
13
|
const profile = params?.profile?.trim();
|
package/index.client.tsx
CHANGED
|
@@ -4,7 +4,6 @@ import type {
|
|
|
4
4
|
PluginClientContext,
|
|
5
5
|
PluginSurfaceProps,
|
|
6
6
|
} from "@getpaseo/plugin/client";
|
|
7
|
-
import type { ComponentType } from "react";
|
|
8
7
|
import { OmpIcon } from "./client/hub-icon";
|
|
9
8
|
import { HubPopover } from "./client/hub-popover";
|
|
10
9
|
import { ConfigSidebarItem, createHubSidebar } from "./client/hub-sidebar";
|
|
@@ -38,7 +37,6 @@ import {
|
|
|
38
37
|
configStoreFromParams,
|
|
39
38
|
HUB_SIDEBAR_ITEM_ID,
|
|
40
39
|
loadHubSnapshot,
|
|
41
|
-
supportsScreens,
|
|
42
40
|
} from "./client/sidebar-compat";
|
|
43
41
|
import {
|
|
44
42
|
type ComposerPillSettings,
|
|
@@ -112,29 +110,16 @@ async function loadAgents(paseo: PaseoApi): Promise<AgentEntry[]> {
|
|
|
112
110
|
}
|
|
113
111
|
|
|
114
112
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* surface and static sidebar row, unchanged.
|
|
113
|
+
* A param-addressable `config` screen (same id as the old sidebar item, so saved links resolve to
|
|
114
|
+
* it), a header row that opens it, and a Hub status row.
|
|
118
115
|
*/
|
|
119
116
|
export function registerConfigAndHub(
|
|
120
|
-
client: Pick<
|
|
121
|
-
|
|
117
|
+
client: Pick<
|
|
118
|
+
PluginClientContext,
|
|
119
|
+
"addScreen" | "addSidebarHeaderItem" | "openScreen" | "rpc" | "paseo"
|
|
120
|
+
>,
|
|
122
121
|
onComposerPillSettingsChange: (settings: ComposerPillSettings) => void,
|
|
123
122
|
): () => void {
|
|
124
|
-
if (!supportsScreens(client)) {
|
|
125
|
-
const removeSurface = client.addSurface(CONFIG_SCREEN_ID, ConfigSurface);
|
|
126
|
-
const removeItem = client.addSidebarItem({
|
|
127
|
-
id: CONFIG_SCREEN_ID,
|
|
128
|
-
title: "OMP",
|
|
129
|
-
icon: "Settings",
|
|
130
|
-
surface: CONFIG_SCREEN_ID,
|
|
131
|
-
});
|
|
132
|
-
return () => {
|
|
133
|
-
removeItem();
|
|
134
|
-
removeSurface();
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
const screens = client;
|
|
138
123
|
function ConfigScreen(props: PluginSurfaceProps & { params: Record<string, string> }) {
|
|
139
124
|
const current = configStoreFromParams(props.params);
|
|
140
125
|
return (
|
|
@@ -145,7 +130,7 @@ export function registerConfigAndHub(
|
|
|
145
130
|
onStoreChange={(store) => {
|
|
146
131
|
// Re-selecting the current store must not push a duplicate screen onto history.
|
|
147
132
|
if (ompStoreKey(store) === ompStoreKey(current)) return;
|
|
148
|
-
|
|
133
|
+
client.openScreen({ screenId: CONFIG_SCREEN_ID, params: configParamsFromStore(store) });
|
|
149
134
|
}}
|
|
150
135
|
/>
|
|
151
136
|
);
|
|
@@ -157,13 +142,13 @@ export function registerConfigAndHub(
|
|
|
157
142
|
),
|
|
158
143
|
);
|
|
159
144
|
const removers = [
|
|
160
|
-
|
|
161
|
-
|
|
145
|
+
client.addScreen({ id: CONFIG_SCREEN_ID, title: configScreenTitle, Component: ConfigScreen }),
|
|
146
|
+
client.addSidebarHeaderItem({
|
|
162
147
|
id: CONFIG_SCREEN_ID,
|
|
163
148
|
title: "OMP",
|
|
164
149
|
Component: ConfigSidebarItem,
|
|
165
150
|
}),
|
|
166
|
-
|
|
151
|
+
client.addSidebarHeaderItem({
|
|
167
152
|
id: HUB_SIDEBAR_ITEM_ID,
|
|
168
153
|
title: "OMP Hub",
|
|
169
154
|
Component: HubSidebarItem,
|
|
@@ -193,10 +178,6 @@ export default function contribute(client: PluginClientContext) {
|
|
|
193
178
|
scheduleReconcile();
|
|
194
179
|
}
|
|
195
180
|
|
|
196
|
-
function ConfigSurface(props: PluginSurfaceProps) {
|
|
197
|
-
return <OmpConfigSurface {...props} onComposerPillSettingsChange={applyComposerPillSettings} />;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
181
|
const removeMemoryPanel = client.addWorkspacePanel({
|
|
201
182
|
id: "memory",
|
|
202
183
|
title: "OMP Memory",
|
|
@@ -233,19 +214,15 @@ export default function contribute(client: PluginClientContext) {
|
|
|
233
214
|
openPanel("workspace", { location: "workspace" });
|
|
234
215
|
},
|
|
235
216
|
});
|
|
236
|
-
const
|
|
237
|
-
const removeConfigEntry = registerConfigAndHub(client, ConfigSurface, applyComposerPillSettings);
|
|
217
|
+
const removeConfigEntry = registerConfigAndHub(client, applyComposerPillSettings);
|
|
238
218
|
const removeOpenConfig = client.addCommandCenterItem({
|
|
239
219
|
id: "open-config",
|
|
240
220
|
title: "Open OMP",
|
|
241
221
|
icon: "Settings",
|
|
242
222
|
keywords: ["omp", "config", "settings", "models", "providers", "composer", "pills"],
|
|
243
223
|
context: "global",
|
|
244
|
-
onSelect(
|
|
245
|
-
|
|
246
|
-
if (usesScreens && "openScreen" in capabilities) {
|
|
247
|
-
capabilities.openScreen({ screenId: CONFIG_SCREEN_ID });
|
|
248
|
-
} else capabilities.openSurface(CONFIG_SCREEN_ID);
|
|
224
|
+
onSelect({ openScreen }) {
|
|
225
|
+
openScreen({ screenId: CONFIG_SCREEN_ID });
|
|
249
226
|
},
|
|
250
227
|
});
|
|
251
228
|
const removeImageRenderer = client.addTimelineRenderer({
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omercnet/paseo-omp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@omercnet/paseo-omp",
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "2.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@getpaseo/protocol": "0.11.1",
|
|
12
12
|
"@modelcontextprotocol/sdk": "1.30.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omercnet/paseo-omp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-next.245.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Paseo integration for OMP, including its direct provider and workspace tooling.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -63,16 +63,16 @@
|
|
|
63
63
|
"@getpaseo/client": "0.11.1",
|
|
64
64
|
"@getpaseo/plugin": "0.11.1",
|
|
65
65
|
"@getpaseo/server": "0.11.1",
|
|
66
|
-
"@oh-my-pi/pi-agent-core": "18.
|
|
67
|
-
"@oh-my-pi/pi-coding-agent": "18.
|
|
66
|
+
"@oh-my-pi/pi-agent-core": "18.8.4",
|
|
67
|
+
"@oh-my-pi/pi-coding-agent": "18.8.4",
|
|
68
68
|
"@tanstack/react-query": "^5.102.3",
|
|
69
69
|
"@types/node": "^24.10.1",
|
|
70
70
|
"@types/react": "^19.2.18",
|
|
71
71
|
"@vitest/coverage-v8": "^5.0.0",
|
|
72
72
|
"esbuild": "0.28.2",
|
|
73
|
+
"pino": "10.3.1",
|
|
73
74
|
"react": "19.1.0",
|
|
74
75
|
"react-native": "0.81.5",
|
|
75
|
-
"pino": "10.3.1",
|
|
76
76
|
"tsx": "^4.20.6",
|
|
77
77
|
"typescript": "^7.0.2",
|
|
78
78
|
"vitest": "^5.0.0",
|
package/paseo-plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "paseo-omp",
|
|
3
3
|
"description": "Paseo integration for OMP, including its direct provider and workspace tooling.",
|
|
4
|
-
"requirements": { "paseo": "^0.
|
|
4
|
+
"requirements": { "paseo": "^0.11.0" },
|
|
5
5
|
"build": [["node", "scripts/prepare-dependencies.mjs"]]
|
|
6
6
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** Release Please keeps this bundle-safe constant aligned with package.json. */
|
|
2
|
-
export const PASEO_OMP_PACKAGE_VERSION = "
|
|
2
|
+
export const PASEO_OMP_PACKAGE_VERSION = "2.0.0-next.245.1"; // x-release-please-version
|
|
@@ -80,6 +80,9 @@ export const OMP_SESSION_EVENT_POLICIES = {
|
|
|
80
80
|
advisor_cost_changed: "ignore",
|
|
81
81
|
advisor_yielded: "normalize",
|
|
82
82
|
ttsr_triggered: "ignore",
|
|
83
|
+
cache_warming_start: "ignore",
|
|
84
|
+
cache_warming_end: "ignore",
|
|
85
|
+
queue_update: "ignore",
|
|
83
86
|
todo_reminder: "normalize",
|
|
84
87
|
todo_auto_clear: "normalize",
|
|
85
88
|
irc_message: "normalize",
|
|
@@ -136,6 +139,26 @@ export const OMP_RPC_COMMAND_POLICIES = {
|
|
|
136
139
|
get_messages_page: "implemented",
|
|
137
140
|
get_login_providers: "unsupported",
|
|
138
141
|
login: "unsupported",
|
|
142
|
+
abort_and_restore_queue: "unsupported",
|
|
143
|
+
btw: "unsupported",
|
|
144
|
+
btw_cancel: "unsupported",
|
|
145
|
+
cancel_subagent: "unsupported",
|
|
146
|
+
fork: "unsupported",
|
|
147
|
+
get_btw_history: "unsupported",
|
|
148
|
+
get_logout_accounts: "unsupported",
|
|
149
|
+
goal: "unsupported",
|
|
150
|
+
live_mute: "unsupported",
|
|
151
|
+
live_start: "unsupported",
|
|
152
|
+
live_stop: "unsupported",
|
|
153
|
+
logout: "unsupported",
|
|
154
|
+
predict_word: "unsupported",
|
|
155
|
+
predict_word_feedback: "unsupported",
|
|
156
|
+
promote_queued_message: "unsupported",
|
|
157
|
+
remove_queued_message: "unsupported",
|
|
158
|
+
set_ask_dialog: "unsupported",
|
|
159
|
+
set_cache_warming: "unsupported",
|
|
160
|
+
set_slow_mode: "unsupported",
|
|
161
|
+
steer_subagent: "unsupported",
|
|
139
162
|
} as const;
|
|
140
163
|
export const OMP_RPC_RESPONSE_POLICIES = OMP_RPC_COMMAND_POLICIES;
|
|
141
164
|
export const OMP_PROTOCOL_VIOLATION_CATEGORIES = [
|
|
@@ -218,6 +241,9 @@ export const OMP_PROTOCOL_EVENT_TYPES = [
|
|
|
218
241
|
"config_warnings_changed",
|
|
219
242
|
"advisor_cost_changed",
|
|
220
243
|
"ttsr_triggered",
|
|
244
|
+
"cache_warming_start",
|
|
245
|
+
"cache_warming_end",
|
|
246
|
+
"queue_update",
|
|
221
247
|
"irc_message",
|
|
222
248
|
"thinking_level_changed",
|
|
223
249
|
"goal_updated",
|
|
@@ -374,6 +400,9 @@ function eventSchemaClassification(
|
|
|
374
400
|
case "config_warnings_changed":
|
|
375
401
|
case "advisor_cost_changed":
|
|
376
402
|
case "ttsr_triggered":
|
|
403
|
+
case "cache_warming_start":
|
|
404
|
+
case "cache_warming_end":
|
|
405
|
+
case "queue_update":
|
|
377
406
|
return { reason: "configuration-event-schema", expected: "valid-configuration-event" };
|
|
378
407
|
case "extension_ui_request":
|
|
379
408
|
return { reason: "extension-ui-schema", expected: "valid-extension-ui-event" };
|
|
@@ -1522,6 +1551,9 @@ const OmpAgentSessionEventSchema = z.discriminatedUnion("type", [
|
|
|
1522
1551
|
...OmpCoreAgentEventSchema.options,
|
|
1523
1552
|
z.object({ type: z.literal("config_warnings_changed") }),
|
|
1524
1553
|
z.object({ type: z.literal("advisor_cost_changed") }),
|
|
1554
|
+
z.object({ type: z.literal("cache_warming_start") }),
|
|
1555
|
+
z.object({ type: z.literal("cache_warming_end") }),
|
|
1556
|
+
z.object({ type: z.literal("queue_update") }),
|
|
1525
1557
|
z.object({ type: z.literal("advisor_yielded") }),
|
|
1526
1558
|
z.object({
|
|
1527
1559
|
type: z.literal("ttsr_triggered"),
|
|
@@ -7,7 +7,7 @@ import { ompAgentDir, ompCacheDir, ompDataDir, ompSessionDir, ompStateDir } from
|
|
|
7
7
|
import { OmpRpcRuntime, type OmpRuntime } from "./omp-rpc";
|
|
8
8
|
import type { OmpStartOptions } from "./omp-rpc-environment";
|
|
9
9
|
import { parseOmpProviderOptions } from "./provider-options";
|
|
10
|
-
import { createOmpProvider, type OmpProviderOptions } from "./registration";
|
|
10
|
+
import { createOmpProvider, createOmpStatus, type OmpProviderOptions } from "./registration";
|
|
11
11
|
import { OmpPublicError } from "./security";
|
|
12
12
|
|
|
13
13
|
const MAX_PROFILES = 128;
|
|
@@ -229,6 +229,18 @@ export function createProfileOmpProvider(profile: string, options: OmpProviderOp
|
|
|
229
229
|
sessionDir,
|
|
230
230
|
);
|
|
231
231
|
};
|
|
232
|
+
const checkAvailability = async (
|
|
233
|
+
input: Parameters<NonNullable<typeof provider.checkAvailability>>[0],
|
|
234
|
+
context?: Parameters<NonNullable<typeof provider.checkAvailability>>[1],
|
|
235
|
+
) => {
|
|
236
|
+
const command = validateCatalog(input);
|
|
237
|
+
if (!provider.checkAvailability)
|
|
238
|
+
throw new OmpPublicError("OMP availability probe is unavailable");
|
|
239
|
+
return provider.checkAvailability(
|
|
240
|
+
{ ...input, providerOptions: { ...input.providerOptions, command } },
|
|
241
|
+
context,
|
|
242
|
+
);
|
|
243
|
+
};
|
|
232
244
|
return {
|
|
233
245
|
...provider,
|
|
234
246
|
async getCatalogCacheKey(
|
|
@@ -237,18 +249,8 @@ export function createProfileOmpProvider(profile: string, options: OmpProviderOp
|
|
|
237
249
|
validateCatalog(input);
|
|
238
250
|
return provider.getCatalogCacheKey?.(input);
|
|
239
251
|
},
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
context?: Parameters<NonNullable<typeof provider.checkAvailability>>[1],
|
|
243
|
-
) {
|
|
244
|
-
const command = validateCatalog(input);
|
|
245
|
-
if (!provider.checkAvailability)
|
|
246
|
-
throw new OmpPublicError("OMP availability probe is unavailable");
|
|
247
|
-
return provider.checkAvailability(
|
|
248
|
-
{ ...input, providerOptions: { ...input.providerOptions, command } },
|
|
249
|
-
context,
|
|
250
|
-
);
|
|
251
|
-
},
|
|
252
|
+
checkAvailability,
|
|
253
|
+
status: createOmpStatus(() => checkAvailability({ scope: "global" })),
|
|
252
254
|
id,
|
|
253
255
|
label: `OMP · ${profile}`,
|
|
254
256
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import type { ProviderRegistration } from "@getpaseo/plugin/server/provider";
|
|
3
|
+
import type { ProviderRegistration, ProviderStatus } from "@getpaseo/plugin/server/provider";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import type { OmpBrowserAuthorizationRegistry } from "../mcp-browser";
|
|
6
6
|
import type { OmpOperationalFailureReporter } from "../operational-failure-diagnostics";
|
|
@@ -41,6 +41,57 @@ type ProviderRegistrationCompat = Omit<
|
|
|
41
41
|
): Promise<ProviderAvailabilityCompat>;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
const STATUS_CACHE_TTL_MS = 30_000;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The 0.11 host calls `status` before every create, resume, and draft request and passes only
|
|
48
|
+
* the daemon-resolved launch, never per-agent `providerOptions`. Only a missing executable is
|
|
49
|
+
* definitive for the plugin-owned default launch; slow, failing, or unrecognized probes stay
|
|
50
|
+
* available with a diagnostic so load or a differently launched agent is never refused.
|
|
51
|
+
*/
|
|
52
|
+
function toProviderStatus(availability: ProviderAvailabilityCompat): ProviderStatus {
|
|
53
|
+
if (availability.status === "available") return { available: true };
|
|
54
|
+
if (availability.status === "missing") {
|
|
55
|
+
return {
|
|
56
|
+
available: false,
|
|
57
|
+
diagnostic: availability.diagnostic ?? "OMP executable was not found",
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
available: true,
|
|
62
|
+
diagnostic: availability.diagnostic ?? `OMP launch probe reported ${availability.status}`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Share one bounded probe across the host's frequent status calls. Only an available result is
|
|
68
|
+
* kept for the interval; an unavailable or failed result is shared by in-flight callers and then
|
|
69
|
+
* dropped, so installing the executable is picked up by the very next host call.
|
|
70
|
+
*/
|
|
71
|
+
export function createOmpStatus(
|
|
72
|
+
probe: () => Promise<ProviderAvailabilityCompat>,
|
|
73
|
+
): () => Promise<ProviderStatus> {
|
|
74
|
+
let cached: { expiresAt: number; result: Promise<ProviderStatus> } | undefined;
|
|
75
|
+
return () => {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
if (cached && cached.expiresAt > now) return cached.result;
|
|
78
|
+
const entry = {
|
|
79
|
+
expiresAt: now + STATUS_CACHE_TTL_MS,
|
|
80
|
+
result: probe().then(toProviderStatus),
|
|
81
|
+
};
|
|
82
|
+
cached = entry;
|
|
83
|
+
entry.result.then(
|
|
84
|
+
(status) => {
|
|
85
|
+
if (!status.available && cached === entry) cached = undefined;
|
|
86
|
+
},
|
|
87
|
+
() => {
|
|
88
|
+
if (cached === entry) cached = undefined;
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
return entry.result;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
44
95
|
const CAPABILITIES = [
|
|
45
96
|
"prompt.message",
|
|
46
97
|
"prompt.command",
|
|
@@ -98,6 +149,26 @@ export function createOmpProvider(options: OmpProviderOptions = {}): ProviderReg
|
|
|
98
149
|
reportProtocolViolation: options.reportProtocolViolation,
|
|
99
150
|
});
|
|
100
151
|
const nativeReservations = new OmpNativeSessionReservations();
|
|
152
|
+
const checkAvailability: NonNullable<ProviderRegistrationCompat["checkAvailability"]> = async (
|
|
153
|
+
catalogOptions,
|
|
154
|
+
context,
|
|
155
|
+
) => {
|
|
156
|
+
if (options.availabilityProbe) {
|
|
157
|
+
return await options.availabilityProbe(catalogOptions, context?.timeoutMs);
|
|
158
|
+
}
|
|
159
|
+
const providerOptions = parseOmpProviderOptions(catalogOptions.providerOptions);
|
|
160
|
+
const environment = { ...(options.environment ?? process.env), ...providerOptions.env };
|
|
161
|
+
const configuredCommand: readonly [string, ...string[]] = providerOptions.command?.[0]
|
|
162
|
+
? [providerOptions.command[0], ...providerOptions.command.slice(1)]
|
|
163
|
+
: [environment.OMP_COMMAND || "omp"];
|
|
164
|
+
return await probeOmpAvailability({
|
|
165
|
+
command: configuredCommand,
|
|
166
|
+
cwd:
|
|
167
|
+
catalogOptions.scope === "workspace" && catalogOptions.cwd ? catalogOptions.cwd : homedir(),
|
|
168
|
+
environment,
|
|
169
|
+
timeoutMs: context?.timeoutMs,
|
|
170
|
+
});
|
|
171
|
+
};
|
|
101
172
|
return {
|
|
102
173
|
id: "omp-plugin",
|
|
103
174
|
label: "OMP Plugin",
|
|
@@ -130,25 +201,8 @@ export function createOmpProvider(options: OmpProviderOptions = {}): ProviderReg
|
|
|
130
201
|
}
|
|
131
202
|
return createHash("sha256").update(stableJson(identity)).digest("base64url");
|
|
132
203
|
},
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return await options.availabilityProbe(catalogOptions, context?.timeoutMs);
|
|
136
|
-
}
|
|
137
|
-
const providerOptions = parseOmpProviderOptions(catalogOptions.providerOptions);
|
|
138
|
-
const environment = { ...(options.environment ?? process.env), ...providerOptions.env };
|
|
139
|
-
const configuredCommand: readonly [string, ...string[]] = providerOptions.command?.[0]
|
|
140
|
-
? [providerOptions.command[0], ...providerOptions.command.slice(1)]
|
|
141
|
-
: [environment.OMP_COMMAND || "omp"];
|
|
142
|
-
return await probeOmpAvailability({
|
|
143
|
-
command: configuredCommand,
|
|
144
|
-
cwd:
|
|
145
|
-
catalogOptions.scope === "workspace" && catalogOptions.cwd
|
|
146
|
-
? catalogOptions.cwd
|
|
147
|
-
: homedir(),
|
|
148
|
-
environment,
|
|
149
|
-
timeoutMs: context?.timeoutMs,
|
|
150
|
-
});
|
|
151
|
-
},
|
|
204
|
+
checkAvailability,
|
|
205
|
+
status: createOmpStatus(() => checkAvailability({ scope: "global" })),
|
|
152
206
|
async connect(request) {
|
|
153
207
|
if (boundedJsonBytes(request, 8 * 1024, 32, 256, 64) === Number.POSITIVE_INFINITY) {
|
|
154
208
|
throw new Error("OMP provider received an oversized connection request");
|
|
@@ -2274,7 +2274,10 @@ export class OmpProviderSession implements OmpOpenedSession {
|
|
|
2274
2274
|
if (
|
|
2275
2275
|
event.type === "config_warnings_changed" ||
|
|
2276
2276
|
event.type === "advisor_cost_changed" ||
|
|
2277
|
-
event.type === "ttsr_triggered"
|
|
2277
|
+
event.type === "ttsr_triggered" ||
|
|
2278
|
+
event.type === "cache_warming_start" ||
|
|
2279
|
+
event.type === "cache_warming_end" ||
|
|
2280
|
+
event.type === "queue_update"
|
|
2278
2281
|
) {
|
|
2279
2282
|
return;
|
|
2280
2283
|
}
|
|
@@ -463,6 +463,9 @@ export class OmpTimelineProjector {
|
|
|
463
463
|
event.type === "config_warnings_changed" ||
|
|
464
464
|
event.type === "advisor_cost_changed" ||
|
|
465
465
|
event.type === "ttsr_triggered" ||
|
|
466
|
+
event.type === "cache_warming_start" ||
|
|
467
|
+
event.type === "cache_warming_end" ||
|
|
468
|
+
event.type === "queue_update" ||
|
|
466
469
|
event.type === "irc_message" ||
|
|
467
470
|
event.type === "model_changed" ||
|
|
468
471
|
event.type === "thinking_level_changed"
|
package/server/quota.ts
CHANGED
|
@@ -15,7 +15,12 @@ const QuotaRowSchema = z.object({
|
|
|
15
15
|
recordedAt: z.number().int(),
|
|
16
16
|
});
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
// Reset-less rows (no resets_at) refresh on every OMP poll, so one this old is an inactive provider.
|
|
19
|
+
const ABANDONED_AFTER_MS = 30 * 86_400_000;
|
|
20
|
+
|
|
21
|
+
// Only live windows are returned: a series whose newest row already reset (or never resets and was
|
|
22
|
+
// abandoned) would otherwise pin the pill at its last exhausted value and bloat the popover.
|
|
23
|
+
export function listOmpQuotasFrom(path: string, now = Date.now()): OmpQuota[] {
|
|
19
24
|
try {
|
|
20
25
|
const database = new DatabaseSync(path, { readOnly: true, timeout: 500 });
|
|
21
26
|
try {
|
|
@@ -33,9 +38,10 @@ export function listOmpQuotasFrom(path: string): OmpQuota[] {
|
|
|
33
38
|
FROM usage_history
|
|
34
39
|
)
|
|
35
40
|
WHERE position = 1
|
|
41
|
+
AND (resets_at > ? OR (resets_at IS NULL AND recorded_at > ?))
|
|
36
42
|
ORDER BY COALESCE(usedFraction, -1) DESC, provider, label`,
|
|
37
43
|
)
|
|
38
|
-
.all();
|
|
44
|
+
.all(now, now - ABANDONED_AFTER_MS);
|
|
39
45
|
return rows.flatMap((row) => {
|
|
40
46
|
const parsed = QuotaRowSchema.safeParse(row);
|
|
41
47
|
return parsed.success ? [parsed.data] : [];
|