@runfusion/fusion 0.13.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 +1250 -522
- package/dist/client/assets/AgentDetailView-CBFUveyO.js +18 -0
- package/dist/client/assets/{AgentsView-Dvf_xUkx.js → AgentsView-DPezXQ-U.js} +4 -4
- package/dist/client/assets/ChatView-5N4-EuhD.js +1 -0
- package/dist/client/assets/{DevServerView-C2qTJch7.js → DevServerView-Daft4YFc.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-DRfhg9zz.js → DirectoryPicker-rew1y6qO.js} +1 -1
- package/dist/client/assets/{DocumentsView-j8ic1xUw.js → DocumentsView-i72qJzwd.js} +1 -1
- package/dist/client/assets/{InsightsView-CpAz3o0i.js → InsightsView-BL5eZJ0a.js} +1 -1
- package/dist/client/assets/{MemoryView-BcQsi_JK.js → MemoryView-pl8Cdg_p.js} +1 -1
- package/dist/client/assets/{NodesView-Bo_Yhr4N.js → NodesView-D6eJ15zc.js} +1 -1
- package/dist/client/assets/PiExtensionsManager-ExInwXWP.js +11 -0
- package/dist/client/assets/{PluginManager-BQhBHWrB.js → PluginManager-CYhtxHun.js} +1 -1
- package/dist/client/assets/{ResearchView-CLyyqAWE.js → ResearchView-B_QPUEjB.js} +1 -1
- package/dist/client/assets/{RoadmapsView-tG7IdOoc.js → RoadmapsView-DBNLaEsK.js} +1 -1
- package/dist/client/assets/SettingsModal-1ET586M3.js +31 -0
- package/dist/client/assets/{SettingsModal-CXUGeZ0_.js → SettingsModal-CL_gWmOj.js} +1 -1
- package/dist/client/assets/SettingsModal-D_AFkDJa.css +1 -0
- package/dist/client/assets/{SetupWizardModal-BMJL6eNR.js → SetupWizardModal-CLkY9HFL.js} +1 -1
- package/dist/client/assets/{SkillMultiselect-ILMft-Kz.js → SkillMultiselect-B0qi32SQ.js} +1 -1
- package/dist/client/assets/{SkillsView-x4_YwBz6.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-DDdJt8aE.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-DFxQ9biT.js → list-checks-sK8xJeH_.js} +1 -1
- package/dist/client/assets/{star-BKs1bgJN.js → star-BRtXbYkB.js} +1 -1
- package/dist/client/assets/{upload-Bb5Pidne.js → upload-BP60eBwN.js} +1 -1
- package/dist/client/assets/{users-BImNn91Q.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 +473 -119
- package/dist/pi-claude-cli/package.json +1 -1
- package/package.json +2 -1
- package/dist/client/assets/AgentDetailView-B7j297GT.js +0 -18
- package/dist/client/assets/ChatView-BgUt38ty.js +0 -1
- package/dist/client/assets/PiExtensionsManager-DHt2zFg8.js +0 -11
- package/dist/client/assets/SettingsModal-9HS8MnmW.css +0 -1
- package/dist/client/assets/SettingsModal-UziTDnLh.js +0 -31
- package/dist/client/assets/TodoView-BBYcMbXE.js +0 -6
- package/dist/client/assets/TodoView-C1g65hJo.css +0 -1
- package/dist/client/assets/index-B15xwijw.css +0 -1
- package/dist/client/assets/index-DmSs2FGE.js +0 -661
package/dist/extension.js
CHANGED
|
@@ -1007,11 +1007,40 @@ function hasAgentIdentity(agent) {
|
|
|
1007
1007
|
if (!agent) return false;
|
|
1008
1008
|
return !!(agent.soul?.trim() || agent.instructionsText?.trim() || agent.instructionsPath?.trim() || agent.memory?.trim());
|
|
1009
1009
|
}
|
|
1010
|
-
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) {
|
|
1011
1039
|
if (!agentId || typeof agentId !== "string") {
|
|
1012
1040
|
throw new Error("getDefaultHeartbeatProcedurePath requires a non-empty agentId");
|
|
1013
1041
|
}
|
|
1014
|
-
|
|
1042
|
+
const directory = agentName ? getCanonicalAgentAssetDirectoryName(agentName, agentId) : getLegacyAgentAssetDirectoryName(agentId);
|
|
1043
|
+
return `.fusion/agents/${directory}/HEARTBEAT.md`;
|
|
1015
1044
|
}
|
|
1016
1045
|
function agentToConfigSnapshot(agent) {
|
|
1017
1046
|
return {
|
|
@@ -2705,7 +2734,7 @@ var init_db = __esm({
|
|
|
2705
2734
|
"use strict";
|
|
2706
2735
|
init_sqlite_adapter();
|
|
2707
2736
|
init_types();
|
|
2708
|
-
SCHEMA_VERSION =
|
|
2737
|
+
SCHEMA_VERSION = 58;
|
|
2709
2738
|
SCHEMA_SQL = `
|
|
2710
2739
|
-- Tasks table with JSON columns for nested data
|
|
2711
2740
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
@@ -4406,6 +4435,27 @@ CREATE INDEX IF NOT EXISTS idxTodoItemsSortOrder ON todo_items(listId, sortOrder
|
|
|
4406
4435
|
}
|
|
4407
4436
|
});
|
|
4408
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
|
+
}
|
|
4409
4459
|
}
|
|
4410
4460
|
/**
|
|
4411
4461
|
* Run a single migration step inside a transaction and bump the version.
|
|
@@ -4669,7 +4719,7 @@ var init_agent_store = __esm({
|
|
|
4669
4719
|
if (agent.heartbeatProcedurePath !== DEFAULT_HEARTBEAT_PROCEDURE_PATH) {
|
|
4670
4720
|
continue;
|
|
4671
4721
|
}
|
|
4672
|
-
const newRelPath =
|
|
4722
|
+
const newRelPath = await this.resolveCompatibleHeartbeatProcedurePath(agent);
|
|
4673
4723
|
const newAbsPath = join3(this.rootDir, "..", newRelPath);
|
|
4674
4724
|
if (legacyContent !== null) {
|
|
4675
4725
|
try {
|
|
@@ -4830,7 +4880,7 @@ var init_agent_store = __esm({
|
|
|
4830
4880
|
const metadata = input.metadata ?? {};
|
|
4831
4881
|
const runtimeConfig = resolveCreationRuntimeConfig(input.runtimeConfig, metadata);
|
|
4832
4882
|
const ephemeral = isEphemeralAgent({ metadata, name: input.name, role: input.role, reportsTo: input.reportsTo });
|
|
4833
|
-
const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId));
|
|
4883
|
+
const resolvedHeartbeatProcedurePath = input.heartbeatProcedurePath ?? (ephemeral ? void 0 : getDefaultHeartbeatProcedurePath(agentId, input.name));
|
|
4834
4884
|
const agent = {
|
|
4835
4885
|
id: agentId,
|
|
4836
4886
|
name: input.name.trim(),
|
|
@@ -5067,14 +5117,16 @@ var init_agent_store = __esm({
|
|
|
5067
5117
|
* Does not create the directory.
|
|
5068
5118
|
*/
|
|
5069
5119
|
getInstructionsDir(agentId) {
|
|
5070
|
-
|
|
5120
|
+
const agent = this.readAgent(agentId);
|
|
5121
|
+
const agentName = agent?.name ?? "";
|
|
5122
|
+
return join3(this.agentsDir, getCanonicalAgentInstructionsBundleDirName(agentName, agentId));
|
|
5071
5123
|
}
|
|
5072
5124
|
/**
|
|
5073
5125
|
* List markdown files in an agent's managed instructions bundle.
|
|
5074
5126
|
* Returns [] when the bundle directory does not exist.
|
|
5075
5127
|
*/
|
|
5076
5128
|
async listBundleFiles(agentId) {
|
|
5077
|
-
const bundleDir = this.
|
|
5129
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
|
|
5078
5130
|
try {
|
|
5079
5131
|
const entries = await readdir(bundleDir, { withFileTypes: true });
|
|
5080
5132
|
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".md")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
@@ -5090,7 +5142,8 @@ var init_agent_store = __esm({
|
|
|
5090
5142
|
*/
|
|
5091
5143
|
async readBundleFile(agentId, filePath) {
|
|
5092
5144
|
this.validateBundleFilePath(filePath);
|
|
5093
|
-
const
|
|
5145
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
|
|
5146
|
+
const resolvedPath = join3(bundleDir, filePath);
|
|
5094
5147
|
return readFile(resolvedPath, "utf-8");
|
|
5095
5148
|
}
|
|
5096
5149
|
/**
|
|
@@ -5099,7 +5152,7 @@ var init_agent_store = __esm({
|
|
|
5099
5152
|
async writeBundleFile(agentId, filePath, content) {
|
|
5100
5153
|
return this.withLock(agentId, async () => {
|
|
5101
5154
|
this.validateBundleFilePath(filePath);
|
|
5102
|
-
const bundleDir = this.
|
|
5155
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, true);
|
|
5103
5156
|
await mkdir(bundleDir, { recursive: true });
|
|
5104
5157
|
const existingFiles = await this.listBundleFiles(agentId);
|
|
5105
5158
|
const isOverwrite = existingFiles.includes(filePath);
|
|
@@ -5118,7 +5171,8 @@ var init_agent_store = __esm({
|
|
|
5118
5171
|
async deleteBundleFile(agentId, filePath) {
|
|
5119
5172
|
return this.withLock(agentId, async () => {
|
|
5120
5173
|
this.validateBundleFilePath(filePath);
|
|
5121
|
-
await
|
|
5174
|
+
const bundleDir = await this.resolveCompatibleBundleDir(agentId, false);
|
|
5175
|
+
await unlink(join3(bundleDir, filePath));
|
|
5122
5176
|
});
|
|
5123
5177
|
}
|
|
5124
5178
|
/**
|
|
@@ -5140,7 +5194,7 @@ var init_agent_store = __esm({
|
|
|
5140
5194
|
};
|
|
5141
5195
|
const updated = await this.updateAgent(agentId, { bundleConfig: normalizedConfig });
|
|
5142
5196
|
if (normalizedConfig.mode === "managed") {
|
|
5143
|
-
await mkdir(this.
|
|
5197
|
+
await mkdir(await this.resolveCompatibleBundleDir(agentId, true), { recursive: true });
|
|
5144
5198
|
}
|
|
5145
5199
|
return updated;
|
|
5146
5200
|
}
|
|
@@ -5163,7 +5217,7 @@ var init_agent_store = __esm({
|
|
|
5163
5217
|
bundleConfig: { mode: "managed", entryFile, files: [] }
|
|
5164
5218
|
});
|
|
5165
5219
|
}
|
|
5166
|
-
await mkdir(this.
|
|
5220
|
+
await mkdir(await this.resolveCompatibleBundleDir(agentId, true), { recursive: true });
|
|
5167
5221
|
const files = [];
|
|
5168
5222
|
if (hasInstructionsText) {
|
|
5169
5223
|
await this.writeBundleFile(agentId, entryFile, agent.instructionsText ?? "");
|
|
@@ -6124,8 +6178,87 @@ var init_agent_store = __esm({
|
|
|
6124
6178
|
}
|
|
6125
6179
|
return null;
|
|
6126
6180
|
}
|
|
6127
|
-
|
|
6128
|
-
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
|
+
}
|
|
6129
6262
|
}
|
|
6130
6263
|
validateBundleFilePath(filePath) {
|
|
6131
6264
|
if (typeof filePath !== "string") {
|
|
@@ -35857,6 +35990,20 @@ function reconcileClaudeCliPaths(paths, vendoredPath) {
|
|
|
35857
35990
|
}
|
|
35858
35991
|
return filtered;
|
|
35859
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
|
+
}
|
|
35860
36007
|
function getDisplayPathWithinRoot(root, targetPath) {
|
|
35861
36008
|
const usesWindowsPaths = /^[A-Za-z]:[\\/]/.test(root) || /^[A-Za-z]:[\\/]/.test(targetPath) || root.includes("\\") || targetPath.includes("\\");
|
|
35862
36009
|
const pathApi = usesWindowsPaths ? win32 : { relative: relative2, isAbsolute: isAbsolute5, sep: sep4 };
|
|
@@ -36114,6 +36261,85 @@ var init_gh_cli = __esm({
|
|
|
36114
36261
|
}
|
|
36115
36262
|
});
|
|
36116
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
|
+
|
|
36117
36343
|
// ../core/src/settings-validation.ts
|
|
36118
36344
|
function validateUnavailableNodePolicy(value) {
|
|
36119
36345
|
if (value === void 0) {
|
|
@@ -50401,6 +50627,10 @@ __export(src_exports, {
|
|
|
50401
50627
|
EXECUTION_MODES: () => EXECUTION_MODES,
|
|
50402
50628
|
FEATURE_LOOP_STATES: () => FEATURE_LOOP_STATES,
|
|
50403
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,
|
|
50404
50634
|
FileMemoryBackend: () => FileMemoryBackend,
|
|
50405
50635
|
FirstRunDetector: () => FirstRunDetector,
|
|
50406
50636
|
GLOBAL_SETTINGS_KEYS: () => GLOBAL_SETTINGS_KEYS,
|
|
@@ -50512,6 +50742,7 @@ __export(src_exports, {
|
|
|
50512
50742
|
createInsightExtractionAutomation: () => createInsightExtractionAutomation,
|
|
50513
50743
|
createMemoryDreamsAutomation: () => createMemoryDreamsAutomation,
|
|
50514
50744
|
dailyMemoryPath: () => dailyMemoryPath,
|
|
50745
|
+
detectFnBinary: () => detectFnBinary,
|
|
50515
50746
|
detectLegacyData: () => detectLegacyData,
|
|
50516
50747
|
diffConfigSnapshots: () => diffConfigSnapshots,
|
|
50517
50748
|
discoverPiExtensions: () => discoverPiExtensions,
|
|
@@ -50633,6 +50864,7 @@ __export(src_exports, {
|
|
|
50633
50864
|
readProjectMemoryWithBackend: () => readProjectMemoryWithBackend,
|
|
50634
50865
|
readWorkingMemory: () => readWorkingMemory,
|
|
50635
50866
|
reconcileClaudeCliPaths: () => reconcileClaudeCliPaths,
|
|
50867
|
+
reconcileDroidCliPaths: () => reconcileDroidCliPaths,
|
|
50636
50868
|
refreshQmdProjectMemoryIndex: () => refreshQmdProjectMemoryIndex,
|
|
50637
50869
|
registerMemoryBackend: () => registerMemoryBackend,
|
|
50638
50870
|
renderMemoryAuditMarkdown: () => renderMemoryAuditMarkdown,
|
|
@@ -50722,6 +50954,7 @@ var init_src = __esm({
|
|
|
50722
50954
|
init_automation();
|
|
50723
50955
|
init_automation_store();
|
|
50724
50956
|
init_run_command();
|
|
50957
|
+
init_fn_binary();
|
|
50725
50958
|
init_node_override_guard();
|
|
50726
50959
|
init_settings_validation();
|
|
50727
50960
|
init_routine();
|
|
@@ -52708,6 +52941,21 @@ function resolveVendoredClaudeCliEntry() {
|
|
|
52708
52941
|
return null;
|
|
52709
52942
|
}
|
|
52710
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
|
+
}
|
|
52711
52959
|
async function registerExtensionProviders(cwd, modelRegistry) {
|
|
52712
52960
|
try {
|
|
52713
52961
|
const agentDir = getPackageManagerAgentDir();
|
|
@@ -52723,8 +52971,13 @@ async function registerExtensionProviders(cwd, modelRegistry) {
|
|
|
52723
52971
|
[...getEnabledPiExtensionPaths(cwd), ...packageExtensionPaths],
|
|
52724
52972
|
vendoredClaudeCli
|
|
52725
52973
|
);
|
|
52726
|
-
const
|
|
52974
|
+
const vendoredDroidCli = resolveVendoredDroidCliEntry();
|
|
52975
|
+
const doubleReconciledPaths = reconcileDroidCliPaths(
|
|
52727
52976
|
reconciledPaths,
|
|
52977
|
+
vendoredDroidCli
|
|
52978
|
+
);
|
|
52979
|
+
const extensionsResult = await discoverAndLoadExtensions(
|
|
52980
|
+
doubleReconciledPaths,
|
|
52728
52981
|
cwd,
|
|
52729
52982
|
join25(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
|
|
52730
52983
|
);
|
|
@@ -54381,14 +54634,25 @@ async function getAgentMemoryWindow(rootDir, agentMemory, path2, startLine = 1,
|
|
|
54381
54634
|
backend: "agent-memory"
|
|
54382
54635
|
};
|
|
54383
54636
|
}
|
|
54384
|
-
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) {
|
|
54385
54649
|
return {
|
|
54386
54650
|
name: "fn_task_create",
|
|
54387
54651
|
label: "Create Task",
|
|
54388
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).",
|
|
54389
54653
|
parameters: taskCreateParams,
|
|
54390
54654
|
execute: async (_id, params) => {
|
|
54391
|
-
const task = await store
|
|
54655
|
+
const task = await createAgentTask(store, {
|
|
54392
54656
|
description: params.description,
|
|
54393
54657
|
dependencies: params.dependencies,
|
|
54394
54658
|
column: "triage",
|
|
@@ -54397,7 +54661,7 @@ function createTaskCreateTool(store, provenance) {
|
|
|
54397
54661
|
sourceAgentId: provenance.sourceAgentId,
|
|
54398
54662
|
sourceRunId: provenance.sourceRunId
|
|
54399
54663
|
} : void 0
|
|
54400
|
-
});
|
|
54664
|
+
}, options);
|
|
54401
54665
|
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
|
54402
54666
|
return {
|
|
54403
54667
|
content: [{
|
|
@@ -54750,7 +55014,7 @@ ${lines.join("\n\n")}` }],
|
|
|
54750
55014
|
}
|
|
54751
55015
|
};
|
|
54752
55016
|
}
|
|
54753
|
-
function createDelegateTaskTool(agentStore, taskStore) {
|
|
55017
|
+
function createDelegateTaskTool(agentStore, taskStore, options) {
|
|
54754
55018
|
return {
|
|
54755
55019
|
name: "fn_delegate_task",
|
|
54756
55020
|
label: "Delegate Task",
|
|
@@ -54770,13 +55034,13 @@ function createDelegateTaskTool(agentStore, taskStore) {
|
|
|
54770
55034
|
details: {}
|
|
54771
55035
|
};
|
|
54772
55036
|
}
|
|
54773
|
-
const task = await taskStore
|
|
55037
|
+
const task = await createAgentTask(taskStore, {
|
|
54774
55038
|
description: params.description,
|
|
54775
55039
|
dependencies: params.dependencies,
|
|
54776
55040
|
column: "todo",
|
|
54777
55041
|
assignedAgentId: params.agent_id,
|
|
54778
55042
|
source: { sourceType: "api" }
|
|
54779
|
-
});
|
|
55043
|
+
}, options);
|
|
54780
55044
|
const deps = task.dependencies.length ? ` (depends on: ${task.dependencies.join(", ")})` : "";
|
|
54781
55045
|
return {
|
|
54782
55046
|
content: [{
|
|
@@ -57603,7 +57867,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
57603
57867
|
// Agent delegation tools — discover and delegate work to other agents.
|
|
57604
57868
|
...this.options.agentStore ? [
|
|
57605
57869
|
createListAgentsTool(this.options.agentStore),
|
|
57606
|
-
createDelegateTaskTool(this.options.agentStore, this.store)
|
|
57870
|
+
createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir })
|
|
57607
57871
|
] : [],
|
|
57608
57872
|
this.createReviewSpecTool(
|
|
57609
57873
|
task.id,
|
|
@@ -58142,7 +58406,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
58142
58406
|
planLog.warn(`${options.parentTaskId}: failed to load parent task for fn_task_create inheritance: ${msg}`);
|
|
58143
58407
|
parentTask = void 0;
|
|
58144
58408
|
}
|
|
58145
|
-
const newTask = await store
|
|
58409
|
+
const newTask = await createAgentTask(store, {
|
|
58146
58410
|
title: params.title,
|
|
58147
58411
|
description: params.description,
|
|
58148
58412
|
dependencies: validDeps,
|
|
@@ -58156,7 +58420,7 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
58156
58420
|
sourceType: "agent_heartbeat",
|
|
58157
58421
|
sourceParentTaskId: options.parentTaskId
|
|
58158
58422
|
}
|
|
58159
|
-
});
|
|
58423
|
+
}, { rootDir: this.rootDir });
|
|
58160
58424
|
options.createdSubtasksRef.current.push(newTask.id);
|
|
58161
58425
|
return {
|
|
58162
58426
|
content: [
|
|
@@ -58584,7 +58848,7 @@ var init_run_audit = __esm({
|
|
|
58584
58848
|
});
|
|
58585
58849
|
|
|
58586
58850
|
// ../engine/src/merger.ts
|
|
58587
|
-
import { execSync, exec as exec2, spawn as
|
|
58851
|
+
import { execSync, exec as exec2, spawn as spawn3 } from "node:child_process";
|
|
58588
58852
|
import { promisify as promisify3 } from "node:util";
|
|
58589
58853
|
import { existsSync as existsSync22 } from "node:fs";
|
|
58590
58854
|
import { join as join29 } from "node:path";
|
|
@@ -58599,7 +58863,7 @@ async function execWithProcessGroup(command, options) {
|
|
|
58599
58863
|
return;
|
|
58600
58864
|
}
|
|
58601
58865
|
const useProcessGroup = process.platform !== "win32";
|
|
58602
|
-
const child =
|
|
58866
|
+
const child = spawn3(command, {
|
|
58603
58867
|
cwd: options.cwd,
|
|
58604
58868
|
shell: true,
|
|
58605
58869
|
detached: useProcessGroup,
|
|
@@ -63335,10 +63599,10 @@ var init_step_session_executor = __esm({
|
|
|
63335
63599
|
] : [];
|
|
63336
63600
|
const memoryTools = createMemoryTools(this.options.rootDir, settings);
|
|
63337
63601
|
const taskLogTool = this.options.store ? [createTaskLogTool(this.options.store, taskDetail.id)] : [];
|
|
63338
|
-
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 })] : [];
|
|
63339
63603
|
const delegationTools = this.options.agentStore ? [
|
|
63340
63604
|
createListAgentsTool(this.options.agentStore),
|
|
63341
|
-
createDelegateTaskTool(this.options.agentStore, this.options.store)
|
|
63605
|
+
createDelegateTaskTool(this.options.agentStore, this.options.store, { rootDir: this.options.rootDir })
|
|
63342
63606
|
] : [];
|
|
63343
63607
|
const messagingTools = this.options.messageStore && taskDetail.assignedAgentId ? [
|
|
63344
63608
|
createSendMessageTool(this.options.messageStore, taskDetail.assignedAgentId),
|
|
@@ -63749,7 +64013,7 @@ var init_task_completion = __esm({
|
|
|
63749
64013
|
});
|
|
63750
64014
|
|
|
63751
64015
|
// ../engine/src/run-verification-tool.ts
|
|
63752
|
-
import { spawn as
|
|
64016
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
63753
64017
|
import { existsSync as existsSync26 } from "node:fs";
|
|
63754
64018
|
import { isAbsolute as isAbsolute10, join as join34 } from "node:path";
|
|
63755
64019
|
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
@@ -63782,7 +64046,7 @@ async function runVerificationCommand2(opts) {
|
|
|
63782
64046
|
const stdoutBuf = { head: "", tail: "", totalBytes: 0 };
|
|
63783
64047
|
const stderrBuf = { head: "", tail: "", totalBytes: 0 };
|
|
63784
64048
|
return new Promise((resolve19) => {
|
|
63785
|
-
const child =
|
|
64049
|
+
const child = spawn4(command, {
|
|
63786
64050
|
cwd,
|
|
63787
64051
|
stdio: ["ignore", "pipe", "pipe"],
|
|
63788
64052
|
env: { ...process.env },
|
|
@@ -66070,7 +66334,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66070
66334
|
// Agent delegation tools — discover and delegate work to other agents.
|
|
66071
66335
|
...this.options.agentStore ? [
|
|
66072
66336
|
createListAgentsTool(this.options.agentStore),
|
|
66073
|
-
createDelegateTaskTool(this.options.agentStore, this.store)
|
|
66337
|
+
createDelegateTaskTool(this.options.agentStore, this.store, { rootDir: this.rootDir })
|
|
66074
66338
|
] : [],
|
|
66075
66339
|
// Messaging tools — allows executor agents to send and receive messages.
|
|
66076
66340
|
...this.options.messageStore && assignedAgentId ? [
|
|
@@ -66764,7 +67028,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
66764
67028
|
return createTaskLogTool(this.store, taskId);
|
|
66765
67029
|
}
|
|
66766
67030
|
createTaskCreateTool() {
|
|
66767
|
-
return createTaskCreateTool(this.store, { sourceType: "api" });
|
|
67031
|
+
return createTaskCreateTool(this.store, { sourceType: "api" }, { rootDir: this.rootDir });
|
|
66768
67032
|
}
|
|
66769
67033
|
createTaskDocumentWriteTool(taskId) {
|
|
66770
67034
|
return createTaskDocumentWriteTool(this.store, taskId);
|
|
@@ -71699,7 +71963,7 @@ function isTickableState(state) {
|
|
|
71699
71963
|
function isHeartbeatManaged(agent) {
|
|
71700
71964
|
return !isEphemeralAgent(agent);
|
|
71701
71965
|
}
|
|
71702
|
-
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;
|
|
71703
71967
|
var init_agent_heartbeat = __esm({
|
|
71704
71968
|
"../engine/src/agent-heartbeat.ts"() {
|
|
71705
71969
|
"use strict";
|
|
@@ -71889,6 +72153,32 @@ When sending messages:
|
|
|
71889
72153
|
Critical: a heartbeat without observable progress (a log, a document write, a
|
|
71890
72154
|
status change, a comment, a delegation, or an explicit "no-op with reason") is
|
|
71891
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.`;
|
|
71892
72182
|
heartbeatDoneParams = Type6.Object({
|
|
71893
72183
|
summary: Type6.Optional(Type6.String({ description: "Summary of what was accomplished this heartbeat" }))
|
|
71894
72184
|
});
|
|
@@ -71928,13 +72218,6 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
71928
72218
|
this.rootDir = options.rootDir;
|
|
71929
72219
|
this.messageStore = options.messageStore;
|
|
71930
72220
|
this.pluginRunner = options.pluginRunner;
|
|
71931
|
-
this.onRecovered = options.onRecovered;
|
|
71932
|
-
this.onTerminated = options.onTerminated;
|
|
71933
|
-
this.onRunStarted = options.onRunStarted;
|
|
71934
|
-
this.onRunCompleted = options.onRunCompleted;
|
|
71935
|
-
this.taskStore = options.taskStore;
|
|
71936
|
-
this.rootDir = options.rootDir;
|
|
71937
|
-
this.messageStore = options.messageStore;
|
|
71938
72221
|
}
|
|
71939
72222
|
/**
|
|
71940
72223
|
* Start the heartbeat monitoring loop.
|
|
@@ -72633,9 +72916,9 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72633
72916
|
sourceType: "agent_heartbeat",
|
|
72634
72917
|
sourceAgentId: agentId,
|
|
72635
72918
|
sourceRunId: runContext?.runId
|
|
72636
|
-
}));
|
|
72919
|
+
}, { rootDir: this.rootDir }));
|
|
72637
72920
|
heartbeatTools.push(createListAgentsTool(this.store));
|
|
72638
|
-
heartbeatTools.push(createDelegateTaskTool(this.store, taskStore));
|
|
72921
|
+
heartbeatTools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
|
|
72639
72922
|
if (this.messageStore) {
|
|
72640
72923
|
heartbeatTools.push(createSendMessageTool(this.messageStore, agentId));
|
|
72641
72924
|
heartbeatTools.push(createReadMessagesTool(this.messageStore, agentId));
|
|
@@ -72658,22 +72941,27 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72658
72941
|
heartbeatLog.warn(`Failed to configure heartbeat memory tools for ${agentId}: ${message}`);
|
|
72659
72942
|
}
|
|
72660
72943
|
const skillContext = buildSessionSkillContextSync2(agent, "heartbeat", rootDir);
|
|
72661
|
-
|
|
72662
|
-
const baseHeartbeatSystemPrompt = systemPrompt;
|
|
72944
|
+
const baseHeartbeatSystemPrompt = isNoTaskRun ? HEARTBEAT_NO_TASK_SYSTEM_PROMPT : HEARTBEAT_SYSTEM_PROMPT;
|
|
72663
72945
|
let resolvedInstructionsForIdentity = "";
|
|
72664
72946
|
try {
|
|
72665
|
-
|
|
72666
|
-
resolvedInstructionsForIdentity = agentInstructions;
|
|
72667
|
-
const memoryInstructions = memorySettings?.memoryEnabled === false ? "" : buildExecutionMemoryInstructions(rootDir, memorySettings);
|
|
72668
|
-
systemPrompt = buildSystemPromptWithInstructions(
|
|
72669
|
-
baseHeartbeatSystemPrompt,
|
|
72670
|
-
[agentInstructions, memoryInstructions].filter((part) => part.trim()).join("\n\n")
|
|
72671
|
-
);
|
|
72947
|
+
resolvedInstructionsForIdentity = await resolveAgentInstructionsWithRatings(agent, rootDir, this.store);
|
|
72672
72948
|
} catch (instructionError) {
|
|
72673
|
-
systemPrompt = baseHeartbeatSystemPrompt;
|
|
72674
72949
|
const message = instructionError instanceof Error ? instructionError.message : String(instructionError);
|
|
72675
|
-
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
|
+
}
|
|
72676
72960
|
}
|
|
72961
|
+
const systemPrompt = buildSystemPromptWithInstructions(
|
|
72962
|
+
baseHeartbeatSystemPrompt,
|
|
72963
|
+
[resolvedInstructionsForIdentity, memoryInstructions].filter((part) => part.trim()).join("\n\n")
|
|
72964
|
+
);
|
|
72677
72965
|
heartbeatTools.push(createIdentityTool({ agent, resolvedInstructions: resolvedInstructionsForIdentity }));
|
|
72678
72966
|
heartbeatTools.push(heartbeatDoneTool);
|
|
72679
72967
|
if (isNoTaskRun) {
|
|
@@ -72734,7 +73022,7 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72734
73022
|
};
|
|
72735
73023
|
const wakeReason = deriveWakeReason();
|
|
72736
73024
|
const customProcedure = await resolveAgentHeartbeatProcedure(agent, rootDir);
|
|
72737
|
-
const heartbeatProcedureText = customProcedure ?? HEARTBEAT_PROCEDURE;
|
|
73025
|
+
const heartbeatProcedureText = customProcedure ?? (isNoTaskRun ? HEARTBEAT_NO_TASK_PROCEDURE : HEARTBEAT_PROCEDURE);
|
|
72738
73026
|
if (isNoTaskRun) {
|
|
72739
73027
|
if (this.messageStore) {
|
|
72740
73028
|
try {
|
|
@@ -72767,6 +73055,8 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72767
73055
|
`- pending messages: ${pendingMessages.length}`,
|
|
72768
73056
|
"",
|
|
72769
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.",
|
|
72770
73060
|
"Run the Heartbeat Procedure (below) before doing anything else \u2014 even a",
|
|
72771
73061
|
"timer-only wake should re-check messages, memory, and project state.",
|
|
72772
73062
|
"",
|
|
@@ -72858,6 +73148,8 @@ a bug. Do not loop on the same plan across heartbeats without recording why.`;
|
|
|
72858
73148
|
`- triggering comments: ${effectiveTriggeringCommentIds?.length ?? 0}`,
|
|
72859
73149
|
"",
|
|
72860
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.",
|
|
72861
73153
|
"Before resuming prior task work, run the Heartbeat Procedure (below) and",
|
|
72862
73154
|
"decide what action this delta requires. Your assigned task is one input",
|
|
72863
73155
|
"to the procedure \u2014 not the only thing to consider.",
|
|
@@ -73017,7 +73309,7 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
|
|
|
73017
73309
|
const baseCreateTool = createTaskCreateTool(taskStore, {
|
|
73018
73310
|
sourceType: "agent_heartbeat",
|
|
73019
73311
|
sourceAgentId: agentId
|
|
73020
|
-
});
|
|
73312
|
+
}, { rootDir: this.rootDir });
|
|
73021
73313
|
const trackedCreateTool = {
|
|
73022
73314
|
...baseCreateTool,
|
|
73023
73315
|
execute: async (id, params, signal, onUpdate, ctx) => {
|
|
@@ -73044,7 +73336,7 @@ ${taskDetail.prompt}` : "No PROMPT.md available.",
|
|
|
73044
73336
|
tools.push(createTaskDocumentWriteTool(taskStore, taskId));
|
|
73045
73337
|
tools.push(createTaskDocumentReadTool(taskStore, taskId));
|
|
73046
73338
|
tools.push(createListAgentsTool(this.store));
|
|
73047
|
-
tools.push(createDelegateTaskTool(this.store, taskStore));
|
|
73339
|
+
tools.push(createDelegateTaskTool(this.store, taskStore, { rootDir: this.rootDir }));
|
|
73048
73340
|
if (messageStore) {
|
|
73049
73341
|
tools.push(createSendMessageTool(messageStore, agentId));
|
|
73050
73342
|
tools.push(createReadMessagesTool(messageStore, agentId));
|
|
@@ -80746,7 +81038,7 @@ var init_provider_adapters = __esm({
|
|
|
80746
81038
|
|
|
80747
81039
|
// ../engine/src/remote-access/tunnel-process-manager.ts
|
|
80748
81040
|
import { EventEmitter as EventEmitter23 } from "node:events";
|
|
80749
|
-
import { exec as exec9, execFile as execFile3, spawn as
|
|
81041
|
+
import { exec as exec9, execFile as execFile3, spawn as spawn5 } from "node:child_process";
|
|
80750
81042
|
import { promisify as promisify9 } from "node:util";
|
|
80751
81043
|
function nowIso() {
|
|
80752
81044
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -80837,7 +81129,7 @@ var init_tunnel_process_manager = __esm({
|
|
|
80837
81129
|
super();
|
|
80838
81130
|
this.maxLogEntries = options.maxLogEntries ?? DEFAULT_MAX_LOG_ENTRIES;
|
|
80839
81131
|
this.defaultStopTimeoutMs = options.stopTimeoutMs ?? DEFAULT_STOP_TIMEOUT_MS2;
|
|
80840
|
-
this.spawnImpl = options.spawnImpl ??
|
|
81132
|
+
this.spawnImpl = options.spawnImpl ?? spawn5;
|
|
80841
81133
|
}
|
|
80842
81134
|
getStatus() {
|
|
80843
81135
|
return { ...this.status, lastError: this.status.lastError ? { ...this.status.lastError } : null };
|
|
@@ -81211,6 +81503,7 @@ var execFileAsync2, MERGE_HANDOFF_GRACE_MS, isRemoteActive, ProjectEngine;
|
|
|
81211
81503
|
var init_project_engine = __esm({
|
|
81212
81504
|
"../engine/src/project-engine.ts"() {
|
|
81213
81505
|
"use strict";
|
|
81506
|
+
init_src();
|
|
81214
81507
|
init_in_process_runtime();
|
|
81215
81508
|
init_pr_monitor();
|
|
81216
81509
|
init_pr_comment_handler();
|
|
@@ -81905,6 +82198,48 @@ ${detail}`
|
|
|
81905
82198
|
if (task.status === "failed") return false;
|
|
81906
82199
|
return (task.mergeRetries ?? 0) < _ProjectEngine.MAX_AUTO_MERGE_RETRIES || this.hasAutoHealableVerificationBufferFailure(task) || this.isRetryCooldownElapsed(task);
|
|
81907
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
|
+
}
|
|
81908
82243
|
internalEnqueueMerge(taskId) {
|
|
81909
82244
|
if (this.shuttingDown) return;
|
|
81910
82245
|
if (this.mergeActive.has(taskId)) return;
|
|
@@ -81912,6 +82247,23 @@ ${detail}`
|
|
|
81912
82247
|
this.mergeQueue.push(taskId);
|
|
81913
82248
|
void this.drainMergeQueue();
|
|
81914
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
|
+
}
|
|
81915
82267
|
async drainMergeQueue() {
|
|
81916
82268
|
if (this.mergeRunning) return;
|
|
81917
82269
|
this.mergeRunning = true;
|
|
@@ -81919,7 +82271,9 @@ ${detail}`
|
|
|
81919
82271
|
const store = this.runtime.getTaskStore();
|
|
81920
82272
|
const cwd = this.config.workingDirectory;
|
|
81921
82273
|
while (this.mergeQueue.length > 0 && !this.shuttingDown) {
|
|
81922
|
-
const taskId = this.
|
|
82274
|
+
const taskId = await this.pickNextMergeTaskId(store);
|
|
82275
|
+
if (!taskId) break;
|
|
82276
|
+
if (this.shuttingDown) break;
|
|
81923
82277
|
const manualResolver = this.manualMergeResolvers.get(taskId);
|
|
81924
82278
|
try {
|
|
81925
82279
|
if (!manualResolver) {
|
|
@@ -82389,12 +82743,9 @@ ${detail}`
|
|
|
82389
82743
|
}
|
|
82390
82744
|
const settings = await store.getSettings();
|
|
82391
82745
|
if (!settings.autoMerge) return;
|
|
82392
|
-
const
|
|
82393
|
-
if (
|
|
82394
|
-
runtimeLog.log(`Auto-merge startup sweep: enqueueing ${
|
|
82395
|
-
for (const t of eligible) {
|
|
82396
|
-
this.internalEnqueueMerge(t.id);
|
|
82397
|
-
}
|
|
82746
|
+
const enqueued = this.enqueueEligibleInReviewTasks(tasks);
|
|
82747
|
+
if (enqueued > 0) {
|
|
82748
|
+
runtimeLog.log(`Auto-merge startup sweep: enqueueing ${enqueued} task(s)`);
|
|
82398
82749
|
}
|
|
82399
82750
|
} catch (err) {
|
|
82400
82751
|
runtimeLog.warn(
|
|
@@ -82410,14 +82761,7 @@ ${detail}`
|
|
|
82410
82761
|
const settings = await store.getSettings();
|
|
82411
82762
|
if (!settings.globalPause && !settings.enginePaused && settings.autoMerge) {
|
|
82412
82763
|
const tasks = await store.listTasks({ column: "in-review" });
|
|
82413
|
-
|
|
82414
|
-
if (t.paused) {
|
|
82415
|
-
continue;
|
|
82416
|
-
}
|
|
82417
|
-
if (this.canMergeTask(t)) {
|
|
82418
|
-
this.internalEnqueueMerge(t.id);
|
|
82419
|
-
}
|
|
82420
|
-
}
|
|
82764
|
+
this.enqueueEligibleInReviewTasks(tasks);
|
|
82421
82765
|
}
|
|
82422
82766
|
} catch (err) {
|
|
82423
82767
|
runtimeLog.warn(
|
|
@@ -82473,14 +82817,7 @@ ${detail}`
|
|
|
82473
82817
|
if (s.autoMerge) {
|
|
82474
82818
|
try {
|
|
82475
82819
|
const tasks = await store.listTasks({ column: "in-review" });
|
|
82476
|
-
|
|
82477
|
-
if (t.paused) {
|
|
82478
|
-
continue;
|
|
82479
|
-
}
|
|
82480
|
-
if (this.canMergeTask(t)) {
|
|
82481
|
-
this.internalEnqueueMerge(t.id);
|
|
82482
|
-
}
|
|
82483
|
-
}
|
|
82820
|
+
this.enqueueEligibleInReviewTasks(tasks);
|
|
82484
82821
|
} catch (err) {
|
|
82485
82822
|
runtimeLog.warn(
|
|
82486
82823
|
`Global unpause: failed to scan in-review tasks for auto-merge: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -82510,14 +82847,7 @@ ${detail}`
|
|
|
82510
82847
|
if (s.autoMerge) {
|
|
82511
82848
|
try {
|
|
82512
82849
|
const tasks = await store.listTasks({ column: "in-review" });
|
|
82513
|
-
|
|
82514
|
-
if (t.paused) {
|
|
82515
|
-
continue;
|
|
82516
|
-
}
|
|
82517
|
-
if (this.canMergeTask(t)) {
|
|
82518
|
-
this.internalEnqueueMerge(t.id);
|
|
82519
|
-
}
|
|
82520
|
-
}
|
|
82850
|
+
this.enqueueEligibleInReviewTasks(tasks);
|
|
82521
82851
|
} catch (err) {
|
|
82522
82852
|
runtimeLog.warn(
|
|
82523
82853
|
`Engine unpause: failed to scan in-review tasks for auto-merge: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -83110,7 +83440,7 @@ var init_peer_exchange_service = __esm({
|
|
|
83110
83440
|
syncIntervalMs;
|
|
83111
83441
|
interval = null;
|
|
83112
83442
|
activeSync = null;
|
|
83113
|
-
|
|
83443
|
+
running = false;
|
|
83114
83444
|
/** Whether settings sync is enabled. Default: false. */
|
|
83115
83445
|
settingsSyncEnabled;
|
|
83116
83446
|
/** Minimum interval between settings syncs with the same node in ms. Default: 5 minutes. */
|
|
@@ -83152,10 +83482,11 @@ var init_peer_exchange_service = __esm({
|
|
|
83152
83482
|
* Begins periodic gossip with all online remote nodes.
|
|
83153
83483
|
*/
|
|
83154
83484
|
start() {
|
|
83155
|
-
if (this.
|
|
83156
|
-
peerExchangeLog.
|
|
83485
|
+
if (this.running) {
|
|
83486
|
+
peerExchangeLog.log("Peer exchange service already running");
|
|
83157
83487
|
return;
|
|
83158
83488
|
}
|
|
83489
|
+
this.running = true;
|
|
83159
83490
|
this.centralCore.listNodes().then((nodes) => {
|
|
83160
83491
|
const onlineRemoteCount = nodes.filter(
|
|
83161
83492
|
(n) => n.type === "remote" && n.status === "online" && n.url
|
|
@@ -83165,6 +83496,7 @@ var init_peer_exchange_service = __esm({
|
|
|
83165
83496
|
peerExchangeLog.warn(`Failed to get initial peer count: ${err}`);
|
|
83166
83497
|
});
|
|
83167
83498
|
this.interval = setInterval(() => {
|
|
83499
|
+
if (!this.running) return;
|
|
83168
83500
|
void this.syncWithAllPeers();
|
|
83169
83501
|
}, this.syncIntervalMs);
|
|
83170
83502
|
}
|
|
@@ -83172,12 +83504,21 @@ var init_peer_exchange_service = __esm({
|
|
|
83172
83504
|
* Stop the peer exchange service.
|
|
83173
83505
|
* Clears the sync interval and prevents further syncs.
|
|
83174
83506
|
*/
|
|
83175
|
-
stop() {
|
|
83507
|
+
async stop() {
|
|
83508
|
+
if (!this.running) {
|
|
83509
|
+
return;
|
|
83510
|
+
}
|
|
83511
|
+
this.running = false;
|
|
83176
83512
|
if (this.interval) {
|
|
83177
83513
|
clearInterval(this.interval);
|
|
83178
83514
|
this.interval = null;
|
|
83179
83515
|
}
|
|
83180
|
-
this.
|
|
83516
|
+
if (this.activeSync) {
|
|
83517
|
+
try {
|
|
83518
|
+
await this.activeSync;
|
|
83519
|
+
} catch {
|
|
83520
|
+
}
|
|
83521
|
+
}
|
|
83181
83522
|
peerExchangeLog.log("Stopped peer exchange service");
|
|
83182
83523
|
}
|
|
83183
83524
|
/**
|
|
@@ -84597,7 +84938,7 @@ For completion:
|
|
|
84597
84938
|
}`;
|
|
84598
84939
|
SESSION_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
84599
84940
|
CLEANUP_INTERVAL_MS2 = 5 * 60 * 1e3;
|
|
84600
|
-
MAX_SESSIONS_PER_IP_PER_HOUR =
|
|
84941
|
+
MAX_SESSIONS_PER_IP_PER_HOUR = 1e3;
|
|
84601
84942
|
RATE_LIMIT_WINDOW_MS2 = 60 * 60 * 1e3;
|
|
84602
84943
|
GENERATION_TIMEOUT_MS = 12e4;
|
|
84603
84944
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -85220,7 +85561,7 @@ var init_src3 = __esm({
|
|
|
85220
85561
|
});
|
|
85221
85562
|
|
|
85222
85563
|
// ../../plugins/fusion-plugin-hermes-runtime/dist/cli-spawn.js
|
|
85223
|
-
import { spawn as
|
|
85564
|
+
import { spawn as spawn6, spawnSync } from "node:child_process";
|
|
85224
85565
|
import os2 from "node:os";
|
|
85225
85566
|
import path, { sep as PATH_SEP } from "node:path";
|
|
85226
85567
|
function resolveBinaryForSpawn(binary) {
|
|
@@ -85330,7 +85671,7 @@ async function invokeHermesCli(prompt, settings, resumeSessionId, signal) {
|
|
|
85330
85671
|
if (settings.profile) {
|
|
85331
85672
|
spawnEnv.HERMES_HOME = hermesProfileHome(settings.profile);
|
|
85332
85673
|
}
|
|
85333
|
-
const child =
|
|
85674
|
+
const child = spawn6(binary, args, {
|
|
85334
85675
|
stdio: ["ignore", "pipe", "pipe"],
|
|
85335
85676
|
env: spawnEnv
|
|
85336
85677
|
});
|
|
@@ -85543,7 +85884,7 @@ var init_dist = __esm({
|
|
|
85543
85884
|
});
|
|
85544
85885
|
|
|
85545
85886
|
// ../../plugins/fusion-plugin-openclaw-runtime/dist/pi-module.js
|
|
85546
|
-
import { spawn as
|
|
85887
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
85547
85888
|
import { randomUUID as randomUUID12 } from "node:crypto";
|
|
85548
85889
|
function asString(v) {
|
|
85549
85890
|
return typeof v === "string" && v.trim() !== "" ? v.trim() : void 0;
|
|
@@ -85620,7 +85961,7 @@ async function promptCli(session, message, config, callbacks, signal) {
|
|
|
85620
85961
|
cb.onToolStart?.("openclaw.agent", { sessionId: session.sessionId });
|
|
85621
85962
|
return new Promise((resolve19, reject) => {
|
|
85622
85963
|
let settled = false;
|
|
85623
|
-
const child =
|
|
85964
|
+
const child = spawn7(config.binaryPath, args, {
|
|
85624
85965
|
stdio: ["ignore", "pipe", "pipe"]
|
|
85625
85966
|
});
|
|
85626
85967
|
const hardKill = setTimeout(() => {
|
|
@@ -85771,7 +86112,7 @@ var init_runtime_adapter2 = __esm({
|
|
|
85771
86112
|
});
|
|
85772
86113
|
|
|
85773
86114
|
// ../../plugins/fusion-plugin-openclaw-runtime/dist/probe.js
|
|
85774
|
-
import { spawn as
|
|
86115
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
85775
86116
|
async function probeOpenClawBinary(opts = {}) {
|
|
85776
86117
|
const startedAt = Date.now();
|
|
85777
86118
|
const binary = opts.binaryPath ?? "openclaw";
|
|
@@ -85782,7 +86123,7 @@ async function probeOpenClawBinary(opts = {}) {
|
|
|
85782
86123
|
resolvePromise({ ...partial, probeDurationMs: Date.now() - startedAt });
|
|
85783
86124
|
};
|
|
85784
86125
|
let settled = false;
|
|
85785
|
-
const child =
|
|
86126
|
+
const child = spawn8(resolvedPath ?? binary, ["--version"], {
|
|
85786
86127
|
stdio: ["ignore", "pipe", "pipe"]
|
|
85787
86128
|
});
|
|
85788
86129
|
const timer = setTimeout(() => {
|
|
@@ -85843,7 +86184,7 @@ async function probeOpenClawBinary(opts = {}) {
|
|
|
85843
86184
|
async function tryResolveBinaryPath(binary) {
|
|
85844
86185
|
return new Promise((resolvePromise) => {
|
|
85845
86186
|
const which = process.platform === "win32" ? "where" : "which";
|
|
85846
|
-
const child =
|
|
86187
|
+
const child = spawn8(which, [binary], { stdio: ["ignore", "pipe", "ignore"] });
|
|
85847
86188
|
let out = "";
|
|
85848
86189
|
child.stdout?.on("data", (chunk) => {
|
|
85849
86190
|
out += chunk.toString("utf-8");
|
|
@@ -92432,7 +92773,7 @@ var init_register_git_github = __esm({
|
|
|
92432
92773
|
});
|
|
92433
92774
|
|
|
92434
92775
|
// ../dashboard/src/terminal.ts
|
|
92435
|
-
import { spawn as
|
|
92776
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
92436
92777
|
import { randomUUID as randomUUID13 } from "node:crypto";
|
|
92437
92778
|
import { EventEmitter as EventEmitter29 } from "node:events";
|
|
92438
92779
|
function extractBaseCommand(command) {
|
|
@@ -92594,7 +92935,7 @@ var init_terminal = __esm({
|
|
|
92594
92935
|
return { sessionId: "", error: validation.error };
|
|
92595
92936
|
}
|
|
92596
92937
|
const sessionId = randomUUID13();
|
|
92597
|
-
const childProcess =
|
|
92938
|
+
const childProcess = spawn9(command, [], {
|
|
92598
92939
|
cwd,
|
|
92599
92940
|
shell: true,
|
|
92600
92941
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -93318,7 +93659,7 @@ function remapSpawnError(err, bin) {
|
|
|
93318
93659
|
return err instanceof Error ? err : new Error(String(err));
|
|
93319
93660
|
}
|
|
93320
93661
|
async function spawnPaperclipCliJson(args, opts) {
|
|
93321
|
-
const { spawn:
|
|
93662
|
+
const { spawn: spawn12 } = await import("node:child_process");
|
|
93322
93663
|
const bin = opts.cliBinaryPath ?? "paperclipai";
|
|
93323
93664
|
const fullArgs = [...args, "--json"];
|
|
93324
93665
|
if (opts.cliConfigPath) {
|
|
@@ -93329,7 +93670,7 @@ async function spawnPaperclipCliJson(args, opts) {
|
|
|
93329
93670
|
return new Promise((resolve19, reject) => {
|
|
93330
93671
|
let child;
|
|
93331
93672
|
try {
|
|
93332
|
-
child =
|
|
93673
|
+
child = spawn12(bin, fullArgs, { stdio: ["ignore", "pipe", "pipe"] });
|
|
93333
93674
|
} catch (err) {
|
|
93334
93675
|
reject(remapSpawnError(err, bin));
|
|
93335
93676
|
return;
|
|
@@ -93841,6 +94182,25 @@ var init_register_runtime_provider_routes = __esm({
|
|
|
93841
94182
|
}
|
|
93842
94183
|
});
|
|
93843
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
|
+
|
|
93844
94204
|
// ../dashboard/src/update-check.ts
|
|
93845
94205
|
var DAY_MS;
|
|
93846
94206
|
var init_update_check = __esm({
|
|
@@ -93850,13 +94210,6 @@ var init_update_check = __esm({
|
|
|
93850
94210
|
}
|
|
93851
94211
|
});
|
|
93852
94212
|
|
|
93853
|
-
// ../dashboard/src/cli-package-version.ts
|
|
93854
|
-
var init_cli_package_version = __esm({
|
|
93855
|
-
"../dashboard/src/cli-package-version.ts"() {
|
|
93856
|
-
"use strict";
|
|
93857
|
-
}
|
|
93858
|
-
});
|
|
93859
|
-
|
|
93860
94213
|
// ../dashboard/src/routes/register-update-check-routes.ts
|
|
93861
94214
|
var init_register_update_check_routes = __esm({
|
|
93862
94215
|
"../dashboard/src/routes/register-update-check-routes.ts"() {
|
|
@@ -94042,6 +94395,7 @@ var init_routes = __esm({
|
|
|
94042
94395
|
init_register_usage_routes();
|
|
94043
94396
|
init_register_auth_routes();
|
|
94044
94397
|
init_register_runtime_provider_routes();
|
|
94398
|
+
init_register_fn_binary_routes();
|
|
94045
94399
|
init_register_update_check_routes();
|
|
94046
94400
|
init_register_integrated_routers();
|
|
94047
94401
|
init_resolve_diff_base();
|
|
@@ -99369,7 +99723,7 @@ async function runTaskPlan(initialPlanArg, yesFlag = false, projectName) {
|
|
|
99369
99723
|
} catch (err) {
|
|
99370
99724
|
clearThinking();
|
|
99371
99725
|
if (err instanceof RateLimitError2) {
|
|
99372
|
-
console.error("\n Rate limit exceeded. Maximum
|
|
99726
|
+
console.error("\n Rate limit exceeded. Maximum 1000 planning sessions per hour.\n");
|
|
99373
99727
|
process.exit(1);
|
|
99374
99728
|
}
|
|
99375
99729
|
console.error(`
|
|
@@ -99526,7 +99880,7 @@ __export(skills_exports, {
|
|
|
99526
99880
|
runSkillsSearch: () => runSkillsSearch,
|
|
99527
99881
|
searchSkills: () => searchSkills
|
|
99528
99882
|
});
|
|
99529
|
-
import { spawn as
|
|
99883
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
99530
99884
|
async function searchSkills(query, limit = 10) {
|
|
99531
99885
|
const url = `${SKILLS_API_BASE}/api/search?q=${encodeURIComponent(query)}&limit=${limit}`;
|
|
99532
99886
|
try {
|
|
@@ -99604,7 +99958,7 @@ async function runSkillsInstall(args, options) {
|
|
|
99604
99958
|
npxArgs.push("--skill", options.skill);
|
|
99605
99959
|
}
|
|
99606
99960
|
npxArgs.push("-y", "-a", "pi");
|
|
99607
|
-
const child =
|
|
99961
|
+
const child = spawn10("npx", npxArgs, {
|
|
99608
99962
|
cwd: process.cwd(),
|
|
99609
99963
|
stdio: "inherit",
|
|
99610
99964
|
shell: true
|
|
@@ -99641,7 +99995,7 @@ import { StringEnum } from "@mariozechner/pi-ai";
|
|
|
99641
99995
|
import { resolve as resolve18, basename as basename11, extname as extname3, join as join41 } from "node:path";
|
|
99642
99996
|
import { readFile as readFile18 } from "node:fs/promises";
|
|
99643
99997
|
import { existsSync as existsSync31 } from "node:fs";
|
|
99644
|
-
import { spawn as
|
|
99998
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
99645
99999
|
var MIME_TYPES2 = {
|
|
99646
100000
|
".png": "image/png",
|
|
99647
100001
|
".jpg": "image/jpeg",
|
|
@@ -101450,7 +101804,7 @@ Status: ${updated.status}`
|
|
|
101450
101804
|
npxArgs.push("--skill", params.skill);
|
|
101451
101805
|
}
|
|
101452
101806
|
npxArgs.push("-y", "-a", "pi");
|
|
101453
|
-
const child =
|
|
101807
|
+
const child = spawn11("npx", npxArgs, {
|
|
101454
101808
|
cwd: resolveProjectRoot(ctx.cwd),
|
|
101455
101809
|
stdio: "pipe",
|
|
101456
101810
|
shell: true
|
|
@@ -101532,7 +101886,7 @@ Status: ${updated.status}`
|
|
|
101532
101886
|
return;
|
|
101533
101887
|
}
|
|
101534
101888
|
const port = trimmed ? parseInt(trimmed, 10) || 4040 : 4040;
|
|
101535
|
-
const child =
|
|
101889
|
+
const child = spawn11("fn", ["dashboard", "--port", String(port)], {
|
|
101536
101890
|
cwd: resolveProjectRoot(ctx.cwd),
|
|
101537
101891
|
stdio: ["ignore", "pipe", "pipe"],
|
|
101538
101892
|
detached: false,
|