@oai404iao/pi-codex-runtime 0.3.0 → 0.3.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/package.json +2 -2
- package/src/broker.ts +1 -1
- package/src/code-mode-contributions.ts +89 -13
- package/src/code-mode-owner.ts +63 -12
- package/src/tool-activation.ts +22 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oai404iao/pi-codex-runtime",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Unpublished pi-codex-runtime workspace for the staged Codex split",
|
|
6
6
|
"type": "module",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": ">=22.19.0"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "c791e1192d0547834c3e2f2bae7853fb214f9808"
|
|
68
68
|
}
|
package/src/broker.ts
CHANGED
|
@@ -20,7 +20,7 @@ export interface CodexBroker {
|
|
|
20
20
|
readonly closed: boolean;
|
|
21
21
|
readonly tools: Map<PackageToolName, InstalledTool>;
|
|
22
22
|
coreEnabled: boolean;
|
|
23
|
-
codeModeDefinitions?: Map<string, { parameters: unknown; description: string }>;
|
|
23
|
+
codeModeDefinitions?: Map<string, { parameters: unknown; description: string; providerId: string }>;
|
|
24
24
|
claim(name: string): boolean;
|
|
25
25
|
addPresentation(name: string, presentation: ProviderPresentation): void;
|
|
26
26
|
presentation: ProviderPresentation;
|
|
@@ -2,17 +2,21 @@ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-a
|
|
|
2
2
|
import { getCodexBroker } from "./broker.js";
|
|
3
3
|
import { codeModeOwner, type DirectBinding } from "./code-mode-owner.js";
|
|
4
4
|
import type { PackageToolName } from "./capabilities.js";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
5
6
|
|
|
6
7
|
/** Unique public schema reference proves which registration won Pi's registry.
|
|
7
8
|
* If a future Pi clones metadata, cooperation fails closed instead of claiming
|
|
8
9
|
* a name/source belonging to another extension. */
|
|
9
|
-
export function registerCodeModeOwnedTool(pi: ExtensionAPI, definition: Record<string, unknown
|
|
10
|
+
export function registerCodeModeOwnedTool(pi: ExtensionAPI, definition: Record<string, unknown>, providerId: string): void {
|
|
10
11
|
if (typeof definition.name !== "string" || typeof definition.description !== "string"
|
|
11
|
-
|| !definition.parameters || typeof definition.parameters !== "object"
|
|
12
|
+
|| !definition.parameters || typeof definition.parameters !== "object"
|
|
13
|
+
|| !/^[a-z][a-z0-9_]{0,39}$/.test(providerId)) throw new Error("Invalid owned Code Mode tool definition");
|
|
12
14
|
const broker = getCodexBroker(pi);
|
|
15
|
+
const definitions = broker.codeModeDefinitions ??= new Map();
|
|
16
|
+
if (definitions.has(definition.name)) throw new Error(`Duplicate Code Mode tool definition: ${definition.name}`);
|
|
13
17
|
const owned = { ...definition, name: definition.name, description: definition.description, parameters: { ...definition.parameters } };
|
|
14
|
-
(broker.codeModeDefinitions ??= new Map()).set(owned.name, owned);
|
|
15
18
|
pi.registerTool(owned as never);
|
|
19
|
+
definitions.set(owned.name, { ...owned, providerId });
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
export interface NestedCodeModeContext {
|
|
@@ -28,36 +32,108 @@ export interface OwnedCodeModeTool {
|
|
|
28
32
|
parameters: unknown;
|
|
29
33
|
effect: "read" | "write";
|
|
30
34
|
parallel?: boolean;
|
|
35
|
+
requires?: readonly string[];
|
|
36
|
+
requiredPolicies?: readonly string[];
|
|
37
|
+
approval?: string;
|
|
38
|
+
availability?: { state: "available" | "unavailable" | "not-ready" | "failed"; reason?: string };
|
|
31
39
|
direct?: DirectBinding;
|
|
32
40
|
invoke(input: unknown, context: NestedCodeModeContext): Promise<{ value: unknown }>;
|
|
33
41
|
}
|
|
42
|
+
const unavailableInvoke = async (): Promise<{ value: unknown }> => { throw new Error("Code Mode tool requirements were not negotiated"); };
|
|
34
43
|
|
|
35
|
-
/** Structural client for the optional v1 bus contract. Neither side installs
|
|
44
|
+
/** Structural client for the optional v2/v1 bus contract. Neither side installs
|
|
36
45
|
* the other package; all authority still comes from Code Mode's exact grants. */
|
|
37
46
|
export function registerCodeModeContribution(pi: ExtensionAPI, id: string,
|
|
38
47
|
tools: (context: ExtensionContext) => readonly OwnedCodeModeTool[]): void {
|
|
39
48
|
let context: ExtensionContext | undefined;
|
|
40
49
|
let disposed = false;
|
|
41
|
-
|
|
50
|
+
let resolved: readonly OwnedCodeModeTool[] | undefined;
|
|
51
|
+
let registration: Readonly<{ owner: string; instanceId: string; revision: number }> = Object.freeze({ owner: id, instanceId: randomUUID(), revision: 1 });
|
|
52
|
+
let accepted = new WeakSet<object>();
|
|
53
|
+
const generations = new Map<string, number>();
|
|
54
|
+
const changed = (phase: "withdrawn" | "ready" | "disposed") => {
|
|
55
|
+
const change = Object.freeze({ protocol: 2, registration, kind: "execution", phase });
|
|
56
|
+
pi.events.emit("@oai404iao/pi-code-mode:changed/v2", change);
|
|
57
|
+
pi.events.emit("@oai404iao/pi-code-mode:changed/v1", { version: 1, change });
|
|
58
|
+
};
|
|
59
|
+
const snapshot = (features: readonly unknown[] = [], legacy = false) => {
|
|
60
|
+
const broker = getCodexBroker(pi);
|
|
61
|
+
const offered = context ? (resolved ??= tools(context).map((tool) => ({ ...tool }))) : [];
|
|
62
|
+
if (offered.length > 64) throw new Error("Code Mode declaration budget exceeded");
|
|
63
|
+
const declarations = offered.map((tool) => {
|
|
64
|
+
if (tool.requires !== undefined && (!Array.isArray(tool.requires) || tool.requires.length > 32))
|
|
65
|
+
throw new Error("Invalid Code Mode feature requirements");
|
|
66
|
+
const requires = [...new Set([...(tool.requires ?? []), ...(tool.approval ? ["approval/1"] : []),
|
|
67
|
+
...(tool.requiredPolicies?.length ? ["required-policies/1"] : [])])];
|
|
68
|
+
if (requires.length > 32 || requires.some((feature) => typeof feature !== "string" || feature.length > 128
|
|
69
|
+
|| !/^[a-z][a-z0-9-]*\/[1-9][0-9]*$/.test(feature)))
|
|
70
|
+
throw new Error("Invalid Code Mode feature requirements");
|
|
71
|
+
return { ...tool, requires };
|
|
72
|
+
});
|
|
73
|
+
return { id, tools: declarations.filter((tool) => !legacy || (!tool.requires.length && !tool.requiredPolicies?.length
|
|
74
|
+
&& tool.approval === undefined && (!tool.availability || tool.availability.state === "available"))).map((tool) => {
|
|
75
|
+
if (tool.requires.some((feature) => !features.includes(feature)) || (tool.availability && tool.availability.state !== "available"))
|
|
76
|
+
return { ...tool, direct: undefined, invoke: unavailableInvoke };
|
|
77
|
+
const owned = broker.tools.get(tool.name as PackageToolName);
|
|
78
|
+
const definition = broker.codeModeDefinitions?.get(tool.name);
|
|
79
|
+
return { ...tool, direct: owned && definition?.providerId === id
|
|
80
|
+
? codeModeOwner(pi, tool.name, owned, definition)?.binding : undefined };
|
|
81
|
+
}) };
|
|
82
|
+
};
|
|
83
|
+
const offV2 = pi.events.on("@oai404iao/pi-code-mode:discover/v2", (value) => {
|
|
84
|
+
const request = value as { hello?: { protocol?: number; instanceId?: string; generation?: number; features?: unknown[] };
|
|
85
|
+
offer?: (offer: unknown) => { status: string } } | undefined;
|
|
86
|
+
const hello = request?.hello;
|
|
87
|
+
if (disposed || !hello || hello.protocol !== 2 || !hello.instanceId || hello.instanceId.length > 128
|
|
88
|
+
|| !Number.isSafeInteger(hello.generation) || hello.generation! < 1 || !Array.isArray(hello.features)
|
|
89
|
+
|| hello.features.length > 32 || typeof request?.offer !== "function") return;
|
|
90
|
+
const previous = generations.get(hello.instanceId);
|
|
91
|
+
if ((previous !== undefined && hello.generation! < previous) || (previous === undefined && generations.size >= 64)) return;
|
|
92
|
+
generations.set(hello.instanceId, hello.generation!);
|
|
93
|
+
try {
|
|
94
|
+
const provider = snapshot(hello.features);
|
|
95
|
+
const availability = { state: context ? "available" : "not-ready" };
|
|
96
|
+
if (request.offer({ kind: "provider", registration, requires: [], availability, provider }).status === "compatible") accepted.add(hello);
|
|
97
|
+
} catch {
|
|
98
|
+
request.offer({ kind: "provider", registration, requires: [], availability: { state: "failed", reason: "owner-not-ready" }, provider: { id, tools: [] } });
|
|
99
|
+
}
|
|
100
|
+
});
|
|
42
101
|
const off = pi.events.on("@oai404iao/pi-code-mode:discover/v1", (value) => {
|
|
43
|
-
const discovery = value as { version?: number; provider?: (provider: unknown) => void
|
|
102
|
+
const discovery = value as { version?: number; provider?: (provider: unknown) => void;
|
|
103
|
+
consumer?: object; receipts?: { registration: object; consumer: object }[] } | undefined;
|
|
44
104
|
if (!disposed && context && discovery?.version === 1 && typeof discovery.provider === "function") {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
105
|
+
if (discovery.consumer) {
|
|
106
|
+
const hello = discovery.consumer as { protocol?: number; instanceId?: string; generation?: number };
|
|
107
|
+
if (hello.protocol !== 2 || typeof hello.instanceId !== "string" || !hello.instanceId || hello.instanceId.length > 128
|
|
108
|
+
|| !Number.isSafeInteger(hello.generation) || hello.generation! < 1) return;
|
|
109
|
+
const previous = generations.get(hello.instanceId);
|
|
110
|
+
if ((previous !== undefined && hello.generation! < previous) || (previous === undefined && generations.size >= 64)) return;
|
|
111
|
+
generations.set(hello.instanceId, hello.generation!);
|
|
112
|
+
}
|
|
113
|
+
if (discovery.consumer && accepted.has(discovery.consumer) && Array.isArray(discovery.receipts) && discovery.receipts.length <= 80
|
|
114
|
+
&& discovery.receipts.some((receipt) => receipt.consumer === discovery.consumer && receipt.registration === registration)) return;
|
|
115
|
+
let provider: ReturnType<typeof snapshot>;
|
|
116
|
+
// Validate the whole declaration before filtering legacy tools:
|
|
117
|
+
// a failed v2 provider must not reappear as a partial v1 mirror.
|
|
118
|
+
try { provider = snapshot([], true); } catch { return; }
|
|
119
|
+
if (provider.tools.length) discovery.provider(provider);
|
|
50
120
|
}
|
|
51
121
|
});
|
|
52
122
|
const update = (_event: unknown, ctx: ExtensionContext) => {
|
|
53
123
|
if (disposed) return;
|
|
124
|
+
context = undefined;
|
|
125
|
+
resolved = undefined;
|
|
126
|
+
changed("withdrawn");
|
|
54
127
|
context = ctx;
|
|
55
|
-
|
|
128
|
+
registration = Object.freeze({ ...registration, revision: registration.revision + 1 });
|
|
129
|
+
accepted = new WeakSet();
|
|
130
|
+
changed("ready");
|
|
56
131
|
};
|
|
57
132
|
pi.on("session_start", update);
|
|
58
133
|
pi.on("model_select", update);
|
|
134
|
+
pi.on("session_tree", update);
|
|
59
135
|
pi.on("session_shutdown", () => {
|
|
60
136
|
if (disposed) return;
|
|
61
|
-
disposed = true; context = undefined; off(); changed();
|
|
137
|
+
disposed = true; context = undefined; off(); offV2(); changed("disposed");
|
|
62
138
|
});
|
|
63
139
|
}
|
package/src/code-mode-owner.ts
CHANGED
|
@@ -12,34 +12,85 @@ interface Control {
|
|
|
12
12
|
interface Factory {
|
|
13
13
|
version: 1; create(pi: ExtensionAPI, options: { name: string; sourcePath: string }): Control;
|
|
14
14
|
}
|
|
15
|
-
export interface OwnerState {
|
|
15
|
+
export interface OwnerState {
|
|
16
|
+
identity: string; replaced?: boolean; factory?: Factory; control?: Control;
|
|
17
|
+
transitioning?: boolean; pendingDispose?: () => void; closed?: boolean;
|
|
18
|
+
}
|
|
16
19
|
export const OWNER_CHANGED = "@oai404iao/pi-code-mode:direct-owner-changed/v1";
|
|
20
|
+
|
|
21
|
+
function retire(state: OwnerState): void {
|
|
22
|
+
if (state.control && !state.pendingDispose) {
|
|
23
|
+
const old = state.control;
|
|
24
|
+
state.pendingDispose = () => old.dispose();
|
|
25
|
+
}
|
|
26
|
+
state.pendingDispose?.();
|
|
27
|
+
state.pendingDispose = undefined;
|
|
28
|
+
state.control = undefined;
|
|
29
|
+
state.factory = undefined;
|
|
30
|
+
}
|
|
31
|
+
export function disposeCodeModeOwner(tool: { codeModeOwner?: OwnerState }, close = true): void {
|
|
32
|
+
const state = tool.codeModeOwner;
|
|
33
|
+
if (!state) return;
|
|
34
|
+
if (close) state.closed = true;
|
|
35
|
+
if (state.transitioning) return;
|
|
36
|
+
state.transitioning = true;
|
|
37
|
+
try { retire(state); } finally { state.transitioning = false; }
|
|
38
|
+
}
|
|
39
|
+
|
|
17
40
|
/** Optional v1 bus client. No private-package dependency or global registry. */
|
|
18
41
|
export function codeModeOwner(pi: ExtensionAPI, name: string, tool: { registered: boolean; codeModeOwner?: OwnerState },
|
|
19
42
|
expected?: { parameters: unknown; description: string }): Control | undefined {
|
|
20
|
-
if (!["apply_patch", "web_search"].includes(name) ||
|
|
43
|
+
if (!["apply_patch", "web_search"].includes(name) || tool.codeModeOwner?.transitioning || tool.codeModeOwner?.closed) return;
|
|
44
|
+
if (!tool.registered) { disposeCodeModeOwner(tool, false); return; }
|
|
21
45
|
const info = pi.getAllTools?.().find((item) => item.name === name);
|
|
22
|
-
if (!info?.sourceInfo || ["builtin", "sdk"].includes(info.sourceInfo.source)
|
|
23
|
-
|
|
24
|
-
|
|
46
|
+
if (!info?.sourceInfo || ["builtin", "sdk"].includes(info.sourceInfo.source)
|
|
47
|
+
|| !expected || info.parameters !== expected.parameters || info.description !== expected.description) {
|
|
48
|
+
(tool.codeModeOwner ??= { identity: "" }).replaced = true;
|
|
49
|
+
disposeCodeModeOwner(tool, false);
|
|
25
50
|
return;
|
|
26
51
|
}
|
|
27
52
|
const identity = JSON.stringify([info.sourceInfo, info.description, info.parameters, info.promptGuidelines]);
|
|
53
|
+
const stillOwns = () => {
|
|
54
|
+
const current = pi.getAllTools().find((item) => item.name === name);
|
|
55
|
+
return Boolean(current && current.parameters === expected.parameters
|
|
56
|
+
&& JSON.stringify([current.sourceInfo, current.description, current.parameters, current.promptGuidelines]) === identity);
|
|
57
|
+
};
|
|
28
58
|
const state = tool.codeModeOwner ??= { identity };
|
|
29
59
|
// Never rebind a replaced registration, even if the optional factory reloads.
|
|
30
|
-
if (state.identity !== identity)
|
|
31
|
-
if (state.replaced) return
|
|
60
|
+
if (state.identity !== identity) state.replaced = true;
|
|
61
|
+
if (state.replaced) { disposeCodeModeOwner(tool, false); return; }
|
|
32
62
|
const factories: Factory[] = [];
|
|
33
63
|
pi.events.emit("@oai404iao/pi-code-mode:direct-owner/v1", { version: 1, accept(value: Factory) {
|
|
34
64
|
if (factories.length < 2) factories.push(value);
|
|
35
65
|
} });
|
|
36
66
|
const factory = factories.length === 1 && factories[0]?.version === 1
|
|
37
67
|
&& typeof factories[0].create === "function" ? factories[0] : undefined;
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
state.
|
|
41
|
-
|
|
42
|
-
|
|
68
|
+
if (state.closed) return;
|
|
69
|
+
if (!stillOwns()) {
|
|
70
|
+
state.replaced = true;
|
|
71
|
+
disposeCodeModeOwner(tool, false);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (state.pendingDispose || factory !== state.factory || (factory && !state.control)) {
|
|
75
|
+
state.transitioning = true;
|
|
76
|
+
try {
|
|
77
|
+
retire(state);
|
|
78
|
+
if (!stillOwns()) state.replaced = true;
|
|
79
|
+
if (state.closed || state.replaced) return;
|
|
80
|
+
if (factory) {
|
|
81
|
+
const control = factory.create(pi, { name, sourcePath: info.sourceInfo.path });
|
|
82
|
+
if (!control || control.binding?.version !== 1 || control.binding.name !== name
|
|
83
|
+
|| typeof control.binding.acquire !== "function" || typeof control.projectActive !== "function"
|
|
84
|
+
|| typeof control.reconcile !== "function" || typeof control.dispose !== "function") {
|
|
85
|
+
if (typeof control?.dispose === "function") state.pendingDispose = () => control.dispose();
|
|
86
|
+
throw new Error("Invalid Code Mode owner control");
|
|
87
|
+
}
|
|
88
|
+
state.control = control;
|
|
89
|
+
state.factory = factory;
|
|
90
|
+
if (!stillOwns()) state.replaced = true;
|
|
91
|
+
if (state.closed || state.replaced) { retire(state); return; }
|
|
92
|
+
}
|
|
93
|
+
} finally { state.transitioning = false; }
|
|
43
94
|
}
|
|
44
95
|
return state.control;
|
|
45
96
|
}
|
package/src/tool-activation.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { installCodexIdentityLifecycle } from "./codex-identity-extension.js";
|
|
9
9
|
import { loadModelSettings } from "./model-catalog/runtime.js";
|
|
10
10
|
import { loadSettings } from "./settings.js";
|
|
11
|
-
import { codeModeOwner, OWNER_CHANGED } from "./code-mode-owner.js";
|
|
11
|
+
import { codeModeOwner, disposeCodeModeOwner, OWNER_CHANGED } from "./code-mode-owner.js";
|
|
12
12
|
|
|
13
13
|
function enableDefinitions(broker: CodexBroker) {
|
|
14
14
|
for (const tool of broker.tools.values()) {
|
|
@@ -38,12 +38,11 @@ export function ensureCodexServices(pi: ExtensionAPI): CodexBroker {
|
|
|
38
38
|
for (const [name, index] of [...suppressed].sort(([, a], [, b]) => a - b)) {
|
|
39
39
|
if (!active.includes(name)) active.splice(Math.min(index, active.length), 0, name);
|
|
40
40
|
}
|
|
41
|
-
suppressed.clear();
|
|
42
41
|
return active;
|
|
43
42
|
};
|
|
44
43
|
let latest: ExtensionContext | undefined;
|
|
45
44
|
let syncing = false;
|
|
46
|
-
const sync = (ctx: ExtensionContext) => {
|
|
45
|
+
const sync = (ctx: ExtensionContext, fromTree = false) => {
|
|
47
46
|
latest = ctx;
|
|
48
47
|
if (syncing) return;
|
|
49
48
|
syncing = true;
|
|
@@ -72,7 +71,7 @@ export function ensureCodexServices(pi: ExtensionAPI): CodexBroker {
|
|
|
72
71
|
);
|
|
73
72
|
const desired = available && owned.registered && capabilities[name].enabled && !hostedWithoutCore;
|
|
74
73
|
if (!desired) active.delete(name);
|
|
75
|
-
else if (settings.autoEnable) active.add(name);
|
|
74
|
+
else if (settings.autoEnable && !(fromTree && control?.activeIntent !== undefined)) active.add(name);
|
|
76
75
|
}
|
|
77
76
|
const ownsPatch = broker.tools.has("apply_patch") && !broker.tools.get("apply_patch")?.codeModeOwner?.replaced;
|
|
78
77
|
if (ownsPatch && active.has("apply_patch")) {
|
|
@@ -90,6 +89,7 @@ export function ensureCodexServices(pi: ExtensionAPI): CodexBroker {
|
|
|
90
89
|
for (const name of physical) if (!next.includes(name)) next.push(name);
|
|
91
90
|
if (!ownsPatch || !active.has("apply_patch")) restore(next);
|
|
92
91
|
if (next.join("\0") !== current.join("\0")) pi.setActiveTools(next);
|
|
92
|
+
if (!ownsPatch || !active.has("apply_patch")) suppressed.clear();
|
|
93
93
|
for (const control of controls.values()) control.reconcile();
|
|
94
94
|
} finally { syncing = false; }
|
|
95
95
|
};
|
|
@@ -98,21 +98,34 @@ export function ensureCodexServices(pi: ExtensionAPI): CodexBroker {
|
|
|
98
98
|
});
|
|
99
99
|
pi.on("session_start", (_event, ctx) => {
|
|
100
100
|
broker.presentation.clear();
|
|
101
|
+
suppressed.clear();
|
|
101
102
|
sync(ctx);
|
|
102
103
|
});
|
|
103
104
|
pi.on("model_select", (_event, ctx) => sync(ctx));
|
|
104
105
|
pi.on("thinking_level_select", (_event, ctx) => sync(ctx));
|
|
106
|
+
pi.on("session_tree", (_event, ctx) => {
|
|
107
|
+
// Restoration receipts belong to the previous physical loadout, not
|
|
108
|
+
// to tools absent from the newly selected branch.
|
|
109
|
+
suppressed.clear();
|
|
110
|
+
sync(ctx, true);
|
|
111
|
+
});
|
|
105
112
|
pi.on("agent_end", () => broker.presentation.scheduleFlush());
|
|
106
113
|
pi.on("session_shutdown", () => {
|
|
107
114
|
offOwner();
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
115
|
+
const errors: unknown[] = [];
|
|
116
|
+
for (const tool of broker.tools.values()) if (tool.codeModeOwner) tool.codeModeOwner.closed = true;
|
|
117
|
+
for (const tool of broker.tools.values()) {
|
|
118
|
+
try { disposeCodeModeOwner(tool); } catch (error) { errors.push(error); }
|
|
119
|
+
}
|
|
120
|
+
try { broker.presentation.flush(); } catch (error) { errors.push(error); }
|
|
121
|
+
try { broker.presentation.clear(); } catch (error) { errors.push(error); }
|
|
122
|
+
try {
|
|
112
123
|
const current = pi.getActiveTools?.() ?? [];
|
|
113
124
|
const next = restore([...current]);
|
|
114
125
|
if (next.join("\0") !== current.join("\0")) pi.setActiveTools(next);
|
|
115
|
-
|
|
126
|
+
suppressed.clear();
|
|
127
|
+
} catch (error) { errors.push(error); }
|
|
128
|
+
if (errors.length) throw new AggregateError(errors, "Codex owner shutdown failed");
|
|
116
129
|
});
|
|
117
130
|
return broker;
|
|
118
131
|
}
|