@runfusion/fusion 0.12.0 → 0.14.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 +13 -0
- package/dist/bin.js +1707 -610
- package/dist/client/assets/AgentDetailView-CBFUveyO.js +18 -0
- package/dist/client/assets/AgentsView-DPezXQ-U.js +522 -0
- package/dist/client/assets/{AgentsView-Bkk-uBij.css → AgentsView-V5GhlBYu.css} +1 -1
- package/dist/client/assets/ChatView-5N4-EuhD.js +1 -0
- package/dist/client/assets/{DevServerView-DQrVLbK5.js → DevServerView-Daft4YFc.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DVmy6sLM.js → DirectoryPicker-rew1y6qO.js} +1 -1
- package/dist/client/assets/{DocumentsView-DHEv-Q2a.js → DocumentsView-i72qJzwd.js} +1 -1
- package/dist/client/assets/{InsightsView-ByyY7GX7.js → InsightsView-BL5eZJ0a.js} +3 -3
- package/dist/client/assets/{MemoryView-Udiu0u8R.js → MemoryView-pl8Cdg_p.js} +2 -2
- package/dist/client/assets/{NodesView-CupS-GGc.js → NodesView-D6eJ15zc.js} +4 -4
- package/dist/client/assets/PiExtensionsManager-ExInwXWP.js +11 -0
- package/dist/client/assets/PluginManager-CYhtxHun.js +1 -0
- package/dist/client/assets/{ResearchView-BG9Feaeb.js → ResearchView-B_QPUEjB.js} +1 -1
- package/dist/client/assets/{RoadmapsView-BTJtmBnF.js → RoadmapsView-DBNLaEsK.js} +2 -2
- package/dist/client/assets/SettingsModal-1ET586M3.js +31 -0
- package/dist/client/assets/{SettingsModal-eNCZiHa6.js → SettingsModal-CL_gWmOj.js} +1 -1
- package/dist/client/assets/SettingsModal-D_AFkDJa.css +1 -0
- package/dist/client/assets/{SetupWizardModal-yf79TN1L.js → SetupWizardModal-CLkY9HFL.js} +1 -1
- package/dist/client/assets/{SkillMultiselect-DOj5vX4U.js → SkillMultiselect-B0qi32SQ.js} +1 -1
- package/dist/client/assets/{SkillsView-CgnCnikX.js → SkillsView-umVjRq6o.js} +1 -1
- package/dist/client/assets/TodoView-CFifSvrD.js +6 -0
- package/dist/client/assets/TodoView-SeO9o7km.css +1 -0
- package/dist/client/assets/{folder-open-D11gjHGK.js → folder-open-nYPrL1W3.js} +1 -1
- package/dist/client/assets/index-Bc8nfKeH.js +661 -0
- package/dist/client/assets/index-C1prPuSl.css +1 -0
- package/dist/client/assets/{list-checks-CBzPc3GA.js → list-checks-sK8xJeH_.js} +1 -1
- package/dist/client/assets/{star-BWcRk8nt.js → star-BRtXbYkB.js} +1 -1
- package/dist/client/assets/{upload-91TM4ljC.js → upload-BP60eBwN.js} +1 -1
- package/dist/client/assets/{users-BAsI___L.js → users-qSGAX2Pf.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/sw.js +6 -0
- package/dist/client/version.json +1 -1
- package/dist/droid-cli/index.ts +127 -0
- package/dist/droid-cli/package.json +37 -0
- package/dist/droid-cli/src/__tests__/control-handler.test.ts +164 -0
- package/dist/droid-cli/src/__tests__/event-bridge.test.ts +1318 -0
- package/dist/droid-cli/src/__tests__/mcp-config.test.ts +310 -0
- package/dist/droid-cli/src/__tests__/process-manager.test.ts +818 -0
- package/dist/droid-cli/src/__tests__/prompt-builder.test.ts +1206 -0
- package/dist/droid-cli/src/__tests__/provider.test.ts +1894 -0
- package/dist/droid-cli/src/__tests__/setup-test-isolation.test.ts +32 -0
- package/dist/droid-cli/src/__tests__/setup-test-isolation.ts +14 -0
- package/dist/droid-cli/src/__tests__/stream-parser.test.ts +188 -0
- package/dist/droid-cli/src/__tests__/thinking-config.test.ts +141 -0
- package/dist/droid-cli/src/__tests__/tool-mapping.test.ts +253 -0
- package/dist/droid-cli/src/control-handler.ts +82 -0
- package/dist/droid-cli/src/event-bridge.ts +397 -0
- package/dist/droid-cli/src/mcp-config.ts +144 -0
- package/dist/droid-cli/src/mcp-schema-server.cjs +49 -0
- package/dist/droid-cli/src/process-manager.ts +358 -0
- package/dist/droid-cli/src/prompt-builder.ts +629 -0
- package/dist/droid-cli/src/provider.ts +447 -0
- package/dist/droid-cli/src/stream-parser.ts +37 -0
- package/dist/droid-cli/src/thinking-config.ts +83 -0
- package/dist/droid-cli/src/tool-mapping.ts +147 -0
- package/dist/droid-cli/src/types.ts +87 -0
- package/dist/extension.js +542 -141
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/pi-claude-cli/src/__tests__/prompt-builder.test.ts +36 -0
- package/dist/pi-claude-cli/src/prompt-builder.ts +19 -28
- package/package.json +2 -1
- package/dist/client/assets/AgentDetailView-B20ApPe1.js +0 -18
- package/dist/client/assets/AgentsView-ChN1tgQ0.js +0 -522
- package/dist/client/assets/ChatView-oPMFwmoc.js +0 -1
- package/dist/client/assets/PiExtensionsManager-DXs2xI8K.js +0 -11
- package/dist/client/assets/PluginManager-BCpiZf4_.js +0 -1
- package/dist/client/assets/SettingsModal-9HS8MnmW.css +0 -1
- package/dist/client/assets/SettingsModal-DZ_LaEhd.js +0 -31
- package/dist/client/assets/TodoView-67BMyICY.js +0 -6
- package/dist/client/assets/TodoView-C1g65hJo.css +0 -1
- package/dist/client/assets/index-BLn1R7Ob.css +0 -1
- package/dist/client/assets/index-CLAHcGnI.js +0 -656
package/dist/extension.js
CHANGED
|
@@ -79,6 +79,7 @@ var init_settings_schema = __esm({
|
|
|
79
79
|
showGitHubStarButton: true,
|
|
80
80
|
modelOnboardingComplete: void 0,
|
|
81
81
|
useClaudeCli: void 0,
|
|
82
|
+
useDroidCli: void 0,
|
|
82
83
|
// Global baseline lanes for per-role model selection
|
|
83
84
|
executionGlobalProvider: void 0,
|
|
84
85
|
executionGlobalModelId: void 0,
|
|
@@ -1006,11 +1007,40 @@ function hasAgentIdentity(agent) {
|
|
|
1006
1007
|
if (!agent) return false;
|
|
1007
1008
|
return !!(agent.soul?.trim() || agent.instructionsText?.trim() || agent.instructionsPath?.trim() || agent.memory?.trim());
|
|
1008
1009
|
}
|
|
1009
|
-
function
|
|
1010
|
+
function slugifyAgentAssetSegment(value) {
|
|
1011
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1012
|
+
}
|
|
1013
|
+
function getSafeAgentAssetIdSegment(agentId) {
|
|
1014
|
+
const slug = slugifyAgentAssetSegment(agentId);
|
|
1015
|
+
return slug || "agent";
|
|
1016
|
+
}
|
|
1017
|
+
function getCanonicalAgentAssetDirectoryName(agentName, agentId) {
|
|
1018
|
+
if (!agentId || typeof agentId !== "string") {
|
|
1019
|
+
throw new Error("getCanonicalAgentAssetDirectoryName requires a non-empty agentId");
|
|
1020
|
+
}
|
|
1021
|
+
const safeId = getSafeAgentAssetIdSegment(agentId);
|
|
1022
|
+
const nameSlug = slugifyAgentAssetSegment(agentName ?? "");
|
|
1023
|
+
const prefix = nameSlug || safeId;
|
|
1024
|
+
return `${prefix}-${safeId}`;
|
|
1025
|
+
}
|
|
1026
|
+
function getLegacyAgentAssetDirectoryName(agentId) {
|
|
1027
|
+
if (!agentId || typeof agentId !== "string") {
|
|
1028
|
+
throw new Error("getLegacyAgentAssetDirectoryName requires a non-empty agentId");
|
|
1029
|
+
}
|
|
1030
|
+
return agentId;
|
|
1031
|
+
}
|
|
1032
|
+
function getCanonicalAgentInstructionsBundleDirName(agentName, agentId) {
|
|
1033
|
+
return `${getCanonicalAgentAssetDirectoryName(agentName, agentId)}-instructions`;
|
|
1034
|
+
}
|
|
1035
|
+
function getLegacyAgentInstructionsBundleDirName(agentId) {
|
|
1036
|
+
return `${getLegacyAgentAssetDirectoryName(agentId)}-instructions`;
|
|
1037
|
+
}
|
|
1038
|
+
function getDefaultHeartbeatProcedurePath(agentId, agentName) {
|
|
1010
1039
|
if (!agentId || typeof agentId !== "string") {
|
|
1011
1040
|
throw new Error("getDefaultHeartbeatProcedurePath requires a non-empty agentId");
|
|
1012
1041
|
}
|
|
1013
|
-
|
|
1042
|
+
const directory = agentName ? getCanonicalAgentAssetDirectoryName(agentName, agentId) : getLegacyAgentAssetDirectoryName(agentId);
|
|
1043
|
+
return `.fusion/agents/${directory}/HEARTBEAT.md`;
|
|
1014
1044
|
}
|
|
1015
1045
|
function agentToConfigSnapshot(agent) {
|
|
1016
1046
|
return {
|
|
@@ -2704,7 +2734,7 @@ var init_db = __esm({
|
|
|
2704
2734
|
"use strict";
|
|
2705
2735
|
init_sqlite_adapter();
|
|
2706
2736
|
init_types();
|
|
2707
|
-
SCHEMA_VERSION =
|
|
2737
|
+
SCHEMA_VERSION = 58;
|
|
2708
2738
|
SCHEMA_SQL = `
|
|
2709
2739
|
-- Tasks table with JSON columns for nested data
|
|
2710
2740
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -4388,6 +4418,44 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
4388
4418
|
this.db.exec(`CREATE INDEX IF NOT EXISTS idxResearchExportsRunId ON research_exports(runId)`);
|
|
4389
4419
|
});
|
|
4390
4420
|
}
|
|
4421
|
+
if (version < 56) {
|
|
4422
|
+
this.applyMigration(56, () => {
|
|
4423
|
+
if (this.hasTable("chat_sessions")) {
|
|
4424
|
+
this.addColumnIfMissing("chat_sessions", "cliSessionFile", "TEXT");
|
|
4425
|
+
}
|
|
4426
|
+
});
|
|
4427
|
+
}
|
|
4428
|
+
if (version < 57) {
|
|
4429
|
+
this.applyMigration(57, () => {
|
|
4430
|
+
if (this.hasTable("ai_sessions")) {
|
|
4431
|
+
this.addColumnIfMissing("ai_sessions", "archived", "INTEGER DEFAULT 0");
|
|
4432
|
+
this.db.exec(
|
|
4433
|
+
"CREATE INDEX IF NOT EXISTS idxAiSessionsArchived ON ai_sessions(archived)"
|
|
4434
|
+
);
|
|
4435
|
+
}
|
|
4436
|
+
});
|
|
4437
|
+
}
|
|
4438
|
+
if (version < 58) {
|
|
4439
|
+
this.applyMigration(58, () => {
|
|
4440
|
+
const newCommand = "npx runfusion.ai backup --create";
|
|
4441
|
+
if (this.hasTable("automations") && this.hasColumn("automations", "command")) {
|
|
4442
|
+
this.db.prepare(
|
|
4443
|
+
`UPDATE automations
|
|
4444
|
+
SET command = ?, updatedAt = ?
|
|
4445
|
+
WHERE name = 'Database Backup'
|
|
4446
|
+
AND (command LIKE 'fn backup%' OR command LIKE 'kb backup%' OR command LIKE 'fusion backup%')`
|
|
4447
|
+
).run(newCommand, (/* @__PURE__ */ new Date()).toISOString());
|
|
4448
|
+
}
|
|
4449
|
+
if (this.hasTable("routines") && this.hasColumn("routines", "command")) {
|
|
4450
|
+
this.db.prepare(
|
|
4451
|
+
`UPDATE routines
|
|
4452
|
+
SET command = ?, updatedAt = ?
|
|
4453
|
+
WHERE name = 'Database Backup'
|
|
4454
|
+
AND (command LIKE 'fn backup%' OR command LIKE 'kb backup%' OR command LIKE 'fusion backup%')`
|
|
4455
|
+
).run(newCommand, (/* @__PURE__ */ new Date()).toISOString());
|
|
4456
|
+
}
|
|
4457
|
+
});
|
|
4458
|
+
}
|
|
4391
4459
|
}
|
|
4392
4460
|
/**
|
|
4393
4461
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -4651,7 +4719,7 @@ var init_agent_store = __esm({
|
|
|
4651
4719
|
if (agent.heartbeatProcedurePath !== DEFAULT_HEARTBEAT_PROCEDURE_PATH) {
|
|
4652
4720
|
continue;
|
|
4653
4721
|
}
|
|
4654
|
-
const newRelPath =
|
|
4722
|
+
const newRelPath = await this.resolveCompatibleHeartbeatProcedurePath(agent);
|
|
4655
4723
|
const newAbsPath = join3(this.rootDir, "..", newRelPath);
|
|
4656
4724
|
if (legacyContent !== null) {
|
|
4657
4725
|
try {
|
|
@@ -4812,7 +4880,7 @@ var init_agent_store = __esm({
|
|
|
4812
4880
|
const metadata = input.metadata ?? {};
|
|
4813
4881
|
const runtimeConfig = resolveCreationRuntimeConfig(input.runtimeConfig, metadata);
|
|
4814
4882
|
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: input.role, reportsTo: input.reportsTo });
|
|
4815
|
-
const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId));
|
|
4883
|
+
const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId, input.name));
|
|
4816
4884
|
const agent = {
|
|
4817
4885
|
id: agentId,
|
|
4818
4886
|
name: input.name.trim(),
|
|
@@ -5049,14 +5117,16 @@ var init_agent_store = __esm({
|
|
|
5049
5117
|
* Does not create the directory.
|
|
5050
5118
|
*/
|
|
5051
5119
|
getInstructionsDir(agentId) {
|
|
5052
|
-
|
|
5120
|
+
const agent = this.readAgent(agentId);
|
|
5121
|
+
const agentName = agent?.name ?? "";
|
|
5122
|
+
return join3(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agentName, agentId));
|
|
5053
5123
|
}
|
|
5054
5124
|
/**
|
|
5055
5125
|
* List markdown files in an agent's managed instructions bundle.
|
|
5056
5126
|
* Returns [] when the bundle directory does not exist.
|
|
5057
5127
|
*/
|
|
5058
5128
|
async listBundleFiles(agentId) {
|
|
5059
|
-
const bundleDir = this.
|
|
5129
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
|
|
5060
5130
|
try {
|
|
5061
5131
|
const entries = await readdir(bundleDir, { withFileTypes: true });
|
|
5062
5132
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
@@ -5072,7 +5142,8 @@ var init_agent_store = __esm({
|
|
|
5072
5142
|
*/
|
|
5073
5143
|
async readBundleFile(agentId, filePath) {
|
|
5074
5144
|
this.validateBundleFilePath(filePath);
|
|
5075
|
-
const
|
|
5145
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
|
|
5146
|
+
const resolvedPath = join3(bundleDir, filePath);
|
|
5076
5147
|
return readFile(resolvedPath, "utf-8");
|
|
5077
5148
|
}
|
|
5078
5149
|
/**
|
|
@@ -5081,7 +5152,7 @@ var init_agent_store = __esm({
|
|
|
5081
5152
|
async writeBundleFile(agentId, filePath, content) {
|
|
5082
5153
|
return this.withLock(agentId, async () => {
|
|
5083
5154
|
this.validateBundleFilePath(filePath);
|
|
5084
|
-
const bundleDir = this.
|
|
5155
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, true);
|
|
5085
5156
|
await mkdir(bundleDir, { recursive: true });
|
|
5086
5157
|
const existingFiles = await this.listBundleFiles(agentId);
|
|
5087
5158
|
const isOverwrite = existingFiles.includes(filePath);
|
|
@@ -5100,7 +5171,8 @@ var init_agent_store = __esm({
|
|
|
5100
5171
|
async deleteBundleFile(agentId, filePath) {
|
|
5101
5172
|
return this.withLock(agentId, async () => {
|
|
5102
5173
|
this.validateBundleFilePath(filePath);
|
|
5103
|
-
await
|
|
5174
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
|
|
5175
|
+
await unlink(join3(bundleDir, filePath));
|
|
5104
5176
|
});
|
|
5105
5177
|
}
|
|
5106
5178
|
/**
|
|
@@ -5122,7 +5194,7 @@ var init_agent_store = __esm({
|
|
|
5122
5194
|
};
|
|
5123
5195
|
const updated = await this.updateAgent(agentId, { bundleConfig: normalizedConfig });
|
|
5124
5196
|
if (normalizedConfig.mode === "managed") {
|
|
5125
|
-
await mkdir(this.
|
|
5197
|
+
await mkdir(await this.resolveCompatibleBundleDir(agentId, true), { recursive: true });
|
|
5126
5198
|
}
|
|
5127
5199
|
return updated;
|
|
5128
5200
|
}
|
|
@@ -5145,7 +5217,7 @@ var init_agent_store = __esm({
|
|
|
5145
5217
|
bundleConfig: { mode: "managed", entryFile, files: [] }
|
|
5146
5218
|
});
|
|
5147
5219
|
}
|
|
5148
|
-
await mkdir(this.
|
|
5220
|
+
await mkdir(await this.resolveCompatibleBundleDir(agentId, true), { recursive: true });
|
|
5149
5221
|
const files = [];
|
|
5150
5222
|
if (hasInstructionsText) {
|
|
5151
5223
|
await this.writeBundleFile(agentId, entryFile, agent.instructionsText ?? "");
|
|
@@ -6106,8 +6178,87 @@ var init_agent_store = __esm({
|
|
|
6106
6178
|
}
|
|
6107
6179
|
return null;
|
|
6108
6180
|
}
|
|
6109
|
-
|
|
6110
|
-
return join3(this.agentsDir,
|
|
6181
|
+
getCanonicalBundleDir(agent) {
|
|
6182
|
+
return join3(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id));
|
|
6183
|
+
}
|
|
6184
|
+
getLegacyBundleDir(agentId) {
|
|
6185
|
+
return join3(this.agentsDir, getLegacyAgentInstructionsBundleDirName(agentId));
|
|
6186
|
+
}
|
|
6187
|
+
async resolveCompatibleBundleDir(agentId, createIfMissing) {
|
|
6188
|
+
const agent = this.readAgent(agentId);
|
|
6189
|
+
if (!agent) {
|
|
6190
|
+
throw new Error(`Agent ${agentId} not found`);
|
|
6191
|
+
}
|
|
6192
|
+
const canonicalDir = this.getCanonicalBundleDir(agent);
|
|
6193
|
+
if (await this.pathExists(canonicalDir)) {
|
|
6194
|
+
return canonicalDir;
|
|
6195
|
+
}
|
|
6196
|
+
const compatibleDir = await this.findExistingDisplayNameBundleDir(agent);
|
|
6197
|
+
if (compatibleDir) {
|
|
6198
|
+
return compatibleDir;
|
|
6199
|
+
}
|
|
6200
|
+
const legacyDir = this.getLegacyBundleDir(agent.id);
|
|
6201
|
+
if (await this.pathExists(legacyDir)) {
|
|
6202
|
+
return legacyDir;
|
|
6203
|
+
}
|
|
6204
|
+
return createIfMissing ? canonicalDir : canonicalDir;
|
|
6205
|
+
}
|
|
6206
|
+
async findExistingDisplayNameBundleDir(agent) {
|
|
6207
|
+
const safeId = getSafeAgentAssetIdSegment(agent.id);
|
|
6208
|
+
try {
|
|
6209
|
+
const entries = await readdir(this.agentsDir, { withFileTypes: true });
|
|
6210
|
+
const candidates = entries.filter((entry) => entry.isDirectory() && entry.name.endsWith("-instructions")).map((entry) => entry.name).filter((name) => {
|
|
6211
|
+
const base = name.slice(0, -"-instructions".length);
|
|
6212
|
+
return base.endsWith(`-${safeId}`);
|
|
6213
|
+
}).sort((a, b) => a.localeCompare(b));
|
|
6214
|
+
if (candidates.length === 0) {
|
|
6215
|
+
return null;
|
|
6216
|
+
}
|
|
6217
|
+
const canonicalName = getCanonicalAgentInstructionsBundleDirName(agent.name, agent.id);
|
|
6218
|
+
const selected = candidates.find((candidate) => candidate === canonicalName) ?? candidates[0];
|
|
6219
|
+
return join3(this.agentsDir, selected);
|
|
6220
|
+
} catch (err) {
|
|
6221
|
+
if (err.code === "ENOENT") {
|
|
6222
|
+
return null;
|
|
6223
|
+
}
|
|
6224
|
+
throw err;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
async resolveCompatibleHeartbeatProcedurePath(agent) {
|
|
6228
|
+
const canonicalPath = getDefaultHeartbeatProcedurePath(agent.id, agent.name);
|
|
6229
|
+
const canonicalAbs = join3(this.rootDir, "..", canonicalPath);
|
|
6230
|
+
if (await this.pathExists(canonicalAbs)) {
|
|
6231
|
+
return canonicalPath;
|
|
6232
|
+
}
|
|
6233
|
+
const safeId = getSafeAgentAssetIdSegment(agent.id);
|
|
6234
|
+
try {
|
|
6235
|
+
const entries = await readdir(this.agentsDir, { withFileTypes: true });
|
|
6236
|
+
const compatibleDir = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name).find((name) => name.endsWith(`-${safeId}`));
|
|
6237
|
+
if (compatibleDir) {
|
|
6238
|
+
const candidatePath = `.fusion/agents/${compatibleDir}/HEARTBEAT.md`;
|
|
6239
|
+
if (await this.pathExists(join3(this.rootDir, "..", candidatePath))) {
|
|
6240
|
+
return candidatePath;
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
6243
|
+
} catch (err) {
|
|
6244
|
+
if (err.code !== "ENOENT") {
|
|
6245
|
+
throw err;
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
const legacyPath = `.fusion/agents/${getLegacyAgentAssetDirectoryName(agent.id)}/HEARTBEAT.md`;
|
|
6249
|
+
const legacyAbs = join3(this.rootDir, "..", legacyPath);
|
|
6250
|
+
if (await this.pathExists(legacyAbs)) {
|
|
6251
|
+
return legacyPath;
|
|
6252
|
+
}
|
|
6253
|
+
return canonicalPath;
|
|
6254
|
+
}
|
|
6255
|
+
async pathExists(path2) {
|
|
6256
|
+
try {
|
|
6257
|
+
await access(path2, fsConstants.F_OK);
|
|
6258
|
+
return true;
|
|
6259
|
+
} catch {
|
|
6260
|
+
return false;
|
|
6261
|
+
}
|
|
6111
6262
|
}
|
|
6112
6263
|
validateBundleFilePath(filePath) {
|
|
6113
6264
|
if (typeof filePath !== "string") {
|
|
@@ -35839,6 +35990,20 @@ function reconcileClaudeCliPaths(paths, vendoredPath) {
|
|
|
35839
35990
|
}
|
|
35840
35991
|
return filtered;
|
|
35841
35992
|
}
|
|
35993
|
+
function isExternalDroidCliPath(p, vendoredPath) {
|
|
35994
|
+
if (vendoredPath && p === vendoredPath) return false;
|
|
35995
|
+
return /(^|[/\\])droid-cli([/\\]|$)/i.test(p);
|
|
35996
|
+
}
|
|
35997
|
+
function reconcileDroidCliPaths(paths, vendoredPath) {
|
|
35998
|
+
if (!vendoredPath) {
|
|
35999
|
+
return [...paths];
|
|
36000
|
+
}
|
|
36001
|
+
const filtered = paths.filter((p) => !isExternalDroidCliPath(p, vendoredPath));
|
|
36002
|
+
if (!filtered.includes(vendoredPath)) {
|
|
36003
|
+
return [vendoredPath, ...filtered];
|
|
36004
|
+
}
|
|
36005
|
+
return filtered;
|
|
36006
|
+
}
|
|
35842
36007
|
function getDisplayPathWithinRoot(root, targetPath) {
|
|
35843
36008
|
const usesWindowsPaths = /^[A-Za-z]:[\\/]/.test(root) || /^[A-Za-z]:[\\/]/.test(targetPath) || root.includes("\\") || targetPath.includes("\\");
|
|
35844
36009
|
const pathApi = usesWindowsPaths ? win32 : { relative: relative2, isAbsolute: isAbsolute5, sep: sep4 };
|
|
@@ -36096,6 +36261,85 @@ var init_gh_cli = __esm({
|
|
|
36096
36261
|
}
|
|
36097
36262
|
});
|
|
36098
36263
|
|
|
36264
|
+
// ../core/src/fn-binary.ts
|
|
36265
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
36266
|
+
import { platform as platform2 } from "node:os";
|
|
36267
|
+
function runProbe(command, args, timeoutMs) {
|
|
36268
|
+
return new Promise((resolve19) => {
|
|
36269
|
+
let stdout = "";
|
|
36270
|
+
let stderr = "";
|
|
36271
|
+
const child = spawn2(command, args, { stdio: ["ignore", "pipe", "pipe"], shell: false });
|
|
36272
|
+
const timer = setTimeout(() => {
|
|
36273
|
+
try {
|
|
36274
|
+
child.kill("SIGKILL");
|
|
36275
|
+
} catch {
|
|
36276
|
+
}
|
|
36277
|
+
}, timeoutMs);
|
|
36278
|
+
child.stdout?.on("data", (chunk) => {
|
|
36279
|
+
stdout += chunk.toString("utf8");
|
|
36280
|
+
});
|
|
36281
|
+
child.stderr?.on("data", (chunk) => {
|
|
36282
|
+
stderr += chunk.toString("utf8");
|
|
36283
|
+
});
|
|
36284
|
+
child.on("error", (err) => {
|
|
36285
|
+
clearTimeout(timer);
|
|
36286
|
+
resolve19({ exitCode: null, stdout, stderr: stderr || err.message });
|
|
36287
|
+
});
|
|
36288
|
+
child.on("close", (exitCode) => {
|
|
36289
|
+
clearTimeout(timer);
|
|
36290
|
+
resolve19({ exitCode, stdout, stderr });
|
|
36291
|
+
});
|
|
36292
|
+
});
|
|
36293
|
+
}
|
|
36294
|
+
async function whichBinary(name) {
|
|
36295
|
+
const isWindows = platform2() === "win32";
|
|
36296
|
+
const lookup = isWindows ? "where" : "which";
|
|
36297
|
+
const result = await runProbe(lookup, [name], 5e3);
|
|
36298
|
+
if (result.exitCode !== 0) return void 0;
|
|
36299
|
+
const firstLine = result.stdout.split(/\r?\n/).map((s) => s.trim()).find(Boolean);
|
|
36300
|
+
return firstLine || void 0;
|
|
36301
|
+
}
|
|
36302
|
+
async function probeVersion(binary) {
|
|
36303
|
+
const result = await runProbe(binary, ["--version"], 1e4);
|
|
36304
|
+
if (result.exitCode !== 0) return void 0;
|
|
36305
|
+
const text = (result.stdout || result.stderr).trim();
|
|
36306
|
+
if (!text) return void 0;
|
|
36307
|
+
const match = text.match(/\d+\.\d+\.\d+(?:-[\w.]+)?/);
|
|
36308
|
+
return match ? match[0] : text.split(/\s+/)[0];
|
|
36309
|
+
}
|
|
36310
|
+
async function detectFnBinary() {
|
|
36311
|
+
for (const candidate of CANDIDATES) {
|
|
36312
|
+
try {
|
|
36313
|
+
const resolvedPath = await whichBinary(candidate);
|
|
36314
|
+
if (!resolvedPath) continue;
|
|
36315
|
+
const version = await probeVersion(candidate);
|
|
36316
|
+
return {
|
|
36317
|
+
installed: true,
|
|
36318
|
+
binary: candidate,
|
|
36319
|
+
path: resolvedPath,
|
|
36320
|
+
version,
|
|
36321
|
+
invocation: candidate
|
|
36322
|
+
};
|
|
36323
|
+
} catch {
|
|
36324
|
+
}
|
|
36325
|
+
}
|
|
36326
|
+
return {
|
|
36327
|
+
installed: false,
|
|
36328
|
+
invocation: FN_NPX_INVOCATION
|
|
36329
|
+
};
|
|
36330
|
+
}
|
|
36331
|
+
var FN_NPM_PACKAGE, FN_INSTALL_CURL, FN_INSTALL_NPM, FN_NPX_INVOCATION, CANDIDATES;
|
|
36332
|
+
var init_fn_binary = __esm({
|
|
36333
|
+
"../core/src/fn-binary.ts"() {
|
|
36334
|
+
"use strict";
|
|
36335
|
+
FN_NPM_PACKAGE = "runfusion.ai";
|
|
36336
|
+
FN_INSTALL_CURL = "curl -fsSL https://runfusion.ai/install.sh | sh";
|
|
36337
|
+
FN_INSTALL_NPM = `npm install -g ${FN_NPM_PACKAGE}`;
|
|
36338
|
+
FN_NPX_INVOCATION = `npx -y ${FN_NPM_PACKAGE}`;
|
|
36339
|
+
CANDIDATES = ["fn", "fusion"];
|
|
36340
|
+
}
|
|
36341
|
+
});
|
|
36342
|
+
|
|
36099
36343
|
// ../core/src/settings-validation.ts
|
|
36100
36344
|
function validateUnavailableNodePolicy(value) {
|
|
36101
36345
|
if (value === void 0) {
|
|
@@ -38004,7 +38248,7 @@ function sanitizeTitle(raw) {
|
|
|
38004
38248
|
const firstLine = raw.split(/\r?\n/).map((l) => l.trim()).find((l) => l.length > 0);
|
|
38005
38249
|
if (!firstLine) return null;
|
|
38006
38250
|
let title = firstLine.replace(/^[-*]\s+/, "").replace(/^["'`]+|["'`]+$/g, "").trim();
|
|
38007
|
-
title = title.replace(/^(?:title|subject|here(?:'s| is)(?: the)? title|generated title)\s*[
|
|
38251
|
+
title = title.replace(/^(?:title|subject|here(?:'s| is)(?: the)? title|generated title)\s*[:-]\s*/i, "").trim();
|
|
38008
38252
|
title = title.replace(/\*\*([^*]+)\*\*/g, "$1").replace(/__([^_]+)__/g, "$1").replace(/(?<![*\w])\*([^*]+)\*(?![*\w])/g, "$1").replace(/(?<![_\w])_([^_]+)_(?![_\w])/g, "$1");
|
|
38009
38253
|
title = title.replace(/[.!?,;:]+$/, "").trim();
|
|
38010
38254
|
if (!title) return null;
|
|
@@ -49952,7 +50196,8 @@ var init_chat_store = __esm({
|
|
|
49952
50196
|
modelProvider: row.modelProvider ?? null,
|
|
49953
50197
|
modelId: row.modelId ?? null,
|
|
49954
50198
|
createdAt: row.createdAt,
|
|
49955
|
-
updatedAt: row.updatedAt
|
|
50199
|
+
updatedAt: row.updatedAt,
|
|
50200
|
+
cliSessionFile: row.cliSessionFile ?? null
|
|
49956
50201
|
};
|
|
49957
50202
|
}
|
|
49958
50203
|
/**
|
|
@@ -49989,7 +50234,8 @@ var init_chat_store = __esm({
|
|
|
49989
50234
|
modelProvider: input.modelProvider ?? null,
|
|
49990
50235
|
modelId: input.modelId ?? null,
|
|
49991
50236
|
createdAt: now,
|
|
49992
|
-
updatedAt: now
|
|
50237
|
+
updatedAt: now,
|
|
50238
|
+
cliSessionFile: null
|
|
49993
50239
|
};
|
|
49994
50240
|
this.db.prepare(`
|
|
49995
50241
|
INSERT INTO chat_sessions (id, agentId, title, status, projectId, modelProvider, modelId, createdAt, updatedAt)
|
|
@@ -50147,6 +50393,21 @@ var init_chat_store = __esm({
|
|
|
50147
50393
|
archiveSession(id) {
|
|
50148
50394
|
return this.updateSession(id, { status: "archived" });
|
|
50149
50395
|
}
|
|
50396
|
+
/**
|
|
50397
|
+
* Persist the pi/Claude CLI session file path for a chat. Called once,
|
|
50398
|
+
* after the SessionManager for the chat first creates its on-disk file,
|
|
50399
|
+
* so subsequent turns can reopen it via SessionManager.open.
|
|
50400
|
+
*
|
|
50401
|
+
* Does not bump updatedAt or emit events — this is internal plumbing,
|
|
50402
|
+
* not a user-visible state change.
|
|
50403
|
+
*
|
|
50404
|
+
* @param id - Session ID
|
|
50405
|
+
* @param cliSessionFile - Absolute path to the session file, or null to clear
|
|
50406
|
+
*/
|
|
50407
|
+
setCliSessionFile(id, cliSessionFile) {
|
|
50408
|
+
this.db.prepare("UPDATE chat_sessions SET cliSessionFile = ? WHERE id = ?").run(cliSessionFile, id);
|
|
50409
|
+
this.db.bumpLastModified();
|
|
50410
|
+
}
|
|
50150
50411
|
/**
|
|
50151
50412
|
* Delete a chat session and all its messages.
|
|
50152
50413
|
* Messages are cascade-deleted via foreign key constraint.
|
|
@@ -50366,6 +50627,10 @@ __export(src_exports, {
|
|
|
50366
50627
|
EXECUTION_MODES: () => EXECUTION_MODES,
|
|
50367
50628
|
FEATURE_LOOP_STATES: () => FEATURE_LOOP_STATES,
|
|
50368
50629
|
FEATURE_STATUSES: () => FEATURE_STATUSES,
|
|
50630
|
+
FN_INSTALL_CURL: () => FN_INSTALL_CURL,
|
|
50631
|
+
FN_INSTALL_NPM: () => FN_INSTALL_NPM,
|
|
50632
|
+
FN_NPM_PACKAGE: () => FN_NPM_PACKAGE,
|
|
50633
|
+
FN_NPX_INVOCATION: () => FN_NPX_INVOCATION,
|
|
50369
50634
|
FileMemoryBackend: () => FileMemoryBackend,
|
|
50370
50635
|
FirstRunDetector: () => FirstRunDetector,
|
|
50371
50636
|
GLOBAL_SETTINGS_KEYS: () => GLOBAL_SETTINGS_KEYS,
|
|
@@ -50477,6 +50742,7 @@ __export(src_exports, {
|
|
|
50477
50742
|
createInsightExtractionAutomation: () => createInsightExtractionAutomation,
|
|
50478
50743
|
createMemoryDreamsAutomation: () => createMemoryDreamsAutomation,
|
|
50479
50744
|
dailyMemoryPath: () => dailyMemoryPath,
|
|
50745
|
+
detectFnBinary: () => detectFnBinary,
|
|
50480
50746
|
detectLegacyData: () => detectLegacyData,
|
|
50481
50747
|
diffConfigSnapshots: () => diffConfigSnapshots,
|
|
50482
50748
|
discoverPiExtensions: () => discoverPiExtensions,
|
|
@@ -50598,6 +50864,7 @@ __export(src_exports, {
|
|
|
50598
50864
|
readProjectMemoryWithBackend: () => readProjectMemoryWithBackend,
|
|
50599
50865
|
readWorkingMemory: () => readWorkingMemory,
|
|
50600
50866
|
reconcileClaudeCliPaths: () => reconcileClaudeCliPaths,
|
|
50867
|
+
reconcileDroidCliPaths: () => reconcileDroidCliPaths,
|
|
50601
50868
|
refreshQmdProjectMemoryIndex: () => refreshQmdProjectMemoryIndex,
|
|
50602
50869
|
registerMemoryBackend: () => registerMemoryBackend,
|
|
50603
50870
|
renderMemoryAuditMarkdown: () => renderMemoryAuditMarkdown,
|
|
@@ -50687,6 +50954,7 @@ var init_src = __esm({
|
|
|
50687
50954
|
init_automation();
|
|
50688
50955
|
init_automation_store();
|
|
50689
50956
|
init_run_command();
|
|
50957
|
+
init_fn_binary();
|
|
50690
50958
|
init_node_override_guard();
|
|
50691
50959
|
init_settings_validation();
|
|
50692
50960
|
init_routine();
|
|
@@ -52673,6 +52941,21 @@ function resolveVendoredClaudeCliEntry() {
|
|
|
52673
52941
|
return null;
|
|
52674
52942
|
}
|
|
52675
52943
|
}
|
|
52944
|
+
function resolveVendoredDroidCliEntry() {
|
|
52945
|
+
try {
|
|
52946
|
+
const require_ = createRequire2(import.meta.url);
|
|
52947
|
+
const pkgJsonPath = require_.resolve("@fusion/droid-cli/package.json");
|
|
52948
|
+
const pkgJson = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
|
|
52949
|
+
const extensions = pkgJson.pi?.extensions;
|
|
52950
|
+
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
52951
|
+
const entry = extensions[0];
|
|
52952
|
+
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
52953
|
+
const path2 = resolve11(dirname8(pkgJsonPath), entry);
|
|
52954
|
+
return existsSync20(path2) ? path2 : null;
|
|
52955
|
+
} catch {
|
|
52956
|
+
return null;
|
|
52957
|
+
}
|
|
52958
|
+
}
|
|
52676
52959
|
async function registerExtensionProviders(cwd, modelRegistry) {
|
|
52677
52960
|
try {
|
|
52678
52961
|
const agentDir = getPackageManagerAgentDir();
|
|
@@ -52688,8 +52971,13 @@ async function registerExtensionProviders(cwd, modelRegistry) {
|
|
|
52688
52971
|
[...getEnabledPiExtensionPaths(cwd), ...packageExtensionPaths],
|
|
52689
52972
|
vendoredClaudeCli
|
|
52690
52973
|
);
|
|
52691
|
-
const
|
|
52974
|
+
const vendoredDroidCli = resolveVendoredDroidCliEntry();
|
|
52975
|
+
const doubleReconciledPaths = reconcileDroidCliPaths(
|
|
52692
52976
|
reconciledPaths,
|
|
52977
|
+
vendoredDroidCli
|
|
52978
|
+
);
|
|
52979
|
+
const extensionsResult = await discoverAndLoadExtensions(
|
|
52980
|
+
doubleReconciledPaths,
|
|
52693
52981
|
cwd,
|
|
52694
52982
|
join25(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
|
|
52695
52983
|
);
|
|
@@ -54346,14 +54634,25 @@ async function getAgentMemoryWindow(rootDir, agentMemory, path2, startLine = 1,
|
|
|
54346
54634
|
backend: "agent-memory"
|
|
54347
54635
|
};
|
|
54348
54636
|
}
|
|
54349
|
-
function
|
|
54637
|
+
async function createAgentTask(store, input, options) {
|
|
54638
|
+
const settings = typeof store.getSettings === "function" ? await store.getSettings() : {};
|
|
54639
|
+
const rootDir = options?.rootDir;
|
|
54640
|
+
return store.createTask(input, {
|
|
54641
|
+
settings: { autoSummarizeTitles: settings.autoSummarizeTitles === true },
|
|
54642
|
+
onSummarize: rootDir ? async (description) => {
|
|
54643
|
+
const resolved = resolveTitleSummarizerSettingsModel(settings);
|
|
54644
|
+
return summarizeTitle(description, rootDir, resolved.provider, resolved.modelId);
|
|
54645
|
+
} : void 0
|
|
54646
|
+
});
|
|
54647
|
+
}
|
|
54648
|
+
function createTaskCreateTool(store, provenance, options) {
|
|
54350
54649
|
return {
|
|
54351
54650
|
name: "fn_task_create",
|
|
54352
54651
|
label: "Create Task",
|
|
54353
54652
|
description: "Create a new task for out-of-scope work discovered during execution. The task goes into triage where it will be specified by the AI. Optionally set dependencies (e.g., the new task depends on the current one, or the current task should wait for the new one).",
|
|
54354
54653
|
parameters: taskCreateParams,
|
|
54355
54654
|
execute: async (_id, params) => {
|
|
54356
|
-
const task = await store
|
|
54655
|
+
const task = await createAgentTask(store, {
|
|
54357
54656
|
description: params.description,
|
|
54358
54657
|
dependencies: params.dependencies,
|
|
54359
54658
|
column: "triage",
|
|
@@ -54362,7 +54661,7 @@ function createTaskCreateTool(store, provenance) {
|
|
|
54362
54661
|
sourceAgentId: provenance.sourceAgentId,
|
|
54363
54662
|
sourceRunId: provenance.sourceRunId
|
|
54364
54663
|
} : void 0
|
|
54365
|
-
});
|
|
54664
|
+
}, options);
|
|
54366
54665
|
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
|
54367
54666
|
return {
|
|
54368
54667
|
content: [{
|
|
@@ -54715,7 +55014,7 @@ ${lines.join("\n\n")}` }],
|
|
|
54715
55014
|
}
|
|
54716
55015
|
};
|
|
54717
55016
|
}
|
|
54718
|
-
function createDelegateTaskTool(agentStore, taskStore) {
|
|
55017
|
+
function createDelegateTaskTool(agentStore, taskStore, options) {
|
|
54719
55018
|
return {
|
|
54720
55019
|
name: "fn_delegate_task",
|
|
54721
55020
|
label: "Delegate Task",
|
|
@@ -54735,13 +55034,13 @@ function createDelegateTaskTool(agentStore, taskStore) {
|
|
|
54735
55034
|
details: {}
|
|
54736
55035
|
};
|
|
54737
55036
|
}
|
|
54738
|
-
const task = await taskStore
|
|
55037
|
+
const task = await createAgentTask(taskStore, {
|
|
54739
55038
|
description: params.description,
|
|
54740
55039
|
dependencies: params.dependencies,
|
|
54741
55040
|
column: "todo",
|
|
54742
55041
|
assignedAgentId: params.agent_id,
|
|
54743
55042
|
source: { sourceType: "api" }
|
|
54744
|
-
});
|
|
55043
|
+
}, options);
|
|
54745
55044
|
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
|
54746
55045
|
return {
|
|
54747
55046
|
content: [{
|
|
@@ -57568,7 +57867,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
57568
57867
|
// Agent delegation tools — discover and delegate work to other agents.
|
|
57569
57868
|
...this.options.agentStore ? [
|
|
57570
57869
|
createListAgentsTool(this.options.agentStore),
|
|
57571
|
-
createDelegateTaskTool(this.options.agentStore, this.store)
|
|
57870
|
+
createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir })
|
|
57572
57871
|
] : [],
|
|
57573
57872
|
this.createReviewSpecTool(
|
|
57574
57873
|
task.id,
|
|
@@ -58107,7 +58406,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
58107
58406
|
planLog.warn(`${options.parentTaskId}: failed to load parent task for fn_task_create inheritance: ${msg}`);
|
|
58108
58407
|
parentTask = void 0;
|
|
58109
58408
|
}
|
|
58110
|
-
const newTask = await store
|
|
58409
|
+
const newTask = await createAgentTask(store, {
|
|
58111
58410
|
title: params.title,
|
|
58112
58411
|
description: params.description,
|
|
58113
58412
|
dependencies: validDeps,
|
|
@@ -58121,7 +58420,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
58121
58420
|
sourceType: "agent_heartbeat",
|
|
58122
58421
|
sourceParentTaskId: options.parentTaskId
|
|
58123
58422
|
}
|
|
58124
|
-
});
|
|
58423
|
+
}, { rootDir: this.rootDir });
|
|
58125
58424
|
options.createdSubtasksRef.current.push(newTask.id);
|
|
58126
58425
|
return {
|
|
58127
58426
|
content: [
|
|
@@ -58549,7 +58848,7 @@ var init_run_audit = __esm({
|
|
|
58549
58848
|
});
|
|
58550
58849
|
|
|
58551
58850
|
// ../engine/src/merger.ts
|
|
58552
|
-
import { execSync, exec as exec2, spawn as
|
|
58851
|
+
import { execSync, exec as exec2, spawn as spawn3 } from "node:child_process";
|
|
58553
58852
|
import { promisify as promisify3 } from "node:util";
|
|
58554
58853
|
import { existsSync as existsSync22 } from "node:fs";
|
|
58555
58854
|
import { join as join29 } from "node:path";
|
|
@@ -58564,7 +58863,7 @@ async function execWithProcessGroup(command, options) {
|
|
|
58564
58863
|
return;
|
|
58565
58864
|
}
|
|
58566
58865
|
const useProcessGroup = process.platform !== "win32";
|
|
58567
|
-
const child =
|
|
58866
|
+
const child = spawn3(command, {
|
|
58568
58867
|
cwd: options.cwd,
|
|
58569
58868
|
shell: true,
|
|
58570
58869
|
detached: useProcessGroup,
|
|
@@ -63300,10 +63599,10 @@ var init_step_session_executor = __esm({
|
|
|
63300
63599
|
] : [];
|
|
63301
63600
|
const memoryTools = createMemoryTools(this.options.rootDir, settings);
|
|
63302
63601
|
const taskLogTool = this.options.store ? [createTaskLogTool(this.options.store, taskDetail.id)] : [];
|
|
63303
|
-
const taskCreateTool = this.options.store ? [createTaskCreateTool(this.options.store)] : [];
|
|
63602
|
+
const taskCreateTool = this.options.store ? [createTaskCreateTool(this.options.store, void 0, { rootDir: this.options.rootDir })] : [];
|
|
63304
63603
|
const delegationTools = this.options.agentStore ? [
|
|
63305
63604
|
createListAgentsTool(this.options.agentStore),
|
|
63306
|
-
createDelegateTaskTool(this.options.agentStore, this.options.store)
|
|
63605
|
+
createDelegateTaskTool(this.options.agentStore, this.options.store, { rootDir: this.options.rootDir })
|
|
63307
63606
|
] : [];
|
|
63308
63607
|
const messagingTools = this.options.messageStore && taskDetail.assignedAgentId ? [
|
|
63309
63608
|
createSendMessageTool(this.options.messageStore, taskDetail.assignedAgentId),
|
|
@@ -63714,7 +64013,7 @@ var init_task_completion = __esm({
|
|
|
63714
64013
|
});
|
|
63715
64014
|
|
|
63716
64015
|
// ../engine/src/run-verification-tool.ts
|
|
63717
|
-
import { spawn as
|
|
64016
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
63718
64017
|
import { existsSync as existsSync26 } from "node:fs";
|
|
63719
64018
|
import { isAbsolute as isAbsolute10, join as join34 } from "node:path";
|
|
63720
64019
|
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
@@ -63747,7 +64046,7 @@ async function runVerificationCommand2(opts) {
|
|
|
63747
64046
|
const stdoutBuf = { head: "", tail: "", totalBytes: 0 };
|
|
63748
64047
|
const stderrBuf = { head: "", tail: "", totalBytes: 0 };
|
|
63749
64048
|
return new Promise((resolve19) => {
|
|
63750
|
-
const child =
|
|
64049
|
+
const child = spawn4(command, {
|
|
63751
64050
|
cwd,
|
|
63752
64051
|
stdio: ["ignore", "pipe", "pipe"],
|
|
63753
64052
|
env: { ...process.env },
|
|
@@ -66035,7 +66334,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66035
66334
|
// Agent delegation tools — discover and delegate work to other agents.
|
|
66036
66335
|
...this.options.agentStore ? [
|
|
66037
66336
|
createListAgentsTool(this.options.agentStore),
|
|
66038
|
-
createDelegateTaskTool(this.options.agentStore, this.store)
|
|
66337
|
+
createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir })
|
|
66039
66338
|
] : [],
|
|
66040
66339
|
// Messaging tools — allows executor agents to send and receive messages.
|
|
66041
66340
|
...this.options.messageStore && assignedAgentId ? [
|
|
@@ -66729,7 +67028,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66729
67028
|
return createTaskLogTool(this.store, taskId);
|
|
66730
67029
|
}
|
|
66731
67030
|
createTaskCreateTool() {
|
|
66732
|
-
return createTaskCreateTool(this.store, { sourceType: "api" });
|
|
67031
|
+
return createTaskCreateTool(this.store, { sourceType: "api" }, { rootDir: this.rootDir });
|
|
66733
67032
|
}
|
|
66734
67033
|
createTaskDocumentWriteTool(taskId) {
|
|
66735
67034
|
return createTaskDocumentWriteTool(this.store, taskId);
|
|
@@ -71664,7 +71963,7 @@ function isTickableState(state) {
|
|
|
71664
71963
|
function isHeartbeatManaged(agent) {
|
|
71665
71964
|
return !isEphemeralAgent(agent);
|
|
71666
71965
|
}
|
|
71667
|
-
var HEARTBEAT_SYSTEM_PROMPT, HEARTBEAT_NO_TASK_SYSTEM_PROMPT, HEARTBEAT_PROCEDURE, heartbeatDoneParams, HeartbeatMonitor, OVERDUE_FIRE_JITTER_MS, HeartbeatTriggerScheduler;
|
|
71966
|
+
var HEARTBEAT_SYSTEM_PROMPT, HEARTBEAT_NO_TASK_SYSTEM_PROMPT, HEARTBEAT_PROCEDURE, HEARTBEAT_NO_TASK_PROCEDURE, heartbeatDoneParams, HeartbeatMonitor, OVERDUE_FIRE_JITTER_MS, HeartbeatTriggerScheduler;
|
|
71668
71967
|
var init_agent_heartbeat = __esm({
|
|
71669
71968
|
"../engine/src/agent-heartbeat.ts"() {
|
|
71670
71969
|
"use strict";
|
|
@@ -71854,6 +72153,32 @@ When sending messages:
|
|
|
71854
72153
|
Critical: a heartbeat without observable progress (a log, a document write, a
|
|
71855
72154
|
status change, a comment, a delegation, or an explicit "no-op with reason") is
|
|
71856
72155
|
a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
72156
|
+
HEARTBEAT_NO_TASK_PROCEDURE = `## Heartbeat Procedure (run every tick, in order)
|
|
72157
|
+
|
|
72158
|
+
1. **Identity & context** \u2014 review the **Identity Snapshot** at the top of
|
|
72159
|
+
this prompt. Confirm your role, soul, instructions, and memory match what
|
|
72160
|
+
you expect, and surface any anomalies in your first text output before
|
|
72161
|
+
doing anything else. (If fn_identity is available in your runtime you may
|
|
72162
|
+
also call it for full structured detail; the snapshot above is the
|
|
72163
|
+
authoritative source.)
|
|
72164
|
+
2. **Inbox** \u2014 when fn_read_messages is available, call it. Process any pending
|
|
72165
|
+
messages first; reply with reply_to_message_id when answering.
|
|
72166
|
+
3. **Wake delta** \u2014 read the Wake Delta block above. The wake reason is the
|
|
72167
|
+
highest-priority change for this heartbeat. If you were woken by a comment
|
|
72168
|
+
or a message, acknowledge it before doing anything else.
|
|
72169
|
+
4. **Ambient review** \u2014 since you have no assigned task, review board/project
|
|
72170
|
+
signals and recent memory context before acting.
|
|
72171
|
+
5. **Pick the next concrete action** \u2014 exactly ONE useful action this heartbeat:
|
|
72172
|
+
create a focused task, delegate work, send/reply to a message, or append
|
|
72173
|
+
durable memory.
|
|
72174
|
+
6. **Persist progress** \u2014 use available ambient tools only:
|
|
72175
|
+
fn_task_create, fn_delegate_task, fn_send_message, fn_memory_append.
|
|
72176
|
+
7. **Exit** \u2014 call fn_heartbeat_done with a one-line summary of what changed
|
|
72177
|
+
this tick. If you took no action, say so and explain why.
|
|
72178
|
+
|
|
72179
|
+
Critical: a heartbeat without observable progress (a created task, delegation,
|
|
72180
|
+
message reply, memory append, or explicit "no-op with reason") is a bug. Do
|
|
72181
|
+
not loop on the same plan across heartbeats without recording why.`;
|
|
71857
72182
|
heartbeatDoneParams = Type6.Object({
|
|
71858
72183
|
summary: Type6.Optional(Type6.String({ description: "Summary of what was accomplished this heartbeat" }))
|
|
71859
72184
|
});
|
|
@@ -71893,13 +72218,6 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
71893
72218
|
this.rootDir = options.rootDir;
|
|
71894
72219
|
this.messageStore = options.messageStore;
|
|
71895
72220
|
this.pluginRunner = options.pluginRunner;
|
|
71896
|
-
this.onRecovered = options.onRecovered;
|
|
71897
|
-
this.onTerminated = options.onTerminated;
|
|
71898
|
-
this.onRunStarted = options.onRunStarted;
|
|
71899
|
-
this.onRunCompleted = options.onRunCompleted;
|
|
71900
|
-
this.taskStore = options.taskStore;
|
|
71901
|
-
this.rootDir = options.rootDir;
|
|
71902
|
-
this.messageStore = options.messageStore;
|
|
71903
72221
|
}
|
|
71904
72222
|
/**
|
|
71905
72223
|
* Start the heartbeat monitoring loop.
|
|
@@ -72598,9 +72916,9 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72598
72916
|
sourceType: "agent_heartbeat",
|
|
72599
72917
|
sourceAgentId: agentId,
|
|
72600
72918
|
sourceRunId: runContext?.runId
|
|
72601
|
-
}));
|
|
72919
|
+
}, { rootDir: this.rootDir }));
|
|
72602
72920
|
heartbeatTools.push(createListAgentsTool(this.store));
|
|
72603
|
-
heartbeatTools.push(createDelegateTaskTool(this.store, taskStore));
|
|
72921
|
+
heartbeatTools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
|
|
72604
72922
|
if (this.messageStore) {
|
|
72605
72923
|
heartbeatTools.push(createSendMessageTool(this.messageStore, agentId));
|
|
72606
72924
|
heartbeatTools.push(createReadMessagesTool(this.messageStore, agentId));
|
|
@@ -72623,22 +72941,27 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72623
72941
|
heartbeatLog.warn(`Failed to configure heartbeat memory tools for ${agentId}: ${message}`);
|
|
72624
72942
|
}
|
|
72625
72943
|
const skillContext = buildSessionSkillContextSync2(agent, "heartbeat", rootDir);
|
|
72626
|
-
|
|
72627
|
-
const baseHeartbeatSystemPrompt = systemPrompt;
|
|
72944
|
+
const baseHeartbeatSystemPrompt = isNoTaskRun ? HEARTBEAT_NO_TASK_SYSTEM_PROMPT : HEARTBEAT_SYSTEM_PROMPT;
|
|
72628
72945
|
let resolvedInstructionsForIdentity = "";
|
|
72629
72946
|
try {
|
|
72630
|
-
|
|
72631
|
-
resolvedInstructionsForIdentity = agentInstructions;
|
|
72632
|
-
const memoryInstructions = memorySettings?.memoryEnabled === false ? "" : buildExecutionMemoryInstructions(rootDir, memorySettings);
|
|
72633
|
-
systemPrompt = buildSystemPromptWithInstructions(
|
|
72634
|
-
baseHeartbeatSystemPrompt,
|
|
72635
|
-
[agentInstructions, memoryInstructions].filter((part) => part.trim()).join("\n\n")
|
|
72636
|
-
);
|
|
72947
|
+
resolvedInstructionsForIdentity = await resolveAgentInstructionsWithRatings(agent, rootDir, this.store);
|
|
72637
72948
|
} catch (instructionError) {
|
|
72638
|
-
systemPrompt = baseHeartbeatSystemPrompt;
|
|
72639
72949
|
const message = instructionError instanceof Error ? instructionError.message : String(instructionError);
|
|
72640
|
-
heartbeatLog.warn(`Failed to
|
|
72950
|
+
heartbeatLog.warn(`Failed to resolve agent instructions for heartbeat ${agentId}: ${message}`);
|
|
72951
|
+
}
|
|
72952
|
+
let memoryInstructions = "";
|
|
72953
|
+
if (memorySettings?.memoryEnabled !== false) {
|
|
72954
|
+
try {
|
|
72955
|
+
memoryInstructions = buildExecutionMemoryInstructions(rootDir, memorySettings);
|
|
72956
|
+
} catch (memoryInstructionErr) {
|
|
72957
|
+
const message = memoryInstructionErr instanceof Error ? memoryInstructionErr.message : String(memoryInstructionErr);
|
|
72958
|
+
heartbeatLog.warn(`Failed to resolve project memory instructions for heartbeat ${agentId}: ${message}`);
|
|
72959
|
+
}
|
|
72641
72960
|
}
|
|
72961
|
+
const systemPrompt = buildSystemPromptWithInstructions(
|
|
72962
|
+
baseHeartbeatSystemPrompt,
|
|
72963
|
+
[resolvedInstructionsForIdentity, memoryInstructions].filter((part) => part.trim()).join("\n\n")
|
|
72964
|
+
);
|
|
72642
72965
|
heartbeatTools.push(createIdentityTool({ agent, resolvedInstructions: resolvedInstructionsForIdentity }));
|
|
72643
72966
|
heartbeatTools.push(heartbeatDoneTool);
|
|
72644
72967
|
if (isNoTaskRun) {
|
|
@@ -72699,7 +73022,7 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72699
73022
|
};
|
|
72700
73023
|
const wakeReason = deriveWakeReason();
|
|
72701
73024
|
const customProcedure = await resolveAgentHeartbeatProcedure(agent, rootDir);
|
|
72702
|
-
const heartbeatProcedureText = customProcedure ?? HEARTBEAT_PROCEDURE;
|
|
73025
|
+
const heartbeatProcedureText = customProcedure ?? (isNoTaskRun ? HEARTBEAT_NO_TASK_PROCEDURE : HEARTBEAT_PROCEDURE);
|
|
72703
73026
|
if (isNoTaskRun) {
|
|
72704
73027
|
if (this.messageStore) {
|
|
72705
73028
|
try {
|
|
@@ -72732,6 +73055,8 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72732
73055
|
`- pending messages: ${pendingMessages.length}`,
|
|
72733
73056
|
"",
|
|
72734
73057
|
"Treat this wake delta as the highest-priority change for this heartbeat.",
|
|
73058
|
+
"This is an autonomous heartbeat run (manual or automatic): re-anchor on",
|
|
73059
|
+
"identity, process wake context, then complete ONE concrete action.",
|
|
72735
73060
|
"Run the Heartbeat Procedure (below) before doing anything else \u2014 even a",
|
|
72736
73061
|
"timer-only wake should re-check messages, memory, and project state.",
|
|
72737
73062
|
"",
|
|
@@ -72823,6 +73148,8 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72823
73148
|
`- triggering comments: ${effectiveTriggeringCommentIds?.length ?? 0}`,
|
|
72824
73149
|
"",
|
|
72825
73150
|
"Treat this wake delta as the highest-priority change for this heartbeat.",
|
|
73151
|
+
"This is an autonomous heartbeat run (manual or automatic): re-anchor on",
|
|
73152
|
+
"identity, process wake context, then complete ONE concrete action.",
|
|
72826
73153
|
"Before resuming prior task work, run the Heartbeat Procedure (below) and",
|
|
72827
73154
|
"decide what action this delta requires. Your assigned task is one input",
|
|
72828
73155
|
"to the procedure \u2014 not the only thing to consider.",
|
|
@@ -72982,7 +73309,7 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
|
|
|
72982
73309
|
const baseCreateTool = createTaskCreateTool(taskStore, {
|
|
72983
73310
|
sourceType: "agent_heartbeat",
|
|
72984
73311
|
sourceAgentId: agentId
|
|
72985
|
-
});
|
|
73312
|
+
}, { rootDir: this.rootDir });
|
|
72986
73313
|
const trackedCreateTool = {
|
|
72987
73314
|
...baseCreateTool,
|
|
72988
73315
|
execute: async (id, params, signal, onUpdate, ctx) => {
|
|
@@ -73009,7 +73336,7 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
|
|
|
73009
73336
|
tools.push(createTaskDocumentWriteTool(taskStore, taskId));
|
|
73010
73337
|
tools.push(createTaskDocumentReadTool(taskStore, taskId));
|
|
73011
73338
|
tools.push(createListAgentsTool(this.store));
|
|
73012
|
-
tools.push(createDelegateTaskTool(this.store, taskStore));
|
|
73339
|
+
tools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
|
|
73013
73340
|
if (messageStore) {
|
|
73014
73341
|
tools.push(createSendMessageTool(messageStore, agentId));
|
|
73015
73342
|
tools.push(createReadMessagesTool(messageStore, agentId));
|
|
@@ -80711,7 +81038,7 @@ var init_provider_adapters = __esm({
|
|
|
80711
81038
|
|
|
80712
81039
|
// ../engine/src/remote-access/tunnel-process-manager.ts
|
|
80713
81040
|
import { EventEmitter as EventEmitter23 } from "node:events";
|
|
80714
|
-
import { exec as exec9, execFile as execFile3, spawn as
|
|
81041
|
+
import { exec as exec9, execFile as execFile3, spawn as spawn5 } from "node:child_process";
|
|
80715
81042
|
import { promisify as promisify9 } from "node:util";
|
|
80716
81043
|
function nowIso() {
|
|
80717
81044
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -80802,7 +81129,7 @@ var init_tunnel_process_manager = __esm({
|
|
|
80802
81129
|
super();
|
|
80803
81130
|
this.maxLogEntries = options.maxLogEntries ?? DEFAULT_MAX_LOG_ENTRIES;
|
|
80804
81131
|
this.defaultStopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS2;
|
|
80805
|
-
this.spawnImpl = options.spawnImpl ??
|
|
81132
|
+
this.spawnImpl = options.spawnImpl ?? spawn5;
|
|
80806
81133
|
}
|
|
80807
81134
|
getStatus() {
|
|
80808
81135
|
return { ...this.status, lastError: this.status.lastError ? { ...this.status.lastError } : null };
|
|
@@ -81176,6 +81503,7 @@ var execFileAsync2, MERGE_HANDOFF_GRACE_MS, isRemoteActive, ProjectEngine;
|
|
|
81176
81503
|
var init_project_engine = __esm({
|
|
81177
81504
|
"../engine/src/project-engine.ts"() {
|
|
81178
81505
|
"use strict";
|
|
81506
|
+
init_src();
|
|
81179
81507
|
init_in_process_runtime();
|
|
81180
81508
|
init_pr_monitor();
|
|
81181
81509
|
init_pr_comment_handler();
|
|
@@ -81870,6 +82198,48 @@ ${detail}`
|
|
|
81870
82198
|
if (task.status === "failed") return false;
|
|
81871
82199
|
return (task.mergeRetries ?? 0) < _ProjectEngine.MAX_AUTO_MERGE_RETRIES || this.hasAutoHealableVerificationBufferFailure(task) || this.isRetryCooldownElapsed(task);
|
|
81872
82200
|
}
|
|
82201
|
+
/**
|
|
82202
|
+
* Remove and return the highest-priority taskId from the merge queue.
|
|
82203
|
+
* Ordering: priority (urgent→low), then createdAt ASC, then id ASC — matching
|
|
82204
|
+
* the triage and scheduler comparators. Manual merges (onMerge resolvers) are
|
|
82205
|
+
* preferred over auto-merges so awaited callers aren't starved by a flood of
|
|
82206
|
+
* higher-priority auto-enqueues. IDs whose tasks can't be loaded fall back to
|
|
82207
|
+
* FIFO order so they still drain.
|
|
82208
|
+
*/
|
|
82209
|
+
async pickNextMergeTaskId(store) {
|
|
82210
|
+
if (this.mergeQueue.length === 0) return void 0;
|
|
82211
|
+
if (this.mergeQueue.length === 1) {
|
|
82212
|
+
return this.mergeQueue.shift();
|
|
82213
|
+
}
|
|
82214
|
+
const queueSnapshot = [...this.mergeQueue];
|
|
82215
|
+
const entries = [];
|
|
82216
|
+
for (let i = 0; i < queueSnapshot.length; i++) {
|
|
82217
|
+
const taskId = queueSnapshot[i];
|
|
82218
|
+
const task = await store.getTask(taskId).catch(() => void 0);
|
|
82219
|
+
entries.push({
|
|
82220
|
+
taskId,
|
|
82221
|
+
task,
|
|
82222
|
+
manual: this.manualMergeResolvers.has(taskId),
|
|
82223
|
+
order: i
|
|
82224
|
+
});
|
|
82225
|
+
}
|
|
82226
|
+
if (this.shuttingDown) return void 0;
|
|
82227
|
+
entries.sort((a, b) => {
|
|
82228
|
+
if (a.manual !== b.manual) return a.manual ? -1 : 1;
|
|
82229
|
+
if (a.task && b.task) return compareTasksByPriorityThenAgeAndId(a.task, b.task);
|
|
82230
|
+
if (a.task) return -1;
|
|
82231
|
+
if (b.task) return 1;
|
|
82232
|
+
return a.order - b.order;
|
|
82233
|
+
});
|
|
82234
|
+
for (const entry of entries) {
|
|
82235
|
+
const liveIndex = this.mergeQueue.indexOf(entry.taskId);
|
|
82236
|
+
if (liveIndex !== -1) {
|
|
82237
|
+
this.mergeQueue.splice(liveIndex, 1);
|
|
82238
|
+
return entry.taskId;
|
|
82239
|
+
}
|
|
82240
|
+
}
|
|
82241
|
+
return void 0;
|
|
82242
|
+
}
|
|
81873
82243
|
internalEnqueueMerge(taskId) {
|
|
81874
82244
|
if (this.shuttingDown) return;
|
|
81875
82245
|
if (this.mergeActive.has(taskId)) return;
|
|
@@ -81877,6 +82247,23 @@ ${detail}`
|
|
|
81877
82247
|
this.mergeQueue.push(taskId);
|
|
81878
82248
|
void this.drainMergeQueue();
|
|
81879
82249
|
}
|
|
82250
|
+
/**
|
|
82251
|
+
* Filter a sweep's listTasks() result to merge-eligible tasks, sort by
|
|
82252
|
+
* priority (urgent → low, then createdAt ASC, then id ASC), and enqueue.
|
|
82253
|
+
* Sorting before enqueue matters because each enqueue may immediately
|
|
82254
|
+
* trigger drainMergeQueue's single-item fast path, so the first task
|
|
82255
|
+
* pushed wins. listTasks returns createdAt ASC — without this sort an
|
|
82256
|
+
* older low-priority task would start before a later urgent one.
|
|
82257
|
+
*/
|
|
82258
|
+
enqueueEligibleInReviewTasks(tasks) {
|
|
82259
|
+
const eligible = sortTasksByPriorityThenAgeAndId(
|
|
82260
|
+
tasks.filter((t) => !t.paused && this.canMergeTask(t))
|
|
82261
|
+
);
|
|
82262
|
+
for (const t of eligible) {
|
|
82263
|
+
this.internalEnqueueMerge(t.id);
|
|
82264
|
+
}
|
|
82265
|
+
return eligible.length;
|
|
82266
|
+
}
|
|
81880
82267
|
async drainMergeQueue() {
|
|
81881
82268
|
if (this.mergeRunning) return;
|
|
81882
82269
|
this.mergeRunning = true;
|
|
@@ -81884,7 +82271,9 @@ ${detail}`
|
|
|
81884
82271
|
const store = this.runtime.getTaskStore();
|
|
81885
82272
|
const cwd = this.config.workingDirectory;
|
|
81886
82273
|
while (this.mergeQueue.length > 0 && !this.shuttingDown) {
|
|
81887
|
-
const taskId = this.
|
|
82274
|
+
const taskId = await this.pickNextMergeTaskId(store);
|
|
82275
|
+
if (!taskId) break;
|
|
82276
|
+
if (this.shuttingDown) break;
|
|
81888
82277
|
const manualResolver = this.manualMergeResolvers.get(taskId);
|
|
81889
82278
|
try {
|
|
81890
82279
|
if (!manualResolver) {
|
|
@@ -82354,12 +82743,9 @@ ${detail}`
|
|
|
82354
82743
|
}
|
|
82355
82744
|
const settings = await store.getSettings();
|
|
82356
82745
|
if (!settings.autoMerge) return;
|
|
82357
|
-
const
|
|
82358
|
-
if (
|
|
82359
|
-
runtimeLog.log(`Auto-merge startup sweep: enqueueing ${
|
|
82360
|
-
for (const t of eligible) {
|
|
82361
|
-
this.internalEnqueueMerge(t.id);
|
|
82362
|
-
}
|
|
82746
|
+
const enqueued = this.enqueueEligibleInReviewTasks(tasks);
|
|
82747
|
+
if (enqueued > 0) {
|
|
82748
|
+
runtimeLog.log(`Auto-merge startup sweep: enqueueing ${enqueued} task(s)`);
|
|
82363
82749
|
}
|
|
82364
82750
|
} catch (err) {
|
|
82365
82751
|
runtimeLog.warn(
|
|
@@ -82375,14 +82761,7 @@ ${detail}`
|
|
|
82375
82761
|
const settings = await store.getSettings();
|
|
82376
82762
|
if (!settings.globalPause && !settings.enginePaused && settings.autoMerge) {
|
|
82377
82763
|
const tasks = await store.listTasks({ column: "in-review" });
|
|
82378
|
-
|
|
82379
|
-
if (t.paused) {
|
|
82380
|
-
continue;
|
|
82381
|
-
}
|
|
82382
|
-
if (this.canMergeTask(t)) {
|
|
82383
|
-
this.internalEnqueueMerge(t.id);
|
|
82384
|
-
}
|
|
82385
|
-
}
|
|
82764
|
+
this.enqueueEligibleInReviewTasks(tasks);
|
|
82386
82765
|
}
|
|
82387
82766
|
} catch (err) {
|
|
82388
82767
|
runtimeLog.warn(
|
|
@@ -82438,14 +82817,7 @@ ${detail}`
|
|
|
82438
82817
|
if (s.autoMerge) {
|
|
82439
82818
|
try {
|
|
82440
82819
|
const tasks = await store.listTasks({ column: "in-review" });
|
|
82441
|
-
|
|
82442
|
-
if (t.paused) {
|
|
82443
|
-
continue;
|
|
82444
|
-
}
|
|
82445
|
-
if (this.canMergeTask(t)) {
|
|
82446
|
-
this.internalEnqueueMerge(t.id);
|
|
82447
|
-
}
|
|
82448
|
-
}
|
|
82820
|
+
this.enqueueEligibleInReviewTasks(tasks);
|
|
82449
82821
|
} catch (err) {
|
|
82450
82822
|
runtimeLog.warn(
|
|
82451
82823
|
`Global unpause: failed to scan in-review tasks for auto-merge: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -82475,14 +82847,7 @@ ${detail}`
|
|
|
82475
82847
|
if (s.autoMerge) {
|
|
82476
82848
|
try {
|
|
82477
82849
|
const tasks = await store.listTasks({ column: "in-review" });
|
|
82478
|
-
|
|
82479
|
-
if (t.paused) {
|
|
82480
|
-
continue;
|
|
82481
|
-
}
|
|
82482
|
-
if (this.canMergeTask(t)) {
|
|
82483
|
-
this.internalEnqueueMerge(t.id);
|
|
82484
|
-
}
|
|
82485
|
-
}
|
|
82850
|
+
this.enqueueEligibleInReviewTasks(tasks);
|
|
82486
82851
|
} catch (err) {
|
|
82487
82852
|
runtimeLog.warn(
|
|
82488
82853
|
`Engine unpause: failed to scan in-review tasks for auto-merge: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -83075,7 +83440,7 @@ var init_peer_exchange_service = __esm({
|
|
|
83075
83440
|
syncIntervalMs;
|
|
83076
83441
|
interval = null;
|
|
83077
83442
|
activeSync = null;
|
|
83078
|
-
|
|
83443
|
+
running = false;
|
|
83079
83444
|
/** Whether settings sync is enabled. Default: false. */
|
|
83080
83445
|
settingsSyncEnabled;
|
|
83081
83446
|
/** Minimum interval between settings syncs with the same node in ms. Default: 5 minutes. */
|
|
@@ -83117,10 +83482,11 @@ var init_peer_exchange_service = __esm({
|
|
|
83117
83482
|
* Begins periodic gossip with all online remote nodes.
|
|
83118
83483
|
*/
|
|
83119
83484
|
start() {
|
|
83120
|
-
if (this.
|
|
83121
|
-
peerExchangeLog.
|
|
83485
|
+
if (this.running) {
|
|
83486
|
+
peerExchangeLog.log("Peer exchange service already running");
|
|
83122
83487
|
return;
|
|
83123
83488
|
}
|
|
83489
|
+
this.running = true;
|
|
83124
83490
|
this.centralCore.listNodes().then((nodes) => {
|
|
83125
83491
|
const onlineRemoteCount = nodes.filter(
|
|
83126
83492
|
(n) => n.type === "remote" && n.status === "online" && n.url
|
|
@@ -83130,6 +83496,7 @@ var init_peer_exchange_service = __esm({
|
|
|
83130
83496
|
peerExchangeLog.warn(`Failed to get initial peer count: ${err}`);
|
|
83131
83497
|
});
|
|
83132
83498
|
this.interval = setInterval(() => {
|
|
83499
|
+
if (!this.running) return;
|
|
83133
83500
|
void this.syncWithAllPeers();
|
|
83134
83501
|
}, this.syncIntervalMs);
|
|
83135
83502
|
}
|
|
@@ -83137,12 +83504,21 @@ var init_peer_exchange_service = __esm({
|
|
|
83137
83504
|
* Stop the peer exchange service.
|
|
83138
83505
|
* Clears the sync interval and prevents further syncs.
|
|
83139
83506
|
*/
|
|
83140
|
-
stop() {
|
|
83507
|
+
async stop() {
|
|
83508
|
+
if (!this.running) {
|
|
83509
|
+
return;
|
|
83510
|
+
}
|
|
83511
|
+
this.running = false;
|
|
83141
83512
|
if (this.interval) {
|
|
83142
83513
|
clearInterval(this.interval);
|
|
83143
83514
|
this.interval = null;
|
|
83144
83515
|
}
|
|
83145
|
-
this.
|
|
83516
|
+
if (this.activeSync) {
|
|
83517
|
+
try {
|
|
83518
|
+
await this.activeSync;
|
|
83519
|
+
} catch {
|
|
83520
|
+
}
|
|
83521
|
+
}
|
|
83146
83522
|
peerExchangeLog.log("Stopped peer exchange service");
|
|
83147
83523
|
}
|
|
83148
83524
|
/**
|
|
@@ -83696,25 +84072,21 @@ async function ensureNtfyHelpersReady() {
|
|
|
83696
84072
|
if (planningNtfyHelpers) {
|
|
83697
84073
|
return;
|
|
83698
84074
|
}
|
|
83699
|
-
|
|
83700
|
-
|
|
83701
|
-
|
|
83702
|
-
|
|
83703
|
-
|
|
83704
|
-
|
|
83705
|
-
|
|
83706
|
-
|
|
83707
|
-
|
|
83708
|
-
|
|
83709
|
-
|
|
83710
|
-
|
|
83711
|
-
|
|
83712
|
-
|
|
83713
|
-
|
|
83714
|
-
{ operation: "notification-service-detection" }
|
|
83715
|
-
);
|
|
83716
|
-
}
|
|
83717
|
-
} catch {
|
|
84075
|
+
const hasNotificationService = "NotificationService" in src_exports2 && typeof NotificationService === "function";
|
|
84076
|
+
const hasAllHelpers = "isNtfyEventEnabled" in src_exports2 && "buildNtfyClickUrl" in src_exports2 && "sendNtfyNotification" in src_exports2 && typeof isNtfyEventEnabled === "function" && typeof buildNtfyClickUrl === "function" && typeof sendNtfyNotification === "function";
|
|
84077
|
+
if (!hasAllHelpers) {
|
|
84078
|
+
return;
|
|
84079
|
+
}
|
|
84080
|
+
planningNtfyHelpers = {
|
|
84081
|
+
isNtfyEventEnabled,
|
|
84082
|
+
buildNtfyClickUrl,
|
|
84083
|
+
sendNtfyNotification
|
|
84084
|
+
};
|
|
84085
|
+
if (hasNotificationService) {
|
|
84086
|
+
diagnostics.info(
|
|
84087
|
+
"NotificationService abstraction detected in engine",
|
|
84088
|
+
{ operation: "notification-service-detection" }
|
|
84089
|
+
);
|
|
83718
84090
|
}
|
|
83719
84091
|
}
|
|
83720
84092
|
function safeParseJson(text, fallback, options) {
|
|
@@ -84504,6 +84876,7 @@ var init_planning = __esm({
|
|
|
84504
84876
|
init_sse_buffer();
|
|
84505
84877
|
init_ai_session_diagnostics();
|
|
84506
84878
|
init_src2();
|
|
84879
|
+
init_src2();
|
|
84507
84880
|
createFnAgent4 = createFnAgent2;
|
|
84508
84881
|
diagnostics = createSessionDiagnostics("planning");
|
|
84509
84882
|
PLANNING_SYSTEM_PROMPT = `You are a planning assistant for the fn task board system.
|
|
@@ -84565,7 +84938,7 @@ For completion:
|
|
|
84565
84938
|
}`;
|
|
84566
84939
|
SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
84567
84940
|
CLEANUP_INTERVAL_MS2 = 5 * 60 * 1e3;
|
|
84568
|
-
MAX_SESSIONS_PER_IP_PER_HOUR =
|
|
84941
|
+
MAX_SESSIONS_PER_IP_PER_HOUR = 1e3;
|
|
84569
84942
|
RATE_LIMIT_WINDOW_MS2 = 60 * 60 * 1e3;
|
|
84570
84943
|
GENERATION_TIMEOUT_MS = 12e4;
|
|
84571
84944
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -85188,7 +85561,7 @@ var init_src3 = __esm({
|
|
|
85188
85561
|
});
|
|
85189
85562
|
|
|
85190
85563
|
// ../../plugins/fusion-plugin-hermes-runtime/dist/cli-spawn.js
|
|
85191
|
-
import { spawn as
|
|
85564
|
+
import { spawn as spawn6, spawnSync } from "node:child_process";
|
|
85192
85565
|
import os2 from "node:os";
|
|
85193
85566
|
import path, { sep as PATH_SEP } from "node:path";
|
|
85194
85567
|
function resolveBinaryForSpawn(binary) {
|
|
@@ -85298,7 +85671,7 @@ async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
|
|
|
85298
85671
|
if (settings.profile) {
|
|
85299
85672
|
spawnEnv.HERMES_HOME = hermesProfileHome(settings.profile);
|
|
85300
85673
|
}
|
|
85301
|
-
const child =
|
|
85674
|
+
const child = spawn6(binary, args, {
|
|
85302
85675
|
stdio: ["ignore", "pipe", "pipe"],
|
|
85303
85676
|
env: spawnEnv
|
|
85304
85677
|
});
|
|
@@ -85511,7 +85884,7 @@ var init_dist = __esm({
|
|
|
85511
85884
|
});
|
|
85512
85885
|
|
|
85513
85886
|
// ../../plugins/fusion-plugin-openclaw-runtime/dist/pi-module.js
|
|
85514
|
-
import { spawn as
|
|
85887
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
85515
85888
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
85516
85889
|
function asString(v) {
|
|
85517
85890
|
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
@@ -85588,7 +85961,7 @@ async function promptCli(session, message, config, callbacks, signal) {
|
|
|
85588
85961
|
cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
|
|
85589
85962
|
return new Promise((resolve19, reject) => {
|
|
85590
85963
|
let settled = false;
|
|
85591
|
-
const child =
|
|
85964
|
+
const child = spawn7(config.binaryPath, args, {
|
|
85592
85965
|
stdio: ["ignore", "pipe", "pipe"]
|
|
85593
85966
|
});
|
|
85594
85967
|
const hardKill = setTimeout(() => {
|
|
@@ -85739,7 +86112,7 @@ var init_runtime_adapter2 = __esm({
|
|
|
85739
86112
|
});
|
|
85740
86113
|
|
|
85741
86114
|
// ../../plugins/fusion-plugin-openclaw-runtime/dist/probe.js
|
|
85742
|
-
import { spawn as
|
|
86115
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
85743
86116
|
async function probeOpenClawBinary(opts = {}) {
|
|
85744
86117
|
const startedAt = Date.now();
|
|
85745
86118
|
const binary = opts.binaryPath ?? "openclaw";
|
|
@@ -85750,7 +86123,7 @@ async function probeOpenClawBinary(opts = {}) {
|
|
|
85750
86123
|
resolvePromise({ ...partial, probeDurationMs: Date.now() - startedAt });
|
|
85751
86124
|
};
|
|
85752
86125
|
let settled = false;
|
|
85753
|
-
const child =
|
|
86126
|
+
const child = spawn8(resolvedPath ?? binary, ["--version"], {
|
|
85754
86127
|
stdio: ["ignore", "pipe", "pipe"]
|
|
85755
86128
|
});
|
|
85756
86129
|
const timer = setTimeout(() => {
|
|
@@ -85811,7 +86184,7 @@ async function probeOpenClawBinary(opts = {}) {
|
|
|
85811
86184
|
async function tryResolveBinaryPath(binary) {
|
|
85812
86185
|
return new Promise((resolvePromise) => {
|
|
85813
86186
|
const which = process.platform === "win32" ? "where" : "which";
|
|
85814
|
-
const child =
|
|
86187
|
+
const child = spawn8(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
|
|
85815
86188
|
let out = "";
|
|
85816
86189
|
child.stdout?.on("data", (chunk) => {
|
|
85817
86190
|
out += chunk.toString("utf-8");
|
|
@@ -92400,7 +92773,7 @@ var init_register_git_github = __esm({
|
|
|
92400
92773
|
});
|
|
92401
92774
|
|
|
92402
92775
|
// ../dashboard/src/terminal.ts
|
|
92403
|
-
import { spawn as
|
|
92776
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
92404
92777
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
92405
92778
|
import { EventEmitter as EventEmitter29 } from "node:events";
|
|
92406
92779
|
function extractBaseCommand(command) {
|
|
@@ -92562,7 +92935,7 @@ var init_terminal = __esm({
|
|
|
92562
92935
|
return { sessionId: "", error: validation.error };
|
|
92563
92936
|
}
|
|
92564
92937
|
const sessionId = randomUUID13();
|
|
92565
|
-
const childProcess =
|
|
92938
|
+
const childProcess = spawn9(command, [], {
|
|
92566
92939
|
cwd,
|
|
92567
92940
|
shell: true,
|
|
92568
92941
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -92836,6 +93209,7 @@ var init_register_agent_core_routes = __esm({
|
|
|
92836
93209
|
"use strict";
|
|
92837
93210
|
init_src();
|
|
92838
93211
|
init_api_error();
|
|
93212
|
+
init_src2();
|
|
92839
93213
|
}
|
|
92840
93214
|
});
|
|
92841
93215
|
|
|
@@ -92848,10 +93222,13 @@ var init_register_agent_runtime_routes = __esm({
|
|
|
92848
93222
|
});
|
|
92849
93223
|
|
|
92850
93224
|
// ../dashboard/src/routes/register-agent-reflection-rating-routes.ts
|
|
93225
|
+
var AgentReflectionServiceBinding;
|
|
92851
93226
|
var init_register_agent_reflection_rating_routes = __esm({
|
|
92852
93227
|
"../dashboard/src/routes/register-agent-reflection-rating-routes.ts"() {
|
|
92853
93228
|
"use strict";
|
|
92854
93229
|
init_api_error();
|
|
93230
|
+
init_src2();
|
|
93231
|
+
AgentReflectionServiceBinding = "AgentReflectionService" in src_exports2 && typeof AgentReflectionService === "function" ? AgentReflectionService : void 0;
|
|
92855
93232
|
}
|
|
92856
93233
|
});
|
|
92857
93234
|
|
|
@@ -92994,12 +93371,20 @@ var init_claude_cli_probe = __esm({
|
|
|
92994
93371
|
}
|
|
92995
93372
|
});
|
|
92996
93373
|
|
|
93374
|
+
// ../dashboard/src/droid-cli-probe.ts
|
|
93375
|
+
var init_droid_cli_probe = __esm({
|
|
93376
|
+
"../dashboard/src/droid-cli-probe.ts"() {
|
|
93377
|
+
"use strict";
|
|
93378
|
+
}
|
|
93379
|
+
});
|
|
93380
|
+
|
|
92997
93381
|
// ../dashboard/src/routes/register-auth-routes.ts
|
|
92998
93382
|
var init_register_auth_routes = __esm({
|
|
92999
93383
|
"../dashboard/src/routes/register-auth-routes.ts"() {
|
|
93000
93384
|
"use strict";
|
|
93001
93385
|
init_src();
|
|
93002
93386
|
init_claude_cli_probe();
|
|
93387
|
+
init_droid_cli_probe();
|
|
93003
93388
|
init_api_error();
|
|
93004
93389
|
init_usage();
|
|
93005
93390
|
init_project_store_resolver();
|
|
@@ -93274,7 +93659,7 @@ function remapSpawnError(err, bin) {
|
|
|
93274
93659
|
return err instanceof Error ? err : new Error(String(err));
|
|
93275
93660
|
}
|
|
93276
93661
|
async function spawnPaperclipCliJson(args, opts) {
|
|
93277
|
-
const { spawn:
|
|
93662
|
+
const { spawn: spawn12 } = await import("node:child_process");
|
|
93278
93663
|
const bin = opts.cliBinaryPath ?? "paperclipai";
|
|
93279
93664
|
const fullArgs = [...args, "--json"];
|
|
93280
93665
|
if (opts.cliConfigPath) {
|
|
@@ -93285,7 +93670,7 @@ async function spawnPaperclipCliJson(args, opts) {
|
|
|
93285
93670
|
return new Promise((resolve19, reject) => {
|
|
93286
93671
|
let child;
|
|
93287
93672
|
try {
|
|
93288
|
-
child =
|
|
93673
|
+
child = spawn12(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
93289
93674
|
} catch (err) {
|
|
93290
93675
|
reject(remapSpawnError(err, bin));
|
|
93291
93676
|
return;
|
|
@@ -93797,6 +94182,25 @@ var init_register_runtime_provider_routes = __esm({
|
|
|
93797
94182
|
}
|
|
93798
94183
|
});
|
|
93799
94184
|
|
|
94185
|
+
// ../dashboard/src/cli-package-version.ts
|
|
94186
|
+
var init_cli_package_version = __esm({
|
|
94187
|
+
"../dashboard/src/cli-package-version.ts"() {
|
|
94188
|
+
"use strict";
|
|
94189
|
+
}
|
|
94190
|
+
});
|
|
94191
|
+
|
|
94192
|
+
// ../dashboard/src/routes/register-fn-binary-routes.ts
|
|
94193
|
+
var MAX_OUTPUT_BYTES2;
|
|
94194
|
+
var init_register_fn_binary_routes = __esm({
|
|
94195
|
+
"../dashboard/src/routes/register-fn-binary-routes.ts"() {
|
|
94196
|
+
"use strict";
|
|
94197
|
+
init_src();
|
|
94198
|
+
init_api_error();
|
|
94199
|
+
init_cli_package_version();
|
|
94200
|
+
MAX_OUTPUT_BYTES2 = 64 * 1024;
|
|
94201
|
+
}
|
|
94202
|
+
});
|
|
94203
|
+
|
|
93800
94204
|
// ../dashboard/src/update-check.ts
|
|
93801
94205
|
var DAY_MS;
|
|
93802
94206
|
var init_update_check = __esm({
|
|
@@ -93806,13 +94210,6 @@ var init_update_check = __esm({
|
|
|
93806
94210
|
}
|
|
93807
94211
|
});
|
|
93808
94212
|
|
|
93809
|
-
// ../dashboard/src/cli-package-version.ts
|
|
93810
|
-
var init_cli_package_version = __esm({
|
|
93811
|
-
"../dashboard/src/cli-package-version.ts"() {
|
|
93812
|
-
"use strict";
|
|
93813
|
-
}
|
|
93814
|
-
});
|
|
93815
|
-
|
|
93816
94213
|
// ../dashboard/src/routes/register-update-check-routes.ts
|
|
93817
94214
|
var init_register_update_check_routes = __esm({
|
|
93818
94215
|
"../dashboard/src/routes/register-update-check-routes.ts"() {
|
|
@@ -93860,6 +94257,7 @@ var init_insights_routes = __esm({
|
|
|
93860
94257
|
"../dashboard/src/insights-routes.ts"() {
|
|
93861
94258
|
"use strict";
|
|
93862
94259
|
init_api_error();
|
|
94260
|
+
init_src2();
|
|
93863
94261
|
}
|
|
93864
94262
|
});
|
|
93865
94263
|
|
|
@@ -93997,6 +94395,7 @@ var init_routes = __esm({
|
|
|
93997
94395
|
init_register_usage_routes();
|
|
93998
94396
|
init_register_auth_routes();
|
|
93999
94397
|
init_register_runtime_provider_routes();
|
|
94398
|
+
init_register_fn_binary_routes();
|
|
94000
94399
|
init_register_update_check_routes();
|
|
94001
94400
|
init_register_integrated_routers();
|
|
94002
94401
|
init_resolve_diff_base();
|
|
@@ -97725,6 +98124,7 @@ var init_terminal_websocket_diagnostics = __esm({
|
|
|
97725
98124
|
|
|
97726
98125
|
// ../dashboard/src/chat.ts
|
|
97727
98126
|
import { EventEmitter as EventEmitter30 } from "node:events";
|
|
98127
|
+
import { SessionManager as SessionManager3 } from "@mariozechner/pi-coding-agent";
|
|
97728
98128
|
var defaultDiagnostics, _diagnostics, diagnostics7, RATE_LIMIT_WINDOW_MS6, MAX_REFERENCED_FILE_SIZE, ChatStreamManager, chatStreamManager;
|
|
97729
98129
|
var init_chat = __esm({
|
|
97730
98130
|
"../dashboard/src/chat.ts"() {
|
|
@@ -97732,6 +98132,7 @@ var init_chat = __esm({
|
|
|
97732
98132
|
init_src();
|
|
97733
98133
|
init_sse_buffer();
|
|
97734
98134
|
init_src2();
|
|
98135
|
+
init_src2();
|
|
97735
98136
|
defaultDiagnostics = {
|
|
97736
98137
|
log(message, ...args) {
|
|
97737
98138
|
console.log(`[chat] ${message}`, ...args);
|
|
@@ -99322,7 +99723,7 @@ async function runTaskPlan(initialPlanArg, yesFlag = false, projectName) {
|
|
|
99322
99723
|
} catch (err) {
|
|
99323
99724
|
clearThinking();
|
|
99324
99725
|
if (err instanceof RateLimitError2) {
|
|
99325
|
-
console.error("\n Rate limit exceeded. Maximum
|
|
99726
|
+
console.error("\n Rate limit exceeded. Maximum 1000 planning sessions per hour.\n");
|
|
99326
99727
|
process.exit(1);
|
|
99327
99728
|
}
|
|
99328
99729
|
console.error(`
|
|
@@ -99479,7 +99880,7 @@ __export(skills_exports, {
|
|
|
99479
99880
|
runSkillsSearch: () => runSkillsSearch,
|
|
99480
99881
|
searchSkills: () => searchSkills
|
|
99481
99882
|
});
|
|
99482
|
-
import { spawn as
|
|
99883
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
99483
99884
|
async function searchSkills(query, limit = 10) {
|
|
99484
99885
|
const url = `${SKILLS_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
99485
99886
|
try {
|
|
@@ -99557,7 +99958,7 @@ async function runSkillsInstall(args, options) {
|
|
|
99557
99958
|
npxArgs.push("--skill", options.skill);
|
|
99558
99959
|
}
|
|
99559
99960
|
npxArgs.push("-y", "-a", "pi");
|
|
99560
|
-
const child =
|
|
99961
|
+
const child = spawn10("npx", npxArgs, {
|
|
99561
99962
|
cwd: process.cwd(),
|
|
99562
99963
|
stdio: "inherit",
|
|
99563
99964
|
shell: true
|
|
@@ -99594,7 +99995,7 @@ import { StringEnum } from "@mariozechner/pi-ai";
|
|
|
99594
99995
|
import { resolve as resolve18, basename as basename11, extname as extname3, join as join41 } from "node:path";
|
|
99595
99996
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
99596
99997
|
import { existsSync as existsSync31 } from "node:fs";
|
|
99597
|
-
import { spawn as
|
|
99998
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
99598
99999
|
var MIME_TYPES2 = {
|
|
99599
100000
|
".png": "image/png",
|
|
99600
100001
|
".jpg": "image/jpeg",
|
|
@@ -101403,7 +101804,7 @@ Status: ${updated.status}`
|
|
|
101403
101804
|
npxArgs.push("--skill", params.skill);
|
|
101404
101805
|
}
|
|
101405
101806
|
npxArgs.push("-y", "-a", "pi");
|
|
101406
|
-
const child =
|
|
101807
|
+
const child = spawn11("npx", npxArgs, {
|
|
101407
101808
|
cwd: resolveProjectRoot(ctx.cwd),
|
|
101408
101809
|
stdio: "pipe",
|
|
101409
101810
|
shell: true
|
|
@@ -101485,7 +101886,7 @@ Status: ${updated.status}`
|
|
|
101485
101886
|
return;
|
|
101486
101887
|
}
|
|
101487
101888
|
const port = trimmed ? parseInt(trimmed, 10) || 4040 : 4040;
|
|
101488
|
-
const child =
|
|
101889
|
+
const child = spawn11("fn", ["dashboard", "--port", String(port)], {
|
|
101489
101890
|
cwd: resolveProjectRoot(ctx.cwd),
|
|
101490
101891
|
stdio: ["ignore", "pipe", "pipe"],
|
|
101491
101892
|
detached: false,
|