@omercnet/paseo-omp 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +25 -13
- package/SUPPORT.md +6 -2
- package/TESTING.md +21 -18
- package/client/composer-pill-settings.tsx +157 -0
- package/client/external-url.ts +15 -0
- package/client/mcp-authorization.tsx +169 -0
- package/client/mcp-popover.tsx +155 -0
- package/client/memory-panel.tsx +8 -3
- package/client/memory-popover.tsx +8 -4
- package/client/omp-config-surface.tsx +189 -29
- package/client/omp-plugin-manager.tsx +302 -131
- package/client/omp-store-picker.tsx +89 -0
- package/client/omp-store-state.ts +45 -0
- package/client/paseo-types.ts +9 -0
- package/client/provider-diagnostics-state.ts +18 -7
- package/client/quota-popover.tsx +8 -3
- package/client/quota-state.ts +16 -7
- package/client/sessions-popover.tsx +8 -3
- package/docs/alpha-release-checklist.md +6 -8
- package/docs/configuration.md +8 -4
- package/docs/core-provider-issue-audit.md +3 -2
- package/docs/images/mcp-authorization-compact.png +0 -0
- package/docs/images/mcp-controls-wide.png +0 -0
- package/docs/images/plugin-manager.png +0 -0
- package/docs/images/workspace-settings.png +0 -0
- package/docs/installation.md +35 -19
- package/index.client.tsx +339 -123
- package/index.server.ts +44 -14
- package/package.json +7 -8
- package/paseo-plugin.json +2 -2
- package/scripts/prepare-dependencies.mjs +24 -0
- package/server/mcp-browser.ts +95 -0
- package/server/memory.ts +2 -2
- package/server/omp-config.ts +16 -7
- package/server/omp-plugins.ts +70 -21
- package/server/omp-settings.ts +232 -24
- package/server/paths.ts +128 -11
- package/server/provider/catalog.ts +3 -4
- package/server/provider/connection.ts +213 -9
- package/server/provider/host-tools.ts +71 -0
- package/server/provider/omp-rpc.ts +82 -15
- package/server/provider/profile-providers.ts +249 -0
- package/server/provider/registration.ts +11 -0
- package/server/provider/session-descriptors.ts +306 -1
- package/server/provider/session.ts +704 -249
- package/server/provider/subsessions.ts +4 -1
- package/server/provider/timeline-projector.ts +70 -33
- package/server/provider-diagnostics.ts +122 -36
- package/server/quota.ts +3 -2
- package/server/sessions.ts +2 -2
- package/shared/composer-pill-settings.ts +28 -0
- package/shared/external-url.ts +21 -0
- package/shared/hub.ts +3 -3
- package/shared/mcp.ts +47 -0
- package/shared/memory.ts +2 -1
- package/shared/omp-config.ts +5 -1
- package/shared/omp-plugins.ts +74 -33
- package/shared/omp-settings.ts +8 -1
- package/shared/omp-store.ts +58 -0
- package/shared/provider-diagnostics.ts +12 -3
- package/shared/quota.ts +2 -1
- package/shared/sessions.ts +2 -1
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type PluginSurfaceProps, useRpc } from "@getpaseo/plugin/client";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { Pressable, Text, View } from "react-native";
|
|
4
|
+
import { listOmpStores, type OmpStore, storeLabel } from "../shared/omp-store";
|
|
5
|
+
import { ompStoreKey } from "./omp-store-state";
|
|
6
|
+
|
|
7
|
+
export function OmpStorePicker({
|
|
8
|
+
theme,
|
|
9
|
+
store,
|
|
10
|
+
onChange,
|
|
11
|
+
disabled = false,
|
|
12
|
+
}: {
|
|
13
|
+
theme: PluginSurfaceProps["theme"];
|
|
14
|
+
store?: OmpStore;
|
|
15
|
+
onChange(store: OmpStore | undefined): void;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
}) {
|
|
18
|
+
const loadStores = useRpc(listOmpStores);
|
|
19
|
+
const stores = useQuery({
|
|
20
|
+
queryKey: ["paseo-omp", "stores"],
|
|
21
|
+
queryFn: () => loadStores({}),
|
|
22
|
+
staleTime: 30_000,
|
|
23
|
+
});
|
|
24
|
+
const profiles = [
|
|
25
|
+
...new Set([...(stores.data?.profiles ?? []), ...(store?.profile ? [store.profile] : [])]),
|
|
26
|
+
];
|
|
27
|
+
const choices: Array<OmpStore | undefined> = [
|
|
28
|
+
undefined,
|
|
29
|
+
...profiles.map((profile) => ({ profile })),
|
|
30
|
+
];
|
|
31
|
+
if (store?.agentDir) choices.push(store);
|
|
32
|
+
return (
|
|
33
|
+
<View style={{ gap: 8 }}>
|
|
34
|
+
<Text style={{ color: theme.colors.foreground, fontWeight: "600" }}>OMP store</Text>
|
|
35
|
+
<View
|
|
36
|
+
accessibilityRole="radiogroup"
|
|
37
|
+
style={{ flexDirection: "row", flexWrap: "wrap", gap: 8 }}
|
|
38
|
+
>
|
|
39
|
+
{choices.map((choice) => {
|
|
40
|
+
const selected = ompStoreKey(choice) === ompStoreKey(store);
|
|
41
|
+
return (
|
|
42
|
+
<Pressable
|
|
43
|
+
key={ompStoreKey(choice)}
|
|
44
|
+
accessibilityRole="radio"
|
|
45
|
+
accessibilityLabel={storeLabel(choice)}
|
|
46
|
+
accessibilityState={{ checked: selected, disabled }}
|
|
47
|
+
aria-checked={selected}
|
|
48
|
+
aria-disabled={disabled}
|
|
49
|
+
disabled={disabled}
|
|
50
|
+
onPress={() => onChange(choice)}
|
|
51
|
+
style={{
|
|
52
|
+
borderWidth: 1,
|
|
53
|
+
borderRadius: 8,
|
|
54
|
+
paddingHorizontal: 10,
|
|
55
|
+
paddingVertical: 8,
|
|
56
|
+
borderColor: selected ? theme.colors.accent : theme.colors.border,
|
|
57
|
+
backgroundColor: theme.colors.surface1,
|
|
58
|
+
opacity: disabled ? 0.5 : 1,
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
<Text
|
|
62
|
+
style={{ color: theme.colors.foreground, fontWeight: selected ? "600" : "400" }}
|
|
63
|
+
>
|
|
64
|
+
{selected ? "✓ " : ""}
|
|
65
|
+
{storeLabel(choice)}
|
|
66
|
+
</Text>
|
|
67
|
+
</Pressable>
|
|
68
|
+
);
|
|
69
|
+
})}
|
|
70
|
+
</View>
|
|
71
|
+
{stores.isLoading ? (
|
|
72
|
+
<Text style={{ color: theme.colors.foregroundMuted }}>Loading profiles…</Text>
|
|
73
|
+
) : null}
|
|
74
|
+
{stores.error ? (
|
|
75
|
+
<Text style={{ color: theme.colors.statusDanger }}>
|
|
76
|
+
Could not list OMP profiles.{" "}
|
|
77
|
+
<Text
|
|
78
|
+
onPress={() => {
|
|
79
|
+
void stores.refetch();
|
|
80
|
+
}}
|
|
81
|
+
accessibilityRole="button"
|
|
82
|
+
>
|
|
83
|
+
Retry
|
|
84
|
+
</Text>
|
|
85
|
+
</Text>
|
|
86
|
+
) : null}
|
|
87
|
+
</View>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type OmpStore, storeForProvider } from "../shared/omp-store";
|
|
2
|
+
import type { OmpQuota } from "../shared/quota";
|
|
3
|
+
|
|
4
|
+
export function ompStoreKey(store?: OmpStore): string {
|
|
5
|
+
return store?.profile
|
|
6
|
+
? `profile:${store.profile}`
|
|
7
|
+
: store?.agentDir
|
|
8
|
+
? `directory:${store.agentDir}`
|
|
9
|
+
: "default";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isOmpPluginProvider(provider?: string): boolean {
|
|
13
|
+
return provider === "omp-plugin" || storeForProvider(provider) !== undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isOmpProvider(provider?: string): boolean {
|
|
17
|
+
return provider === "omp" || isOmpPluginProvider(provider);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Each store owns its own result and in-flight read. Failed reads never become empty success. */
|
|
21
|
+
export function createStoreQuotaLoader(
|
|
22
|
+
load: (input: { store?: OmpStore }) => Promise<{ quotas: OmpQuota[] }>,
|
|
23
|
+
maxAgeMs: number,
|
|
24
|
+
now = Date.now,
|
|
25
|
+
) {
|
|
26
|
+
const cache = new Map<string, { value: { quotas: OmpQuota[] }; at: number }>();
|
|
27
|
+
const pending = new Map<string, Promise<{ quotas: OmpQuota[] }>>();
|
|
28
|
+
return (store?: OmpStore): Promise<{ quotas: OmpQuota[] }> => {
|
|
29
|
+
const key = ompStoreKey(store);
|
|
30
|
+
const cached = cache.get(key);
|
|
31
|
+
if (cached && now() - cached.at < maxAgeMs) return Promise.resolve(cached.value);
|
|
32
|
+
const current = pending.get(key);
|
|
33
|
+
if (current) return current;
|
|
34
|
+
const request = load({ store })
|
|
35
|
+
.then((value) => {
|
|
36
|
+
cache.set(key, { value, at: now() });
|
|
37
|
+
return value;
|
|
38
|
+
})
|
|
39
|
+
.finally(() => {
|
|
40
|
+
pending.delete(key);
|
|
41
|
+
});
|
|
42
|
+
pending.set(key, request);
|
|
43
|
+
return request;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PluginClientContext } from "@getpaseo/plugin/client";
|
|
2
|
+
|
|
3
|
+
type AsyncResult<Callable> = Callable extends (...args: never[]) => Promise<infer Result>
|
|
4
|
+
? Result
|
|
5
|
+
: never;
|
|
6
|
+
|
|
7
|
+
export type PaseoApi = PluginClientContext["paseo"];
|
|
8
|
+
export type PaseoAgentListResult = AsyncResult<PaseoApi["agents"]["list"]>;
|
|
9
|
+
export type PaseoProviderSnapshotResult = AsyncResult<PaseoApi["providers"]["snapshot"]>;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { PaseoApi, PaseoProviderSnapshotResult } from "@getpaseo/client";
|
|
2
1
|
import type {
|
|
3
2
|
OmpLspDiagnostics,
|
|
4
3
|
OmpMcpDiagnostics,
|
|
@@ -8,10 +7,11 @@ import type {
|
|
|
8
7
|
OmpVersionStatus,
|
|
9
8
|
PathState,
|
|
10
9
|
} from "../shared/provider-diagnostics";
|
|
10
|
+
import { isOmpProvider } from "./omp-store-state";
|
|
11
|
+
import type { PaseoApi, PaseoProviderSnapshotResult } from "./paseo-types";
|
|
11
12
|
|
|
12
13
|
export type ProviderHealthTone = "ok" | "warning" | "danger" | "muted";
|
|
13
14
|
export const OMP_PROVIDER_IDS = ["omp", "omp-plugin"] as const;
|
|
14
|
-
const OMP_PROVIDER_ID_SET: ReadonlySet<string> = new Set(OMP_PROVIDER_IDS);
|
|
15
15
|
|
|
16
16
|
export function isUnsupportedHostError(error: unknown): boolean {
|
|
17
17
|
if (typeof error !== "object" || error === null) return false;
|
|
@@ -40,6 +40,7 @@ export async function loadReadyProviderSnapshot(
|
|
|
40
40
|
|
|
41
41
|
export interface RefreshDiagnosticsOptions {
|
|
42
42
|
providers: ProviderActions;
|
|
43
|
+
providerIds?: readonly string[];
|
|
43
44
|
loadForcedHealth(): Promise<OmpProviderHealth>;
|
|
44
45
|
cacheHealth(health: OmpProviderHealth): void;
|
|
45
46
|
cacheProviders(snapshot: PaseoProviderSnapshotResult): void;
|
|
@@ -51,7 +52,11 @@ export async function refreshProviderDiagnostics(
|
|
|
51
52
|
options: RefreshDiagnosticsOptions,
|
|
52
53
|
): Promise<{ failed: boolean }> {
|
|
53
54
|
const [providerRefresh, forcedHealth] = await Promise.allSettled([
|
|
54
|
-
options.providers.refresh({
|
|
55
|
+
options.providers.refresh({
|
|
56
|
+
providers: [
|
|
57
|
+
...new Set([...OMP_PROVIDER_IDS, ...(options.providerIds ?? []).filter(isOmpProvider)]),
|
|
58
|
+
],
|
|
59
|
+
}),
|
|
55
60
|
options.loadForcedHealth(),
|
|
56
61
|
]);
|
|
57
62
|
if (forcedHealth.status === "fulfilled") options.cacheHealth(forcedHealth.value);
|
|
@@ -152,15 +157,21 @@ export function summarizeProcessDiagnostics(diagnostics: OmpProcessDiagnostics):
|
|
|
152
157
|
if (diagnostics.status === "unavailable") return "No hub run directory found";
|
|
153
158
|
if (diagnostics.status === "unknown") return "Unknown (could not read the hub run directory)";
|
|
154
159
|
const count = diagnostics.trackedCount ?? 0;
|
|
155
|
-
|
|
156
|
-
|
|
160
|
+
const statesKnown =
|
|
161
|
+
diagnostics.activeCount != null &&
|
|
162
|
+
diagnostics.historicalCount != null &&
|
|
163
|
+
diagnostics.unknownCount != null;
|
|
164
|
+
const detail = statesKnown
|
|
165
|
+
? `${diagnostics.activeCount} active-state, ${diagnostics.historicalCount} historical, ${diagnostics.unknownCount} unknown`
|
|
166
|
+
: "states not reported";
|
|
167
|
+
return `${count} metadata records (${detail}${diagnostics.status === "partial" ? "; partial access" : ""}); live processes not verified`;
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
export function processTone(diagnostics: OmpProcessDiagnostics): ProviderHealthTone {
|
|
160
171
|
if (diagnostics.status === "unknown") return "warning";
|
|
161
172
|
if (diagnostics.status === "partial") return "warning";
|
|
162
173
|
if (diagnostics.status === "unavailable") return "muted";
|
|
163
|
-
return
|
|
174
|
+
return "muted";
|
|
164
175
|
}
|
|
165
176
|
|
|
166
177
|
const PATH_STATE_LABELS: Record<PathState, string> = {
|
|
@@ -236,7 +247,7 @@ export function selectKnownOmpProviders(
|
|
|
236
247
|
entries: readonly PaseoProviderSnapshotResult["entries"][number][],
|
|
237
248
|
): KnownOmpProviderSummary[] {
|
|
238
249
|
return entries.flatMap((entry) =>
|
|
239
|
-
|
|
250
|
+
isOmpProvider(entry.provider)
|
|
240
251
|
? [
|
|
241
252
|
{
|
|
242
253
|
id: entry.provider,
|
package/client/quota-popover.tsx
CHANGED
|
@@ -3,7 +3,9 @@ import { Icon } from "@getpaseo/plugin/client/react-native";
|
|
|
3
3
|
import { useQuery } from "@tanstack/react-query";
|
|
4
4
|
import { useMemo } from "react";
|
|
5
5
|
import { Text, View } from "react-native";
|
|
6
|
+
import { storeForProvider, storeLabel } from "../shared/omp-store";
|
|
6
7
|
import { listOmpQuotas } from "../shared/quota";
|
|
8
|
+
import { ompStoreKey } from "./omp-store-state";
|
|
7
9
|
import {
|
|
8
10
|
type QuotaProviderGroup,
|
|
9
11
|
quotaProviderFromSession,
|
|
@@ -21,10 +23,12 @@ export function QuotaPopover(props: PluginButtonContentProps) {
|
|
|
21
23
|
const agentId = props.context === "agent" ? props.agentId : "";
|
|
22
24
|
const session = useAgent(agentId, (agent) => ({ model: agent.model, provider: agent.provider }));
|
|
23
25
|
const currentProvider = quotaProviderFromSession(session?.provider ?? "", session?.model ?? null);
|
|
26
|
+
const store = storeForProvider(session?.provider);
|
|
24
27
|
const loadQuotas = useRpc(listOmpQuotas);
|
|
25
28
|
const quotas = useQuery({
|
|
26
|
-
queryKey: ["paseo-omp", "quotas"],
|
|
27
|
-
queryFn: () => loadQuotas({}),
|
|
29
|
+
queryKey: ["paseo-omp", "quotas", ompStoreKey(store)],
|
|
30
|
+
queryFn: () => loadQuotas({ store }),
|
|
31
|
+
enabled: session !== undefined && session !== null,
|
|
28
32
|
refetchInterval: QUOTA_POLL_MS,
|
|
29
33
|
});
|
|
30
34
|
|
|
@@ -84,7 +88,7 @@ export function QuotaPopover(props: PluginButtonContentProps) {
|
|
|
84
88
|
[layout.compact, theme],
|
|
85
89
|
);
|
|
86
90
|
|
|
87
|
-
if (quotas.isLoading) return <Text style={styles.muted}>Loading omp quotas…</Text>;
|
|
91
|
+
if (!session || quotas.isLoading) return <Text style={styles.muted}>Loading omp quotas…</Text>;
|
|
88
92
|
if (quotas.error) return <Text style={styles.error}>Could not read omp quota state.</Text>;
|
|
89
93
|
|
|
90
94
|
const groups = quotaProviderGroups(quotas.data?.quotas ?? [], currentProvider);
|
|
@@ -96,6 +100,7 @@ export function QuotaPopover(props: PluginButtonContentProps) {
|
|
|
96
100
|
|
|
97
101
|
return (
|
|
98
102
|
<View style={styles.root}>
|
|
103
|
+
<Text style={styles.muted}>{storeLabel(store)}</Text>
|
|
99
104
|
{currentProvider && !hasCurrent ? (
|
|
100
105
|
<Text style={styles.muted}>
|
|
101
106
|
{`No recorded quota yet for ${quotaProviderLabel(currentProvider)} (this session's provider).`}
|
package/client/quota-state.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { OmpQuota } from "../shared/quota";
|
|
2
|
+
import { isOmpProvider } from "./omp-store-state";
|
|
2
3
|
|
|
3
4
|
const PROVIDER_LABELS: Record<string, string> = {
|
|
4
5
|
anthropic: "Anthropic",
|
|
@@ -37,9 +38,9 @@ export function quotaProviderFromSession(
|
|
|
37
38
|
model: string | null = null,
|
|
38
39
|
): string | null {
|
|
39
40
|
const [runtime, modelProvider] = provider.split("/");
|
|
40
|
-
if (runtime
|
|
41
|
+
if (!isOmpProvider(runtime)) return null;
|
|
41
42
|
if (modelProvider) return modelProvider;
|
|
42
|
-
return model?.split("/")[0]
|
|
43
|
+
return model?.includes("/") ? model.split("/")[0] : null;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export function quotaProviderLabel(provider: string | null): string {
|
|
@@ -51,32 +52,40 @@ export function quotaProviderLabel(provider: string | null): string {
|
|
|
51
52
|
export function quotasForProvider(
|
|
52
53
|
quotas: readonly OmpQuota[],
|
|
53
54
|
provider: string | null,
|
|
55
|
+
includeAll = false,
|
|
54
56
|
): OmpQuota[] {
|
|
55
|
-
return provider
|
|
57
|
+
return provider
|
|
58
|
+
? quotas.filter((quota) => quota.provider === provider)
|
|
59
|
+
: includeAll
|
|
60
|
+
? [...quotas]
|
|
61
|
+
: [];
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
export function quotaSummaryForProvider(
|
|
59
65
|
quotas: readonly OmpQuota[],
|
|
60
66
|
provider: string | null,
|
|
67
|
+
includeAll = false,
|
|
61
68
|
): { visible: boolean; label: string } {
|
|
62
|
-
const matching = quotasForProvider(quotas, provider);
|
|
69
|
+
const matching = quotasForProvider(quotas, provider, includeAll);
|
|
70
|
+
const label = provider ? quotaProviderLabel(provider) : "Quotas";
|
|
63
71
|
const used = matching.flatMap((quota) =>
|
|
64
72
|
quota.usedFraction === null ? [] : [quota.usedFraction],
|
|
65
73
|
);
|
|
66
74
|
if (used.length === 0) {
|
|
67
75
|
return provider
|
|
68
76
|
? { visible: true, label: `${quotaProviderLabel(provider)} · —` }
|
|
69
|
-
: { visible:
|
|
77
|
+
: { visible: includeAll, label: "Quotas · —" };
|
|
70
78
|
}
|
|
71
79
|
const peak = Math.round(Math.max(...used) * 100);
|
|
72
|
-
return { visible: true, label: `${
|
|
80
|
+
return { visible: true, label: `${label} · ${peak}%` };
|
|
73
81
|
}
|
|
74
82
|
|
|
75
83
|
export function quotaSeverityForProvider(
|
|
76
84
|
quotas: readonly OmpQuota[],
|
|
77
85
|
provider: string | null,
|
|
86
|
+
includeAll = false,
|
|
78
87
|
): QuotaSeverity {
|
|
79
|
-
const used = quotasForProvider(quotas, provider).flatMap((quota) =>
|
|
88
|
+
const used = quotasForProvider(quotas, provider, includeAll).flatMap((quota) =>
|
|
80
89
|
quota.usedFraction === null ? [] : [quota.usedFraction],
|
|
81
90
|
);
|
|
82
91
|
return used.length === 0 ? "unknown" : quotaSeverityFromFraction(Math.max(...used));
|
|
@@ -2,7 +2,9 @@ import { type PluginButtonContentProps, useAgent, useRpc } from "@getpaseo/plugi
|
|
|
2
2
|
import { useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { ScrollView, Text, View } from "react-native";
|
|
5
|
+
import { storeForProvider, storeLabel } from "../shared/omp-store";
|
|
5
6
|
import { listOmpSessions } from "../shared/sessions";
|
|
7
|
+
import { ompStoreKey } from "./omp-store-state";
|
|
6
8
|
|
|
7
9
|
const SESSIONS_POLL_MS = 20_000;
|
|
8
10
|
const PREVIEW_LIMIT = 20;
|
|
@@ -20,11 +22,13 @@ function age(epochSeconds: number): string {
|
|
|
20
22
|
export function SessionsPopover(props: PluginButtonContentProps) {
|
|
21
23
|
const { theme, layout } = props;
|
|
22
24
|
const agentId = props.context === "agent" ? props.agentId : "";
|
|
23
|
-
const
|
|
25
|
+
const agent = useAgent(agentId, ({ cwd, provider }) => ({ cwd, provider }));
|
|
26
|
+
const cwd = agent?.cwd ?? "";
|
|
27
|
+
const store = storeForProvider(agent?.provider);
|
|
24
28
|
const loadSessions = useRpc(listOmpSessions);
|
|
25
29
|
const sessions = useQuery({
|
|
26
|
-
queryKey: ["paseo-omp", "sessions", cwd],
|
|
27
|
-
queryFn: () => loadSessions({ cwd }),
|
|
30
|
+
queryKey: ["paseo-omp", "sessions", ompStoreKey(store), cwd],
|
|
31
|
+
queryFn: () => loadSessions({ cwd, store }),
|
|
28
32
|
enabled: cwd.length > 0,
|
|
29
33
|
refetchInterval: SESSIONS_POLL_MS,
|
|
30
34
|
});
|
|
@@ -56,6 +60,7 @@ export function SessionsPopover(props: PluginButtonContentProps) {
|
|
|
56
60
|
|
|
57
61
|
return (
|
|
58
62
|
<ScrollView contentContainerStyle={styles.root}>
|
|
63
|
+
<Text style={styles.muted}>{storeLabel(store)}</Text>
|
|
59
64
|
{items.slice(0, PREVIEW_LIMIT).map((entry) => (
|
|
60
65
|
<View key={entry.id} style={styles.row}>
|
|
61
66
|
{entry.title ? <Text style={styles.title}>{entry.title}</Text> : null}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Alpha release checklist
|
|
2
2
|
|
|
3
|
-
This checklist prepares `paseo-omp-v0.1.0-alpha.1`. It does not authorize publication. A maintainer must explicitly approve the tested
|
|
3
|
+
This checklist prepares `paseo-omp-v0.1.0-alpha.1`. It does not authorize publication. A maintainer must explicitly approve the tested package before any push, tag, GitHub release, or npm publication.
|
|
4
4
|
|
|
5
5
|
## Release identity
|
|
6
6
|
|
|
7
7
|
- [ ] Release Please proposes `0.1.0-alpha.1` from manifest version `0.0.0`.
|
|
8
|
-
- [ ] Package
|
|
8
|
+
- [ ] Package and tag names are `@omercnet/paseo-omp` and `paseo-omp-v0.1.0-alpha.1`.
|
|
9
9
|
- [ ] Provider identity remains `omp-plugin`; bundled `omp` remains independent and enabled or disabled by the user.
|
|
10
|
-
- [ ] Paseo requirement remains
|
|
10
|
+
- [ ] Paseo requirement remains the reviewed dual-version range, currently `>=0.8.0 <0.10.0`.
|
|
11
11
|
- [ ] Minimum tested OMP version, checksum, CI job, README, SUPPORT, and TESTING agree.
|
|
12
12
|
|
|
13
13
|
## Required gates
|
|
@@ -16,24 +16,22 @@ This checklist prepares `paseo-omp-v0.1.0-alpha.1`. It does not authorize public
|
|
|
16
16
|
- [ ] `npm run typecheck`
|
|
17
17
|
- [ ] `npm run test:coverage`; aggregate loaded-source coverage meets the configured threshold.
|
|
18
18
|
- [ ] Real installed OMP regression against the documented minimum version.
|
|
19
|
-
- [ ] `npm run package:release`
|
|
20
19
|
- [ ] `npm run test:integration:install`
|
|
21
20
|
- [ ] `npm run test:integration:docker`
|
|
22
21
|
- [ ] Windows/WSL host ownership job passes in CI.
|
|
23
22
|
- [ ] Docker canary matrix passes on the exact release candidate.
|
|
24
23
|
- [ ] GitHub Actions syntax and release-configuration schemas pass.
|
|
25
|
-
- [ ] Release ZIP contents contain documentation, production dependencies, and both plugin entries without development-only files.
|
|
26
24
|
|
|
27
25
|
## Required manual acceptance
|
|
28
26
|
|
|
29
|
-
- [ ] Maintainer installs the exact candidate
|
|
27
|
+
- [ ] Maintainer installs the exact `npm pack` candidate into the controlled official-Paseo Docker canary.
|
|
30
28
|
- [ ] Maintainer verifies catalog, prompt, tools, configured MCP, permissions, steer, interrupt, import/resume, subagents, rewind, usage, Hub, and plugin surfaces.
|
|
31
29
|
- [ ] Maintainer confirms the known limitations are acceptable for alpha.
|
|
32
30
|
- [ ] Maintainer explicitly authorizes publication after testing. Silence or prior approval for development is not release authorization.
|
|
33
31
|
|
|
34
32
|
## Alpha blocker
|
|
35
33
|
|
|
36
|
-
- [x] `omp-audit.1`: incomplete or compacted `agent_end` frames recover success or
|
|
34
|
+
- [x] `omp-audit.1`: incomplete or compacted `agent_end` frames recover success, failure, or native cancellation from complete streamed `message_end` evidence, or from bounded history whose entry IDs correlate with the streamed turn. Idle state is confirmed before and after retrieval, and concurrent interrupts remain authoritative. Missing, unavailable, non-correlatable, or conflicting terminal evidence fails closed with content-free count diagnostics.
|
|
37
35
|
|
|
38
36
|
## Accepted alpha limitations
|
|
39
37
|
|
|
@@ -67,4 +65,4 @@ The prerelease notes must include:
|
|
|
67
65
|
|
|
68
66
|
## Publication boundary
|
|
69
67
|
|
|
70
|
-
Release Please may prepare metadata.
|
|
68
|
+
Release Please may prepare metadata. Publication must use npm trusted publishing from the immutable release commit. Never retag an alpha commit as stable.
|
package/docs/configuration.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Configuration
|
|
2
2
|
|
|
3
|
-
Open the **OMP** sidebar to browse
|
|
3
|
+
Open the global **OMP** sidebar to browse and edit machine-wide state, or open the workspace **OMP** panel from the workspace tab or Explorer to manage project-scoped state. Scalar edits in the global surface use OMP's native `config set` and `config reset` commands. Workspace edits create validated overrides in `<workspace>/.omp/config.yml`; removing an override restores the effective global or default value. Arrays, records, and credentials remain read-only in both surfaces.
|
|
4
4
|
|
|
5
5
|
The **Plugin** tab documents the supported `omp-plugin` launch options, including names-only inherited environment configuration. Paseo's public plugin API does not expose the effective provider options for active launches, so the tab does not claim profile values are active. Choose **OMP Plugin** when creating an agent. Model, mode, thinking level, system prompt, persistence, MCP servers, workspace, and agent environment use Paseo's standard provider controls.
|
|
6
6
|
|
|
@@ -49,17 +49,21 @@ These options cover every plugin-specific launch value. Values that belong to an
|
|
|
49
49
|
|
|
50
50
|
## OMP-native plugins
|
|
51
51
|
|
|
52
|
-
Open **OMP → OMP plugins** to
|
|
52
|
+
Open **OMP → OMP plugins** globally for user-scoped management, or use the workspace **OMP** panel to include project-scoped installations and effective project overrides. The manager uses OMP's documented singular `omp plugin` CLI and supports install, enable, disable, upgrade, and uninstall operations. Every state-changing action requires explicit confirmation; already-running OMP sessions are unchanged.
|
|
53
53
|
|
|
54
|
-
Project-scoped installations
|
|
54
|
+
Project-scoped lifecycle commands run from the selected workspace and use `--scope project`. Duplicate path installations that share one npm package identity remain read-only because OMP's lifecycle CLI addresses npm plugins by package name rather than installation path. Plugin configuration exposes schema metadata without returning current or default values. Non-secret scalar plugin settings can be set or deleted through write-only controls. Secret settings are presence-only and delete-only because OMP's CLI would otherwise expose a new secret through process arguments.
|
|
55
55
|
|
|
56
56
|
The Configuration view links to the official OMP settings reference, value parsing and precedence guides, relevant category sections, and a small curated set of setting-specific anchors.
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
## MCP tools and policy boundary
|
|
59
|
+
## MCP tools, management, and policy boundary
|
|
60
60
|
|
|
61
61
|
Configured MCP servers and Paseo's caller-scoped MCP tools are supported. The plugin discovers their schemas, assigns collision-safe OMP names, binds them before `session.ready`, forwards progress and terminal results, propagates cancellation, and renders calls with friendly labels.
|
|
62
62
|
|
|
63
|
+
Use the **MCP** control beside the composer on an **OMP Plugin** agent to run OMP's native list, add, reload, test, authorize, enable, disable, resource, and prompt commands. The control is agent-scoped because OMP MCP discovery depends on both the active profile and the workspace directory. The global OMP sidebar remains a host-level health and settings surface; a separate workspace manager would duplicate OMP's own discovery and precedence rules.
|
|
64
|
+
|
|
65
|
+
Command output, setup questions, and OAuth prompts appear in the agent timeline. OAuth URLs render as an interactive card and always retain the full provider authorization URL, never substituting OMP's daemon-local `/launch` shortcut. **Open in Paseo Browser** calls the current agent's caller-scoped `browser_new_tab` tool, so the authorization page becomes a browser tab in the same workspace; it requires Paseo tools to be injected into the agent, browser tools to be enabled, and a connected Paseo desktop browser host. **Open on this device** remains available when no browser host is connected. For a loopback callback to complete automatically, the chosen browser host must run on the daemon machine. Otherwise, finish authorization in either browser, copy the final redirect URL or authorization code, and submit it in the OMP authorization prompt. Tokens and refresh material are stored by OMP on the daemon (or its configured auth broker), never in the Paseo client or plugin timeline.
|
|
66
|
+
|
|
63
67
|
Paseo's exact session `toolPolicy` preapproval grants are not equivalent to OMP's `set_host_tools` contract. The plugin cannot preserve that policy exactly, so any non-empty `toolPolicy` rejects session startup. It never converts exact grants into broader access. `disallowedTools` is separate: it controls only recognized native OMP built-ins and rejects unknown names.
|
|
64
68
|
|
|
65
69
|
## Credentials and environment
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Core OMP provider issue audit
|
|
2
2
|
|
|
3
|
-
This audit compares reports in `getpaseo/paseo` with the community `omp-plugin` provider. It was refreshed on 2026-09-
|
|
3
|
+
This audit compares reports in `getpaseo/paseo` with the community `omp-plugin` provider. It was refreshed on 2026-09-18 from GitHub issue titles and bodies containing `OMP`, `oh-my-pi`, or `rpc-ui`, OMP-related pull requests, and materially equivalent Pi/RPC reports. GitHub Discussions were also inspected; the repository had no OMP-related discussion.
|
|
4
4
|
|
|
5
5
|
Issue and pull-request pairs are consolidated by root cause. A closed upstream issue does not prove the plugin implements the behavior, and an open upstream issue does not imply the plugin is affected.
|
|
6
6
|
|
|
@@ -17,9 +17,10 @@ Status meanings:
|
|
|
17
17
|
| --- | --- | --- |
|
|
18
18
|
| [#3838](https://github.com/getpaseo/paseo/issues/3838), [PR #3839](https://github.com/getpaseo/paseo/pull/3839) | **Verified** | A dead OMP subprocess invalidates its generation. A later write lazily resumes the same native session. Process-tree cleanup, recovery, and registry replacement are covered. |
|
|
19
19
|
| [#3252](https://github.com/getpaseo/paseo/issues/3252), equivalent Pi [#3496](https://github.com/getpaseo/paseo/issues/3496), [PR #3258](https://github.com/getpaseo/paseo/pull/3258) | **Verified** | Hidden and custom notices do not terminalize a turn before the native user echo and terminal evidence. |
|
|
20
|
-
| [#2260](https://github.com/getpaseo/paseo/issues/2260), [PR #2261](https://github.com/getpaseo/paseo/pull/2261) | **Verified** | Incomplete or compacted `agent_end` frames use complete streamed `message_end` evidence
|
|
20
|
+
| [#2260](https://github.com/getpaseo/paseo/issues/2260), [PR #2261](https://github.com/getpaseo/paseo/pull/2261) | **Verified** | Incomplete or compacted `agent_end` frames use complete streamed `message_end` evidence or bounded history correlated by entry ID, with idle state confirmed before and after retrieval. Success, failure, native cancellation, and concurrent interrupts are preserved; missing, unavailable, non-correlatable, or conflicting terminal evidence fails closed with content-free count diagnostics. |
|
|
21
21
|
| [#2281](https://github.com/getpaseo/paseo/issues/2281), [PR #2282](https://github.com/getpaseo/paseo/pull/2282) | **Verified** | Local-only prompt results and structured commands have explicit terminal ownership. |
|
|
22
22
|
| [#3654](https://github.com/getpaseo/paseo/issues/3654), [#3998](https://github.com/getpaseo/paseo/issues/3998), [PR #3667](https://github.com/getpaseo/paseo/pull/3667) | **Verified** | Post-`agent_end` state reconciliation is bounded. Stale or unavailable `get_state` cannot leave a turn running forever. |
|
|
23
|
+
| Upstream OMP [PR #12331](https://github.com/can1357/oh-my-pi/pull/12331) and released OMP 18.2.x unkeyed `agent_end` frames | **Mitigated** | Request-keyed terminals use exact matching. For released binaries that omit the key, the plugin accepts only ordered current-prompt evidence: a fresh branch-correlated user entry followed by assistant activity, confirmed native idle/non-compacting state, and no active permission, tool, steer, or child work. Active or pre-evidence stale candidates are ignored. Confirmed-idle ambiguity fails only the Paseo turn, not the OMP runtime. The remaining same-agent stale-event misattribution risk is explicit and bounded; waiting for the upstream protocol fix would make every later turn unusable on published releases. Client reconnects, including mobile reconnect grace, are host transport behavior and do not own the daemon-managed provider session. |
|
|
23
24
|
| [#3999](https://github.com/getpaseo/paseo/issues/3999), [#4000](https://github.com/getpaseo/paseo/issues/4000), [#4039](https://github.com/getpaseo/paseo/issues/4039), [PR #3772](https://github.com/getpaseo/paseo/pull/3772), [PR #4217](https://github.com/getpaseo/paseo/pull/4217) | **Verified** | `prompt.steer` is negotiated and implemented with expected-turn checks, acknowledgement ordering, duplicate correlation, and interrupt/terminal race coverage. |
|
|
24
25
|
| Shared RPC cancellation [#3540](https://github.com/getpaseo/paseo/issues/3540), Pi [#3749](https://github.com/getpaseo/paseo/issues/3749) | **Verified in provider** | Native abort, exactly-one terminal event, permission cleanup, descendant cleanup, and uncertain-cleanup quarantine are tested. UI keyboard delivery remains host-owned. |
|
|
25
26
|
| [#3218](https://github.com/getpaseo/paseo/issues/3218) | **Verified at provider boundary** | Session close owns and awaits OMP process cleanup. Whether every archive UI route invokes provider close is a host concern. |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/docs/installation.md
CHANGED
|
@@ -4,43 +4,61 @@ Paseo plugins are trusted, unsandboxed code. Review this plugin and its producti
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
- Paseo daemon and apps:
|
|
7
|
+
- Paseo daemon and apps: `>=0.8.0 <0.10.0`; `0.9.0-beta.1` is recommended
|
|
8
8
|
- OMP: `18.1.15` or newer is the supported floor
|
|
9
9
|
- OMP RPC: protocol v2 must negotiate successfully
|
|
10
10
|
|
|
11
11
|
The first public build is an alpha. Alpha releases are compatibility previews and may require deleting and re-importing agents created by an earlier preview.
|
|
12
12
|
|
|
13
|
-
## Install
|
|
13
|
+
## Install or update from npm on Paseo 0.9
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Paseo 0.9.0-beta.1 can acquire the published package and its production dependencies directly from npm on the daemon host:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
paseo plugin install npm:@omercnet/paseo-omp@<version>
|
|
19
|
+
paseo plugin ls paseo-omp
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Check for the registry's current `latest` version and approve the proposed update, or select an exact version explicitly:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
paseo plugin update paseo-omp
|
|
26
|
+
paseo plugin update paseo-omp --version <new-version>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The daemon uses its own npm registry and authentication configuration. npm acquisition installs the artifact's production dependencies before preparation; because npm artifacts omit `package-lock.json`, the preparation helper leaves that tree unchanged. A Git checkout with the committed lockfile runs frozen `npm ci --omit=dev --ignore-scripts`. A failed download, preparation, compatibility check, or activation keeps the installed revision active. Paseo 0.8 does not support npm plugin sources; use a Git tag or local directory instead.
|
|
30
|
+
|
|
31
|
+
## Install a Git release on Paseo 0.8 or 0.9
|
|
32
|
+
|
|
33
|
+
Install the matching reviewed Git tag:
|
|
16
34
|
|
|
17
35
|
```bash
|
|
18
36
|
paseo plugin add omercnet/paseo-plugins:paseo-omp --ref paseo-omp-v<version>
|
|
19
37
|
paseo plugin ls paseo-omp
|
|
20
38
|
```
|
|
21
39
|
|
|
22
|
-
|
|
40
|
+
### Update a Git installation on Paseo 0.9
|
|
41
|
+
|
|
42
|
+
An ordinary update reviews the remote repository's current default HEAD. To move directly to another reviewed tag or commit, select it explicitly:
|
|
23
43
|
|
|
24
44
|
```bash
|
|
25
|
-
paseo plugin
|
|
26
|
-
paseo plugin
|
|
27
|
-
paseo plugin add omercnet/paseo-plugins:paseo-omp --ref paseo-omp-v<new-version>
|
|
45
|
+
paseo plugin update paseo-omp
|
|
46
|
+
paseo plugin update paseo-omp --ref paseo-omp-v<new-version>
|
|
28
47
|
```
|
|
29
48
|
|
|
30
|
-
|
|
49
|
+
The install-time `--ref` does not constrain later updates. Paseo 0.9 stages and validates the selected revision before replacing the active installation, so plugin settings remain intact and a failed update leaves the previous revision running.
|
|
31
50
|
|
|
32
|
-
|
|
51
|
+
### Update a tag-pinned Git installation on Paseo 0.8
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
Paseo 0.8 does not provide the 0.9 non-destructive explicit-ref update flow. Record the current installation, then replace it with the new tag in one maintenance window:
|
|
35
54
|
|
|
36
55
|
```bash
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
paseo plugin install "$PWD/paseo-omp"
|
|
56
|
+
paseo plugin ls paseo-omp --json > paseo-omp-before-update.json
|
|
57
|
+
paseo plugin remove paseo-omp
|
|
58
|
+
paseo plugin add omercnet/paseo-plugins:paseo-omp --ref paseo-omp-v<new-version>
|
|
41
59
|
```
|
|
42
60
|
|
|
43
|
-
|
|
61
|
+
Removal deletes plugin-scoped settings and briefly makes `omp-plugin` unavailable. It does not modify Paseo's bundled `omp` provider or native OMP transcripts. Roll back by repeating the remove/add sequence with the recorded tag or commit.
|
|
44
62
|
|
|
45
63
|
## Install a local checkout
|
|
46
64
|
|
|
@@ -62,12 +80,10 @@ paseo plugin ls paseo-omp
|
|
|
62
80
|
|
|
63
81
|
## Track a branch
|
|
64
82
|
|
|
65
|
-
Tracking `main` executes future dependency and plugin updates with the daemon user's privileges
|
|
83
|
+
Tracking `main` executes future dependency and plugin updates with the daemon user's privileges:
|
|
66
84
|
|
|
67
85
|
```bash
|
|
68
86
|
paseo plugin add omercnet/paseo-plugins:paseo-omp --ref main
|
|
69
|
-
paseo plugin ls paseo-omp --json > paseo-omp-before-update.json
|
|
70
|
-
paseo plugin update paseo-omp
|
|
71
87
|
```
|
|
72
88
|
|
|
73
|
-
|
|
89
|
+
On Paseo 0.9, preview and approve the remote default HEAD with `paseo plugin update paseo-omp`, or select `main` explicitly with `paseo plugin update paseo-omp --ref main`. On Paseo 0.8, record the installed commit before its existing branch-update workflow. A failed build or compatibility check leaves the previous revision active.
|