@subshell-ai/plugin-oh-my-pi 0.2.2 → 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/dist/index.d.ts +36 -2
- package/dist/index.js +322 -4
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,31 @@
|
|
|
1
|
-
//#region ../../plugin-api/dist/types-
|
|
1
|
+
//#region ../../plugin-api/dist/types-DPLl6DHG.d.ts
|
|
2
|
+
//#region src/previews.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* How a harness reports loopback ports its own tooling started, for the
|
|
5
|
+
* preview picker.
|
|
6
|
+
*
|
|
7
|
+
* Port discovery attributes sockets by walking the pane's process tree. A
|
|
8
|
+
* harness that brokers its tool shells through a shared daemon (oh-my-pi
|
|
9
|
+
* does) leaves that tree, so a server its tools start is reachable but
|
|
10
|
+
* invisible. A plugin that declares the `previews` capability ships a
|
|
11
|
+
* reporter the control plane can list those ports from.
|
|
12
|
+
*
|
|
13
|
+
* The report carries ports only, never a host: the node dials loopback, so a
|
|
14
|
+
* report cannot point the proxy at another machine.
|
|
15
|
+
*/
|
|
16
|
+
type HarnessPreviewReporting = "extension" | "hooks";
|
|
17
|
+
/**
|
|
18
|
+
* What a harness declares about its preview reporting. The rules mirror
|
|
19
|
+
* {@link HarnessSubagentSupport}: `reporting` is checked against the union at
|
|
20
|
+
* load, and `setup` is the copy the preset editor shows when a human act is
|
|
21
|
+
* needed before reports start.
|
|
22
|
+
*/
|
|
23
|
+
interface HarnessPreviews {
|
|
24
|
+
/** How reports reach the plane: a per-launch extension, or harness lifecycle hooks. */
|
|
25
|
+
reporting: HarnessPreviewReporting;
|
|
26
|
+
/** One-time human action required for reporting to work, if any. */
|
|
27
|
+
setup?: string;
|
|
28
|
+
}
|
|
2
29
|
/** How the plugin learns about children: harness lifecycle hooks, or a loaded extension. */
|
|
3
30
|
type HarnessSubagentDiscovery = "hooks" | "extension";
|
|
4
31
|
/** Whether the sidebar can move the parent's own view to a child, or the harness keeps that in-app. */
|
|
@@ -43,7 +70,7 @@ type PluginType = "agent-harness" | "terminal" | "network";
|
|
|
43
70
|
* and a network plugin claiming `resume` are both refused at load, for the
|
|
44
71
|
* same reason a claimed-but-unimplemented capability is.
|
|
45
72
|
*/
|
|
46
|
-
type PluginCapability = "mcp" | "resume" | "attention" | "subagents" | "publish" | "supervise" | "guard" | "settings";
|
|
73
|
+
type PluginCapability = "mcp" | "resume" | "attention" | "subagents" | "previews" | "publish" | "supervise" | "guard" | "settings";
|
|
47
74
|
/** A harness preset as defined by the user (decoded JSON blobs). */
|
|
48
75
|
interface PresetDefinition {
|
|
49
76
|
/** Human-friendly preset name */
|
|
@@ -380,6 +407,13 @@ interface PluginSecrets {
|
|
|
380
407
|
interface SubshellPlugin {
|
|
381
408
|
/** Native child-agent reporting and optional in-place navigation. */
|
|
382
409
|
subagents?: HarnessSubagentSupport;
|
|
410
|
+
/**
|
|
411
|
+
* Native preview-port reporting: the harness tells the plane which loopback
|
|
412
|
+
* ports its tooling serves, so the picker lists servers discovery cannot
|
|
413
|
+
* attribute to the pane's process tree. Declare the `previews` capability
|
|
414
|
+
* with it.
|
|
415
|
+
*/
|
|
416
|
+
previews?: HarnessPreviews;
|
|
383
417
|
/** Builds the argv (no shell) used to launch a subshell. */
|
|
384
418
|
buildCommand(input: BuildCommandInput): string[];
|
|
385
419
|
/** Validates a preset definition before saving. */
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../plugin-api/dist/manifest-
|
|
1
|
+
//#region ../../plugin-api/dist/manifest-DgWWfiH-.js
|
|
2
2
|
/** Every {@link PluginType}, for anything that has to iterate or validate them. */
|
|
3
3
|
const PLUGIN_TYPES = [
|
|
4
4
|
"agent-harness",
|
|
@@ -287,6 +287,158 @@ function validateGenericPreset(preset) {
|
|
|
287
287
|
};
|
|
288
288
|
}
|
|
289
289
|
//#endregion
|
|
290
|
+
//#region src/previews.ts
|
|
291
|
+
/**
|
|
292
|
+
* omp brokers tool shells through its own daemon, so servers those tools
|
|
293
|
+
* start live outside the pane's process tree and port discovery cannot see
|
|
294
|
+
* them. This extension watches tool results for loopback URLs and reports
|
|
295
|
+
* the ports to the control plane, where the preview picker lists them.
|
|
296
|
+
*
|
|
297
|
+
* It deliberately reports what the output says, not a scan: a tool result
|
|
298
|
+
* from something that executes (bash or a custom tool) is the harness's own
|
|
299
|
+
* evidence that it started something, so file-reading tools are skipped. The
|
|
300
|
+
* report is port-only (the node dials loopback), bounded, and coalesced. A
|
|
301
|
+
* port the harness has not mentioned for ten minutes stops being refreshed,
|
|
302
|
+
* and the server's TTL retires it. A pane without the identity environment
|
|
303
|
+
* has no endpoint and stays quiet.
|
|
304
|
+
*/
|
|
305
|
+
const PREVIEW_BRIDGE = `
|
|
306
|
+
export default function (pi) {
|
|
307
|
+
const REPORT_DELAY_MS = 750;
|
|
308
|
+
const HEARTBEAT_MS = 60_000;
|
|
309
|
+
const FRESH_MS = 10 * 60_000;
|
|
310
|
+
const MAX_PORTS = 32;
|
|
311
|
+
const MAX_TITLE = 120;
|
|
312
|
+
const MAX_SCAN = 64 * 1024;
|
|
313
|
+
const SKIP_TOOLS = { read: true, edit: true, write: true, grep: true, glob: true };
|
|
314
|
+
const URL_RE = /(?:https?:\\/\\/)?(?:localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0|\\[::1\\]):(\\d{1,5})(?!\\d)/gi;
|
|
315
|
+
|
|
316
|
+
const ports = new Map();
|
|
317
|
+
let timer = null;
|
|
318
|
+
let heartbeat = null;
|
|
319
|
+
let reporting = false;
|
|
320
|
+
let queued = false;
|
|
321
|
+
let warned = false;
|
|
322
|
+
let disabled = false;
|
|
323
|
+
const prune = now => {
|
|
324
|
+
for (const [port, entry] of ports) {
|
|
325
|
+
if (now - entry.seenAt > FRESH_MS) ports.delete(port);
|
|
326
|
+
}
|
|
327
|
+
if (ports.size === 0 && heartbeat !== null) {
|
|
328
|
+
clearInterval(heartbeat);
|
|
329
|
+
heartbeat = null;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
const remember = (port, title) => {
|
|
333
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) return;
|
|
334
|
+
const now = Date.now();
|
|
335
|
+
prune(now);
|
|
336
|
+
const previous = ports.get(port);
|
|
337
|
+
if (previous) {
|
|
338
|
+
previous.seenAt = now;
|
|
339
|
+
if (title && !previous.title) previous.title = title;
|
|
340
|
+
} else {
|
|
341
|
+
if (ports.size >= MAX_PORTS) return;
|
|
342
|
+
ports.set(port, { title, seenAt: now });
|
|
343
|
+
}
|
|
344
|
+
if (heartbeat === null) heartbeat = setInterval(beat, HEARTBEAT_MS);
|
|
345
|
+
schedule();
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
const scan = text => {
|
|
349
|
+
const bounded = text.slice(0, MAX_SCAN);
|
|
350
|
+
URL_RE.lastIndex = 0;
|
|
351
|
+
let match;
|
|
352
|
+
while ((match = URL_RE.exec(bounded))) {
|
|
353
|
+
const port = Number(match[1]);
|
|
354
|
+
const start = bounded.lastIndexOf('\\n', match.index) + 1;
|
|
355
|
+
const end = bounded.indexOf('\\n', match.index);
|
|
356
|
+
const line = bounded.slice(start, end === -1 ? bounded.length : end).trim();
|
|
357
|
+
remember(port, line ? line.slice(0, MAX_TITLE) : undefined);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const report = async () => {
|
|
362
|
+
if (disabled || ports.size === 0) return;
|
|
363
|
+
if (reporting) {
|
|
364
|
+
queued = true;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
const base = process.env.SUBSHELL_BASE_URL;
|
|
368
|
+
const token = process.env.SUBSHELL_API_KEY;
|
|
369
|
+
const id = process.env.SUBSHELL_ID;
|
|
370
|
+
if (!base || !token || !id) {
|
|
371
|
+
if (!warned) {
|
|
372
|
+
warned = true;
|
|
373
|
+
pi.logger?.debug?.('subshell preview report skipped: the pane identity environment is missing');
|
|
374
|
+
}
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
prune(Date.now());
|
|
378
|
+
if (ports.size === 0) return;
|
|
379
|
+
reporting = true;
|
|
380
|
+
try {
|
|
381
|
+
const response = await fetch(base + '/api/subshells/' + encodeURIComponent(id) + '/views/report', {
|
|
382
|
+
method: 'POST',
|
|
383
|
+
headers: { authorization: 'Bearer ' + token, 'content-type': 'application/json' },
|
|
384
|
+
body: JSON.stringify({
|
|
385
|
+
ports: [...ports.entries()].map(([port, entry]) => ({
|
|
386
|
+
port,
|
|
387
|
+
...(entry.title ? { title: entry.title } : {}),
|
|
388
|
+
})),
|
|
389
|
+
}),
|
|
390
|
+
signal: AbortSignal.timeout(2000),
|
|
391
|
+
});
|
|
392
|
+
if (response.status === 404 || response.status === 401) {
|
|
393
|
+
// The server either predates the route or no longer accepts this
|
|
394
|
+
// pane's key. Retrying every heartbeat cannot fix either, so stop
|
|
395
|
+
// until a restart hands the extension a new process.
|
|
396
|
+
disabled = true;
|
|
397
|
+
if (heartbeat !== null) {
|
|
398
|
+
clearInterval(heartbeat);
|
|
399
|
+
heartbeat = null;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
if (!response.ok && !warned) {
|
|
403
|
+
warned = true;
|
|
404
|
+
pi.logger?.debug?.('subshell preview report failed', { status: response.status });
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
// A failed report is not fatal: the next tool result reports again.
|
|
408
|
+
} finally {
|
|
409
|
+
reporting = false;
|
|
410
|
+
if (queued) {
|
|
411
|
+
queued = false;
|
|
412
|
+
void report();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
const schedule = () => {
|
|
418
|
+
if (timer !== null) return;
|
|
419
|
+
timer = setTimeout(() => {
|
|
420
|
+
timer = null;
|
|
421
|
+
void report();
|
|
422
|
+
}, REPORT_DELAY_MS);
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const beat = () => {
|
|
426
|
+
prune(Date.now());
|
|
427
|
+
if (ports.size > 0) void report();
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
pi.on('tool_result', event => {
|
|
431
|
+
if (!event || event.isError) return;
|
|
432
|
+
const name = typeof event.toolName === 'string' ? event.toolName : '';
|
|
433
|
+
if (SKIP_TOOLS[name] === true) return;
|
|
434
|
+
const content = Array.isArray(event.content) ? event.content : [];
|
|
435
|
+
for (const block of content) {
|
|
436
|
+
if (block && block.type === 'text' && typeof block.text === 'string') scan(block.text);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
`;
|
|
441
|
+
//#endregion
|
|
290
442
|
//#region src/subagents.ts
|
|
291
443
|
/**
|
|
292
444
|
* omp's session event bus already carries the roster the task tool is running:
|
|
@@ -486,7 +638,7 @@ export default function (pi) {
|
|
|
486
638
|
const parsed = parseManifest({
|
|
487
639
|
name: "@subshell-ai/plugin-oh-my-pi",
|
|
488
640
|
description: "Oh My Pi harness plugin for Subshell",
|
|
489
|
-
version: "0.
|
|
641
|
+
version: "0.3.0",
|
|
490
642
|
license: "Apache-2.0",
|
|
491
643
|
type: "module",
|
|
492
644
|
main: "dist/index.js",
|
|
@@ -598,6 +750,163 @@ const SETTINGS = [
|
|
|
598
750
|
*/
|
|
599
751
|
const TITLE_PREFIX = /^π(?:$|\s+(?:[>!:]|[⠁-⣿]|[◔◑◕○●]|[-\\/|])|:(?=\s|$))\s*/u;
|
|
600
752
|
/**
|
|
753
|
+
* Loads that provider from the environment. The source is fixed: it names the
|
|
754
|
+
* variable and never interpolates its value, so no preset data (and no
|
|
755
|
+
* credential) is written to a node. A launch without the variable loads
|
|
756
|
+
* nothing, which keeps the module inert for every other omp preset.
|
|
757
|
+
*
|
|
758
|
+
* The value is preset-controlled, so malformed or incomplete JSON leaves the
|
|
759
|
+
* module inert too. It must match what `model-cli omp` generates and nothing
|
|
760
|
+
* else: the provider keeps model-cli's namespace, so a preset cannot override
|
|
761
|
+
* a built-in provider; the apiKey is the env-var name rather than a literal
|
|
762
|
+
* secret; and no field may carry omp's `!command` config syntax, which would
|
|
763
|
+
* run shell commands as the pane user. Registration is queued outside the
|
|
764
|
+
* extension factory's error guard, so a value omp would reject there would
|
|
765
|
+
* take the pane down at startup instead of warning. One debug line names the
|
|
766
|
+
* problem, never the value.
|
|
767
|
+
*
|
|
768
|
+
* A preset built from `model-cli omp` used to name its provider by an
|
|
769
|
+
* `--extension` path in the generating host's cache, so it only worked on
|
|
770
|
+
* machines where model-cli had run. This module rides the per-launch
|
|
771
|
+
* extension directory the host already ships to every node instead.
|
|
772
|
+
*/
|
|
773
|
+
const MODEL_CLI_PROVIDER_SHIM = `export default function(pi) {
|
|
774
|
+
const raw = process.env.MODEL_CLI_OMP_CONFIG;
|
|
775
|
+
if (!raw) return;
|
|
776
|
+
let config;
|
|
777
|
+
try {
|
|
778
|
+
config = JSON.parse(raw);
|
|
779
|
+
} catch {
|
|
780
|
+
pi.logger?.debug?.("model-cli provider config ignored: MODEL_CLI_OMP_CONFIG is not JSON");
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const CONNECTION_KEYS = ["baseUrl", "api", "apiKey", "models"];
|
|
785
|
+
const MODEL_KEYS = [
|
|
786
|
+
"id",
|
|
787
|
+
"name",
|
|
788
|
+
"contextWindow",
|
|
789
|
+
"maxTokens",
|
|
790
|
+
"reasoning",
|
|
791
|
+
"input",
|
|
792
|
+
"cost",
|
|
793
|
+
"compat",
|
|
794
|
+
"thinking",
|
|
795
|
+
];
|
|
796
|
+
const COST_KEYS = ["input", "output", "cacheRead", "cacheWrite"];
|
|
797
|
+
const COMPAT_KEYS = [
|
|
798
|
+
"maxTokensField",
|
|
799
|
+
"reasoningContentField",
|
|
800
|
+
"replayReasoningContent",
|
|
801
|
+
"requiresReasoningContentForAllAssistantTurns",
|
|
802
|
+
"supportsDeveloperRole",
|
|
803
|
+
"supportsStore",
|
|
804
|
+
"supportsStrictMode",
|
|
805
|
+
"thinkingFormat",
|
|
806
|
+
];
|
|
807
|
+
const THINKING_KEYS = ["mode", "efforts", "effortMap"];
|
|
808
|
+
|
|
809
|
+
const isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
810
|
+
const only = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
|
|
811
|
+
const text = (value) => typeof value === "string" && value !== "";
|
|
812
|
+
const amount = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
813
|
+
const positive = (value) => amount(value) && value > 0;
|
|
814
|
+
const command = (value) => {
|
|
815
|
+
if (typeof value === "string") return value.startsWith("!");
|
|
816
|
+
if (Array.isArray(value)) return value.some(command);
|
|
817
|
+
if (isObject(value)) return Object.values(value).some(command);
|
|
818
|
+
return false;
|
|
819
|
+
};
|
|
820
|
+
const cost = (value) => isObject(value) && only(value, COST_KEYS) && Object.values(value).every(amount);
|
|
821
|
+
const compat = (value) =>
|
|
822
|
+
isObject(value) &&
|
|
823
|
+
only(value, COMPAT_KEYS) &&
|
|
824
|
+
Object.values(value).every((item) => typeof item === "string" || typeof item === "boolean");
|
|
825
|
+
const thinking = (value) =>
|
|
826
|
+
isObject(value) &&
|
|
827
|
+
only(value, THINKING_KEYS) &&
|
|
828
|
+
(value.mode === "anthropic-adaptive" || value.mode === "effort") &&
|
|
829
|
+
Array.isArray(value.efforts) &&
|
|
830
|
+
value.efforts.every(text) &&
|
|
831
|
+
isObject(value.effortMap) &&
|
|
832
|
+
Object.values(value.effortMap).every(text);
|
|
833
|
+
const model = (value) =>
|
|
834
|
+
isObject(value) &&
|
|
835
|
+
only(value, MODEL_KEYS) &&
|
|
836
|
+
text(value.id) &&
|
|
837
|
+
(value.name === undefined || text(value.name)) &&
|
|
838
|
+
(value.contextWindow === undefined || positive(value.contextWindow)) &&
|
|
839
|
+
(value.maxTokens === undefined || positive(value.maxTokens)) &&
|
|
840
|
+
(value.reasoning === undefined || typeof value.reasoning === "boolean") &&
|
|
841
|
+
(value.input === undefined ||
|
|
842
|
+
(Array.isArray(value.input) && value.input.every((item) => item === "text" || item === "image"))) &&
|
|
843
|
+
(value.cost === undefined || cost(value.cost)) &&
|
|
844
|
+
(value.compat === undefined || compat(value.compat)) &&
|
|
845
|
+
(value.thinking === undefined || thinking(value.thinking));
|
|
846
|
+
|
|
847
|
+
const connection = config?.connection;
|
|
848
|
+
const models = connection?.models;
|
|
849
|
+
const usable =
|
|
850
|
+
typeof config?.provider === "string" &&
|
|
851
|
+
/^model-cli-omp-(?:litellm|direct)-\\S+$/.test(config.provider) &&
|
|
852
|
+
isObject(connection) &&
|
|
853
|
+
only(connection, CONNECTION_KEYS) &&
|
|
854
|
+
typeof connection.baseUrl === "string" &&
|
|
855
|
+
/^https?:\\/\\/.+/.test(connection.baseUrl) &&
|
|
856
|
+
(connection.api === "anthropic-messages" || connection.api === "openai-completions") &&
|
|
857
|
+
connection.apiKey === "MODEL_CLI_OMP_API_KEY" &&
|
|
858
|
+
Array.isArray(models) &&
|
|
859
|
+
models.length > 0 &&
|
|
860
|
+
models.every(model) &&
|
|
861
|
+
!command(config);
|
|
862
|
+
if (!usable) {
|
|
863
|
+
pi.logger?.debug?.("model-cli provider config ignored: MODEL_CLI_OMP_CONFIG is not the model-cli shape");
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
pi.registerProvider(config.provider, connection);
|
|
867
|
+
}
|
|
868
|
+
`;
|
|
869
|
+
/**
|
|
870
|
+
* A preset built from `model-cli omp` used to prove its provider with an
|
|
871
|
+
* extension path in the generating host's cache. That file is writable by
|
|
872
|
+
* anything running as the pane user and persists between launches, so the
|
|
873
|
+
* provider module below is a replacement, not a companion: the legacy flag is
|
|
874
|
+
* dropped when this launch writes its own module. omp accepts the path as
|
|
875
|
+
* `--extension`, `--trusted-extension`, `-e` or `--hook`, so every spelling is
|
|
876
|
+
* matched, and the path is normalized first because `./` and doubled
|
|
877
|
+
* separators name the same file.
|
|
878
|
+
*/
|
|
879
|
+
const LEGACY_PROVIDER_FLAGS = [
|
|
880
|
+
"--extension",
|
|
881
|
+
"--trusted-extension",
|
|
882
|
+
"-e",
|
|
883
|
+
"--hook"
|
|
884
|
+
];
|
|
885
|
+
const LEGACY_PROVIDER_INLINE = [
|
|
886
|
+
"--extension=",
|
|
887
|
+
"--trusted-extension=",
|
|
888
|
+
"-e=",
|
|
889
|
+
"--hook="
|
|
890
|
+
];
|
|
891
|
+
function isLegacyProviderPath(value) {
|
|
892
|
+
const segments = value.trim().replace(/\\/g, "/").split("/").filter((segment) => segment !== "" && segment !== ".");
|
|
893
|
+
return /(?:^|\/)model-cli\/omp-provider\.mjs$/.test(segments.join("/"));
|
|
894
|
+
}
|
|
895
|
+
function withoutLegacyProviderExtension(flags) {
|
|
896
|
+
const kept = [];
|
|
897
|
+
for (let i = 0; i < flags.length; i++) {
|
|
898
|
+
const flag = flags[i];
|
|
899
|
+
if (LEGACY_PROVIDER_FLAGS.includes(flag) && isLegacyProviderPath(flags[i + 1] ?? "")) {
|
|
900
|
+
i += 1;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
const inline = LEGACY_PROVIDER_INLINE.find((prefix) => flag.startsWith(prefix));
|
|
904
|
+
if (inline && isLegacyProviderPath(flag.slice(inline.length))) continue;
|
|
905
|
+
kept.push(flag);
|
|
906
|
+
}
|
|
907
|
+
return kept;
|
|
908
|
+
}
|
|
909
|
+
/**
|
|
601
910
|
* Oh My Pi uses its own executable and native MCP discovery, independently of
|
|
602
911
|
* pi. An explicit extension directory contributes mcp.json without redirecting
|
|
603
912
|
* the user's config or credentials. Verified against omp 18.12.6.
|
|
@@ -612,8 +921,10 @@ const createPlugin = () => ({
|
|
|
612
921
|
discovery: "extension",
|
|
613
922
|
navigation: "unavailable"
|
|
614
923
|
},
|
|
924
|
+
previews: { reporting: "extension" },
|
|
615
925
|
capabilities: () => [
|
|
616
926
|
"subagents",
|
|
927
|
+
"previews",
|
|
617
928
|
"mcp",
|
|
618
929
|
"settings"
|
|
619
930
|
],
|
|
@@ -630,7 +941,7 @@ const createPlugin = () => ({
|
|
|
630
941
|
const value = settings[key];
|
|
631
942
|
if (typeof value === "string" && value) args.push(`--${key}`, value);
|
|
632
943
|
}
|
|
633
|
-
args.push(...preset.flags, ...extraFlags ?? []);
|
|
944
|
+
args.push(...mcp ? withoutLegacyProviderExtension(preset.flags) : preset.flags, ...extraFlags ?? []);
|
|
634
945
|
return args;
|
|
635
946
|
},
|
|
636
947
|
mcpRegistration(launch, configPath) {
|
|
@@ -646,10 +957,17 @@ const createPlugin = () => ({
|
|
|
646
957
|
name: "subshell-mcp",
|
|
647
958
|
private: true,
|
|
648
959
|
type: "module",
|
|
649
|
-
omp: { extensions: [
|
|
960
|
+
omp: { extensions: [
|
|
961
|
+
"bridge.mjs",
|
|
962
|
+
"subagents.mjs",
|
|
963
|
+
"previews.mjs",
|
|
964
|
+
"provider.mjs"
|
|
965
|
+
] }
|
|
650
966
|
}),
|
|
651
967
|
"bridge.mjs": "export default function () {}\n",
|
|
652
968
|
"subagents.mjs": SUBAGENT_BRIDGE,
|
|
969
|
+
"previews.mjs": PREVIEW_BRIDGE,
|
|
970
|
+
"provider.mjs": MODEL_CLI_PROVIDER_SHIM,
|
|
653
971
|
"mcp.json": fileContent
|
|
654
972
|
},
|
|
655
973
|
args: ["--extension", `${configPath}.d`]
|