@tt-a1i/openpi 0.4.0 → 0.5.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/README.md +102 -40
- package/SETUP.md +22 -6
- package/assets/openpi-launch-card-v1.webp +0 -0
- package/bin/openpi.js +145 -0
- package/extensions/background-terminals/index.ts +30 -2
- package/extensions/background-terminals/src/domain.ts +2 -0
- package/extensions/background-terminals/src/manager.ts +486 -106
- package/extensions/background-terminals/src/output.ts +33 -0
- package/extensions/background-terminals/src/prompt.ts +13 -5
- package/extensions/background-terminals/src/result-delivery.ts +4 -1
- package/extensions/clear-context/index.ts +83 -0
- package/extensions/context-pivot/index.ts +16 -6
- package/extensions/cron/schedule.ts +7 -1
- package/extensions/file-mutation-display/render.ts +17 -257
- package/extensions/file-search/src/binaries.ts +57 -41
- package/extensions/git-read/index.ts +1 -3
- package/extensions/model-info/index.ts +21 -33
- package/extensions/model-info/session-metrics.ts +96 -0
- package/extensions/plan-mode/bash-policy.ts +54 -9
- package/extensions/plan-mode/index.ts +7 -2
- package/extensions/post-edit/index.ts +16 -6
- package/extensions/sessions/git-stats.ts +258 -72
- package/extensions/sessions/index.ts +153 -86
- package/extensions/sessions/preview-cache.ts +104 -0
- package/extensions/sessions/preview-loader.ts +856 -0
- package/extensions/sessions/sessions.ts +43 -4
- package/extensions/setup/index.ts +123 -127
- package/extensions/shared/activity-status.ts +30 -0
- package/extensions/shared/agent-session-page.ts +319 -0
- package/extensions/shared/agent-tool-renderer.ts +218 -0
- package/extensions/shared/agent-transcript.ts +524 -0
- package/extensions/shared/capability-intent.ts +1 -1
- package/extensions/shared/child-session.ts +437 -21
- package/extensions/shared/result-delivery.ts +34 -0
- package/extensions/shared/setup-config.ts +73 -33
- package/extensions/shared/setup-episode-state.ts +1 -1
- package/extensions/shared/terminal-text.ts +110 -23
- package/extensions/shared/text-projection.ts +72 -15
- package/extensions/shared/tool-activity.ts +382 -0
- package/extensions/shared/tool-surface.ts +29 -2
- package/extensions/shared/transcript-viewport.ts +46 -0
- package/extensions/shared/web-observer-registry.ts +390 -0
- package/extensions/shared/worktree.ts +11 -0
- package/extensions/subagents/index.ts +270 -59
- package/extensions/subagents/navigation.ts +34 -5
- package/extensions/subagents/src/backend.ts +12 -1
- package/extensions/subagents/src/backends/pi.ts +375 -66
- package/extensions/subagents/src/domain.ts +5 -0
- package/extensions/subagents/src/manager.ts +34 -2
- package/extensions/subagents/src/prompt.ts +32 -4
- package/extensions/subagents/src/result-artifact.ts +4 -0
- package/extensions/subagents/src/result-delivery.ts +7 -1
- package/extensions/subagents/src/runtime.ts +15 -1
- package/extensions/subagents/src/ui/takeover.ts +73 -257
- package/extensions/subagents/src/ui/transcript.ts +38 -535
- package/extensions/subagents/src/ui/wait-result.ts +103 -15
- package/extensions/suggestions/src/ui.ts +10 -4
- package/extensions/tasks/index.ts +0 -3
- package/extensions/ui-customization/footer.ts +0 -40
- package/extensions/ui-customization/index.ts +0 -4
- package/extensions/user-input-fold/index.ts +1 -1
- package/extensions/web/index.ts +234 -0
- package/extensions/workflows/artifacts.ts +137 -47
- package/extensions/workflows/completion-projection.ts +457 -0
- package/extensions/workflows/coordinator.ts +8 -10
- package/extensions/workflows/dashboard.ts +167 -228
- package/extensions/workflows/handoff.ts +70 -16
- package/extensions/workflows/index.ts +488 -198
- package/extensions/workflows/journal.ts +148 -13
- package/extensions/workflows/model.ts +74 -4
- package/extensions/workflows/navigation.ts +32 -8
- package/extensions/workflows/progress-projection.ts +306 -0
- package/extensions/workflows/prompt.ts +66 -6
- package/extensions/workflows/replay-safety.ts +42 -21
- package/extensions/workflows/result-delivery.ts +128 -64
- package/extensions/workflows/retention.ts +593 -0
- package/extensions/workflows/runner.ts +388 -279
- package/extensions/workflows/sandbox-child.cjs +25 -3
- package/extensions/workflows/sandbox.ts +62 -8
- package/extensions/workflows/serialization.ts +325 -17
- package/extensions/workflows/tool-renderer.ts +22 -0
- package/extensions/workflows/transcript.ts +149 -0
- package/extensions/workspace-cleanup-guard/index.ts +54 -0
- package/extensions/workspace-cleanup-guard/workspace-provenance.ts +563 -0
- package/package.json +28 -8
- package/skills/subagents/REFERENCE.md +189 -0
- package/skills/subagents/SKILL.md +1 -1
- package/skills/workflows/REFERENCE.md +4 -2
- package/web/adapter/pi-adapter.ts +661 -0
- package/web/host/browser-launcher.ts +20 -0
- package/web/host/static-assets.ts +4 -0
- package/web/host/terminal-status.ts +38 -0
- package/web/host/web-host.ts +789 -0
- package/web/http-dispatcher.ts +125 -0
- package/web/protocol/types.ts +462 -0
- package/web/runtime/pi-runtime.ts +991 -0
- package/web/runtime/types.ts +71 -0
- package/web/runtime/web-host-lease.ts +497 -0
- package/web/trace.ts +18 -0
- package/web/ui/app.js +1398 -0
- package/web/ui/index.html +139 -0
- package/web/ui/styles.css +598 -0
- package/web/vite.config.mjs +34 -0
- package/extensions/execution-convergence/active-evidence.ts +0 -129
- package/extensions/execution-convergence/index.ts +0 -442
- package/extensions/execution-convergence/workspace-provenance.ts +0 -338
- package/extensions/setup/intercom-fs-helper.cjs +0 -130
- package/extensions/setup/intercom.ts +0 -603
- package/extensions/subagents/src/backends/stub.ts +0 -303
|
@@ -1,12 +1,22 @@
|
|
|
1
|
+
import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
1
2
|
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
2
4
|
import {
|
|
3
|
-
|
|
5
|
+
DefaultPackageManager,
|
|
4
6
|
DefaultResourceLoader,
|
|
5
7
|
getAgentDir,
|
|
8
|
+
type LoadExtensionsResult,
|
|
9
|
+
type PackageSource,
|
|
6
10
|
ProjectTrustStore,
|
|
11
|
+
type ResolvedPaths,
|
|
7
12
|
type SessionShutdownEvent,
|
|
8
13
|
SettingsManager,
|
|
9
14
|
} from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import {
|
|
16
|
+
OPENPI_OWNER_SOURCE_PATHS,
|
|
17
|
+
OPENPI_TOOL_SURFACE,
|
|
18
|
+
type OpenPiToolOwner,
|
|
19
|
+
} from "./tool-surface.ts";
|
|
10
20
|
|
|
11
21
|
export const CHILD_SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
12
22
|
|
|
@@ -37,16 +47,385 @@ export const CHILD_SAFE_PACKAGE_TOOL_NAMES = [
|
|
|
37
47
|
* Loading it in children can therefore cross-wire identities; the parent
|
|
38
48
|
* extension remains loaded and fully usable.
|
|
39
49
|
*/
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
function manifestNamesPiIntercom(manifestPath: string) {
|
|
51
|
+
try {
|
|
52
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8")) as unknown;
|
|
53
|
+
return (
|
|
54
|
+
typeof manifest === "object" &&
|
|
55
|
+
manifest !== null &&
|
|
56
|
+
!Array.isArray(manifest) &&
|
|
57
|
+
(manifest as Record<string, unknown>).name === "pi-intercom"
|
|
58
|
+
);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`Cannot verify child package identity from ${manifestPath}`,
|
|
62
|
+
{ cause: error },
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function installedPathNamesPiIntercom(installedPath: string) {
|
|
68
|
+
try {
|
|
69
|
+
const stats = statSync(installedPath);
|
|
70
|
+
if (stats.isDirectory()) {
|
|
71
|
+
const manifestPath = path.join(installedPath, "package.json");
|
|
72
|
+
if (existsSync(manifestPath)) {
|
|
73
|
+
return manifestNamesPiIntercom(manifestPath);
|
|
74
|
+
}
|
|
75
|
+
if (path.basename(installedPath) === "pi-intercom") {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`Cannot verify child package identity from ${installedPath}`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
if (!stats.isFile()) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Cannot verify child package identity from ${installedPath}`,
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
let current = path.dirname(installedPath);
|
|
89
|
+
let hasIntercomDirectoryName = false;
|
|
90
|
+
while (true) {
|
|
91
|
+
hasIntercomDirectoryName ||= path.basename(current) === "pi-intercom";
|
|
92
|
+
const manifestPath = path.join(current, "package.json");
|
|
93
|
+
if (existsSync(manifestPath)) {
|
|
94
|
+
return manifestNamesPiIntercom(manifestPath);
|
|
95
|
+
}
|
|
96
|
+
const parent = path.dirname(current);
|
|
97
|
+
if (parent === current) {
|
|
98
|
+
if (hasIntercomDirectoryName) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Cannot verify child package identity from ${installedPath}`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
current = parent;
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (
|
|
109
|
+
error instanceof Error &&
|
|
110
|
+
error.message.startsWith("Cannot verify child package identity")
|
|
111
|
+
) {
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Cannot verify child package identity from ${installedPath}`,
|
|
116
|
+
{ cause: error },
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function piMatchesPublishedIntercomSource(options: {
|
|
122
|
+
source: string;
|
|
123
|
+
cwd: string;
|
|
124
|
+
agentDir: string;
|
|
43
125
|
}) {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
126
|
+
const settingsManager = SettingsManager.inMemory({
|
|
127
|
+
packages: [options.source],
|
|
128
|
+
});
|
|
129
|
+
const packageManager = new DefaultPackageManager({
|
|
130
|
+
cwd: options.cwd,
|
|
131
|
+
agentDir: options.agentDir,
|
|
132
|
+
settingsManager,
|
|
133
|
+
});
|
|
134
|
+
return (
|
|
135
|
+
packageManager.removeSourceFromSettings("npm:pi-intercom") ||
|
|
136
|
+
packageManager.removeSourceFromSettings(
|
|
137
|
+
"git:https://github.com/nicobailon/pi-intercom",
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function createPiIntercomPackageMatcher(options: {
|
|
143
|
+
cwd: string;
|
|
144
|
+
agentDir: string;
|
|
145
|
+
}) {
|
|
146
|
+
const sourceMatches = new Map<string, boolean>();
|
|
147
|
+
const installedPathMatches = new Map<string, boolean>();
|
|
148
|
+
return (source: string, installedPath?: string) => {
|
|
149
|
+
let sourceMatch = sourceMatches.get(source);
|
|
150
|
+
if (sourceMatch === undefined) {
|
|
151
|
+
sourceMatch = piMatchesPublishedIntercomSource({
|
|
152
|
+
source,
|
|
153
|
+
cwd: options.cwd,
|
|
154
|
+
agentDir: options.agentDir,
|
|
155
|
+
});
|
|
156
|
+
sourceMatches.set(source, sourceMatch);
|
|
157
|
+
}
|
|
158
|
+
if (sourceMatch || installedPath === undefined) return sourceMatch;
|
|
159
|
+
|
|
160
|
+
const resolvedPath = path.resolve(installedPath);
|
|
161
|
+
let installedPathMatch = installedPathMatches.get(resolvedPath);
|
|
162
|
+
if (installedPathMatch === undefined) {
|
|
163
|
+
installedPathMatch = installedPathNamesPiIntercom(resolvedPath);
|
|
164
|
+
installedPathMatches.set(resolvedPath, installedPathMatch);
|
|
165
|
+
}
|
|
166
|
+
return installedPathMatch;
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function packageSourceValue(source: PackageSource) {
|
|
171
|
+
return typeof source === "string" ? source : source.source;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const CHILD_DISABLED_OPENPI_EXTENSION =
|
|
175
|
+
"-extensions/git-info/index.ts" as const;
|
|
176
|
+
const OPENPI_GIT_INFO_EXTENSION_PATH = realpathSync.native(
|
|
177
|
+
fileURLToPath(new URL("../git-info/index.ts", import.meta.url)),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
function canonicalExistingPath(value: string) {
|
|
181
|
+
try {
|
|
182
|
+
return realpathSync.native(value);
|
|
183
|
+
} catch {
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function excludeOpenPiGitInfoExtension(
|
|
189
|
+
resources: LoadExtensionsResult,
|
|
190
|
+
): LoadExtensionsResult {
|
|
191
|
+
return {
|
|
192
|
+
...resources,
|
|
193
|
+
extensions: resources.extensions.filter(
|
|
194
|
+
(extension) =>
|
|
195
|
+
canonicalExistingPath(extension.resolvedPath) !==
|
|
196
|
+
OPENPI_GIT_INFO_EXTENSION_PATH,
|
|
197
|
+
),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function installedPathNamesOpenPi(installedPath: string) {
|
|
202
|
+
try {
|
|
203
|
+
const stats = statSync(installedPath);
|
|
204
|
+
if (stats.isDirectory()) {
|
|
205
|
+
const manifestPath = path.join(installedPath, "package.json");
|
|
206
|
+
if (!existsSync(manifestPath)) return false;
|
|
207
|
+
const manifest = JSON.parse(
|
|
208
|
+
readFileSync(manifestPath, "utf8"),
|
|
209
|
+
) as unknown;
|
|
210
|
+
return (
|
|
211
|
+
typeof manifest === "object" &&
|
|
212
|
+
manifest !== null &&
|
|
213
|
+
!Array.isArray(manifest) &&
|
|
214
|
+
(manifest as Record<string, unknown>).name === "@tt-a1i/openpi"
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
let current = stats.isFile() ? path.dirname(installedPath) : undefined;
|
|
218
|
+
while (current) {
|
|
219
|
+
const manifestPath = path.join(current, "package.json");
|
|
220
|
+
if (existsSync(manifestPath)) {
|
|
221
|
+
const manifest = JSON.parse(
|
|
222
|
+
readFileSync(manifestPath, "utf8"),
|
|
223
|
+
) as unknown;
|
|
224
|
+
return (
|
|
225
|
+
typeof manifest === "object" &&
|
|
226
|
+
manifest !== null &&
|
|
227
|
+
!Array.isArray(manifest) &&
|
|
228
|
+
(manifest as Record<string, unknown>).name === "@tt-a1i/openpi"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const parent = path.dirname(current);
|
|
232
|
+
if (parent === current) return false;
|
|
233
|
+
current = parent;
|
|
234
|
+
}
|
|
235
|
+
return false;
|
|
236
|
+
} catch {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function piMatchesPublishedOpenPiSource(options: {
|
|
242
|
+
source: string;
|
|
243
|
+
cwd: string;
|
|
244
|
+
agentDir: string;
|
|
245
|
+
}) {
|
|
246
|
+
const settingsManager = SettingsManager.inMemory({
|
|
247
|
+
packages: [options.source],
|
|
248
|
+
});
|
|
249
|
+
const packageManager = new DefaultPackageManager({
|
|
250
|
+
cwd: options.cwd,
|
|
251
|
+
agentDir: options.agentDir,
|
|
252
|
+
settingsManager,
|
|
253
|
+
});
|
|
254
|
+
return (
|
|
255
|
+
packageManager.removeSourceFromSettings("npm:@tt-a1i/openpi") ||
|
|
256
|
+
packageManager.removeSourceFromSettings(
|
|
257
|
+
"git:https://github.com/openpi-dev/openpi",
|
|
258
|
+
) ||
|
|
259
|
+
packageManager.removeSourceFromSettings(
|
|
260
|
+
"git:https://github.com/tt-a1i/openpi",
|
|
261
|
+
)
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function createOpenPiPackageMatcher(options: {
|
|
266
|
+
cwd: string;
|
|
267
|
+
agentDir: string;
|
|
268
|
+
}) {
|
|
269
|
+
const sourceMatches = new Map<string, boolean>();
|
|
270
|
+
const installedPathMatches = new Map<string, boolean>();
|
|
271
|
+
return (source: string, installedPath?: string) => {
|
|
272
|
+
let sourceMatch = sourceMatches.get(source);
|
|
273
|
+
if (sourceMatch === undefined) {
|
|
274
|
+
sourceMatch = piMatchesPublishedOpenPiSource({ source, ...options });
|
|
275
|
+
sourceMatches.set(source, sourceMatch);
|
|
276
|
+
}
|
|
277
|
+
if (sourceMatch || installedPath === undefined) return sourceMatch;
|
|
278
|
+
|
|
279
|
+
const resolvedPath = path.resolve(installedPath);
|
|
280
|
+
let installedPathMatch = installedPathMatches.get(resolvedPath);
|
|
281
|
+
if (installedPathMatch === undefined) {
|
|
282
|
+
installedPathMatch = installedPathNamesOpenPi(resolvedPath);
|
|
283
|
+
installedPathMatches.set(resolvedPath, installedPathMatch);
|
|
284
|
+
}
|
|
285
|
+
return installedPathMatch;
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function openPiPackageSources(
|
|
290
|
+
packageManager: DefaultPackageManager,
|
|
291
|
+
options: { cwd: string; agentDir: string },
|
|
292
|
+
) {
|
|
293
|
+
const isOpenPiPackage = createOpenPiPackageMatcher(options);
|
|
294
|
+
const sources = {
|
|
295
|
+
user: new Set<string>(),
|
|
296
|
+
project: new Set<string>(),
|
|
297
|
+
};
|
|
298
|
+
for (const configured of packageManager.listConfiguredPackages()) {
|
|
299
|
+
if (isOpenPiPackage(configured.source, configured.installedPath)) {
|
|
300
|
+
sources[configured.scope].add(configured.source);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return sources;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function disablesOpenPiGitInfo(pattern: string) {
|
|
307
|
+
return (
|
|
308
|
+
pattern.startsWith("-") &&
|
|
309
|
+
pattern.slice(1).replace(/^\.\//, "") ===
|
|
310
|
+
CHILD_DISABLED_OPENPI_EXTENSION.slice(1)
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function disableOpenPiGitInfo(source: PackageSource): PackageSource {
|
|
315
|
+
if (typeof source !== "string") {
|
|
316
|
+
if (source.extensions?.length === 0) return source;
|
|
317
|
+
if (source.autoload === false && source.extensions === undefined) {
|
|
318
|
+
return source;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
const configured = typeof source === "string" ? { source } : source;
|
|
322
|
+
const extensions = [...(configured.extensions ?? [])];
|
|
323
|
+
if (!extensions.some(disablesOpenPiGitInfo)) {
|
|
324
|
+
extensions.push(CHILD_DISABLED_OPENPI_EXTENSION);
|
|
325
|
+
}
|
|
326
|
+
return { ...configured, extensions };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function blockedPackageSources(
|
|
330
|
+
packageManager: DefaultPackageManager,
|
|
331
|
+
resolvedPaths: ResolvedPaths,
|
|
332
|
+
options: { cwd: string; agentDir: string },
|
|
333
|
+
) {
|
|
334
|
+
const isPiIntercomPackage = createPiIntercomPackageMatcher(options);
|
|
335
|
+
const blocked = {
|
|
336
|
+
user: new Set<string>(),
|
|
337
|
+
project: new Set<string>(),
|
|
338
|
+
};
|
|
339
|
+
for (const configured of packageManager.listConfiguredPackages()) {
|
|
340
|
+
if (isPiIntercomPackage(configured.source, configured.installedPath)) {
|
|
341
|
+
blocked[configured.scope].add(configured.source);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const resources = [
|
|
346
|
+
...resolvedPaths.extensions,
|
|
347
|
+
...resolvedPaths.skills,
|
|
348
|
+
...resolvedPaths.prompts,
|
|
349
|
+
...resolvedPaths.themes,
|
|
350
|
+
];
|
|
351
|
+
for (const resource of resources) {
|
|
352
|
+
const { metadata } = resource;
|
|
353
|
+
if (
|
|
354
|
+
metadata.origin !== "package" ||
|
|
355
|
+
metadata.scope === "temporary" ||
|
|
356
|
+
!isPiIntercomPackage(metadata.source, metadata.baseDir)
|
|
357
|
+
) {
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
blocked[metadata.scope].add(metadata.source);
|
|
361
|
+
}
|
|
362
|
+
return blocked;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function createEphemeralChildSettings(
|
|
366
|
+
sourceSettings: SettingsManager,
|
|
367
|
+
blockedSources: { user: Set<string>; project: Set<string> },
|
|
368
|
+
openPiSources: { user: Set<string>; project: Set<string> },
|
|
369
|
+
projectTrusted: boolean,
|
|
370
|
+
) {
|
|
371
|
+
const globalSettings = sourceSettings.getGlobalSettings();
|
|
372
|
+
const projectSettings = sourceSettings.getProjectSettings();
|
|
373
|
+
const settingsForScope = (
|
|
374
|
+
settings: typeof globalSettings,
|
|
375
|
+
scope: "user" | "project",
|
|
376
|
+
) => ({
|
|
377
|
+
...settings,
|
|
378
|
+
packages: settings.packages
|
|
379
|
+
?.filter(
|
|
380
|
+
(source) => !blockedSources[scope].has(packageSourceValue(source)),
|
|
381
|
+
)
|
|
382
|
+
.map((source) =>
|
|
383
|
+
openPiSources[scope].has(packageSourceValue(source))
|
|
384
|
+
? disableOpenPiGitInfo(source)
|
|
385
|
+
: source,
|
|
386
|
+
),
|
|
387
|
+
});
|
|
388
|
+
const contents = {
|
|
389
|
+
global: JSON.stringify(settingsForScope(globalSettings, "user")),
|
|
390
|
+
project: JSON.stringify(settingsForScope(projectSettings, "project")),
|
|
391
|
+
};
|
|
392
|
+
const storage: Parameters<typeof SettingsManager.fromStorage>[0] = {
|
|
393
|
+
withLock(scope, update) {
|
|
394
|
+
const next = update(contents[scope]);
|
|
395
|
+
if (next !== undefined) contents[scope] = next;
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
return SettingsManager.fromStorage(storage, { projectTrusted });
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async function createChildSettingsManager(options: {
|
|
402
|
+
cwd: string;
|
|
403
|
+
agentDir: string;
|
|
404
|
+
projectTrusted: boolean;
|
|
405
|
+
}) {
|
|
406
|
+
const sourceSettings = SettingsManager.create(options.cwd, options.agentDir, {
|
|
407
|
+
projectTrusted: options.projectTrusted,
|
|
408
|
+
});
|
|
409
|
+
const packageManager = new DefaultPackageManager({
|
|
410
|
+
cwd: options.cwd,
|
|
411
|
+
agentDir: options.agentDir,
|
|
412
|
+
settingsManager: sourceSettings,
|
|
413
|
+
});
|
|
414
|
+
const resolvedPaths = await packageManager.resolve(async () => "skip");
|
|
415
|
+
const blockedSources = blockedPackageSources(packageManager, resolvedPaths, {
|
|
416
|
+
cwd: options.cwd,
|
|
417
|
+
agentDir: options.agentDir,
|
|
418
|
+
});
|
|
419
|
+
const openPiSources = openPiPackageSources(packageManager, {
|
|
420
|
+
cwd: options.cwd,
|
|
421
|
+
agentDir: options.agentDir,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
return createEphemeralChildSettings(
|
|
425
|
+
sourceSettings,
|
|
426
|
+
blockedSources,
|
|
427
|
+
openPiSources,
|
|
428
|
+
options.projectTrusted,
|
|
50
429
|
);
|
|
51
430
|
}
|
|
52
431
|
|
|
@@ -96,6 +475,42 @@ export const CHILD_EXCLUDED_TOOL_NAMES = [
|
|
|
96
475
|
"context_pivot",
|
|
97
476
|
] as const;
|
|
98
477
|
|
|
478
|
+
const PARENT_ONLY_OPENPI_EXTENSION_PATHS = new Set(
|
|
479
|
+
(Object.keys(OPENPI_TOOL_SURFACE) as OpenPiToolOwner[])
|
|
480
|
+
.filter((owner) => {
|
|
481
|
+
const { entry, deferred } = OPENPI_TOOL_SURFACE[owner];
|
|
482
|
+
const toolNames = [...entry, ...deferred];
|
|
483
|
+
return (
|
|
484
|
+
toolNames.length > 0 &&
|
|
485
|
+
toolNames.every((name) =>
|
|
486
|
+
CHILD_EXCLUDED_TOOL_NAMES.includes(name as never),
|
|
487
|
+
)
|
|
488
|
+
);
|
|
489
|
+
})
|
|
490
|
+
.map((owner) => canonicalExistingPath(OPENPI_OWNER_SOURCE_PATHS[owner]))
|
|
491
|
+
.filter(
|
|
492
|
+
(extensionPath): extensionPath is string => extensionPath !== undefined,
|
|
493
|
+
),
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
function isVerifiedParentOnlyOpenPiExtension(extension: {
|
|
497
|
+
path: string;
|
|
498
|
+
resolvedPath: string;
|
|
499
|
+
sourceInfo: { path: string };
|
|
500
|
+
}) {
|
|
501
|
+
return [
|
|
502
|
+
extension.path,
|
|
503
|
+
extension.resolvedPath,
|
|
504
|
+
extension.sourceInfo.path,
|
|
505
|
+
].some((candidate) => {
|
|
506
|
+
const canonicalPath = canonicalExistingPath(candidate);
|
|
507
|
+
return (
|
|
508
|
+
canonicalPath !== undefined &&
|
|
509
|
+
PARENT_ONLY_OPENPI_EXTENSION_PATHS.has(canonicalPath)
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
|
|
99
514
|
/**
|
|
100
515
|
* Fresh SDK options avoid turning the denylist into an accidental allowlist.
|
|
101
516
|
*
|
|
@@ -133,26 +548,27 @@ export interface ChildResourceOptions {
|
|
|
133
548
|
/** Load normal global/package resources and trust-gated project resources. */
|
|
134
549
|
export async function createChildResources(options: ChildResourceOptions) {
|
|
135
550
|
const agentDir = options.agentDir ?? getAgentDir();
|
|
136
|
-
const settingsManager =
|
|
551
|
+
const settingsManager = await createChildSettingsManager({
|
|
552
|
+
cwd: options.cwd,
|
|
553
|
+
agentDir,
|
|
137
554
|
projectTrusted: options.projectTrusted,
|
|
138
555
|
});
|
|
139
556
|
const loader = new DefaultResourceLoader({
|
|
140
557
|
cwd: options.cwd,
|
|
141
558
|
agentDir,
|
|
142
559
|
settingsManager,
|
|
560
|
+
extensionsOverride(base) {
|
|
561
|
+
const withoutGitInfo = excludeOpenPiGitInfoExtension(base);
|
|
562
|
+
return {
|
|
563
|
+
...withoutGitInfo,
|
|
564
|
+
extensions: withoutGitInfo.extensions.filter(
|
|
565
|
+
(extension) => !isVerifiedParentOnlyOpenPiExtension(extension),
|
|
566
|
+
),
|
|
567
|
+
};
|
|
568
|
+
},
|
|
143
569
|
...(options.appendSystemPrompt
|
|
144
570
|
? { appendSystemPrompt: options.appendSystemPrompt }
|
|
145
571
|
: {}),
|
|
146
|
-
extensionsOverride: (base) => ({
|
|
147
|
-
...base,
|
|
148
|
-
extensions: base.extensions.filter(
|
|
149
|
-
(extension) => !isPiIntercomNpmResource(extension),
|
|
150
|
-
),
|
|
151
|
-
}),
|
|
152
|
-
skillsOverride: (base) => ({
|
|
153
|
-
...base,
|
|
154
|
-
skills: base.skills.filter((skill) => !isPiIntercomNpmResource(skill)),
|
|
155
|
-
}),
|
|
156
572
|
});
|
|
157
573
|
await loader.reload();
|
|
158
574
|
return { loader, settingsManager };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Minimum lifecycle contract shared by deferred result queues. */
|
|
2
|
+
export interface ResultDeliveryQueue<T> {
|
|
3
|
+
defer(result: T): void;
|
|
4
|
+
size(): number;
|
|
5
|
+
clear(): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** One-shot results are removed by either an explicit consumer or delivery. */
|
|
9
|
+
export interface ConsumableResultDeliveryQueue<T, TId = string>
|
|
10
|
+
extends ResultDeliveryQueue<T> {
|
|
11
|
+
consume(ids: Iterable<TId>): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Stable identity carried by a durable, at-least-once result. */
|
|
15
|
+
export interface DurableResultEnvelope {
|
|
16
|
+
deliveryId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Per-result acknowledgement for a durable delivery attempt. */
|
|
20
|
+
export interface DurableResultDeliveryReceipt {
|
|
21
|
+
deliveryId: string;
|
|
22
|
+
delivered: boolean;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Durable queues can reconstruct pending ownership after restart and retry it
|
|
28
|
+
* at an explicit lifecycle boundary without changing the stable delivery id.
|
|
29
|
+
*/
|
|
30
|
+
export interface DurableResultDeliveryQueue<T extends DurableResultEnvelope>
|
|
31
|
+
extends ResultDeliveryQueue<T> {
|
|
32
|
+
restore(result: T): boolean;
|
|
33
|
+
retryPending(): Promise<void>;
|
|
34
|
+
}
|