pi-subagents 0.31.1 → 0.33.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 +67 -4
- package/README.md +219 -40
- package/install.mjs +2 -1
- package/package.json +3 -2
- package/skills/pi-subagents/SKILL.md +71 -10
- package/src/agents/agent-management.ts +179 -2
- package/src/agents/agent-memory.ts +254 -0
- package/src/agents/agent-serializer.ts +11 -0
- package/src/agents/agents.ts +193 -19
- package/src/agents/chain-serializer.ts +27 -2
- package/src/extension/config.ts +27 -4
- package/src/extension/doctor.ts +1 -7
- package/src/extension/fanout-child.ts +3 -2
- package/src/extension/index.ts +70 -41
- package/src/extension/rpc.ts +369 -0
- package/src/extension/schemas.ts +54 -10
- package/src/extension/tool-description.ts +200 -0
- package/src/intercom/intercom-bridge.ts +21 -253
- package/src/intercom/native-supervisor-channel.ts +510 -0
- package/src/runs/background/async-execution.ts +187 -38
- package/src/runs/background/async-job-tracker.ts +88 -2
- package/src/runs/background/async-status.ts +67 -10
- package/src/runs/background/chain-root-attachment.ts +34 -4
- package/src/runs/background/completion-batcher.ts +166 -0
- package/src/runs/background/control-channel.ts +156 -1
- package/src/runs/background/fleet-view.ts +515 -0
- package/src/runs/background/notify.ts +161 -44
- package/src/runs/background/result-watcher.ts +1 -2
- package/src/runs/background/run-id-resolver.ts +3 -2
- package/src/runs/background/run-status.ts +167 -6
- package/src/runs/background/scheduled-runs.ts +514 -0
- package/src/runs/background/stale-run-reconciler.ts +28 -1
- package/src/runs/background/subagent-runner.ts +840 -127
- package/src/runs/background/wait.ts +353 -0
- package/src/runs/foreground/chain-execution.ts +123 -27
- package/src/runs/foreground/execution.ts +174 -27
- package/src/runs/foreground/subagent-executor.ts +569 -81
- package/src/runs/shared/acceptance.ts +45 -22
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/model-fallback.ts +171 -20
- package/src/runs/shared/model-scope.ts +128 -0
- package/src/runs/shared/nested-events.ts +89 -0
- package/src/runs/shared/parallel-utils.ts +50 -1
- package/src/runs/shared/pi-args.ts +35 -4
- package/src/runs/shared/pi-spawn.ts +52 -20
- package/src/runs/shared/single-output.ts +2 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +110 -4
- package/src/runs/shared/tool-budget.ts +74 -0
- package/src/runs/shared/turn-budget.ts +52 -0
- package/src/runs/shared/worktree.ts +28 -5
- package/src/shared/artifacts.ts +16 -1
- package/src/shared/atomic-json.ts +15 -2
- package/src/shared/child-transcript.ts +212 -0
- package/src/shared/fork-context.ts +133 -22
- package/src/shared/settings.ts +3 -1
- package/src/shared/types.ts +197 -4
- package/src/shared/utils.ts +99 -14
- package/src/slash/prompt-workflows.ts +330 -0
- package/src/slash/slash-commands.ts +133 -2
- package/src/tui/render.ts +32 -12
|
@@ -1,35 +1,16 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
1
|
import * as fs from "node:fs";
|
|
3
2
|
import * as os from "node:os";
|
|
4
3
|
import * as path from "node:path";
|
|
5
4
|
import type { AgentConfig } from "../agents/agents.ts";
|
|
6
5
|
import type { ExtensionConfig, IntercomBridgeConfig, IntercomBridgeMode } from "../shared/types.ts";
|
|
7
|
-
import { getAgentDir
|
|
6
|
+
import { getAgentDir } from "../shared/utils.ts";
|
|
8
7
|
|
|
9
|
-
const
|
|
8
|
+
export const NATIVE_INTERCOM_EXTENSION_DIR = "native:pi-subagents-supervisor-channel";
|
|
10
9
|
|
|
11
10
|
function defaultAgentDir(): string {
|
|
12
11
|
return getAgentDir();
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
function defaultIntercomExtensionDir(agentDir = defaultAgentDir()): string {
|
|
16
|
-
return path.join(agentDir, "extensions", PI_INTERCOM_PACKAGE_NAME);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const INTERCOM_EXTENSION_DIR_ENV = "PI_INTERCOM_EXTENSION_DIR";
|
|
20
|
-
|
|
21
|
-
// Launcher-provided override for the pi-intercom package directory. Lets a hermetic
|
|
22
|
-
// wrapper point the subagent intercom bridge at a read-only install (e.g. a Nix-store
|
|
23
|
-
// path) instead of seeding the package into the writable agent dir.
|
|
24
|
-
function envIntercomExtensionDir(): string | undefined {
|
|
25
|
-
const dir = process.env[INTERCOM_EXTENSION_DIR_ENV]?.trim();
|
|
26
|
-
return dir ? dir : undefined;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function defaultIntercomConfigPath(agentDir = defaultAgentDir()): string {
|
|
30
|
-
return path.join(agentDir, "intercom", "config.json");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
14
|
function defaultSubagentConfigDir(agentDir = defaultAgentDir()): string {
|
|
34
15
|
return path.join(agentDir, "extensions", "subagent");
|
|
35
16
|
}
|
|
@@ -59,25 +40,18 @@ export interface IntercomBridgeDiagnostic {
|
|
|
59
40
|
active: boolean;
|
|
60
41
|
mode: IntercomBridgeMode;
|
|
61
42
|
wantsIntercom: boolean;
|
|
62
|
-
|
|
43
|
+
supervisorChannelAvailable: boolean;
|
|
63
44
|
extensionDir: string;
|
|
64
|
-
configPath?: string;
|
|
65
45
|
orchestratorTarget?: string;
|
|
66
46
|
reason?: string;
|
|
67
|
-
intercomConfigEnabled?: boolean;
|
|
68
|
-
intercomConfigError?: string;
|
|
69
47
|
}
|
|
70
48
|
|
|
71
49
|
interface ResolveIntercomBridgeInput {
|
|
72
50
|
config: ExtensionConfig["intercomBridge"];
|
|
73
51
|
context: "fresh" | "fork" | undefined;
|
|
74
52
|
orchestratorTarget?: string;
|
|
75
|
-
extensionDir?: string;
|
|
76
|
-
configPath?: string;
|
|
77
53
|
settingsDir?: string;
|
|
78
|
-
cwd?: string;
|
|
79
54
|
agentDir?: string;
|
|
80
|
-
globalNpmRoot?: string | null;
|
|
81
55
|
}
|
|
82
56
|
|
|
83
57
|
export function resolveIntercomSessionTarget(sessionName: string | undefined, sessionId: string): string {
|
|
@@ -103,10 +77,7 @@ export function resolveIntercomBridgeMode(value: unknown): IntercomBridgeMode {
|
|
|
103
77
|
|
|
104
78
|
function resolveIntercomBridgeConfig(value: ExtensionConfig["intercomBridge"]): Required<IntercomBridgeConfig> {
|
|
105
79
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
106
|
-
return {
|
|
107
|
-
mode: "always",
|
|
108
|
-
instructionFile: "",
|
|
109
|
-
};
|
|
80
|
+
return { mode: "always", instructionFile: "" };
|
|
110
81
|
}
|
|
111
82
|
return {
|
|
112
83
|
mode: resolveIntercomBridgeMode(value.mode),
|
|
@@ -114,169 +85,6 @@ function resolveIntercomBridgeConfig(value: ExtensionConfig["intercomBridge"]):
|
|
|
114
85
|
};
|
|
115
86
|
}
|
|
116
87
|
|
|
117
|
-
function intercomConfigStatus(configPath: string): { enabled: boolean; error?: unknown } {
|
|
118
|
-
if (!fs.existsSync(configPath)) return { enabled: true };
|
|
119
|
-
try {
|
|
120
|
-
const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8")) as { enabled?: unknown };
|
|
121
|
-
return { enabled: parsed.enabled !== false };
|
|
122
|
-
} catch (error) {
|
|
123
|
-
return { enabled: true, error };
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function readJsonBestEffort(filePath: string): unknown {
|
|
128
|
-
try {
|
|
129
|
-
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
130
|
-
} catch (error) {
|
|
131
|
-
const code = error && typeof error === "object" && "code" in error ? (error as NodeJS.ErrnoException).code : undefined;
|
|
132
|
-
if (code !== "ENOENT") console.warn(`Failed to read JSON from '${filePath}'.`, error);
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function packageHasPiExtension(packageRoot: string): boolean {
|
|
138
|
-
if (!fs.existsSync(packageRoot)) return false;
|
|
139
|
-
const pkg = readJsonBestEffort(path.join(packageRoot, "package.json"));
|
|
140
|
-
if (pkg && typeof pkg === "object" && !Array.isArray(pkg)) {
|
|
141
|
-
const pi = (pkg as { pi?: unknown }).pi;
|
|
142
|
-
if (pi && typeof pi === "object" && !Array.isArray(pi)) {
|
|
143
|
-
const extensions = (pi as { extensions?: unknown }).extensions;
|
|
144
|
-
return Array.isArray(extensions) && extensions.some((entry) => typeof entry === "string" && entry.trim() !== "");
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return fs.existsSync(path.join(packageRoot, "extensions"));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function isSafePackagePath(value: string): boolean {
|
|
151
|
-
return value.length > 0
|
|
152
|
-
&& !path.isAbsolute(value)
|
|
153
|
-
&& value.split(/[\\/]/).every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function parseNpmPackageName(source: string): string | undefined {
|
|
157
|
-
const spec = source.slice(4).trim();
|
|
158
|
-
if (!spec) return undefined;
|
|
159
|
-
const match = spec.match(/^(@?[^@]+(?:\/[^@]+)?)(?:@(.+))?$/);
|
|
160
|
-
const packageName = match?.[1] ?? spec;
|
|
161
|
-
return isSafePackagePath(packageName) ? packageName : undefined;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function packageEntrySource(entry: unknown): string | undefined {
|
|
165
|
-
if (typeof entry === "string") return entry;
|
|
166
|
-
if (entry && typeof entry === "object" && !Array.isArray(entry) && typeof (entry as { source?: unknown }).source === "string") {
|
|
167
|
-
return (entry as { source: string }).source;
|
|
168
|
-
}
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function packageEntryAllowsExtensions(entry: unknown): boolean {
|
|
173
|
-
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
|
|
174
|
-
const extensions = (entry as { extensions?: unknown }).extensions;
|
|
175
|
-
return !Array.isArray(extensions) || extensions.length > 0;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function findNearestProjectConfigDir(cwd: string): string | undefined {
|
|
179
|
-
let current = path.resolve(cwd);
|
|
180
|
-
while (true) {
|
|
181
|
-
const configDir = getProjectConfigDir(current);
|
|
182
|
-
if (fs.existsSync(path.join(configDir, "settings.json"))) return configDir;
|
|
183
|
-
const parent = path.dirname(current);
|
|
184
|
-
if (parent === current) return undefined;
|
|
185
|
-
current = parent;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
let cachedGlobalNpmRoot: string | null | undefined;
|
|
190
|
-
|
|
191
|
-
function getGlobalNpmRoot(): string | null {
|
|
192
|
-
if (cachedGlobalNpmRoot !== undefined) return cachedGlobalNpmRoot;
|
|
193
|
-
try {
|
|
194
|
-
cachedGlobalNpmRoot = execSync("npm root -g", { encoding: "utf-8", timeout: 5000 }).trim();
|
|
195
|
-
return cachedGlobalNpmRoot;
|
|
196
|
-
} catch {
|
|
197
|
-
cachedGlobalNpmRoot = null;
|
|
198
|
-
return null;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function tmpNpmIntercomPackageDir(agentDir: string): string | undefined {
|
|
203
|
-
const tmpNpmDir = path.join(agentDir, "tmp", "extensions", "npm");
|
|
204
|
-
try {
|
|
205
|
-
const entries = fs.readdirSync(tmpNpmDir, { withFileTypes: true });
|
|
206
|
-
for (const entry of entries) {
|
|
207
|
-
if (!entry.isDirectory()) continue;
|
|
208
|
-
const pkgDir = path.join(tmpNpmDir, entry.name, "node_modules", PI_INTERCOM_PACKAGE_NAME);
|
|
209
|
-
if (fs.existsSync(pkgDir) && packageHasPiExtension(pkgDir)) {
|
|
210
|
-
return path.resolve(pkgDir);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
} catch {
|
|
214
|
-
// ignore ENOTDIR, ENOENT, permission errors
|
|
215
|
-
}
|
|
216
|
-
return undefined;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
function configuredPiIntercomPackageDir(input: ResolveIntercomBridgeInput, agentDir: string): string | undefined {
|
|
220
|
-
const projectConfigDir = input.cwd ? findNearestProjectConfigDir(path.resolve(input.cwd)) : undefined;
|
|
221
|
-
const settingsFiles = [
|
|
222
|
-
...(projectConfigDir ? [{ file: path.join(projectConfigDir, "settings.json"), configDir: projectConfigDir, scope: "project" as const }] : []),
|
|
223
|
-
{ file: path.join(agentDir, "settings.json"), configDir: agentDir, scope: "user" as const },
|
|
224
|
-
];
|
|
225
|
-
const globalNpmRoot = input.globalNpmRoot === undefined ? getGlobalNpmRoot() : input.globalNpmRoot;
|
|
226
|
-
|
|
227
|
-
for (const { file, configDir, scope } of settingsFiles) {
|
|
228
|
-
const settings = readJsonBestEffort(file);
|
|
229
|
-
if (!settings || typeof settings !== "object" || Array.isArray(settings)) continue;
|
|
230
|
-
const packages = (settings as { packages?: unknown }).packages;
|
|
231
|
-
if (!Array.isArray(packages)) continue;
|
|
232
|
-
|
|
233
|
-
for (const entry of packages) {
|
|
234
|
-
if (!packageEntryAllowsExtensions(entry)) continue;
|
|
235
|
-
const source = packageEntrySource(entry)?.trim();
|
|
236
|
-
if (!source?.startsWith("npm:")) continue;
|
|
237
|
-
const packageName = parseNpmPackageName(source);
|
|
238
|
-
if (packageName !== PI_INTERCOM_PACKAGE_NAME) continue;
|
|
239
|
-
const candidates = scope === "project"
|
|
240
|
-
? [path.join(configDir, "npm", "node_modules", packageName)]
|
|
241
|
-
: [
|
|
242
|
-
...(globalNpmRoot ? [path.join(globalNpmRoot, packageName)] : []),
|
|
243
|
-
path.join(agentDir, "npm", "node_modules", packageName),
|
|
244
|
-
];
|
|
245
|
-
const packageRoot = candidates.find(packageHasPiExtension);
|
|
246
|
-
if (packageRoot) return path.resolve(packageRoot);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
return undefined;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function resolveIntercomExtensionDir(input: ResolveIntercomBridgeInput, agentDir: string): string {
|
|
253
|
-
const legacyDir = path.resolve(input.extensionDir ?? envIntercomExtensionDir() ?? defaultIntercomExtensionDir(agentDir));
|
|
254
|
-
if (fs.existsSync(legacyDir)) return legacyDir;
|
|
255
|
-
|
|
256
|
-
const configured = configuredPiIntercomPackageDir(input, agentDir);
|
|
257
|
-
if (configured) return configured;
|
|
258
|
-
|
|
259
|
-
const tmpDir = tmpNpmIntercomPackageDir(agentDir);
|
|
260
|
-
if (tmpDir) return tmpDir;
|
|
261
|
-
|
|
262
|
-
return legacyDir;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function extensionSandboxAllowsIntercom(extensions: string[] | undefined, extensionDir: string): boolean {
|
|
266
|
-
if (extensions === undefined) return true;
|
|
267
|
-
|
|
268
|
-
const intercomDir = path.resolve(extensionDir).replaceAll("\\", "/").toLowerCase();
|
|
269
|
-
for (const entry of extensions) {
|
|
270
|
-
const normalized = entry.trim().replaceAll("\\", "/").toLowerCase();
|
|
271
|
-
if (normalized === "pi-intercom") return true;
|
|
272
|
-
if (normalized === intercomDir) return true;
|
|
273
|
-
if (normalized.startsWith(`${intercomDir}/`)) return true;
|
|
274
|
-
if (normalized.endsWith("/pi-intercom")) return true;
|
|
275
|
-
if (normalized.includes("/pi-intercom/")) return true;
|
|
276
|
-
}
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
88
|
function expandTilde(filePath: string): string {
|
|
281
89
|
return filePath.startsWith("~/") ? path.join(os.homedir(), filePath.slice(2)) : filePath;
|
|
282
90
|
}
|
|
@@ -298,98 +106,58 @@ function resolveInstructionTemplate(instructionFile: string, settingsDir: string
|
|
|
298
106
|
function buildIntercomBridgeInstruction(orchestratorTarget: string, template: string): string {
|
|
299
107
|
const instruction = template.replaceAll("{orchestratorTarget}", orchestratorTarget).trim();
|
|
300
108
|
if (instruction.startsWith(INTERCOM_BRIDGE_MARKER)) return instruction;
|
|
301
|
-
return `${INTERCOM_BRIDGE_MARKER}
|
|
302
|
-
|
|
109
|
+
return `${INTERCOM_BRIDGE_MARKER}\n${instruction}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function inactiveReason(mode: IntercomBridgeMode, context: "fresh" | "fork" | undefined, orchestratorTarget: string | undefined): string | undefined {
|
|
113
|
+
if (mode === "off") return "bridge mode is off";
|
|
114
|
+
if (mode === "fork-only" && context !== "fork") return "bridge mode is fork-only and context is not fork";
|
|
115
|
+
if (!orchestratorTarget) return "orchestrator target is not available";
|
|
116
|
+
return undefined;
|
|
303
117
|
}
|
|
304
118
|
|
|
305
119
|
export function diagnoseIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeDiagnostic {
|
|
306
120
|
const config = resolveIntercomBridgeConfig(input.config);
|
|
307
121
|
const mode = config.mode;
|
|
308
|
-
const agentDir = path.resolve(input.agentDir ?? defaultAgentDir());
|
|
309
|
-
const extensionDir = resolveIntercomExtensionDir(input, agentDir);
|
|
310
122
|
const orchestratorTarget = input.orchestratorTarget?.trim();
|
|
311
|
-
const configPath = path.resolve(input.configPath ?? defaultIntercomConfigPath(agentDir));
|
|
312
123
|
const wantsIntercom = mode !== "off" && !(mode === "fork-only" && input.context !== "fork");
|
|
313
|
-
const
|
|
314
|
-
let configStatus: ReturnType<typeof intercomConfigStatus> | undefined;
|
|
315
|
-
let reason: string | undefined;
|
|
316
|
-
if (mode === "off") reason = "bridge mode is off";
|
|
317
|
-
else if (mode === "fork-only" && input.context !== "fork") reason = "bridge mode is fork-only and context is not fork";
|
|
318
|
-
else if (!orchestratorTarget) reason = "orchestrator target is not available";
|
|
319
|
-
else if (!piIntercomAvailable) reason = "pi-intercom extension was not found";
|
|
320
|
-
else {
|
|
321
|
-
configStatus = intercomConfigStatus(configPath);
|
|
322
|
-
if (!configStatus.enabled) reason = "intercom config is disabled";
|
|
323
|
-
}
|
|
324
|
-
let intercomConfigError: string | undefined;
|
|
325
|
-
if (configStatus?.error) {
|
|
326
|
-
const error = configStatus.error;
|
|
327
|
-
intercomConfigError = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
328
|
-
}
|
|
329
|
-
|
|
124
|
+
const reason = inactiveReason(mode, input.context, orchestratorTarget);
|
|
330
125
|
return {
|
|
331
126
|
active: reason === undefined,
|
|
332
127
|
mode,
|
|
333
128
|
wantsIntercom,
|
|
334
|
-
|
|
335
|
-
extensionDir,
|
|
336
|
-
configPath,
|
|
129
|
+
supervisorChannelAvailable: true,
|
|
130
|
+
extensionDir: NATIVE_INTERCOM_EXTENSION_DIR,
|
|
337
131
|
...(orchestratorTarget ? { orchestratorTarget } : {}),
|
|
338
132
|
...(reason ? { reason } : {}),
|
|
339
|
-
...(configStatus ? { intercomConfigEnabled: configStatus.enabled } : {}),
|
|
340
|
-
...(intercomConfigError ? { intercomConfigError } : {}),
|
|
341
133
|
};
|
|
342
134
|
}
|
|
343
135
|
|
|
344
136
|
export function resolveIntercomBridge(input: ResolveIntercomBridgeInput): IntercomBridgeState {
|
|
345
137
|
const config = resolveIntercomBridgeConfig(input.config);
|
|
346
138
|
const mode = config.mode;
|
|
347
|
-
const agentDir = path.resolve(input.agentDir ?? defaultAgentDir());
|
|
348
|
-
const extensionDir = resolveIntercomExtensionDir(input, agentDir);
|
|
349
139
|
const orchestratorTarget = input.orchestratorTarget?.trim();
|
|
140
|
+
const agentDir = path.resolve(input.agentDir ?? defaultAgentDir());
|
|
350
141
|
const settingsDir = path.resolve(input.settingsDir ?? defaultSubagentConfigDir(agentDir));
|
|
351
142
|
const defaultInstruction = buildIntercomBridgeInstruction(
|
|
352
143
|
orchestratorTarget || "{orchestratorTarget}",
|
|
353
144
|
DEFAULT_INTERCOM_BRIDGE_TEMPLATE,
|
|
354
145
|
);
|
|
355
|
-
|
|
356
|
-
if (
|
|
357
|
-
return { active: false, mode, extensionDir, instruction: defaultInstruction };
|
|
146
|
+
const reason = inactiveReason(mode, input.context, orchestratorTarget);
|
|
147
|
+
if (reason || !orchestratorTarget) {
|
|
148
|
+
return { active: false, mode, extensionDir: NATIVE_INTERCOM_EXTENSION_DIR, instruction: defaultInstruction };
|
|
358
149
|
}
|
|
359
|
-
if (mode === "fork-only" && input.context !== "fork") {
|
|
360
|
-
return { active: false, mode, extensionDir, instruction: defaultInstruction };
|
|
361
|
-
}
|
|
362
|
-
if (!orchestratorTarget) {
|
|
363
|
-
return { active: false, mode, extensionDir, instruction: defaultInstruction };
|
|
364
|
-
}
|
|
365
|
-
if (!fs.existsSync(extensionDir)) {
|
|
366
|
-
return { active: false, mode, extensionDir, instruction: defaultInstruction };
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
const configPath = path.resolve(input.configPath ?? defaultIntercomConfigPath(agentDir));
|
|
370
|
-
const intercomStatus = intercomConfigStatus(configPath);
|
|
371
|
-
if (intercomStatus.error) console.warn(`Failed to parse intercom config at '${configPath}'. Assuming enabled.`, intercomStatus.error);
|
|
372
|
-
if (!intercomStatus.enabled) {
|
|
373
|
-
return { active: false, mode, extensionDir, instruction: defaultInstruction };
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const instruction = buildIntercomBridgeInstruction(
|
|
377
|
-
orchestratorTarget,
|
|
378
|
-
resolveInstructionTemplate(config.instructionFile, settingsDir),
|
|
379
|
-
);
|
|
380
|
-
|
|
381
150
|
return {
|
|
382
151
|
active: true,
|
|
383
152
|
mode,
|
|
384
153
|
orchestratorTarget,
|
|
385
|
-
extensionDir,
|
|
386
|
-
instruction,
|
|
154
|
+
extensionDir: NATIVE_INTERCOM_EXTENSION_DIR,
|
|
155
|
+
instruction: buildIntercomBridgeInstruction(orchestratorTarget, resolveInstructionTemplate(config.instructionFile, settingsDir)),
|
|
387
156
|
};
|
|
388
157
|
}
|
|
389
158
|
|
|
390
159
|
export function applyIntercomBridgeToAgent(agent: AgentConfig, bridge: IntercomBridgeState): AgentConfig {
|
|
391
160
|
if (!bridge.active || !bridge.orchestratorTarget) return agent;
|
|
392
|
-
if (!extensionSandboxAllowsIntercom(agent.extensions, bridge.extensionDir)) return agent;
|
|
393
161
|
|
|
394
162
|
const bridgeTools = ["intercom", "contact_supervisor"];
|
|
395
163
|
const tools = agent.tools
|