@runfusion/fusion 0.17.1 → 0.17.2
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/dist/bin.js +1706 -787
- package/dist/client/assets/{AgentDetailView-BmxnuM0D.js → AgentDetailView-17J-F0Rl.js} +1 -1
- package/dist/client/assets/{AgentsView-1xSqjJxs.js → AgentsView-sbBkb7Wd.js} +3 -3
- package/dist/client/assets/ChatView-BR5cvK_B.js +1 -0
- package/dist/client/assets/{DevServerView-DIrmWI5T.js → DevServerView-GFFVXHVP.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-Sqwdifcb.js → DirectoryPicker-WPDSBdT6.js} +1 -1
- package/dist/client/assets/{DocumentsView-Cx_02o_z.js → DocumentsView-BHpDsIIt.js} +1 -1
- package/dist/client/assets/{InsightsView-DAJSq4gV.js → InsightsView-Bxu0TJkt.js} +1 -1
- package/dist/client/assets/{MemoryView-CCIBAre3.js → MemoryView-CmnzZorw.js} +1 -1
- package/dist/client/assets/{NodesView-D02HxGCl.js → NodesView-CO9_4hCr.js} +1 -1
- package/dist/client/assets/{PiExtensionsManager-DD0fTQNf.js → PiExtensionsManager-4e3MlD62.js} +2 -2
- package/dist/client/assets/{PluginManager-Cfl0VBX9.js → PluginManager-DGN2rvOY.js} +1 -1
- package/dist/client/assets/{ResearchView-B9RqOVbr.js → ResearchView-Dsa6Gykl.js} +1 -1
- package/dist/client/assets/{RoadmapsView-DsH7Hicx.js → RoadmapsView-jHTOK0RQ.js} +1 -1
- package/dist/client/assets/{SettingsModal-Cn_CIPXu.js → SettingsModal-4Z8ZJMzD.js} +1 -1
- package/dist/client/assets/SettingsModal-D0kuJpBA.js +31 -0
- package/dist/client/assets/{SetupWizardModal-k5vqrHZU.js → SetupWizardModal-Bhumd4Rf.js} +1 -1
- package/dist/client/assets/{SkillsView-BIdt5cfB.js → SkillsView-MHweJTz4.js} +1 -1
- package/dist/client/assets/{folder-open-B3TO7t7Z.js → folder-open-BNQW9dE9.js} +1 -1
- package/dist/client/assets/{index-BIJgrHEn.css → index-DEVBHvyW.css} +1 -1
- package/dist/client/assets/index-k_85J1DS.js +682 -0
- package/dist/client/assets/{star-DW-M-BD_.js → star-7L86NZrT.js} +1 -1
- package/dist/client/assets/{upload-BzG6fknr.js → upload-DsAS6tno.js} +1 -1
- package/dist/client/assets/{users-DEicv0kj.js → users-D3u6f2Rz.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +1215 -524
- package/dist/pi-claude-cli/package.json +1 -1
- package/dist/plugins/fusion-plugin-dependency-graph/package.json +1 -1
- package/package.json +1 -1
- package/dist/client/assets/ChatView-CkWkEwXL.js +0 -1
- package/dist/client/assets/SettingsModal-YH_rM1ZT.js +0 -31
- package/dist/client/assets/index-BlkXZ4C5.js +0 -682
package/dist/bin.js
CHANGED
|
@@ -1391,7 +1391,14 @@ Note: Refs (@e1, @e2) are invalidated after page navigation. Re-snapshot after c
|
|
|
1391
1391
|
toolMode: "readonly",
|
|
1392
1392
|
prompt: `You are a UX design reviewer. Verify frontend changes maintain visual polish and consistency with existing UI patterns and design tokens.
|
|
1393
1393
|
|
|
1394
|
-
|
|
1394
|
+
FAST-BAIL RULE (check this FIRST):
|
|
1395
|
+
- The task harness gives you a "Diff Scope" listing the files this task actually changed.
|
|
1396
|
+
- If that list contains NO frontend/UI files (no .tsx/.jsx/.ts/.js component files, no .css/.scss/.sass/.styl, no .html/.vue/.svelte/.astro, no design-token/theme files), respond IMMEDIATELY with a single short line such as "No UI changes in scope \u2014 approved." and STOP.
|
|
1397
|
+
- Do NOT explore the worktree looking for related-looking UI code to critique. If this task didn't change a UI file, your review is a no-op by definition.
|
|
1398
|
+
|
|
1399
|
+
Otherwise, restrict your review to the UI files actually present in the diff scope.
|
|
1400
|
+
|
|
1401
|
+
Design System Review (only for UI files in the diff scope):
|
|
1395
1402
|
1. **Visual Hierarchy** \u2014 Check that the changes maintain consistent heading levels, content flow, and information architecture
|
|
1396
1403
|
2. **Spacing and Typography** \u2014 Verify consistent spacing (margins, padding, gaps) and typography scale usage
|
|
1397
1404
|
3. **Color and Token Consistency** \u2014 Check that CSS custom properties and design tokens are used correctly; no hardcoded color values that bypass the design system
|
|
@@ -1399,15 +1406,16 @@ Design System Review:
|
|
|
1399
1406
|
5. **Responsive Behavior** \u2014 Check that layouts adapt properly across viewport sizes and maintain usability on mobile
|
|
1400
1407
|
6. **Fit with Design Language** \u2014 Verify the visual style matches existing patterns (border radius, shadows, transitions, icon style, etc.)
|
|
1401
1408
|
|
|
1402
|
-
Files to Review:
|
|
1409
|
+
Files to Review (only those that appear in the Diff Scope):
|
|
1403
1410
|
- Modified UI components (React, Vue, Angular, HTML)
|
|
1404
1411
|
- CSS/SCSS/styled-component files
|
|
1405
1412
|
- Design token or theme configuration files
|
|
1406
1413
|
|
|
1407
1414
|
Output Requirements:
|
|
1408
|
-
- If design is consistent and polished
|
|
1409
|
-
- If issues found: describe each finding with specific file paths and suggested corrections
|
|
1410
|
-
- Prioritize issues by impact: layout breaks > visual inconsistency > style preferences
|
|
1415
|
+
- If design is consistent and polished (or there are no UI files in scope): respond with a brief approval line and stop.
|
|
1416
|
+
- If issues found: start your response with "REQUEST REVISION" and describe each finding with specific file paths and suggested corrections.
|
|
1417
|
+
- Prioritize issues by impact: layout breaks > visual inconsistency > style preferences.
|
|
1418
|
+
- Do NOT spend time on stylistic nits when no real issues exist.`
|
|
1411
1419
|
}
|
|
1412
1420
|
];
|
|
1413
1421
|
DOCUMENT_KEY_RE = /^[a-zA-Z0-9_-]{1,64}$/;
|
|
@@ -52659,6 +52667,193 @@ var init_chat_store = __esm({
|
|
|
52659
52667
|
}
|
|
52660
52668
|
});
|
|
52661
52669
|
|
|
52670
|
+
// ../core/src/oauth-credential-interop.ts
|
|
52671
|
+
import { existsSync as existsSync19, readFileSync as readFileSync6 } from "node:fs";
|
|
52672
|
+
import { homedir as homedir4 } from "node:os";
|
|
52673
|
+
import { join as join22 } from "node:path";
|
|
52674
|
+
function getHomeDir4() {
|
|
52675
|
+
return process.env.HOME || process.env.USERPROFILE || homedir4();
|
|
52676
|
+
}
|
|
52677
|
+
function getCodexCliAuthPath(home = getHomeDir4()) {
|
|
52678
|
+
return join22(home, ".codex", "auth.json");
|
|
52679
|
+
}
|
|
52680
|
+
function parseJwtPayload(token) {
|
|
52681
|
+
try {
|
|
52682
|
+
const [, payload = ""] = token.split(".", 3);
|
|
52683
|
+
if (!payload) {
|
|
52684
|
+
return null;
|
|
52685
|
+
}
|
|
52686
|
+
return JSON.parse(Buffer.from(payload, "base64url").toString("utf-8"));
|
|
52687
|
+
} catch {
|
|
52688
|
+
return null;
|
|
52689
|
+
}
|
|
52690
|
+
}
|
|
52691
|
+
function getJwtExpiryMs(token) {
|
|
52692
|
+
if (!token) {
|
|
52693
|
+
return void 0;
|
|
52694
|
+
}
|
|
52695
|
+
const payload = parseJwtPayload(token);
|
|
52696
|
+
const exp = payload?.exp;
|
|
52697
|
+
if (typeof exp !== "number" || !Number.isFinite(exp)) {
|
|
52698
|
+
return void 0;
|
|
52699
|
+
}
|
|
52700
|
+
return exp * 1e3;
|
|
52701
|
+
}
|
|
52702
|
+
function getCodexAccountId(accessToken, fallbackAccountId) {
|
|
52703
|
+
const payload = parseJwtPayload(accessToken);
|
|
52704
|
+
const authClaim = payload?.[OPENAI_AUTH_CLAIM];
|
|
52705
|
+
const claimAccountId = authClaim && typeof authClaim === "object" ? authClaim.chatgpt_account_id : void 0;
|
|
52706
|
+
if (typeof claimAccountId === "string" && claimAccountId.trim().length > 0) {
|
|
52707
|
+
return claimAccountId;
|
|
52708
|
+
}
|
|
52709
|
+
if (typeof fallbackAccountId === "string" && fallbackAccountId.trim().length > 0) {
|
|
52710
|
+
return fallbackAccountId;
|
|
52711
|
+
}
|
|
52712
|
+
return void 0;
|
|
52713
|
+
}
|
|
52714
|
+
function getLastRefreshFallbackExpiryMs(lastRefresh) {
|
|
52715
|
+
if (typeof lastRefresh !== "string" || lastRefresh.trim().length === 0) {
|
|
52716
|
+
return void 0;
|
|
52717
|
+
}
|
|
52718
|
+
const parsed = Date.parse(lastRefresh);
|
|
52719
|
+
if (!Number.isFinite(parsed)) {
|
|
52720
|
+
return void 0;
|
|
52721
|
+
}
|
|
52722
|
+
return parsed + CODEX_REFRESH_FALLBACK_WINDOW_MS;
|
|
52723
|
+
}
|
|
52724
|
+
function isStoredAuthCredential(value) {
|
|
52725
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
52726
|
+
return false;
|
|
52727
|
+
}
|
|
52728
|
+
const record = value;
|
|
52729
|
+
return record.type === "oauth" || record.type === "api_key";
|
|
52730
|
+
}
|
|
52731
|
+
function isValidOauthCredential(credential) {
|
|
52732
|
+
return credential?.type === "oauth" && typeof credential.access === "string" && credential.access.length > 0 && typeof credential.refresh === "string" && credential.refresh.length > 0 && typeof credential.expires === "number" && Number.isFinite(credential.expires) && Date.now() < credential.expires;
|
|
52733
|
+
}
|
|
52734
|
+
function isRefreshableOauthCredential(credential) {
|
|
52735
|
+
return credential?.type === "oauth" && typeof credential.refresh === "string" && credential.refresh.length > 0 && typeof credential.expires === "number" && Number.isFinite(credential.expires);
|
|
52736
|
+
}
|
|
52737
|
+
function compareStoredCredentials(left, right) {
|
|
52738
|
+
if (!left && !right) {
|
|
52739
|
+
return 0;
|
|
52740
|
+
}
|
|
52741
|
+
if (left && !right) {
|
|
52742
|
+
return 1;
|
|
52743
|
+
}
|
|
52744
|
+
if (!left && right) {
|
|
52745
|
+
return -1;
|
|
52746
|
+
}
|
|
52747
|
+
if (left?.type === "api_key" && right?.type !== "api_key") {
|
|
52748
|
+
return 1;
|
|
52749
|
+
}
|
|
52750
|
+
if (right?.type === "api_key" && left?.type !== "api_key") {
|
|
52751
|
+
return -1;
|
|
52752
|
+
}
|
|
52753
|
+
if (left?.type === "oauth" && right?.type === "oauth") {
|
|
52754
|
+
const leftValid = isValidOauthCredential(left);
|
|
52755
|
+
const rightValid = isValidOauthCredential(right);
|
|
52756
|
+
if (leftValid !== rightValid) {
|
|
52757
|
+
return leftValid ? 1 : -1;
|
|
52758
|
+
}
|
|
52759
|
+
const leftRefreshable = isRefreshableOauthCredential(left);
|
|
52760
|
+
const rightRefreshable = isRefreshableOauthCredential(right);
|
|
52761
|
+
if (leftRefreshable !== rightRefreshable) {
|
|
52762
|
+
return leftRefreshable ? 1 : -1;
|
|
52763
|
+
}
|
|
52764
|
+
const leftExpiry = typeof left.expires === "number" && Number.isFinite(left.expires) ? left.expires : -Infinity;
|
|
52765
|
+
const rightExpiry = typeof right.expires === "number" && Number.isFinite(right.expires) ? right.expires : -Infinity;
|
|
52766
|
+
if (leftExpiry !== rightExpiry) {
|
|
52767
|
+
return leftExpiry > rightExpiry ? 1 : -1;
|
|
52768
|
+
}
|
|
52769
|
+
const leftAccessLength = typeof left.access === "string" ? left.access.length : 0;
|
|
52770
|
+
const rightAccessLength = typeof right.access === "string" ? right.access.length : 0;
|
|
52771
|
+
if (leftAccessLength !== rightAccessLength) {
|
|
52772
|
+
return leftAccessLength > rightAccessLength ? 1 : -1;
|
|
52773
|
+
}
|
|
52774
|
+
}
|
|
52775
|
+
return 0;
|
|
52776
|
+
}
|
|
52777
|
+
function choosePreferredStoredCredential(...credentials) {
|
|
52778
|
+
let best;
|
|
52779
|
+
for (const credential of credentials) {
|
|
52780
|
+
if (compareStoredCredentials(credential, best) > 0) {
|
|
52781
|
+
best = credential;
|
|
52782
|
+
}
|
|
52783
|
+
}
|
|
52784
|
+
return best;
|
|
52785
|
+
}
|
|
52786
|
+
function shouldHydrateStoredCredential(current, candidate) {
|
|
52787
|
+
if (!candidate || candidate.type !== "oauth") {
|
|
52788
|
+
return false;
|
|
52789
|
+
}
|
|
52790
|
+
if (current?.type === "api_key") {
|
|
52791
|
+
return false;
|
|
52792
|
+
}
|
|
52793
|
+
return compareStoredCredentials(candidate, current) > 0;
|
|
52794
|
+
}
|
|
52795
|
+
function extractCodexCliStoredCredential(raw) {
|
|
52796
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
52797
|
+
return void 0;
|
|
52798
|
+
}
|
|
52799
|
+
const record = raw;
|
|
52800
|
+
const tokens = record.tokens;
|
|
52801
|
+
if (!tokens || typeof tokens !== "object" || Array.isArray(tokens)) {
|
|
52802
|
+
return void 0;
|
|
52803
|
+
}
|
|
52804
|
+
const tokenRecord = tokens;
|
|
52805
|
+
const access12 = typeof tokenRecord.access_token === "string" ? tokenRecord.access_token : void 0;
|
|
52806
|
+
const refresh = typeof tokenRecord.refresh_token === "string" ? tokenRecord.refresh_token : void 0;
|
|
52807
|
+
if (!access12 || !refresh) {
|
|
52808
|
+
return void 0;
|
|
52809
|
+
}
|
|
52810
|
+
const expires = getJwtExpiryMs(access12) ?? getJwtExpiryMs(typeof tokenRecord.id_token === "string" ? tokenRecord.id_token : void 0) ?? getLastRefreshFallbackExpiryMs(record.last_refresh);
|
|
52811
|
+
if (typeof expires !== "number" || !Number.isFinite(expires)) {
|
|
52812
|
+
return void 0;
|
|
52813
|
+
}
|
|
52814
|
+
const accountId = getCodexAccountId(access12, tokenRecord.account_id);
|
|
52815
|
+
return {
|
|
52816
|
+
type: "oauth",
|
|
52817
|
+
access: access12,
|
|
52818
|
+
refresh,
|
|
52819
|
+
expires,
|
|
52820
|
+
...accountId ? { accountId } : {}
|
|
52821
|
+
};
|
|
52822
|
+
}
|
|
52823
|
+
function readStoredCredentialsFromAuthFile(authPath) {
|
|
52824
|
+
if (!existsSync19(authPath)) {
|
|
52825
|
+
return {};
|
|
52826
|
+
}
|
|
52827
|
+
try {
|
|
52828
|
+
const parsed = JSON.parse(readFileSync6(authPath, "utf-8"));
|
|
52829
|
+
const codexCliCredential = extractCodexCliStoredCredential(parsed);
|
|
52830
|
+
if (codexCliCredential) {
|
|
52831
|
+
return { "openai-codex": codexCliCredential };
|
|
52832
|
+
}
|
|
52833
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
52834
|
+
return {};
|
|
52835
|
+
}
|
|
52836
|
+
const credentials = {};
|
|
52837
|
+
for (const [providerId, value] of Object.entries(parsed)) {
|
|
52838
|
+
if (!isStoredAuthCredential(value)) {
|
|
52839
|
+
continue;
|
|
52840
|
+
}
|
|
52841
|
+
credentials[providerId] = value;
|
|
52842
|
+
}
|
|
52843
|
+
return credentials;
|
|
52844
|
+
} catch {
|
|
52845
|
+
return {};
|
|
52846
|
+
}
|
|
52847
|
+
}
|
|
52848
|
+
var OPENAI_AUTH_CLAIM, CODEX_REFRESH_FALLBACK_WINDOW_MS;
|
|
52849
|
+
var init_oauth_credential_interop = __esm({
|
|
52850
|
+
"../core/src/oauth-credential-interop.ts"() {
|
|
52851
|
+
"use strict";
|
|
52852
|
+
OPENAI_AUTH_CLAIM = "https://api.openai.com/auth";
|
|
52853
|
+
CODEX_REFRESH_FALLBACK_WINDOW_MS = 55 * 60 * 1e3;
|
|
52854
|
+
}
|
|
52855
|
+
});
|
|
52856
|
+
|
|
52662
52857
|
// ../core/src/index.ts
|
|
52663
52858
|
var src_exports = {};
|
|
52664
52859
|
__export(src_exports, {
|
|
@@ -52815,6 +53010,7 @@ __export(src_exports, {
|
|
|
52815
53010
|
buildTriageMemoryInstructions: () => buildTriageMemoryInstructions,
|
|
52816
53011
|
canTransition: () => canTransition,
|
|
52817
53012
|
checkRateLimit: () => checkRateLimit,
|
|
53013
|
+
choosePreferredStoredCredential: () => choosePreferredStoredCredential,
|
|
52818
53014
|
classifyInsightRunError: () => classifyInsightRunError,
|
|
52819
53015
|
clearOverrides: () => clearOverrides,
|
|
52820
53016
|
collectSystemMetrics: () => collectSystemMetrics,
|
|
@@ -52845,6 +53041,7 @@ __export(src_exports, {
|
|
|
52845
53041
|
executeInsightRunLifecycle: () => executeInsightRunLifecycle,
|
|
52846
53042
|
exportAgentsToDirectory: () => exportAgentsToDirectory,
|
|
52847
53043
|
exportSettings: () => exportSettings,
|
|
53044
|
+
extractCodexCliStoredCredential: () => extractCodexCliStoredCredential,
|
|
52848
53045
|
extractDreamProcessorResult: () => extractDreamProcessorResult,
|
|
52849
53046
|
formatPiExtensionSource: () => formatPiExtensionSource,
|
|
52850
53047
|
fromJson: () => fromJson,
|
|
@@ -52855,6 +53052,7 @@ __export(src_exports, {
|
|
|
52855
53052
|
generateMemoryAudit: () => generateMemoryAudit,
|
|
52856
53053
|
getAppVersion: () => getAppVersion,
|
|
52857
53054
|
getAvailableTemplates: () => getAvailableTemplates,
|
|
53055
|
+
getCodexCliAuthPath: () => getCodexCliAuthPath,
|
|
52858
53056
|
getCurrentRepo: () => getCurrentRepo,
|
|
52859
53057
|
getDefaultDailyMemoryScaffold: () => getDefaultDailyMemoryScaffold,
|
|
52860
53058
|
getDefaultDreamsScaffold: () => getDefaultDreamsScaffold,
|
|
@@ -52952,6 +53150,7 @@ __export(src_exports, {
|
|
|
52952
53150
|
readProjectMemoryFile: () => readProjectMemoryFile,
|
|
52953
53151
|
readProjectMemoryFileContent: () => readProjectMemoryFileContent,
|
|
52954
53152
|
readProjectMemoryWithBackend: () => readProjectMemoryWithBackend,
|
|
53153
|
+
readStoredCredentialsFromAuthFile: () => readStoredCredentialsFromAuthFile,
|
|
52955
53154
|
readWorkingMemory: () => readWorkingMemory,
|
|
52956
53155
|
reconcileClaudeCliPaths: () => reconcileClaudeCliPaths,
|
|
52957
53156
|
reconcileDroidCliPaths: () => reconcileDroidCliPaths,
|
|
@@ -52987,6 +53186,7 @@ __export(src_exports, {
|
|
|
52987
53186
|
scheduleQmdProjectMemoryRefresh: () => scheduleQmdProjectMemoryRefresh,
|
|
52988
53187
|
searchProjectMemory: () => searchProjectMemory,
|
|
52989
53188
|
setCreateFnAgent: () => setCreateFnAgent,
|
|
53189
|
+
shouldHydrateStoredCredential: () => shouldHydrateStoredCredential,
|
|
52990
53190
|
shouldSkipBackgroundQmdRefresh: () => shouldSkipBackgroundQmdRefresh,
|
|
52991
53191
|
shouldTriggerExtraction: () => shouldTriggerExtraction,
|
|
52992
53192
|
slugify: () => slugify,
|
|
@@ -53091,13 +53291,14 @@ var init_src = __esm({
|
|
|
53091
53291
|
init_agent_companies_parser();
|
|
53092
53292
|
init_agent_companies_exporter();
|
|
53093
53293
|
init_chat_store();
|
|
53294
|
+
init_oauth_credential_interop();
|
|
53094
53295
|
init_error_message();
|
|
53095
53296
|
}
|
|
53096
53297
|
});
|
|
53097
53298
|
|
|
53098
53299
|
// ../dashboard/src/github-webhooks.ts
|
|
53099
53300
|
import { createHmac, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
|
|
53100
|
-
import { readFileSync as
|
|
53301
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
53101
53302
|
function getGitHubAppConfig() {
|
|
53102
53303
|
const appId = process.env.FUSION_GITHUB_APP_ID;
|
|
53103
53304
|
const webhookSecret = process.env.FUSION_GITHUB_WEBHOOK_SECRET;
|
|
@@ -53107,7 +53308,7 @@ function getGitHubAppConfig() {
|
|
|
53107
53308
|
} else if (process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH) {
|
|
53108
53309
|
if (cachedPrivateKey === void 0) {
|
|
53109
53310
|
try {
|
|
53110
|
-
cachedPrivateKey =
|
|
53311
|
+
cachedPrivateKey = readFileSync7(process.env.FUSION_GITHUB_APP_PRIVATE_KEY_PATH, "utf-8");
|
|
53111
53312
|
} catch {
|
|
53112
53313
|
cachedPrivateKey = null;
|
|
53113
53314
|
}
|
|
@@ -55186,12 +55387,12 @@ var init_github_provider = __esm({
|
|
|
55186
55387
|
});
|
|
55187
55388
|
|
|
55188
55389
|
// ../engine/src/skill-resolver.ts
|
|
55189
|
-
import { existsSync as
|
|
55190
|
-
import { dirname as dirname8, join as
|
|
55390
|
+
import { existsSync as existsSync20, readFileSync as readFileSync8 } from "node:fs";
|
|
55391
|
+
import { dirname as dirname8, join as join23, resolve as resolve11 } from "node:path";
|
|
55191
55392
|
function resolveProjectRoot(cwd) {
|
|
55192
55393
|
let current = resolve11(cwd);
|
|
55193
55394
|
while (true) {
|
|
55194
|
-
if (
|
|
55395
|
+
if (existsSync20(join23(current, ".fusion"))) {
|
|
55195
55396
|
return current;
|
|
55196
55397
|
}
|
|
55197
55398
|
const parent2 = dirname8(current);
|
|
@@ -55202,19 +55403,19 @@ function resolveProjectRoot(cwd) {
|
|
|
55202
55403
|
}
|
|
55203
55404
|
}
|
|
55204
55405
|
function readJsonObject(path5) {
|
|
55205
|
-
if (!
|
|
55406
|
+
if (!existsSync20(path5)) {
|
|
55206
55407
|
return {};
|
|
55207
55408
|
}
|
|
55208
55409
|
try {
|
|
55209
|
-
const parsed = JSON.parse(
|
|
55410
|
+
const parsed = JSON.parse(readFileSync8(path5, "utf-8"));
|
|
55210
55411
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
55211
55412
|
} catch {
|
|
55212
55413
|
return {};
|
|
55213
55414
|
}
|
|
55214
55415
|
}
|
|
55215
55416
|
function readProjectSettings(projectRootDir) {
|
|
55216
|
-
const fusionSettings =
|
|
55217
|
-
if (
|
|
55417
|
+
const fusionSettings = join23(projectRootDir, ".fusion", "settings.json");
|
|
55418
|
+
if (existsSync20(fusionSettings)) {
|
|
55218
55419
|
const parsed = readJsonObject(fusionSettings);
|
|
55219
55420
|
return {
|
|
55220
55421
|
skills: Array.isArray(parsed.skills) ? parsed.skills : void 0,
|
|
@@ -55439,51 +55640,51 @@ var init_context_limit_detector = __esm({
|
|
|
55439
55640
|
});
|
|
55440
55641
|
|
|
55441
55642
|
// ../engine/src/auth-storage.ts
|
|
55442
|
-
import { existsSync as
|
|
55443
|
-
import { homedir as
|
|
55444
|
-
import { join as
|
|
55643
|
+
import { existsSync as existsSync21, readFileSync as readFileSync9 } from "node:fs";
|
|
55644
|
+
import { homedir as homedir5 } from "node:os";
|
|
55645
|
+
import { join as join24 } from "node:path";
|
|
55445
55646
|
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
55446
55647
|
import { getOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
|
55447
|
-
function
|
|
55448
|
-
return process.env.HOME || process.env.USERPROFILE ||
|
|
55648
|
+
function getHomeDir5() {
|
|
55649
|
+
return process.env.HOME || process.env.USERPROFILE || homedir5();
|
|
55449
55650
|
}
|
|
55450
|
-
function getFusionAuthPath(home =
|
|
55451
|
-
return
|
|
55651
|
+
function getFusionAuthPath(home = getHomeDir5()) {
|
|
55652
|
+
return join24(home, ".fusion", "agent", "auth.json");
|
|
55452
55653
|
}
|
|
55453
|
-
function getFusionModelsPath(home =
|
|
55454
|
-
return
|
|
55654
|
+
function getFusionModelsPath(home = getHomeDir5()) {
|
|
55655
|
+
return join24(home, ".fusion", "agent", "models.json");
|
|
55455
55656
|
}
|
|
55456
|
-
function getLegacyAuthPaths(home =
|
|
55657
|
+
function getLegacyAuthPaths(home = getHomeDir5()) {
|
|
55457
55658
|
return [
|
|
55458
|
-
|
|
55459
|
-
|
|
55659
|
+
join24(home, ".pi", "agent", "auth.json"),
|
|
55660
|
+
join24(home, ".pi", "auth.json")
|
|
55460
55661
|
];
|
|
55461
55662
|
}
|
|
55462
|
-
function
|
|
55663
|
+
function getSupplementalAuthPaths(home = getHomeDir5()) {
|
|
55463
55664
|
return [
|
|
55464
|
-
|
|
55465
|
-
|
|
55665
|
+
...getLegacyAuthPaths(home),
|
|
55666
|
+
getCodexCliAuthPath(home)
|
|
55466
55667
|
];
|
|
55467
55668
|
}
|
|
55468
|
-
function
|
|
55669
|
+
function getLegacyModelsPaths(home = getHomeDir5()) {
|
|
55670
|
+
return [
|
|
55671
|
+
join24(home, ".pi", "agent", "models.json"),
|
|
55672
|
+
join24(home, ".pi", "models.json")
|
|
55673
|
+
];
|
|
55674
|
+
}
|
|
55675
|
+
function getModelRegistryModelsPath(home = getHomeDir5()) {
|
|
55469
55676
|
const fusionModelsPath = getFusionModelsPath(home);
|
|
55470
|
-
if (
|
|
55677
|
+
if (existsSync21(fusionModelsPath)) {
|
|
55471
55678
|
return fusionModelsPath;
|
|
55472
55679
|
}
|
|
55473
|
-
return getLegacyModelsPaths(home).find((modelsPath) =>
|
|
55680
|
+
return getLegacyModelsPaths(home).find((modelsPath) => existsSync21(modelsPath)) ?? fusionModelsPath;
|
|
55474
55681
|
}
|
|
55475
|
-
function
|
|
55682
|
+
function readSupplementalCredentials(authPaths = getSupplementalAuthPaths()) {
|
|
55476
55683
|
const credentials = {};
|
|
55477
55684
|
for (const authPath of authPaths) {
|
|
55478
|
-
|
|
55479
|
-
|
|
55480
|
-
|
|
55481
|
-
try {
|
|
55482
|
-
const parsed = JSON.parse(readFileSync8(authPath, "utf-8"));
|
|
55483
|
-
for (const [provider, credential] of Object.entries(parsed)) {
|
|
55484
|
-
credentials[provider] ??= credential;
|
|
55485
|
-
}
|
|
55486
|
-
} catch {
|
|
55685
|
+
const parsed = readStoredCredentialsFromAuthFile(authPath);
|
|
55686
|
+
for (const [provider, credential] of Object.entries(parsed)) {
|
|
55687
|
+
credentials[provider] = choosePreferredStoredCredential(credentials[provider], credential) ?? credential;
|
|
55487
55688
|
}
|
|
55488
55689
|
}
|
|
55489
55690
|
return credentials;
|
|
@@ -55507,14 +55708,14 @@ function resolveStoredCredentialApiKey(providerId, credential) {
|
|
|
55507
55708
|
}
|
|
55508
55709
|
return void 0;
|
|
55509
55710
|
}
|
|
55510
|
-
function readModelsJsonApiKeys(home =
|
|
55711
|
+
function readModelsJsonApiKeys(home = getHomeDir5()) {
|
|
55511
55712
|
const apiKeys = /* @__PURE__ */ new Map();
|
|
55512
55713
|
const modelsPath = getModelRegistryModelsPath(home);
|
|
55513
|
-
if (!
|
|
55714
|
+
if (!existsSync21(modelsPath)) {
|
|
55514
55715
|
return apiKeys;
|
|
55515
55716
|
}
|
|
55516
55717
|
try {
|
|
55517
|
-
const parsed = JSON.parse(
|
|
55718
|
+
const parsed = JSON.parse(readFileSync9(modelsPath, "utf-8"));
|
|
55518
55719
|
const providers = parsed?.providers;
|
|
55519
55720
|
if (providers) {
|
|
55520
55721
|
for (const [providerId, config] of Object.entries(providers)) {
|
|
@@ -55529,8 +55730,20 @@ function readModelsJsonApiKeys(home = getHomeDir4()) {
|
|
|
55529
55730
|
}
|
|
55530
55731
|
function createFusionAuthStorage() {
|
|
55531
55732
|
const primary = AuthStorage.create(getFusionAuthPath());
|
|
55532
|
-
let
|
|
55733
|
+
let supplementalCredentials = readSupplementalCredentials();
|
|
55533
55734
|
let modelsJsonApiKeys = readModelsJsonApiKeys();
|
|
55735
|
+
const syncSupplementalOauthCredentials = () => {
|
|
55736
|
+
for (const [provider, credential] of Object.entries(supplementalCredentials)) {
|
|
55737
|
+
const current = primary.get(provider);
|
|
55738
|
+
if (!shouldHydrateStoredCredential(current, credential)) {
|
|
55739
|
+
continue;
|
|
55740
|
+
}
|
|
55741
|
+
if (credential.type === "oauth" || credential.type === "api_key") {
|
|
55742
|
+
primary.set(provider, credential);
|
|
55743
|
+
}
|
|
55744
|
+
}
|
|
55745
|
+
};
|
|
55746
|
+
syncSupplementalOauthCredentials();
|
|
55534
55747
|
return new Proxy(primary, {
|
|
55535
55748
|
// Forward property writes to the target so that methods like
|
|
55536
55749
|
// `setFallbackResolver` (called by ModelRegistry) correctly update the
|
|
@@ -55544,31 +55757,51 @@ function createFusionAuthStorage() {
|
|
|
55544
55757
|
if (prop === "reload") {
|
|
55545
55758
|
return () => {
|
|
55546
55759
|
target.reload();
|
|
55547
|
-
|
|
55760
|
+
supplementalCredentials = readSupplementalCredentials();
|
|
55761
|
+
syncSupplementalOauthCredentials();
|
|
55548
55762
|
modelsJsonApiKeys = readModelsJsonApiKeys();
|
|
55549
55763
|
};
|
|
55550
55764
|
}
|
|
55551
55765
|
if (prop === "get") {
|
|
55552
|
-
return (provider) =>
|
|
55766
|
+
return (provider) => choosePreferredStoredCredential(
|
|
55767
|
+
target.get(provider),
|
|
55768
|
+
supplementalCredentials[provider]
|
|
55769
|
+
);
|
|
55553
55770
|
}
|
|
55554
55771
|
if (prop === "has") {
|
|
55555
|
-
return (provider) => target.has(provider) || provider in
|
|
55772
|
+
return (provider) => target.has(provider) || provider in supplementalCredentials || modelsJsonApiKeys.has(provider);
|
|
55556
55773
|
}
|
|
55557
55774
|
if (prop === "hasAuth") {
|
|
55558
|
-
return (provider) => target.hasAuth(provider) || Boolean(
|
|
55775
|
+
return (provider) => target.hasAuth(provider) || Boolean(supplementalCredentials[provider]) || modelsJsonApiKeys.has(provider);
|
|
55559
55776
|
}
|
|
55560
55777
|
if (prop === "getAll") {
|
|
55561
|
-
return () =>
|
|
55778
|
+
return () => {
|
|
55779
|
+
const providerIds = /* @__PURE__ */ new Set([
|
|
55780
|
+
...Object.keys(supplementalCredentials),
|
|
55781
|
+
...Object.keys(target.getAll())
|
|
55782
|
+
]);
|
|
55783
|
+
const merged = {};
|
|
55784
|
+
for (const providerId of providerIds) {
|
|
55785
|
+
const credential = choosePreferredStoredCredential(
|
|
55786
|
+
target.get(providerId),
|
|
55787
|
+
supplementalCredentials[providerId]
|
|
55788
|
+
);
|
|
55789
|
+
if (credential) {
|
|
55790
|
+
merged[providerId] = credential;
|
|
55791
|
+
}
|
|
55792
|
+
}
|
|
55793
|
+
return merged;
|
|
55794
|
+
};
|
|
55562
55795
|
}
|
|
55563
55796
|
if (prop === "list") {
|
|
55564
|
-
return () => Array.from(/* @__PURE__ */ new Set([...Object.keys(
|
|
55797
|
+
return () => Array.from(/* @__PURE__ */ new Set([...Object.keys(supplementalCredentials), ...target.list(), ...modelsJsonApiKeys.keys()]));
|
|
55565
55798
|
}
|
|
55566
55799
|
if (prop === "getApiKey") {
|
|
55567
55800
|
return async (provider) => {
|
|
55568
55801
|
const primaryKey = await target.getApiKey(provider);
|
|
55569
55802
|
if (primaryKey) return primaryKey;
|
|
55570
|
-
const
|
|
55571
|
-
if (
|
|
55803
|
+
const supplementalKey = resolveStoredCredentialApiKey(provider, supplementalCredentials[provider]);
|
|
55804
|
+
if (supplementalKey) return supplementalKey;
|
|
55572
55805
|
return modelsJsonApiKeys.get(provider);
|
|
55573
55806
|
};
|
|
55574
55807
|
}
|
|
@@ -55579,17 +55812,18 @@ function createFusionAuthStorage() {
|
|
|
55579
55812
|
var init_auth_storage = __esm({
|
|
55580
55813
|
"../engine/src/auth-storage.ts"() {
|
|
55581
55814
|
"use strict";
|
|
55815
|
+
init_src();
|
|
55582
55816
|
}
|
|
55583
55817
|
});
|
|
55584
55818
|
|
|
55585
55819
|
// ../engine/src/custom-providers.ts
|
|
55586
|
-
import { readFileSync as
|
|
55587
|
-
import { homedir as
|
|
55588
|
-
import { join as
|
|
55820
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
55821
|
+
import { homedir as homedir6 } from "node:os";
|
|
55822
|
+
import { join as join25 } from "node:path";
|
|
55589
55823
|
function readCustomProviders() {
|
|
55590
55824
|
try {
|
|
55591
|
-
const settingsPath =
|
|
55592
|
-
const raw =
|
|
55825
|
+
const settingsPath = join25(homedir6(), ".fusion", "settings.json");
|
|
55826
|
+
const raw = readFileSync10(settingsPath, "utf-8");
|
|
55593
55827
|
const parsed = JSON.parse(raw);
|
|
55594
55828
|
return Array.isArray(parsed.customProviders) ? parsed.customProviders : [];
|
|
55595
55829
|
} catch {
|
|
@@ -55603,11 +55837,11 @@ var init_custom_providers = __esm({
|
|
|
55603
55837
|
});
|
|
55604
55838
|
|
|
55605
55839
|
// ../engine/src/pi.ts
|
|
55606
|
-
import { existsSync as
|
|
55840
|
+
import { existsSync as existsSync22, readFileSync as readFileSync11 } from "node:fs";
|
|
55607
55841
|
import { exec as exec2 } from "node:child_process";
|
|
55608
55842
|
import { promisify as promisify3 } from "node:util";
|
|
55609
55843
|
import { createRequire as createRequire2 } from "node:module";
|
|
55610
|
-
import { basename as basename7, dirname as dirname9, join as
|
|
55844
|
+
import { basename as basename7, dirname as dirname9, join as join26, relative as relative3, isAbsolute as isAbsolute7, resolve as resolve12 } from "node:path";
|
|
55611
55845
|
import {
|
|
55612
55846
|
createAgentSession,
|
|
55613
55847
|
createBashTool,
|
|
@@ -55861,11 +56095,11 @@ function isRetryableModelSelectionError(message) {
|
|
|
55861
56095
|
return normalized.includes("rate limit") || normalized.includes("too many requests") || normalized.includes("429") || normalized.includes("401") || normalized.includes("403") || normalized.includes("unauthorized") || normalized.includes("forbidden") || normalized.includes("authentication") || normalized.includes("invalid api key") || normalized.includes("invalid key") || normalized.includes("api key") || normalized.includes("overloaded") || normalized.includes("quota") || normalized.includes("capacity") || normalized.includes("temporarily unavailable") || normalized.includes("invalid temperature");
|
|
55862
56096
|
}
|
|
55863
56097
|
function readJsonObject2(path5) {
|
|
55864
|
-
if (!
|
|
56098
|
+
if (!existsSync22(path5)) {
|
|
55865
56099
|
return {};
|
|
55866
56100
|
}
|
|
55867
56101
|
try {
|
|
55868
|
-
const parsed = JSON.parse(
|
|
56102
|
+
const parsed = JSON.parse(readFileSync11(path5, "utf-8"));
|
|
55869
56103
|
return parsed && typeof parsed === "object" ? parsed : {};
|
|
55870
56104
|
} catch {
|
|
55871
56105
|
return {};
|
|
@@ -56002,17 +56236,17 @@ function siblingAgentDir(agentDir, siblingRoot) {
|
|
|
56002
56236
|
if (basename7(agentDir) !== "agent") {
|
|
56003
56237
|
return void 0;
|
|
56004
56238
|
}
|
|
56005
|
-
return
|
|
56239
|
+
return join26(dirname9(dirname9(agentDir)), siblingRoot, "agent");
|
|
56006
56240
|
}
|
|
56007
56241
|
function createReadOnlyPiSettingsView(cwd, agentDir) {
|
|
56008
56242
|
const projectRoot = resolvePiExtensionProjectRoot(cwd);
|
|
56009
|
-
const fusionAgentDir = agentDir.includes(`${
|
|
56010
|
-
const legacyAgentDir = agentDir.includes(`${
|
|
56011
|
-
const legacyGlobalSettings = legacyAgentDir ? readJsonObject2(
|
|
56012
|
-
const fusionGlobalSettings = fusionAgentDir ? readJsonObject2(
|
|
56013
|
-
const directGlobalSettings = readJsonObject2(
|
|
56243
|
+
const fusionAgentDir = agentDir.includes(`${join26(".fusion", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".fusion");
|
|
56244
|
+
const legacyAgentDir = agentDir.includes(`${join26(".pi", "agent")}`) ? agentDir : siblingAgentDir(agentDir, ".pi");
|
|
56245
|
+
const legacyGlobalSettings = legacyAgentDir ? readJsonObject2(join26(legacyAgentDir, "settings.json")) : {};
|
|
56246
|
+
const fusionGlobalSettings = fusionAgentDir ? readJsonObject2(join26(fusionAgentDir, "settings.json")) : {};
|
|
56247
|
+
const directGlobalSettings = readJsonObject2(join26(agentDir, "settings.json"));
|
|
56014
56248
|
const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
|
|
56015
|
-
const fusionProjectSettings = readJsonObject2(
|
|
56249
|
+
const fusionProjectSettings = readJsonObject2(join26(projectRoot, ".fusion", "settings.json"));
|
|
56016
56250
|
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
|
56017
56251
|
return {
|
|
56018
56252
|
getGlobalSettings: () => structuredClone(globalSettings),
|
|
@@ -56023,27 +56257,27 @@ function createReadOnlyPiSettingsView(cwd, agentDir) {
|
|
|
56023
56257
|
function getPackageManagerAgentDir() {
|
|
56024
56258
|
const fusionAgentDir = getFusionAgentDir();
|
|
56025
56259
|
const legacyAgentDir = getLegacyPiAgentDir();
|
|
56026
|
-
const fusionSettings = readJsonObject2(
|
|
56027
|
-
const legacySettings = readJsonObject2(
|
|
56028
|
-
if (hasPackageManagerSettings(fusionSettings) || !
|
|
56260
|
+
const fusionSettings = readJsonObject2(join26(fusionAgentDir, "settings.json"));
|
|
56261
|
+
const legacySettings = readJsonObject2(join26(legacyAgentDir, "settings.json"));
|
|
56262
|
+
if (hasPackageManagerSettings(fusionSettings) || !existsSync22(legacyAgentDir)) {
|
|
56029
56263
|
return fusionAgentDir;
|
|
56030
56264
|
}
|
|
56031
56265
|
if (hasPackageManagerSettings(legacySettings)) {
|
|
56032
56266
|
return legacyAgentDir;
|
|
56033
56267
|
}
|
|
56034
|
-
return
|
|
56268
|
+
return existsSync22(fusionAgentDir) ? fusionAgentDir : legacyAgentDir;
|
|
56035
56269
|
}
|
|
56036
56270
|
function resolveVendoredClaudeCliEntry() {
|
|
56037
56271
|
try {
|
|
56038
56272
|
const require_3 = createRequire2(import.meta.url);
|
|
56039
56273
|
const pkgJsonPath = require_3.resolve("@fusion/pi-claude-cli/package.json");
|
|
56040
|
-
const pkgJson = JSON.parse(
|
|
56274
|
+
const pkgJson = JSON.parse(readFileSync11(pkgJsonPath, "utf-8"));
|
|
56041
56275
|
const extensions = pkgJson.pi?.extensions;
|
|
56042
56276
|
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
56043
56277
|
const entry = extensions[0];
|
|
56044
56278
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
56045
56279
|
const path5 = resolve12(dirname9(pkgJsonPath), entry);
|
|
56046
|
-
return
|
|
56280
|
+
return existsSync22(path5) ? path5 : null;
|
|
56047
56281
|
} catch {
|
|
56048
56282
|
return null;
|
|
56049
56283
|
}
|
|
@@ -56052,13 +56286,13 @@ function resolveVendoredDroidCliEntry() {
|
|
|
56052
56286
|
try {
|
|
56053
56287
|
const require_3 = createRequire2(import.meta.url);
|
|
56054
56288
|
const pkgJsonPath = require_3.resolve("@fusion/droid-cli/package.json");
|
|
56055
|
-
const pkgJson = JSON.parse(
|
|
56289
|
+
const pkgJson = JSON.parse(readFileSync11(pkgJsonPath, "utf-8"));
|
|
56056
56290
|
const extensions = pkgJson.pi?.extensions;
|
|
56057
56291
|
if (!Array.isArray(extensions) || extensions.length === 0) return null;
|
|
56058
56292
|
const entry = extensions[0];
|
|
56059
56293
|
if (typeof entry !== "string" || entry.length === 0) return null;
|
|
56060
56294
|
const path5 = resolve12(dirname9(pkgJsonPath), entry);
|
|
56061
|
-
return
|
|
56295
|
+
return existsSync22(path5) ? path5 : null;
|
|
56062
56296
|
} catch {
|
|
56063
56297
|
return null;
|
|
56064
56298
|
}
|
|
@@ -56086,7 +56320,7 @@ async function registerExtensionProviders(cwd, modelRegistry) {
|
|
|
56086
56320
|
const extensionsResult = await discoverAndLoadExtensions(
|
|
56087
56321
|
doubleReconciledPaths,
|
|
56088
56322
|
cwd,
|
|
56089
|
-
|
|
56323
|
+
join26(resolvePiExtensionProjectRoot(cwd), ".fusion", "disabled-auto-extension-discovery")
|
|
56090
56324
|
);
|
|
56091
56325
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
56092
56326
|
extensionsLog.warn(`Failed to load ${path5}: ${error}`);
|
|
@@ -56141,10 +56375,10 @@ async function isCompleteGitWorktree(worktreePath) {
|
|
|
56141
56375
|
}
|
|
56142
56376
|
}
|
|
56143
56377
|
async function assertValidWorktreeSession(cwd, projectRoot) {
|
|
56144
|
-
if (!
|
|
56378
|
+
if (!existsSync22(cwd)) {
|
|
56145
56379
|
throw new Error(`Refusing to start coding agent in missing worktree: ${cwd}`);
|
|
56146
56380
|
}
|
|
56147
|
-
if (!
|
|
56381
|
+
if (!existsSync22(join26(cwd, ".git")) || !await isCompleteGitWorktree(cwd)) {
|
|
56148
56382
|
throw new Error(`Refusing to start coding agent in incomplete worktree: ${cwd}`);
|
|
56149
56383
|
}
|
|
56150
56384
|
if (!await isRegisteredGitWorktree(projectRoot, cwd)) {
|
|
@@ -56725,7 +56959,7 @@ ${source.content ?? ""}`;
|
|
|
56725
56959
|
|
|
56726
56960
|
// ../engine/src/research/providers/local-docs-provider.ts
|
|
56727
56961
|
import { promises as fs } from "node:fs";
|
|
56728
|
-
import { extname as extname2, join as
|
|
56962
|
+
import { extname as extname2, join as join27, relative as relative4, resolve as resolve13 } from "node:path";
|
|
56729
56963
|
function buildExcerpt(content, terms) {
|
|
56730
56964
|
const lower = content.toLowerCase();
|
|
56731
56965
|
const first = terms.find((term) => lower.includes(term));
|
|
@@ -56854,7 +57088,7 @@ var init_local_docs_provider = __esm({
|
|
|
56854
57088
|
const rootEntries = await fs.readdir(this.projectRoot, { withFileTypes: true });
|
|
56855
57089
|
for (const entry of rootEntries) {
|
|
56856
57090
|
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) {
|
|
56857
|
-
files.push(
|
|
57091
|
+
files.push(join27(this.projectRoot, entry.name));
|
|
56858
57092
|
}
|
|
56859
57093
|
}
|
|
56860
57094
|
return [...new Set(files)];
|
|
@@ -56864,7 +57098,7 @@ var init_local_docs_provider = __esm({
|
|
|
56864
57098
|
const entries = await fs.readdir(dir2, { withFileTypes: true });
|
|
56865
57099
|
for (const entry of entries) {
|
|
56866
57100
|
this.throwIfAborted(signal);
|
|
56867
|
-
const fullPath =
|
|
57101
|
+
const fullPath = join27(dir2, entry.name);
|
|
56868
57102
|
const relPath = relative4(this.projectRoot, fullPath).replace(/\\/g, "/");
|
|
56869
57103
|
if (matchesGitignore(relPath, ignorePatterns)) continue;
|
|
56870
57104
|
if (entry.isDirectory()) {
|
|
@@ -56889,7 +57123,7 @@ var init_local_docs_provider = __esm({
|
|
|
56889
57123
|
}
|
|
56890
57124
|
async readGitignore() {
|
|
56891
57125
|
try {
|
|
56892
|
-
const content = await fs.readFile(
|
|
57126
|
+
const content = await fs.readFile(join27(this.projectRoot, ".gitignore"), "utf-8");
|
|
56893
57127
|
return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
56894
57128
|
} catch {
|
|
56895
57129
|
return [];
|
|
@@ -57499,9 +57733,9 @@ var init_research_step_runner = __esm({
|
|
|
57499
57733
|
|
|
57500
57734
|
// ../engine/src/agent-tools.ts
|
|
57501
57735
|
import { appendFile as appendFile3, mkdir as mkdir11, readFile as readFile12, readdir as readdir7, stat as stat4, writeFile as writeFile10 } from "node:fs/promises";
|
|
57502
|
-
import { existsSync as
|
|
57736
|
+
import { existsSync as existsSync23 } from "node:fs";
|
|
57503
57737
|
import { createHash as createHash4 } from "node:crypto";
|
|
57504
|
-
import { join as
|
|
57738
|
+
import { join as join28 } from "node:path";
|
|
57505
57739
|
import { Type } from "@mariozechner/pi-ai";
|
|
57506
57740
|
function sanitizeAgentMemoryId(agentId) {
|
|
57507
57741
|
return agentId.trim().replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "agent";
|
|
@@ -57513,16 +57747,16 @@ function agentDreamsDisplayPath(agentId) {
|
|
|
57513
57747
|
return `${AGENT_MEMORY_ROOT2}/${sanitizeAgentMemoryId(agentId)}/${AGENT_DREAMS_FILENAME2}`;
|
|
57514
57748
|
}
|
|
57515
57749
|
function agentMemoryDirectory(rootDir, agentId) {
|
|
57516
|
-
return
|
|
57750
|
+
return join28(rootDir, AGENT_MEMORY_ROOT2, sanitizeAgentMemoryId(agentId));
|
|
57517
57751
|
}
|
|
57518
57752
|
function agentMemoryFilePath(rootDir, agentId) {
|
|
57519
|
-
return
|
|
57753
|
+
return join28(agentMemoryDirectory(rootDir, agentId), AGENT_MEMORY_FILENAME2);
|
|
57520
57754
|
}
|
|
57521
57755
|
function agentDreamsFilePath(rootDir, agentId) {
|
|
57522
|
-
return
|
|
57756
|
+
return join28(agentMemoryDirectory(rootDir, agentId), AGENT_DREAMS_FILENAME2);
|
|
57523
57757
|
}
|
|
57524
57758
|
function agentDailyFilePath(rootDir, agentId, date = /* @__PURE__ */ new Date()) {
|
|
57525
|
-
return
|
|
57759
|
+
return join28(agentMemoryDirectory(rootDir, agentId), `${date.toISOString().slice(0, 10)}.md`);
|
|
57526
57760
|
}
|
|
57527
57761
|
function qmdAgentMemoryCollectionName(rootDir, agentId) {
|
|
57528
57762
|
const hash = createHash4("sha1").update(`${rootDir}:${agentId}`).digest("hex").slice(0, 12);
|
|
@@ -57558,7 +57792,7 @@ async function syncAgentMemoryFile(rootDir, agentMemory) {
|
|
|
57558
57792
|
const dir2 = agentMemoryDirectory(rootDir, agentMemory.agentId);
|
|
57559
57793
|
await mkdir11(dir2, { recursive: true });
|
|
57560
57794
|
const longTermPath = agentMemoryFilePath(rootDir, agentMemory.agentId);
|
|
57561
|
-
if (!
|
|
57795
|
+
if (!existsSync23(longTermPath)) {
|
|
57562
57796
|
const title = agentMemory.agentName?.trim() ? `# Agent Memory: ${agentMemory.agentName.trim()}` : "# Agent Memory";
|
|
57563
57797
|
const fileContent = `${title}
|
|
57564
57798
|
|
|
@@ -57569,11 +57803,11 @@ ${content || ""}
|
|
|
57569
57803
|
await writeFile10(longTermPath, fileContent, "utf-8");
|
|
57570
57804
|
}
|
|
57571
57805
|
const dreamsPath = agentDreamsFilePath(rootDir, agentMemory.agentId);
|
|
57572
|
-
if (!
|
|
57806
|
+
if (!existsSync23(dreamsPath)) {
|
|
57573
57807
|
await writeFile10(dreamsPath, "# Agent Memory Dreams\n\n<!-- Synthesized patterns from this agent's daily notes. -->\n", "utf-8");
|
|
57574
57808
|
}
|
|
57575
57809
|
const dailyPath = agentDailyFilePath(rootDir, agentMemory.agentId);
|
|
57576
|
-
if (!
|
|
57810
|
+
if (!existsSync23(dailyPath)) {
|
|
57577
57811
|
await writeFile10(dailyPath, `# Agent Daily Memory ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}
|
|
57578
57812
|
|
|
57579
57813
|
<!-- Running observations for this agent. -->
|
|
@@ -57597,7 +57831,7 @@ async function listAgentMemoryFiles2(rootDir, agentMemory) {
|
|
|
57597
57831
|
}
|
|
57598
57832
|
for (const entry of entries) {
|
|
57599
57833
|
if (!DAILY_AGENT_MEMORY_RE2.test(entry)) continue;
|
|
57600
|
-
const absPath =
|
|
57834
|
+
const absPath = join28(dir2, entry);
|
|
57601
57835
|
const fileStat = await stat4(absPath);
|
|
57602
57836
|
if (fileStat.isFile()) {
|
|
57603
57837
|
files.push({
|
|
@@ -57759,7 +57993,7 @@ function resolveAgentMemoryPath(rootDir, agentId, path5) {
|
|
|
57759
57993
|
return null;
|
|
57760
57994
|
}
|
|
57761
57995
|
return {
|
|
57762
|
-
absPath:
|
|
57996
|
+
absPath: join28(agentMemoryDirectory(rootDir, agentId), filename),
|
|
57763
57997
|
displayPath: `${prefix}${filename}`
|
|
57764
57998
|
};
|
|
57765
57999
|
}
|
|
@@ -60056,6 +60290,38 @@ var init_notifier = __esm({
|
|
|
60056
60290
|
}
|
|
60057
60291
|
});
|
|
60058
60292
|
|
|
60293
|
+
// ../engine/src/fallback-model-observer.ts
|
|
60294
|
+
function buildFallbackLogMessage(label, payload) {
|
|
60295
|
+
return `[fallback] ${label} switched from ${payload.primaryModel} to ${payload.fallbackModel} (${payload.triggerPoint})`;
|
|
60296
|
+
}
|
|
60297
|
+
function createFallbackModelObserver(options) {
|
|
60298
|
+
return async (payload) => {
|
|
60299
|
+
const taskId = options.taskId ?? payload.taskId;
|
|
60300
|
+
const taskTitle = options.taskTitle ?? payload.taskTitle;
|
|
60301
|
+
const message = buildFallbackLogMessage(options.label, payload);
|
|
60302
|
+
if (taskId && options.store?.logEntry) {
|
|
60303
|
+
await options.store.logEntry(taskId, message).catch(() => void 0);
|
|
60304
|
+
}
|
|
60305
|
+
if (taskId && options.store?.appendAgentLog) {
|
|
60306
|
+
await options.store.appendAgentLog(taskId, message, "text", void 0, options.agent).catch(() => void 0);
|
|
60307
|
+
}
|
|
60308
|
+
await notifyFallbackUsed({
|
|
60309
|
+
primaryModel: payload.primaryModel,
|
|
60310
|
+
fallbackModel: payload.fallbackModel,
|
|
60311
|
+
triggerPoint: payload.triggerPoint,
|
|
60312
|
+
taskId,
|
|
60313
|
+
taskTitle,
|
|
60314
|
+
timestamp: payload.timestamp
|
|
60315
|
+
});
|
|
60316
|
+
};
|
|
60317
|
+
}
|
|
60318
|
+
var init_fallback_model_observer = __esm({
|
|
60319
|
+
"../engine/src/fallback-model-observer.ts"() {
|
|
60320
|
+
"use strict";
|
|
60321
|
+
init_notifier();
|
|
60322
|
+
}
|
|
60323
|
+
});
|
|
60324
|
+
|
|
60059
60325
|
// ../engine/src/reviewer.ts
|
|
60060
60326
|
async function reviewStep(cwd, taskId, stepNumber, stepName, reviewType, promptContent, baseline, options = {}) {
|
|
60061
60327
|
let liveSettings = options.settings;
|
|
@@ -60186,7 +60452,13 @@ async function reviewStep(cwd, taskId, stepNumber, stepName, reviewType, promptC
|
|
|
60186
60452
|
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
60187
60453
|
taskId: options.taskId,
|
|
60188
60454
|
taskTitle: options.taskTitle,
|
|
60189
|
-
onFallbackModelUsed:
|
|
60455
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
60456
|
+
agent: "reviewer",
|
|
60457
|
+
label: "reviewer",
|
|
60458
|
+
store: options.store,
|
|
60459
|
+
taskId: options.taskId,
|
|
60460
|
+
taskTitle: options.taskTitle
|
|
60461
|
+
}),
|
|
60190
60462
|
beforeSpawnSession: async () => {
|
|
60191
60463
|
if (!options.store) return;
|
|
60192
60464
|
let finalSettings;
|
|
@@ -60373,7 +60645,7 @@ var init_reviewer = __esm({
|
|
|
60373
60645
|
init_logger2();
|
|
60374
60646
|
init_usage_limit_detector();
|
|
60375
60647
|
init_agent_instructions();
|
|
60376
|
-
|
|
60648
|
+
init_fallback_model_observer();
|
|
60377
60649
|
init_agent_tools();
|
|
60378
60650
|
REVIEWER_SYSTEM_PROMPT = `You are an independent code and plan reviewer.
|
|
60379
60651
|
|
|
@@ -60734,7 +61006,7 @@ var init_recovery_policy = __esm({
|
|
|
60734
61006
|
// ../engine/src/triage.ts
|
|
60735
61007
|
import { Type as Type2 } from "@mariozechner/pi-ai";
|
|
60736
61008
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
60737
|
-
import { join as
|
|
61009
|
+
import { join as join29 } from "node:path";
|
|
60738
61010
|
function extractPromptDeclaredTitle(prompt, taskId) {
|
|
60739
61011
|
const headingMatch = prompt.match(/^#\s+Task:\s+([A-Z]+-\d+)\s+-\s+(.+)$/m);
|
|
60740
61012
|
if (!headingMatch) return null;
|
|
@@ -60763,9 +61035,9 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
60763
61035
|
return { attachmentContents, imageContents };
|
|
60764
61036
|
}
|
|
60765
61037
|
const { readFile: readFile26 } = await import("node:fs/promises");
|
|
60766
|
-
const { join:
|
|
61038
|
+
const { join: join72 } = await import("node:path");
|
|
60767
61039
|
for (const att of attachments) {
|
|
60768
|
-
const filePath =
|
|
61040
|
+
const filePath = join72(
|
|
60769
61041
|
rootDir,
|
|
60770
61042
|
".fusion",
|
|
60771
61043
|
"tasks",
|
|
@@ -60990,7 +61262,7 @@ var init_triage = __esm({
|
|
|
60990
61262
|
init_concurrency();
|
|
60991
61263
|
init_agent_logger();
|
|
60992
61264
|
init_agent_instructions();
|
|
60993
|
-
|
|
61265
|
+
init_fallback_model_observer();
|
|
60994
61266
|
init_logger2();
|
|
60995
61267
|
init_usage_limit_detector();
|
|
60996
61268
|
init_transient_error_detector();
|
|
@@ -61600,7 +61872,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
61600
61872
|
return false;
|
|
61601
61873
|
}
|
|
61602
61874
|
const settings = await this.store.getSettings();
|
|
61603
|
-
const promptPath =
|
|
61875
|
+
const promptPath = join29(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
|
|
61604
61876
|
const written = await readFile14(promptPath, "utf-8").catch((err) => {
|
|
61605
61877
|
const msg = err instanceof Error ? err.message : String(err);
|
|
61606
61878
|
planLog.warn(`${task.id}: failed to read PROMPT.md during approved-spec recovery (${promptPath}): ${msg}`);
|
|
@@ -61830,7 +62102,13 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
61830
62102
|
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
61831
62103
|
taskId: task.id,
|
|
61832
62104
|
taskTitle: task.title,
|
|
61833
|
-
onFallbackModelUsed:
|
|
62105
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
62106
|
+
agent: "triage",
|
|
62107
|
+
label: "triage",
|
|
62108
|
+
store: this.store,
|
|
62109
|
+
taskId: task.id,
|
|
62110
|
+
taskTitle: task.title
|
|
62111
|
+
})
|
|
61834
62112
|
});
|
|
61835
62113
|
const modelDesc = describeModel(session);
|
|
61836
62114
|
planLog.log(`${task.id}: using model ${modelDesc}`);
|
|
@@ -61973,7 +62251,13 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
61973
62251
|
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
61974
62252
|
taskId: task.id,
|
|
61975
62253
|
taskTitle: task.title,
|
|
61976
|
-
onFallbackModelUsed:
|
|
62254
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
62255
|
+
agent: "triage",
|
|
62256
|
+
label: "triage",
|
|
62257
|
+
store: this.store,
|
|
62258
|
+
taskId: task.id,
|
|
62259
|
+
taskTitle: task.title
|
|
62260
|
+
})
|
|
61977
62261
|
});
|
|
61978
62262
|
session = fallbackResult.session;
|
|
61979
62263
|
const fallbackModelDesc = describeModel(session);
|
|
@@ -62058,7 +62342,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
62058
62342
|
return;
|
|
62059
62343
|
}
|
|
62060
62344
|
const written = await readFile14(
|
|
62061
|
-
|
|
62345
|
+
join29(this.rootDir, promptPath),
|
|
62062
62346
|
"utf-8"
|
|
62063
62347
|
).catch((err) => {
|
|
62064
62348
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -62377,9 +62661,9 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
62377
62661
|
}
|
|
62378
62662
|
try {
|
|
62379
62663
|
const { readFile: readFile26 } = await import("node:fs/promises");
|
|
62380
|
-
const { join:
|
|
62664
|
+
const { join: join72 } = await import("node:path");
|
|
62381
62665
|
const promptContent = await readFile26(
|
|
62382
|
-
|
|
62666
|
+
join72(rootDir, promptPath),
|
|
62383
62667
|
"utf-8"
|
|
62384
62668
|
).catch((err) => {
|
|
62385
62669
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -62607,160 +62891,8 @@ Take a completely different approach to writing this specification. Do NOT repea
|
|
|
62607
62891
|
}
|
|
62608
62892
|
});
|
|
62609
62893
|
|
|
62610
|
-
// ../engine/src/
|
|
62611
|
-
|
|
62612
|
-
__export(session_token_usage_exports, {
|
|
62613
|
-
accumulateSessionTokenUsage: () => accumulateSessionTokenUsage
|
|
62614
|
-
});
|
|
62615
|
-
function readSessionStats(session) {
|
|
62616
|
-
const accessor = session.getSessionStats;
|
|
62617
|
-
if (typeof accessor !== "function") return void 0;
|
|
62618
|
-
try {
|
|
62619
|
-
return accessor.call(session);
|
|
62620
|
-
} catch {
|
|
62621
|
-
return void 0;
|
|
62622
|
-
}
|
|
62623
|
-
}
|
|
62624
|
-
async function accumulateSessionTokenUsage(store, taskId, session) {
|
|
62625
|
-
try {
|
|
62626
|
-
const stats = readSessionStats(session);
|
|
62627
|
-
const tokens = stats?.tokens;
|
|
62628
|
-
if (!tokens) return;
|
|
62629
|
-
const currentInput = (tokens.input ?? 0) + (tokens.cacheWrite ?? 0);
|
|
62630
|
-
const currentOutput = tokens.output ?? 0;
|
|
62631
|
-
const currentCached = tokens.cacheRead ?? 0;
|
|
62632
|
-
const baseline = sessionBaselines.get(session) ?? { input: 0, output: 0, cached: 0 };
|
|
62633
|
-
const inputDelta = Math.max(0, currentInput - baseline.input);
|
|
62634
|
-
const outputDelta = Math.max(0, currentOutput - baseline.output);
|
|
62635
|
-
const cachedDelta = Math.max(0, currentCached - baseline.cached);
|
|
62636
|
-
sessionBaselines.set(session, {
|
|
62637
|
-
input: currentInput,
|
|
62638
|
-
output: currentOutput,
|
|
62639
|
-
cached: currentCached
|
|
62640
|
-
});
|
|
62641
|
-
if (inputDelta === 0 && outputDelta === 0 && cachedDelta === 0) return;
|
|
62642
|
-
const task = await store.getTask(taskId);
|
|
62643
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
62644
|
-
const newInput = (task.tokenUsage?.inputTokens ?? 0) + inputDelta;
|
|
62645
|
-
const newOutput = (task.tokenUsage?.outputTokens ?? 0) + outputDelta;
|
|
62646
|
-
const newCached = (task.tokenUsage?.cachedTokens ?? 0) + cachedDelta;
|
|
62647
|
-
await store.updateTask(taskId, {
|
|
62648
|
-
tokenUsage: {
|
|
62649
|
-
inputTokens: newInput,
|
|
62650
|
-
outputTokens: newOutput,
|
|
62651
|
-
cachedTokens: newCached,
|
|
62652
|
-
totalTokens: newInput + newOutput + newCached,
|
|
62653
|
-
firstUsedAt: task.tokenUsage?.firstUsedAt ?? now,
|
|
62654
|
-
lastUsedAt: now
|
|
62655
|
-
}
|
|
62656
|
-
});
|
|
62657
|
-
} catch (err) {
|
|
62658
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
62659
|
-
log14.warn(`${taskId}: session token usage accumulate failed: ${message}`);
|
|
62660
|
-
}
|
|
62661
|
-
}
|
|
62662
|
-
var log14, sessionBaselines;
|
|
62663
|
-
var init_session_token_usage = __esm({
|
|
62664
|
-
"../engine/src/session-token-usage.ts"() {
|
|
62665
|
-
"use strict";
|
|
62666
|
-
init_logger2();
|
|
62667
|
-
log14 = createLogger2("session-token-usage");
|
|
62668
|
-
sessionBaselines = /* @__PURE__ */ new WeakMap();
|
|
62669
|
-
}
|
|
62670
|
-
});
|
|
62671
|
-
|
|
62672
|
-
// ../engine/src/run-audit.ts
|
|
62673
|
-
function createRunAuditor(store, context) {
|
|
62674
|
-
if (!context) {
|
|
62675
|
-
return {
|
|
62676
|
-
git: async () => {
|
|
62677
|
-
},
|
|
62678
|
-
database: async () => {
|
|
62679
|
-
},
|
|
62680
|
-
filesystem: async () => {
|
|
62681
|
-
}
|
|
62682
|
-
};
|
|
62683
|
-
}
|
|
62684
|
-
const hasRecordAuditEvent = typeof store.recordRunAuditEvent === "function";
|
|
62685
|
-
if (!hasRecordAuditEvent) {
|
|
62686
|
-
return {
|
|
62687
|
-
git: async () => {
|
|
62688
|
-
},
|
|
62689
|
-
database: async () => {
|
|
62690
|
-
},
|
|
62691
|
-
filesystem: async () => {
|
|
62692
|
-
}
|
|
62693
|
-
};
|
|
62694
|
-
}
|
|
62695
|
-
return {
|
|
62696
|
-
git: async (input) => {
|
|
62697
|
-
const eventInput = {
|
|
62698
|
-
taskId: context.taskId,
|
|
62699
|
-
agentId: context.agentId,
|
|
62700
|
-
runId: context.runId,
|
|
62701
|
-
domain: "git",
|
|
62702
|
-
mutationType: input.type,
|
|
62703
|
-
target: input.target,
|
|
62704
|
-
metadata: {
|
|
62705
|
-
phase: context.phase,
|
|
62706
|
-
...context.source ? { source: context.source } : {},
|
|
62707
|
-
...input.metadata
|
|
62708
|
-
}
|
|
62709
|
-
};
|
|
62710
|
-
await store.recordRunAuditEvent(eventInput);
|
|
62711
|
-
},
|
|
62712
|
-
database: async (input) => {
|
|
62713
|
-
const inferredTaskId = input.target.startsWith("FN-") || input.target.startsWith("KB-") ? input.target : context.taskId;
|
|
62714
|
-
const eventInput = {
|
|
62715
|
-
taskId: inferredTaskId,
|
|
62716
|
-
agentId: context.agentId,
|
|
62717
|
-
runId: context.runId,
|
|
62718
|
-
domain: "database",
|
|
62719
|
-
mutationType: input.type,
|
|
62720
|
-
target: input.target,
|
|
62721
|
-
metadata: {
|
|
62722
|
-
phase: context.phase,
|
|
62723
|
-
...context.source ? { source: context.source } : {},
|
|
62724
|
-
...input.metadata
|
|
62725
|
-
}
|
|
62726
|
-
};
|
|
62727
|
-
await store.recordRunAuditEvent(eventInput);
|
|
62728
|
-
},
|
|
62729
|
-
filesystem: async (input) => {
|
|
62730
|
-
const eventInput = {
|
|
62731
|
-
taskId: context.taskId,
|
|
62732
|
-
agentId: context.agentId,
|
|
62733
|
-
runId: context.runId,
|
|
62734
|
-
domain: "filesystem",
|
|
62735
|
-
mutationType: input.type,
|
|
62736
|
-
target: input.target,
|
|
62737
|
-
metadata: {
|
|
62738
|
-
phase: context.phase,
|
|
62739
|
-
...context.source ? { source: context.source } : {},
|
|
62740
|
-
...input.metadata
|
|
62741
|
-
}
|
|
62742
|
-
};
|
|
62743
|
-
await store.recordRunAuditEvent(eventInput);
|
|
62744
|
-
}
|
|
62745
|
-
};
|
|
62746
|
-
}
|
|
62747
|
-
function generateSyntheticRunId(prefix, taskId) {
|
|
62748
|
-
const timestamp = Date.now();
|
|
62749
|
-
const random = Math.random().toString(36).slice(2, 6);
|
|
62750
|
-
return `${prefix}-${taskId}-${timestamp}-${random}`;
|
|
62751
|
-
}
|
|
62752
|
-
var init_run_audit = __esm({
|
|
62753
|
-
"../engine/src/run-audit.ts"() {
|
|
62754
|
-
"use strict";
|
|
62755
|
-
}
|
|
62756
|
-
});
|
|
62757
|
-
|
|
62758
|
-
// ../engine/src/merger.ts
|
|
62759
|
-
import { execSync, exec as exec3, spawn as spawn3 } from "node:child_process";
|
|
62760
|
-
import { promisify as promisify4 } from "node:util";
|
|
62761
|
-
import { existsSync as existsSync23 } from "node:fs";
|
|
62762
|
-
import { join as join29 } from "node:path";
|
|
62763
|
-
import { Type as Type3 } from "typebox";
|
|
62894
|
+
// ../engine/src/verification-utils.ts
|
|
62895
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
62764
62896
|
async function execWithProcessGroup(command, options) {
|
|
62765
62897
|
return new Promise((resolve44, reject2) => {
|
|
62766
62898
|
if (options.signal?.aborted) {
|
|
@@ -62872,6 +63004,11 @@ function truncateWithEllipsis(text, maxChars) {
|
|
|
62872
63004
|
return `${text.slice(0, maxChars)}
|
|
62873
63005
|
... (truncated)`;
|
|
62874
63006
|
}
|
|
63007
|
+
function truncateOutput(output) {
|
|
63008
|
+
if (output.length <= VERIFICATION_LOG_MAX_CHARS) return output;
|
|
63009
|
+
return `... output truncated to last ${VERIFICATION_LOG_MAX_CHARS} characters ...
|
|
63010
|
+
${output.slice(-VERIFICATION_LOG_MAX_CHARS)}`;
|
|
63011
|
+
}
|
|
62875
63012
|
function summarizeVerificationOutput(output, type) {
|
|
62876
63013
|
const lines = output.split("\n");
|
|
62877
63014
|
let summaryLine = null;
|
|
@@ -62937,6 +63074,13 @@ function summarizeVerificationOutput(output, type) {
|
|
|
62937
63074
|
failureNames.add(truncated);
|
|
62938
63075
|
}
|
|
62939
63076
|
const footer = "(full output available in engine logs)";
|
|
63077
|
+
if (type === "build") {
|
|
63078
|
+
const buildError = output.length > 500 ? `${output.slice(0, 500)}
|
|
63079
|
+
... (truncated)` : output;
|
|
63080
|
+
return `Build output:
|
|
63081
|
+
${buildError}
|
|
63082
|
+
${footer}`;
|
|
63083
|
+
}
|
|
62940
63084
|
const parts = [];
|
|
62941
63085
|
if (summaryLine) {
|
|
62942
63086
|
parts.push(summaryLine);
|
|
@@ -62954,29 +63098,292 @@ function summarizeVerificationOutput(output, type) {
|
|
|
62954
63098
|
parts.push(` \u2022 ... and ${names.length - 5} more failures`);
|
|
62955
63099
|
}
|
|
62956
63100
|
}
|
|
62957
|
-
if (parts.length
|
|
62958
|
-
|
|
62959
|
-
|
|
62960
|
-
|
|
62961
|
-
|
|
62962
|
-
|
|
62963
|
-
return `Verification command failed with no output
|
|
63101
|
+
if (parts.length === 0) {
|
|
63102
|
+
if (output.trim().length === 0) {
|
|
63103
|
+
return `no output
|
|
63104
|
+
${footer}`;
|
|
63105
|
+
}
|
|
63106
|
+
return `${truncateOutput(output)}
|
|
62964
63107
|
${footer}`;
|
|
62965
63108
|
}
|
|
62966
|
-
|
|
62967
|
-
return `${trimmed}
|
|
63109
|
+
return parts.join("\n") + `
|
|
62968
63110
|
${footer}`;
|
|
63111
|
+
}
|
|
63112
|
+
async function runVerificationCommand(store, rootDir, taskId, command, type, signal, log19, agentLabel) {
|
|
63113
|
+
const logger2 = log19 ?? { log: console.log, error: console.error, warn: console.warn };
|
|
63114
|
+
const label = agentLabel ?? "merger";
|
|
63115
|
+
if (signal?.aborted) {
|
|
63116
|
+
throw Object.assign(
|
|
63117
|
+
new Error(`Command aborted before start: ${command}`),
|
|
63118
|
+
{ code: "ABORT_ERR", aborted: true }
|
|
63119
|
+
);
|
|
62969
63120
|
}
|
|
62970
|
-
|
|
62971
|
-
|
|
62972
|
-
|
|
62973
|
-
|
|
62974
|
-
|
|
63121
|
+
logger2.log(`${taskId}: running ${type} command: ${command}`);
|
|
63122
|
+
await store.logEntry(taskId, `[verification] Running ${type} command: ${command}`);
|
|
63123
|
+
await store.appendAgentLog(taskId, `Running ${type} command`, "tool", command, label);
|
|
63124
|
+
const result = {
|
|
63125
|
+
command,
|
|
63126
|
+
exitCode: null,
|
|
63127
|
+
stdout: "",
|
|
63128
|
+
stderr: "",
|
|
63129
|
+
success: false
|
|
63130
|
+
};
|
|
63131
|
+
const verificationStartedAt = Date.now();
|
|
63132
|
+
try {
|
|
63133
|
+
const { stdout, stderr, bufferOverflow } = await execWithProcessGroup(command, {
|
|
63134
|
+
cwd: rootDir,
|
|
63135
|
+
timeout: VERIFICATION_COMMAND_TIMEOUT_MS,
|
|
63136
|
+
maxBuffer: VERIFICATION_COMMAND_MAX_BUFFER,
|
|
63137
|
+
signal
|
|
63138
|
+
});
|
|
63139
|
+
if (signal?.aborted) {
|
|
63140
|
+
throw Object.assign(
|
|
63141
|
+
new Error(`Command aborted: ${command}`),
|
|
63142
|
+
{ code: "ABORT_ERR", aborted: true }
|
|
63143
|
+
);
|
|
63144
|
+
}
|
|
63145
|
+
result.stdout = stdout?.toString?.() || "";
|
|
63146
|
+
result.stderr = stderr?.toString?.() || "";
|
|
63147
|
+
result.exitCode = 0;
|
|
63148
|
+
result.success = true;
|
|
63149
|
+
const verificationDurationMs = Date.now() - verificationStartedAt;
|
|
63150
|
+
const timingDetail = `${verificationDurationMs}ms`;
|
|
63151
|
+
if (bufferOverflow) {
|
|
63152
|
+
logger2.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`);
|
|
63153
|
+
await store.logEntry(
|
|
63154
|
+
taskId,
|
|
63155
|
+
`[timing] [verification] ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`
|
|
63156
|
+
);
|
|
63157
|
+
await store.appendAgentLog(
|
|
63158
|
+
taskId,
|
|
63159
|
+
`${type} command succeeded (exit 0)`,
|
|
63160
|
+
"tool_result",
|
|
63161
|
+
timingDetail,
|
|
63162
|
+
label
|
|
63163
|
+
);
|
|
63164
|
+
} else {
|
|
63165
|
+
logger2.log(`${taskId}: ${type} command succeeded in ${verificationDurationMs}ms`);
|
|
63166
|
+
await store.logEntry(taskId, `[timing] [verification] ${type} command succeeded (exit 0) in ${verificationDurationMs}ms`);
|
|
63167
|
+
await store.appendAgentLog(
|
|
63168
|
+
taskId,
|
|
63169
|
+
`${type} command succeeded (exit 0)`,
|
|
63170
|
+
"tool_result",
|
|
63171
|
+
timingDetail,
|
|
63172
|
+
label
|
|
63173
|
+
);
|
|
63174
|
+
}
|
|
63175
|
+
return result;
|
|
63176
|
+
} catch (error) {
|
|
63177
|
+
if (signal?.aborted) {
|
|
63178
|
+
throw Object.assign(
|
|
63179
|
+
new Error(`Command aborted: ${command}`),
|
|
63180
|
+
{ code: "ABORT_ERR", aborted: true }
|
|
63181
|
+
);
|
|
63182
|
+
}
|
|
63183
|
+
const verificationDurationMs = Date.now() - verificationStartedAt;
|
|
63184
|
+
const err = error;
|
|
63185
|
+
result.stdout = err?.stdout?.toString?.() || "";
|
|
63186
|
+
result.stderr = err?.stderr?.toString?.() || "";
|
|
63187
|
+
result.exitCode = typeof err?.status === "number" ? err.status : typeof err?.code === "number" ? err.code : null;
|
|
63188
|
+
const maxBufferExceeded = err?.code === "ENOBUFS" || err?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || String(err?.message ?? "").includes("maxBuffer");
|
|
63189
|
+
result.success = maxBufferExceeded && result.exitCode === 0;
|
|
63190
|
+
if (result.success) {
|
|
63191
|
+
logger2.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`);
|
|
63192
|
+
await store.logEntry(
|
|
63193
|
+
taskId,
|
|
63194
|
+
`[timing] [verification] ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`
|
|
63195
|
+
);
|
|
63196
|
+
await store.appendAgentLog(
|
|
63197
|
+
taskId,
|
|
63198
|
+
`${type} command succeeded (exit 0)`,
|
|
63199
|
+
"tool_result",
|
|
63200
|
+
`${verificationDurationMs}ms`,
|
|
63201
|
+
label
|
|
63202
|
+
);
|
|
63203
|
+
return result;
|
|
62975
63204
|
}
|
|
63205
|
+
const output = result.stderr || result.stdout || err?.message || "Unknown error";
|
|
63206
|
+
const summary = summarizeVerificationOutput(output, type);
|
|
63207
|
+
logger2.error(`${taskId}: ${type} command failed (exit ${result.exitCode}) in ${verificationDurationMs}ms; output captured in task log`);
|
|
63208
|
+
await store.logEntry(
|
|
63209
|
+
taskId,
|
|
63210
|
+
`[timing] [verification] ${type} command failed (exit ${result.exitCode}) after ${verificationDurationMs}ms:
|
|
63211
|
+
${summary}`
|
|
63212
|
+
);
|
|
63213
|
+
await store.appendAgentLog(
|
|
63214
|
+
taskId,
|
|
63215
|
+
`${type} command failed (exit ${result.exitCode})`,
|
|
63216
|
+
"tool_error",
|
|
63217
|
+
summary,
|
|
63218
|
+
label
|
|
63219
|
+
);
|
|
62976
63220
|
}
|
|
62977
|
-
return
|
|
62978
|
-
${footer}`;
|
|
63221
|
+
return result;
|
|
62979
63222
|
}
|
|
63223
|
+
var VERIFICATION_COMMAND_MAX_BUFFER, VERIFICATION_COMMAND_TIMEOUT_MS, VERIFICATION_LOG_MAX_CHARS;
|
|
63224
|
+
var init_verification_utils = __esm({
|
|
63225
|
+
"../engine/src/verification-utils.ts"() {
|
|
63226
|
+
"use strict";
|
|
63227
|
+
VERIFICATION_COMMAND_MAX_BUFFER = 50 * 1024 * 1024;
|
|
63228
|
+
VERIFICATION_COMMAND_TIMEOUT_MS = 6e5;
|
|
63229
|
+
VERIFICATION_LOG_MAX_CHARS = 2e4;
|
|
63230
|
+
}
|
|
63231
|
+
});
|
|
63232
|
+
|
|
63233
|
+
// ../engine/src/session-token-usage.ts
|
|
63234
|
+
var session_token_usage_exports = {};
|
|
63235
|
+
__export(session_token_usage_exports, {
|
|
63236
|
+
accumulateSessionTokenUsage: () => accumulateSessionTokenUsage
|
|
63237
|
+
});
|
|
63238
|
+
function readSessionStats(session) {
|
|
63239
|
+
const accessor = session.getSessionStats;
|
|
63240
|
+
if (typeof accessor !== "function") return void 0;
|
|
63241
|
+
try {
|
|
63242
|
+
return accessor.call(session);
|
|
63243
|
+
} catch {
|
|
63244
|
+
return void 0;
|
|
63245
|
+
}
|
|
63246
|
+
}
|
|
63247
|
+
async function accumulateSessionTokenUsage(store, taskId, session) {
|
|
63248
|
+
try {
|
|
63249
|
+
const stats = readSessionStats(session);
|
|
63250
|
+
const tokens = stats?.tokens;
|
|
63251
|
+
if (!tokens) return;
|
|
63252
|
+
const currentInput = (tokens.input ?? 0) + (tokens.cacheWrite ?? 0);
|
|
63253
|
+
const currentOutput = tokens.output ?? 0;
|
|
63254
|
+
const currentCached = tokens.cacheRead ?? 0;
|
|
63255
|
+
const baseline = sessionBaselines.get(session) ?? { input: 0, output: 0, cached: 0 };
|
|
63256
|
+
const inputDelta = Math.max(0, currentInput - baseline.input);
|
|
63257
|
+
const outputDelta = Math.max(0, currentOutput - baseline.output);
|
|
63258
|
+
const cachedDelta = Math.max(0, currentCached - baseline.cached);
|
|
63259
|
+
sessionBaselines.set(session, {
|
|
63260
|
+
input: currentInput,
|
|
63261
|
+
output: currentOutput,
|
|
63262
|
+
cached: currentCached
|
|
63263
|
+
});
|
|
63264
|
+
if (inputDelta === 0 && outputDelta === 0 && cachedDelta === 0) return;
|
|
63265
|
+
const task = await store.getTask(taskId);
|
|
63266
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
63267
|
+
const newInput = (task.tokenUsage?.inputTokens ?? 0) + inputDelta;
|
|
63268
|
+
const newOutput = (task.tokenUsage?.outputTokens ?? 0) + outputDelta;
|
|
63269
|
+
const newCached = (task.tokenUsage?.cachedTokens ?? 0) + cachedDelta;
|
|
63270
|
+
await store.updateTask(taskId, {
|
|
63271
|
+
tokenUsage: {
|
|
63272
|
+
inputTokens: newInput,
|
|
63273
|
+
outputTokens: newOutput,
|
|
63274
|
+
cachedTokens: newCached,
|
|
63275
|
+
totalTokens: newInput + newOutput + newCached,
|
|
63276
|
+
firstUsedAt: task.tokenUsage?.firstUsedAt ?? now,
|
|
63277
|
+
lastUsedAt: now
|
|
63278
|
+
}
|
|
63279
|
+
});
|
|
63280
|
+
} catch (err) {
|
|
63281
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
63282
|
+
log14.warn(`${taskId}: session token usage accumulate failed: ${message}`);
|
|
63283
|
+
}
|
|
63284
|
+
}
|
|
63285
|
+
var log14, sessionBaselines;
|
|
63286
|
+
var init_session_token_usage = __esm({
|
|
63287
|
+
"../engine/src/session-token-usage.ts"() {
|
|
63288
|
+
"use strict";
|
|
63289
|
+
init_logger2();
|
|
63290
|
+
log14 = createLogger2("session-token-usage");
|
|
63291
|
+
sessionBaselines = /* @__PURE__ */ new WeakMap();
|
|
63292
|
+
}
|
|
63293
|
+
});
|
|
63294
|
+
|
|
63295
|
+
// ../engine/src/run-audit.ts
|
|
63296
|
+
function createRunAuditor(store, context) {
|
|
63297
|
+
if (!context) {
|
|
63298
|
+
return {
|
|
63299
|
+
git: async () => {
|
|
63300
|
+
},
|
|
63301
|
+
database: async () => {
|
|
63302
|
+
},
|
|
63303
|
+
filesystem: async () => {
|
|
63304
|
+
}
|
|
63305
|
+
};
|
|
63306
|
+
}
|
|
63307
|
+
const hasRecordAuditEvent = typeof store.recordRunAuditEvent === "function";
|
|
63308
|
+
if (!hasRecordAuditEvent) {
|
|
63309
|
+
return {
|
|
63310
|
+
git: async () => {
|
|
63311
|
+
},
|
|
63312
|
+
database: async () => {
|
|
63313
|
+
},
|
|
63314
|
+
filesystem: async () => {
|
|
63315
|
+
}
|
|
63316
|
+
};
|
|
63317
|
+
}
|
|
63318
|
+
return {
|
|
63319
|
+
git: async (input) => {
|
|
63320
|
+
const eventInput = {
|
|
63321
|
+
taskId: context.taskId,
|
|
63322
|
+
agentId: context.agentId,
|
|
63323
|
+
runId: context.runId,
|
|
63324
|
+
domain: "git",
|
|
63325
|
+
mutationType: input.type,
|
|
63326
|
+
target: input.target,
|
|
63327
|
+
metadata: {
|
|
63328
|
+
phase: context.phase,
|
|
63329
|
+
...context.source ? { source: context.source } : {},
|
|
63330
|
+
...input.metadata
|
|
63331
|
+
}
|
|
63332
|
+
};
|
|
63333
|
+
await store.recordRunAuditEvent(eventInput);
|
|
63334
|
+
},
|
|
63335
|
+
database: async (input) => {
|
|
63336
|
+
const inferredTaskId = input.target.startsWith("FN-") || input.target.startsWith("KB-") ? input.target : context.taskId;
|
|
63337
|
+
const eventInput = {
|
|
63338
|
+
taskId: inferredTaskId,
|
|
63339
|
+
agentId: context.agentId,
|
|
63340
|
+
runId: context.runId,
|
|
63341
|
+
domain: "database",
|
|
63342
|
+
mutationType: input.type,
|
|
63343
|
+
target: input.target,
|
|
63344
|
+
metadata: {
|
|
63345
|
+
phase: context.phase,
|
|
63346
|
+
...context.source ? { source: context.source } : {},
|
|
63347
|
+
...input.metadata
|
|
63348
|
+
}
|
|
63349
|
+
};
|
|
63350
|
+
await store.recordRunAuditEvent(eventInput);
|
|
63351
|
+
},
|
|
63352
|
+
filesystem: async (input) => {
|
|
63353
|
+
const eventInput = {
|
|
63354
|
+
taskId: context.taskId,
|
|
63355
|
+
agentId: context.agentId,
|
|
63356
|
+
runId: context.runId,
|
|
63357
|
+
domain: "filesystem",
|
|
63358
|
+
mutationType: input.type,
|
|
63359
|
+
target: input.target,
|
|
63360
|
+
metadata: {
|
|
63361
|
+
phase: context.phase,
|
|
63362
|
+
...context.source ? { source: context.source } : {},
|
|
63363
|
+
...input.metadata
|
|
63364
|
+
}
|
|
63365
|
+
};
|
|
63366
|
+
await store.recordRunAuditEvent(eventInput);
|
|
63367
|
+
}
|
|
63368
|
+
};
|
|
63369
|
+
}
|
|
63370
|
+
function generateSyntheticRunId(prefix, taskId) {
|
|
63371
|
+
const timestamp = Date.now();
|
|
63372
|
+
const random = Math.random().toString(36).slice(2, 6);
|
|
63373
|
+
return `${prefix}-${taskId}-${timestamp}-${random}`;
|
|
63374
|
+
}
|
|
63375
|
+
var init_run_audit = __esm({
|
|
63376
|
+
"../engine/src/run-audit.ts"() {
|
|
63377
|
+
"use strict";
|
|
63378
|
+
}
|
|
63379
|
+
});
|
|
63380
|
+
|
|
63381
|
+
// ../engine/src/merger.ts
|
|
63382
|
+
import { execSync, exec as exec3 } from "node:child_process";
|
|
63383
|
+
import { promisify as promisify4 } from "node:util";
|
|
63384
|
+
import { existsSync as existsSync24 } from "node:fs";
|
|
63385
|
+
import { join as join30 } from "node:path";
|
|
63386
|
+
import { Type as Type3 } from "typebox";
|
|
62980
63387
|
function truncateWorkflowScriptOutput(output) {
|
|
62981
63388
|
if (output.length <= WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS) return output;
|
|
62982
63389
|
return `... output truncated to last ${WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS} characters ...
|
|
@@ -63020,7 +63427,7 @@ async function getStagedFiles(cwd) {
|
|
|
63020
63427
|
}
|
|
63021
63428
|
}
|
|
63022
63429
|
function hasInstallState(rootDir) {
|
|
63023
|
-
return
|
|
63430
|
+
return existsSync24(join30(rootDir, "node_modules")) || existsSync24(join30(rootDir, ".pnp.cjs"));
|
|
63024
63431
|
}
|
|
63025
63432
|
function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
63026
63433
|
if (!installStatePresent) return true;
|
|
@@ -63029,10 +63436,10 @@ function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
|
63029
63436
|
);
|
|
63030
63437
|
}
|
|
63031
63438
|
function getDependencySyncCommand(rootDir) {
|
|
63032
|
-
if (
|
|
63033
|
-
if (
|
|
63034
|
-
if (
|
|
63035
|
-
if (
|
|
63439
|
+
if (existsSync24(join30(rootDir, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
63440
|
+
if (existsSync24(join30(rootDir, "package-lock.json"))) return "npm install";
|
|
63441
|
+
if (existsSync24(join30(rootDir, "yarn.lock"))) return "yarn install --frozen-lockfile";
|
|
63442
|
+
if (existsSync24(join30(rootDir, "bun.lock")) || existsSync24(join30(rootDir, "bun.lockb"))) {
|
|
63036
63443
|
return "bun install --frozen-lockfile";
|
|
63037
63444
|
}
|
|
63038
63445
|
return null;
|
|
@@ -63065,8 +63472,8 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
63065
63472
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63066
63473
|
};
|
|
63067
63474
|
}
|
|
63068
|
-
if (
|
|
63069
|
-
if (
|
|
63475
|
+
if (existsSync24(join30(rootDir, "pnpm-lock.yaml"))) {
|
|
63476
|
+
if (existsSync24(join30(rootDir, "pnpm-workspace.yaml"))) {
|
|
63070
63477
|
mergerLog.warn(
|
|
63071
63478
|
`Inferred test command "pnpm test" in a pnpm workspace (${rootDir}). This runs the full monorepo suite on every merge. Consider setting an explicit scoped testCommand in project settings, e.g. \`pnpm -r --filter "...[main]" test\`.`
|
|
63072
63479
|
);
|
|
@@ -63077,21 +63484,21 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
63077
63484
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63078
63485
|
};
|
|
63079
63486
|
}
|
|
63080
|
-
if (
|
|
63487
|
+
if (existsSync24(join30(rootDir, "yarn.lock"))) {
|
|
63081
63488
|
return {
|
|
63082
63489
|
command: "yarn test",
|
|
63083
63490
|
testSource: "inferred",
|
|
63084
63491
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63085
63492
|
};
|
|
63086
63493
|
}
|
|
63087
|
-
if (
|
|
63494
|
+
if (existsSync24(join30(rootDir, "bun.lock")) || existsSync24(join30(rootDir, "bun.lockb"))) {
|
|
63088
63495
|
return {
|
|
63089
63496
|
command: "bun test",
|
|
63090
63497
|
testSource: "inferred",
|
|
63091
63498
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63092
63499
|
};
|
|
63093
63500
|
}
|
|
63094
|
-
if (
|
|
63501
|
+
if (existsSync24(join30(rootDir, "package-lock.json"))) {
|
|
63095
63502
|
return {
|
|
63096
63503
|
command: "npm test",
|
|
63097
63504
|
testSource: "inferred",
|
|
@@ -63128,7 +63535,7 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
63128
63535
|
await store.logEntry(taskId, deterministicVerificationMessage);
|
|
63129
63536
|
await store.appendAgentLog(taskId, deterministicVerificationMessage, "text", void 0, "merger");
|
|
63130
63537
|
if (hasTestCommand) {
|
|
63131
|
-
const testResult = await
|
|
63538
|
+
const testResult = await runVerificationCommand2(
|
|
63132
63539
|
store,
|
|
63133
63540
|
rootDir,
|
|
63134
63541
|
taskId,
|
|
@@ -63159,7 +63566,7 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
63159
63566
|
}
|
|
63160
63567
|
}
|
|
63161
63568
|
if (hasBuildCommand) {
|
|
63162
|
-
const buildResult = await
|
|
63569
|
+
const buildResult = await runVerificationCommand2(
|
|
63163
63570
|
store,
|
|
63164
63571
|
rootDir,
|
|
63165
63572
|
taskId,
|
|
@@ -63194,98 +63601,9 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
63194
63601
|
await store.appendAgentLog(taskId, "Deterministic merge verification passed", "text", void 0, "merger");
|
|
63195
63602
|
return result;
|
|
63196
63603
|
}
|
|
63197
|
-
async function
|
|
63604
|
+
async function runVerificationCommand2(store, rootDir, taskId, command, type, signal) {
|
|
63198
63605
|
throwIfAborted(signal, taskId);
|
|
63199
|
-
|
|
63200
|
-
await store.logEntry(taskId, `[verification] Running ${type} command: ${command}`);
|
|
63201
|
-
await store.appendAgentLog(taskId, `Running ${type} command`, "tool", command, "merger");
|
|
63202
|
-
const result = {
|
|
63203
|
-
command,
|
|
63204
|
-
exitCode: null,
|
|
63205
|
-
stdout: "",
|
|
63206
|
-
stderr: "",
|
|
63207
|
-
success: false
|
|
63208
|
-
};
|
|
63209
|
-
const verificationStartedAt = Date.now();
|
|
63210
|
-
try {
|
|
63211
|
-
const { stdout, stderr, bufferOverflow } = await execWithProcessGroup(command, {
|
|
63212
|
-
cwd: rootDir,
|
|
63213
|
-
timeout: VERIFICATION_COMMAND_TIMEOUT_MS,
|
|
63214
|
-
maxBuffer: VERIFICATION_COMMAND_MAX_BUFFER,
|
|
63215
|
-
signal
|
|
63216
|
-
});
|
|
63217
|
-
throwIfAborted(signal, taskId);
|
|
63218
|
-
result.stdout = stdout?.toString?.() || "";
|
|
63219
|
-
result.stderr = stderr?.toString?.() || "";
|
|
63220
|
-
result.exitCode = 0;
|
|
63221
|
-
result.success = true;
|
|
63222
|
-
const verificationDurationMs = Date.now() - verificationStartedAt;
|
|
63223
|
-
const timingDetail = `${verificationDurationMs}ms`;
|
|
63224
|
-
if (bufferOverflow) {
|
|
63225
|
-
mergerLog.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`);
|
|
63226
|
-
await store.logEntry(
|
|
63227
|
-
taskId,
|
|
63228
|
-
`[timing] [verification] ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`
|
|
63229
|
-
);
|
|
63230
|
-
await store.appendAgentLog(
|
|
63231
|
-
taskId,
|
|
63232
|
-
`${type} command succeeded (exit 0)`,
|
|
63233
|
-
"tool_result",
|
|
63234
|
-
timingDetail,
|
|
63235
|
-
"merger"
|
|
63236
|
-
);
|
|
63237
|
-
} else {
|
|
63238
|
-
mergerLog.log(`${taskId}: ${type} command succeeded in ${verificationDurationMs}ms`);
|
|
63239
|
-
await store.logEntry(taskId, `[timing] [verification] ${type} command succeeded (exit 0) in ${verificationDurationMs}ms`);
|
|
63240
|
-
await store.appendAgentLog(
|
|
63241
|
-
taskId,
|
|
63242
|
-
`${type} command succeeded (exit 0)`,
|
|
63243
|
-
"tool_result",
|
|
63244
|
-
timingDetail,
|
|
63245
|
-
"merger"
|
|
63246
|
-
);
|
|
63247
|
-
}
|
|
63248
|
-
return result;
|
|
63249
|
-
} catch (error) {
|
|
63250
|
-
throwIfAborted(signal, taskId);
|
|
63251
|
-
const verificationDurationMs = Date.now() - verificationStartedAt;
|
|
63252
|
-
result.stdout = error?.stdout?.toString?.() || "";
|
|
63253
|
-
result.stderr = error?.stderr?.toString?.() || "";
|
|
63254
|
-
result.exitCode = typeof error?.status === "number" ? error.status : typeof error?.code === "number" ? error.code : null;
|
|
63255
|
-
const maxBufferExceeded = error?.code === "ENOBUFS" || error?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || String(error?.message ?? "").includes("maxBuffer");
|
|
63256
|
-
result.success = maxBufferExceeded && result.exitCode === 0;
|
|
63257
|
-
if (result.success) {
|
|
63258
|
-
mergerLog.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`);
|
|
63259
|
-
await store.logEntry(
|
|
63260
|
-
taskId,
|
|
63261
|
-
`[timing] [verification] ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`
|
|
63262
|
-
);
|
|
63263
|
-
await store.appendAgentLog(
|
|
63264
|
-
taskId,
|
|
63265
|
-
`${type} command succeeded (exit 0)`,
|
|
63266
|
-
"tool_result",
|
|
63267
|
-
`${verificationDurationMs}ms`,
|
|
63268
|
-
"merger"
|
|
63269
|
-
);
|
|
63270
|
-
return result;
|
|
63271
|
-
}
|
|
63272
|
-
const output = result.stderr || result.stdout || error?.message || "Unknown error";
|
|
63273
|
-
const summary = summarizeVerificationOutput(output, type);
|
|
63274
|
-
mergerLog.error(`${taskId}: ${type} command failed (exit ${result.exitCode}) in ${verificationDurationMs}ms; output captured in task log`);
|
|
63275
|
-
await store.logEntry(
|
|
63276
|
-
taskId,
|
|
63277
|
-
`[timing] [verification] ${type} command failed (exit ${result.exitCode}) after ${verificationDurationMs}ms:
|
|
63278
|
-
${summary}`
|
|
63279
|
-
);
|
|
63280
|
-
await store.appendAgentLog(
|
|
63281
|
-
taskId,
|
|
63282
|
-
`${type} command failed (exit ${result.exitCode})`,
|
|
63283
|
-
"tool_error",
|
|
63284
|
-
summary,
|
|
63285
|
-
"merger"
|
|
63286
|
-
);
|
|
63287
|
-
}
|
|
63288
|
-
return result;
|
|
63606
|
+
return runVerificationCommand(store, rootDir, taskId, command, type, signal, mergerLog, "merger");
|
|
63289
63607
|
}
|
|
63290
63608
|
async function attemptInMergeVerificationFix(store, rootDir, taskId, failureContext, settings, options, mergeRunContext, fixAttemptNumber, _testCommand, _buildCommand) {
|
|
63291
63609
|
try {
|
|
@@ -63348,9 +63666,20 @@ Do not refactor, rename broadly, or make opportunistic improvements.
|
|
|
63348
63666
|
onToolEnd: logger2.onToolEnd,
|
|
63349
63667
|
defaultProvider: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultProviderOverride : settings.defaultProvider,
|
|
63350
63668
|
defaultModelId: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultModelIdOverride : settings.defaultModelId,
|
|
63669
|
+
fallbackProvider: settings.fallbackProvider,
|
|
63670
|
+
fallbackModelId: settings.fallbackModelId,
|
|
63351
63671
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
63352
63672
|
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
63353
|
-
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}
|
|
63673
|
+
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
63674
|
+
taskId,
|
|
63675
|
+
taskTitle: taskForSkillContext?.title,
|
|
63676
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
63677
|
+
agent: "merger",
|
|
63678
|
+
label: "merge verification fix agent",
|
|
63679
|
+
store,
|
|
63680
|
+
taskId,
|
|
63681
|
+
taskTitle: taskForSkillContext?.title
|
|
63682
|
+
})
|
|
63354
63683
|
});
|
|
63355
63684
|
const runId = mergeRunContext?.runId;
|
|
63356
63685
|
const agentId = mergeRunContext?.agentId ?? "merger";
|
|
@@ -63403,7 +63732,7 @@ ${failureContext.output.slice(0, VERIFICATION_LOG_MAX_CHARS)}
|
|
|
63403
63732
|
void 0,
|
|
63404
63733
|
"merger"
|
|
63405
63734
|
);
|
|
63406
|
-
const reRunResult = await
|
|
63735
|
+
const reRunResult = await runVerificationCommand2(
|
|
63407
63736
|
store,
|
|
63408
63737
|
rootDir,
|
|
63409
63738
|
taskId,
|
|
@@ -64145,9 +64474,16 @@ You are assisting with a paused \`git pull --rebase\`.
|
|
|
64145
64474
|
onToolEnd: agentLogger.onToolEnd,
|
|
64146
64475
|
defaultProvider: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultProviderOverride : settings.defaultProvider,
|
|
64147
64476
|
defaultModelId: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultModelIdOverride : settings.defaultModelId,
|
|
64477
|
+
fallbackProvider: settings.fallbackProvider,
|
|
64478
|
+
fallbackModelId: settings.fallbackModelId,
|
|
64148
64479
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
64149
64480
|
taskId,
|
|
64150
|
-
onFallbackModelUsed:
|
|
64481
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
64482
|
+
agent: "merger",
|
|
64483
|
+
label: "rebase conflict resolver",
|
|
64484
|
+
store,
|
|
64485
|
+
taskId
|
|
64486
|
+
})
|
|
64151
64487
|
});
|
|
64152
64488
|
const prompt = [
|
|
64153
64489
|
`Resolve rebase conflicts for task ${taskId}.`,
|
|
@@ -64339,7 +64675,7 @@ async function pushToRemoteAfterMerge(store, rootDir, taskId, settings, options)
|
|
|
64339
64675
|
}
|
|
64340
64676
|
async function createPostMergeWorktree(rootDir, taskId) {
|
|
64341
64677
|
const randomSuffix = Math.random().toString(36).slice(2, 10);
|
|
64342
|
-
const postMergeWorktree =
|
|
64678
|
+
const postMergeWorktree = join30(rootDir, ".worktrees", `post-merge-${taskId}-${randomSuffix}`);
|
|
64343
64679
|
try {
|
|
64344
64680
|
await execAsync2(`git worktree add ${quoteArg(postMergeWorktree)} HEAD`, { cwd: rootDir });
|
|
64345
64681
|
return postMergeWorktree;
|
|
@@ -65280,7 +65616,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
65280
65616
|
}
|
|
65281
65617
|
}
|
|
65282
65618
|
throwIfAborted(options.signal, taskId);
|
|
65283
|
-
if (worktreePath &&
|
|
65619
|
+
if (worktreePath && existsSync24(worktreePath)) {
|
|
65284
65620
|
const otherUser = await findWorktreeUser(store, worktreePath, taskId);
|
|
65285
65621
|
if (otherUser) {
|
|
65286
65622
|
mergerLog.log(`Worktree retained \u2014 still needed by ${otherUser}`);
|
|
@@ -65934,9 +66270,20 @@ async function runAiAgentForCommit(params) {
|
|
|
65934
66270
|
onToolEnd: agentLogger.onToolEnd,
|
|
65935
66271
|
defaultProvider: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultProviderOverride : settings.defaultProvider,
|
|
65936
66272
|
defaultModelId: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultModelIdOverride : settings.defaultModelId,
|
|
66273
|
+
fallbackProvider: settings.fallbackProvider,
|
|
66274
|
+
fallbackModelId: settings.fallbackModelId,
|
|
65937
66275
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
65938
66276
|
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
65939
|
-
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}
|
|
66277
|
+
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
66278
|
+
taskId,
|
|
66279
|
+
taskTitle: taskForSkillContext?.title,
|
|
66280
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
66281
|
+
agent: "merger",
|
|
66282
|
+
label: "merge agent",
|
|
66283
|
+
store,
|
|
66284
|
+
taskId,
|
|
66285
|
+
taskTitle: taskForSkillContext?.title
|
|
66286
|
+
})
|
|
65940
66287
|
});
|
|
65941
66288
|
options.onSession?.(session);
|
|
65942
66289
|
try {
|
|
@@ -66367,7 +66714,14 @@ If issues are found that need attention, describe them clearly and include concr
|
|
|
66367
66714
|
fallbackModelId: settings.fallbackModelId,
|
|
66368
66715
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
66369
66716
|
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
66370
|
-
...postMergeSkillContext?.skillSelectionContext ? { skillSelection: postMergeSkillContext.skillSelectionContext } : {}
|
|
66717
|
+
...postMergeSkillContext?.skillSelectionContext ? { skillSelection: postMergeSkillContext.skillSelectionContext } : {},
|
|
66718
|
+
taskId,
|
|
66719
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
66720
|
+
agent: "merger",
|
|
66721
|
+
label: `post-merge workflow step '${workflowStep.name}'`,
|
|
66722
|
+
store,
|
|
66723
|
+
taskId
|
|
66724
|
+
})
|
|
66371
66725
|
});
|
|
66372
66726
|
mergerLog.log(`${taskId}: [post-merge] workflow step '${workflowStep.name}' using model ${describeModel(session)}${useOverride ? " (workflow step override)" : ""}`);
|
|
66373
66727
|
await store.logEntry(taskId, `[post-merge] Workflow step '${workflowStep.name}' using model: ${describeModel(session)}${useOverride ? " (workflow step override)" : ""}`);
|
|
@@ -66403,15 +66757,17 @@ async function completeTask(store, taskId, result) {
|
|
|
66403
66757
|
result.task = task;
|
|
66404
66758
|
store.emit("task:merged", result);
|
|
66405
66759
|
}
|
|
66406
|
-
var execAsync2, LOCKFILE_PATTERNS, GENERATED_PATTERNS, DEPENDENCY_SYNC_TRIGGER_PATTERNS,
|
|
66760
|
+
var execAsync2, LOCKFILE_PATTERNS, GENERATED_PATTERNS, DEPENDENCY_SYNC_TRIGGER_PATTERNS, WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS, PULL_REBASE_TIMEOUT_MS, PUSH_TIMEOUT_MS, MERGE_COMMIT_LOG_MAX_CHARS, MERGE_DIFF_STAT_MAX_CHARS, VerificationError, MergeAbortedError, FUSION_TASK_ID_TRAILER_KEY;
|
|
66407
66761
|
var init_merger = __esm({
|
|
66408
66762
|
"../engine/src/merger.ts"() {
|
|
66409
66763
|
"use strict";
|
|
66764
|
+
init_verification_utils();
|
|
66765
|
+
init_verification_utils();
|
|
66410
66766
|
init_src();
|
|
66411
66767
|
init_pi();
|
|
66412
66768
|
init_session_token_usage();
|
|
66413
66769
|
init_agent_session_helpers();
|
|
66414
|
-
|
|
66770
|
+
init_fallback_model_observer();
|
|
66415
66771
|
init_session_skill_context();
|
|
66416
66772
|
init_agent_logger();
|
|
66417
66773
|
init_logger2();
|
|
@@ -66457,9 +66813,6 @@ var init_merger = __esm({
|
|
|
66457
66813
|
"bun.lock",
|
|
66458
66814
|
"packages/*/package.json"
|
|
66459
66815
|
];
|
|
66460
|
-
VERIFICATION_COMMAND_MAX_BUFFER = 50 * 1024 * 1024;
|
|
66461
|
-
VERIFICATION_COMMAND_TIMEOUT_MS = 6e5;
|
|
66462
|
-
VERIFICATION_LOG_MAX_CHARS = 2e4;
|
|
66463
66816
|
WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS = 4e3;
|
|
66464
66817
|
PULL_REBASE_TIMEOUT_MS = 12e4;
|
|
66465
66818
|
PUSH_TIMEOUT_MS = 6e4;
|
|
@@ -66484,8 +66837,8 @@ var init_merger = __esm({
|
|
|
66484
66837
|
|
|
66485
66838
|
// ../engine/src/worktree-names.ts
|
|
66486
66839
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
66487
|
-
import { join as
|
|
66488
|
-
import { existsSync as
|
|
66840
|
+
import { join as join31 } from "node:path";
|
|
66841
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
66489
66842
|
function slugify2(str) {
|
|
66490
66843
|
return str.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
66491
66844
|
}
|
|
@@ -66496,7 +66849,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
|
|
|
66496
66849
|
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
66497
66850
|
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
66498
66851
|
const baseName = `${adjective}-${noun}`;
|
|
66499
|
-
const worktreesDir =
|
|
66852
|
+
const worktreesDir = join31(rootDir, ".worktrees");
|
|
66500
66853
|
const existing = getExistingWorktreeNames(worktreesDir);
|
|
66501
66854
|
for (const reserved of reservedNames) {
|
|
66502
66855
|
existing.add(reserved);
|
|
@@ -66511,7 +66864,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
|
|
|
66511
66864
|
return `${baseName}-${suffix}`;
|
|
66512
66865
|
}
|
|
66513
66866
|
function getExistingWorktreeNames(worktreesDir) {
|
|
66514
|
-
if (!
|
|
66867
|
+
if (!existsSync25(worktreesDir)) {
|
|
66515
66868
|
return /* @__PURE__ */ new Set();
|
|
66516
66869
|
}
|
|
66517
66870
|
try {
|
|
@@ -66648,8 +67001,8 @@ __export(worktree_pool_exports, {
|
|
|
66648
67001
|
});
|
|
66649
67002
|
import { exec as exec4 } from "node:child_process";
|
|
66650
67003
|
import { promisify as promisify5 } from "node:util";
|
|
66651
|
-
import { existsSync as
|
|
66652
|
-
import { join as
|
|
67004
|
+
import { existsSync as existsSync26, lstatSync, readdirSync as readdirSync4, rmSync as rmSync2 } from "node:fs";
|
|
67005
|
+
import { join as join32, relative as relative6, resolve as resolve15, isAbsolute as isAbsolute9 } from "node:path";
|
|
66653
67006
|
function getExecStdout(result) {
|
|
66654
67007
|
if (typeof result === "string") return result;
|
|
66655
67008
|
if (result && typeof result === "object" && "stdout" in result) {
|
|
@@ -66695,10 +67048,10 @@ async function isRegisteredGitWorktree2(rootDir, worktreePath) {
|
|
|
66695
67048
|
return (await getRegisteredWorktreePaths(rootDir)).has(resolve15(worktreePath));
|
|
66696
67049
|
}
|
|
66697
67050
|
function hasRequiredWorktreeFiles(worktreePath) {
|
|
66698
|
-
return
|
|
67051
|
+
return existsSync26(join32(worktreePath, ".git")) && existsSync26(join32(worktreePath, "package.json"));
|
|
66699
67052
|
}
|
|
66700
67053
|
async function isUsableTaskWorktree(rootDir, worktreePath) {
|
|
66701
|
-
return
|
|
67054
|
+
return existsSync26(worktreePath) && await isRegisteredGitWorktree2(rootDir, worktreePath) && hasRequiredWorktreeFiles(worktreePath);
|
|
66702
67055
|
}
|
|
66703
67056
|
function isInsideWorktreesDir(rootDir, worktreePath) {
|
|
66704
67057
|
const worktreesDir = resolve15(rootDir, ".worktrees");
|
|
@@ -66707,14 +67060,14 @@ function isInsideWorktreesDir(rootDir, worktreePath) {
|
|
|
66707
67060
|
return rel !== "" && !rel.startsWith("..") && !isAbsolute9(rel);
|
|
66708
67061
|
}
|
|
66709
67062
|
async function scanIdleWorktrees(rootDir, store) {
|
|
66710
|
-
const worktreesDir =
|
|
66711
|
-
if (!
|
|
67063
|
+
const worktreesDir = join32(rootDir, ".worktrees");
|
|
67064
|
+
if (!existsSync26(worktreesDir)) {
|
|
66712
67065
|
return [];
|
|
66713
67066
|
}
|
|
66714
67067
|
let dirs;
|
|
66715
67068
|
try {
|
|
66716
67069
|
const entries = readdirSync4(worktreesDir, { withFileTypes: true });
|
|
66717
|
-
dirs = entries.filter((e) => e.isDirectory()).map((e) =>
|
|
67070
|
+
dirs = entries.filter((e) => e.isDirectory()).map((e) => join32(worktreesDir, e.name));
|
|
66718
67071
|
} catch (err) {
|
|
66719
67072
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
66720
67073
|
worktreePoolLog.warn(`Failed to read .worktrees/ directory: ${errorMessage}`);
|
|
@@ -66737,16 +67090,16 @@ async function scanIdleWorktrees(rootDir, store) {
|
|
|
66737
67090
|
return registeredDirs.filter((dir2) => !activeWorktrees.has(resolve15(dir2)));
|
|
66738
67091
|
}
|
|
66739
67092
|
async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
66740
|
-
const worktreesDir =
|
|
66741
|
-
if (!
|
|
67093
|
+
const worktreesDir = join32(rootDir, ".worktrees");
|
|
67094
|
+
if (!existsSync26(worktreesDir)) {
|
|
66742
67095
|
return 0;
|
|
66743
67096
|
}
|
|
66744
67097
|
const orphaned = await scanIdleWorktrees(rootDir, store);
|
|
66745
67098
|
const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
|
|
66746
67099
|
let dirs = [];
|
|
66747
|
-
if (
|
|
67100
|
+
if (existsSync26(worktreesDir)) {
|
|
66748
67101
|
try {
|
|
66749
|
-
dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) =>
|
|
67102
|
+
dirs = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join32(worktreesDir, e.name));
|
|
66750
67103
|
} catch (err) {
|
|
66751
67104
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
66752
67105
|
worktreePoolLog.warn(`Failed to read .worktrees/ directory for cleanup: ${errorMessage}`);
|
|
@@ -66778,8 +67131,8 @@ async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
|
66778
67131
|
return cleaned;
|
|
66779
67132
|
}
|
|
66780
67133
|
async function reapOrphanWorktrees(projectRoot) {
|
|
66781
|
-
const worktreesDir =
|
|
66782
|
-
if (!
|
|
67134
|
+
const worktreesDir = join32(projectRoot, ".worktrees");
|
|
67135
|
+
if (!existsSync26(worktreesDir)) {
|
|
66783
67136
|
return 0;
|
|
66784
67137
|
}
|
|
66785
67138
|
let entries;
|
|
@@ -66787,11 +67140,11 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
66787
67140
|
entries = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => {
|
|
66788
67141
|
if (!e.isDirectory()) return false;
|
|
66789
67142
|
try {
|
|
66790
|
-
return lstatSync(
|
|
67143
|
+
return lstatSync(join32(worktreesDir, e.name)).isDirectory() && !lstatSync(join32(worktreesDir, e.name)).isSymbolicLink();
|
|
66791
67144
|
} catch {
|
|
66792
67145
|
return false;
|
|
66793
67146
|
}
|
|
66794
|
-
}).map((e) => ({ name: e.name, fullPath:
|
|
67147
|
+
}).map((e) => ({ name: e.name, fullPath: join32(worktreesDir, e.name) }));
|
|
66795
67148
|
} catch (err) {
|
|
66796
67149
|
const msg = err instanceof Error ? err.message : String(err);
|
|
66797
67150
|
worktreePoolLog.warn(`reapOrphanWorktrees: failed to read .worktrees/ \u2014 ${msg}`);
|
|
@@ -66810,8 +67163,8 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
66810
67163
|
if (registered.has(resolvedFull)) {
|
|
66811
67164
|
continue;
|
|
66812
67165
|
}
|
|
66813
|
-
const dotGit =
|
|
66814
|
-
if (
|
|
67166
|
+
const dotGit = join32(resolvedFull, ".git");
|
|
67167
|
+
if (existsSync26(dotGit)) {
|
|
66815
67168
|
worktreePoolLog.log(`reapOrphanWorktrees: skipping ${name} (has .git entry but not in registered list \u2014 may be partially registered)`);
|
|
66816
67169
|
continue;
|
|
66817
67170
|
}
|
|
@@ -66871,7 +67224,7 @@ var init_worktree_pool = __esm({
|
|
|
66871
67224
|
acquire() {
|
|
66872
67225
|
for (const path5 of this.idle) {
|
|
66873
67226
|
this.idle.delete(path5);
|
|
66874
|
-
if (
|
|
67227
|
+
if (existsSync26(path5)) {
|
|
66875
67228
|
return path5;
|
|
66876
67229
|
}
|
|
66877
67230
|
worktreePoolLog.log(`Pruned stale entry: ${path5}`);
|
|
@@ -66918,7 +67271,7 @@ var init_worktree_pool = __esm({
|
|
|
66918
67271
|
*/
|
|
66919
67272
|
rehydrate(idlePaths) {
|
|
66920
67273
|
for (const path5 of idlePaths) {
|
|
66921
|
-
if (
|
|
67274
|
+
if (existsSync26(path5)) {
|
|
66922
67275
|
this.idle.add(path5);
|
|
66923
67276
|
} else {
|
|
66924
67277
|
worktreePoolLog.log(`Rehydrate skipped (not on disk): ${path5}`);
|
|
@@ -66974,7 +67327,7 @@ var init_worktree_pool = __esm({
|
|
|
66974
67327
|
throw err;
|
|
66975
67328
|
}
|
|
66976
67329
|
const conflictingPath = match[1];
|
|
66977
|
-
if (!
|
|
67330
|
+
if (!existsSync26(conflictingPath)) {
|
|
66978
67331
|
await execAsync3("git worktree prune", { cwd: worktreePath });
|
|
66979
67332
|
await execAsync3(checkoutCmd, { cwd: worktreePath });
|
|
66980
67333
|
return branchName;
|
|
@@ -67051,8 +67404,8 @@ var init_token_cap_detector = __esm({
|
|
|
67051
67404
|
// ../engine/src/step-session-executor.ts
|
|
67052
67405
|
import { exec as exec5 } from "node:child_process";
|
|
67053
67406
|
import { promisify as promisify6 } from "node:util";
|
|
67054
|
-
import { existsSync as
|
|
67055
|
-
import { join as
|
|
67407
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
67408
|
+
import { join as join33 } from "node:path";
|
|
67056
67409
|
function parseStepFileScopes(prompt) {
|
|
67057
67410
|
const result = /* @__PURE__ */ new Map();
|
|
67058
67411
|
if (!prompt) return result;
|
|
@@ -67339,7 +67692,7 @@ var init_step_session_executor = __esm({
|
|
|
67339
67692
|
init_worktree_names();
|
|
67340
67693
|
init_agent_logger();
|
|
67341
67694
|
init_logger2();
|
|
67342
|
-
|
|
67695
|
+
init_fallback_model_observer();
|
|
67343
67696
|
init_context_limit_detector();
|
|
67344
67697
|
init_usage_limit_detector();
|
|
67345
67698
|
init_agent_tools();
|
|
@@ -67456,7 +67809,7 @@ var init_step_session_executor = __esm({
|
|
|
67456
67809
|
}
|
|
67457
67810
|
for (const [stepIdx, worktreePath] of this.parallelWorktrees) {
|
|
67458
67811
|
try {
|
|
67459
|
-
if (
|
|
67812
|
+
if (existsSync27(worktreePath)) {
|
|
67460
67813
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
67461
67814
|
cwd: this.options.rootDir
|
|
67462
67815
|
});
|
|
@@ -67621,7 +67974,13 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67621
67974
|
...this.options.skillSelection ? { skillSelection: this.options.skillSelection } : {},
|
|
67622
67975
|
taskId: taskDetail.id,
|
|
67623
67976
|
taskTitle: taskDetail.title,
|
|
67624
|
-
onFallbackModelUsed:
|
|
67977
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
67978
|
+
agent: "executor",
|
|
67979
|
+
label: "workflow step agent",
|
|
67980
|
+
store: this.store,
|
|
67981
|
+
taskId: taskDetail.id,
|
|
67982
|
+
taskTitle: taskDetail.title
|
|
67983
|
+
})
|
|
67625
67984
|
});
|
|
67626
67985
|
session = createResult.session;
|
|
67627
67986
|
const handle = {
|
|
@@ -67801,7 +68160,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67801
68160
|
for (const [stepIdx, worktreePath] of worktreePaths) {
|
|
67802
68161
|
if (worktreePath !== this.options.worktreePath) {
|
|
67803
68162
|
try {
|
|
67804
|
-
if (
|
|
68163
|
+
if (existsSync27(worktreePath)) {
|
|
67805
68164
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
67806
68165
|
cwd: this.options.rootDir
|
|
67807
68166
|
});
|
|
@@ -67831,7 +68190,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67831
68190
|
async createStepWorktree(stepIndex) {
|
|
67832
68191
|
const { rootDir } = this.options;
|
|
67833
68192
|
const name = generateWorktreeName(rootDir);
|
|
67834
|
-
const worktreePath =
|
|
68193
|
+
const worktreePath = join33(rootDir, ".worktrees", name);
|
|
67835
68194
|
const branchName = `fusion/step-${stepIndex}-${name}`;
|
|
67836
68195
|
stepExecLog.log(`Creating worktree for step ${stepIndex}: ${worktreePath} (branch: ${branchName})`);
|
|
67837
68196
|
try {
|
|
@@ -67906,7 +68265,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67906
68265
|
|
|
67907
68266
|
// ../engine/src/spec-staleness.ts
|
|
67908
68267
|
import { stat as stat5 } from "node:fs/promises";
|
|
67909
|
-
import { join as
|
|
68268
|
+
import { join as join34 } from "node:path";
|
|
67910
68269
|
async function evaluateSpecStaleness(options) {
|
|
67911
68270
|
const { settings, promptPath, nowMs } = options;
|
|
67912
68271
|
if (settings.specStalenessEnabled !== true) {
|
|
@@ -67946,7 +68305,7 @@ async function evaluateSpecStaleness(options) {
|
|
|
67946
68305
|
};
|
|
67947
68306
|
}
|
|
67948
68307
|
function getPromptPath(tasksDir, taskId) {
|
|
67949
|
-
return
|
|
68308
|
+
return join34(tasksDir, taskId, "PROMPT.md");
|
|
67950
68309
|
}
|
|
67951
68310
|
var DEFAULT_SPEC_STALENESS_MAX_AGE_MS;
|
|
67952
68311
|
var init_spec_staleness = __esm({
|
|
@@ -67977,8 +68336,8 @@ var init_task_completion = __esm({
|
|
|
67977
68336
|
|
|
67978
68337
|
// ../engine/src/run-verification-tool.ts
|
|
67979
68338
|
import { spawn as spawn4 } from "node:child_process";
|
|
67980
|
-
import { existsSync as
|
|
67981
|
-
import { isAbsolute as isAbsolute10, join as
|
|
68339
|
+
import { existsSync as existsSync28 } from "node:fs";
|
|
68340
|
+
import { isAbsolute as isAbsolute10, join as join35 } from "node:path";
|
|
67982
68341
|
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
67983
68342
|
function createBuffer() {
|
|
67984
68343
|
return { headChunks: [], headBytes: 0, tailChunks: [], tailBytes: 0, totalBytes: 0 };
|
|
@@ -68011,7 +68370,7 @@ function flattenBuffer(buf) {
|
|
|
68011
68370
|
|
|
68012
68371
|
` + tail;
|
|
68013
68372
|
}
|
|
68014
|
-
async function
|
|
68373
|
+
async function runVerificationCommand3(opts) {
|
|
68015
68374
|
const { command, cwd, timeoutMs, expectFailure = false, onHeartbeat, onLine } = opts;
|
|
68016
68375
|
const startMs = Date.now();
|
|
68017
68376
|
const warnings = [];
|
|
@@ -68151,7 +68510,7 @@ function createRunVerificationTool(opts) {
|
|
|
68151
68510
|
if (params.cwd && isAbsolute10(params.cwd)) {
|
|
68152
68511
|
resolvedCwd = params.cwd;
|
|
68153
68512
|
} else if (params.cwd) {
|
|
68154
|
-
resolvedCwd =
|
|
68513
|
+
resolvedCwd = join35(worktreePath, params.cwd);
|
|
68155
68514
|
} else {
|
|
68156
68515
|
resolvedCwd = worktreePath;
|
|
68157
68516
|
}
|
|
@@ -68166,8 +68525,8 @@ function createRunVerificationTool(opts) {
|
|
|
68166
68525
|
}
|
|
68167
68526
|
let effectiveCommand = command;
|
|
68168
68527
|
if (command.trimStart().startsWith("pnpm --filter")) {
|
|
68169
|
-
const modulesYaml =
|
|
68170
|
-
if (!
|
|
68528
|
+
const modulesYaml = join35(rootDir, "node_modules", ".modules.yaml");
|
|
68529
|
+
if (!existsSync28(modulesYaml)) {
|
|
68171
68530
|
const installCmd = "pnpm install --prefer-offline";
|
|
68172
68531
|
const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
|
|
68173
68532
|
warnings.push(msg);
|
|
@@ -68178,7 +68537,7 @@ function createRunVerificationTool(opts) {
|
|
|
68178
68537
|
log19.info(
|
|
68179
68538
|
`[fn_run_verification] ${taskId}: scope=${scope} timeout=${timeoutSec}s cwd=${resolvedCwd} cmd=${effectiveCommand}`
|
|
68180
68539
|
);
|
|
68181
|
-
const result = await
|
|
68540
|
+
const result = await runVerificationCommand3({
|
|
68182
68541
|
command: effectiveCommand,
|
|
68183
68542
|
cwd: resolvedCwd,
|
|
68184
68543
|
timeoutMs,
|
|
@@ -68278,8 +68637,8 @@ var init_run_verification_tool = __esm({
|
|
|
68278
68637
|
// ../engine/src/executor.ts
|
|
68279
68638
|
import { exec as exec6 } from "node:child_process";
|
|
68280
68639
|
import { promisify as promisify7 } from "node:util";
|
|
68281
|
-
import { isAbsolute as isAbsolute11, join as
|
|
68282
|
-
import { existsSync as
|
|
68640
|
+
import { isAbsolute as isAbsolute11, join as join36, relative as relative7, resolve as resolvePath } from "node:path";
|
|
68641
|
+
import { existsSync as existsSync29 } from "node:fs";
|
|
68283
68642
|
import { readFile as readFile15, writeFile as writeFile12 } from "node:fs/promises";
|
|
68284
68643
|
import { Type as Type5 } from "@mariozechner/pi-ai";
|
|
68285
68644
|
import { ModelRegistry as ModelRegistry2, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
|
|
@@ -68573,6 +68932,7 @@ var init_executor = __esm({
|
|
|
68573
68932
|
"use strict";
|
|
68574
68933
|
init_src();
|
|
68575
68934
|
init_merger();
|
|
68935
|
+
init_verification_utils();
|
|
68576
68936
|
init_worktree_names();
|
|
68577
68937
|
init_pi();
|
|
68578
68938
|
init_session_token_usage();
|
|
@@ -68597,7 +68957,7 @@ var init_executor = __esm({
|
|
|
68597
68957
|
init_task_completion();
|
|
68598
68958
|
init_auth_storage();
|
|
68599
68959
|
init_run_verification_tool();
|
|
68600
|
-
|
|
68960
|
+
init_fallback_model_observer();
|
|
68601
68961
|
init_agent_logger();
|
|
68602
68962
|
init_agent_tools();
|
|
68603
68963
|
execAsync5 = promisify7(exec6);
|
|
@@ -69744,7 +70104,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
69744
70104
|
);
|
|
69745
70105
|
return false;
|
|
69746
70106
|
}
|
|
69747
|
-
if (task.worktree &&
|
|
70107
|
+
if (task.worktree && existsSync29(task.worktree)) {
|
|
69748
70108
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
69749
70109
|
if (modifiedFiles.length > 0) {
|
|
69750
70110
|
await this.store.updateTask(task.id, { modifiedFiles });
|
|
@@ -69924,7 +70284,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
69924
70284
|
if (task.dependencies.length === 0) return null;
|
|
69925
70285
|
for (const depId of task.dependencies) {
|
|
69926
70286
|
const dep = allTasks.find((t) => t.id === depId);
|
|
69927
|
-
if (dep && dep.worktree && (dep.column === "done" || dep.column === "in-review") &&
|
|
70287
|
+
if (dep && dep.worktree && (dep.column === "done" || dep.column === "in-review") && existsSync29(dep.worktree)) {
|
|
69928
70288
|
return dep.worktree;
|
|
69929
70289
|
}
|
|
69930
70290
|
}
|
|
@@ -69975,7 +70335,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
69975
70335
|
const activeMergeStatuses = /* @__PURE__ */ new Set(["merging", "merging-pr"]);
|
|
69976
70336
|
const isActiveTask = activeColumns.has(task.column) || activeMergeStatuses.has(task.status ?? "");
|
|
69977
70337
|
if (!isActiveTask) {
|
|
69978
|
-
const tasksDir =
|
|
70338
|
+
const tasksDir = join36(this.store.getFusionDir(), "tasks");
|
|
69979
70339
|
const promptPath = getPromptPath(tasksDir, task.id);
|
|
69980
70340
|
const staleness = await evaluateSpecStaleness({ settings, promptPath });
|
|
69981
70341
|
if (staleness.isStale) {
|
|
@@ -70022,7 +70382,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70022
70382
|
worktreeName = generateWorktreeName(this.rootDir);
|
|
70023
70383
|
break;
|
|
70024
70384
|
}
|
|
70025
|
-
worktreePath =
|
|
70385
|
+
worktreePath = join36(this.rootDir, ".worktrees", worktreeName);
|
|
70026
70386
|
}
|
|
70027
70387
|
let stuckRequeue = null;
|
|
70028
70388
|
let taskDone = false;
|
|
@@ -70046,7 +70406,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70046
70406
|
);
|
|
70047
70407
|
}
|
|
70048
70408
|
const branchName = task.branch || `fusion/${task.id.toLowerCase()}`;
|
|
70049
|
-
let isResume =
|
|
70409
|
+
let isResume = existsSync29(worktreePath);
|
|
70050
70410
|
let acquiredFromPool = false;
|
|
70051
70411
|
const baseBranch = task.baseBranch || null;
|
|
70052
70412
|
if (task.worktree && isResume && !await isUsableTaskWorktree(this.rootDir, worktreePath)) {
|
|
@@ -70059,8 +70419,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70059
70419
|
this.currentRunContext
|
|
70060
70420
|
);
|
|
70061
70421
|
await this.store.updateTask(task.id, { worktree: null, branch: null });
|
|
70062
|
-
worktreePath =
|
|
70063
|
-
isResume =
|
|
70422
|
+
worktreePath = join36(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
70423
|
+
isResume = existsSync29(worktreePath);
|
|
70064
70424
|
}
|
|
70065
70425
|
if (!isResume) {
|
|
70066
70426
|
if (this.options.pool && settings.recycleWorktrees) {
|
|
@@ -70286,6 +70646,84 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70286
70646
|
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after step-session completion")) {
|
|
70287
70647
|
return;
|
|
70288
70648
|
}
|
|
70649
|
+
if (executionMode !== "fast") {
|
|
70650
|
+
if (settings.testCommand?.trim() || settings.buildCommand?.trim()) {
|
|
70651
|
+
const verificationResult = await this.runExecutorDeterministicVerification(task, worktreePath, settings);
|
|
70652
|
+
if (!verificationResult.allPassed) {
|
|
70653
|
+
const failedType = verificationResult.failedCommand === "testCommand" ? "test" : "build";
|
|
70654
|
+
const failedResult = failedType === "test" ? verificationResult.testResult : verificationResult.buildResult;
|
|
70655
|
+
const failedCommand = failedResult.command;
|
|
70656
|
+
const failureOutput = failedResult.stderr || failedResult.stdout || "Unknown error";
|
|
70657
|
+
const summary = summarizeVerificationOutput(failureOutput, failedType);
|
|
70658
|
+
executorLog.log(`${task.id}: [verification] ${failedType} failed \u2014 attempting fix agent`);
|
|
70659
|
+
await this.store.logEntry(
|
|
70660
|
+
task.id,
|
|
70661
|
+
`[verification] ${failedType} command failed (exit ${failedResult.exitCode}). Attempting fix agent...`,
|
|
70662
|
+
summary,
|
|
70663
|
+
this.currentRunContext
|
|
70664
|
+
);
|
|
70665
|
+
const maxFixRetries = Math.min(settings.verificationFixRetries ?? 3, 3);
|
|
70666
|
+
if (maxFixRetries === 0) {
|
|
70667
|
+
executorLog.log(`${task.id}: [verification] fix retries set to 0 \u2014 sending task back immediately`);
|
|
70668
|
+
await this.sendTaskBackForFix(
|
|
70669
|
+
task,
|
|
70670
|
+
worktreePath,
|
|
70671
|
+
`${failedType} command \`${failedCommand}\` failed (exit ${failedResult.exitCode}):
|
|
70672
|
+
${summary}`,
|
|
70673
|
+
`Verification (${failedType})`,
|
|
70674
|
+
`Deterministic verification failed (${failedType})`
|
|
70675
|
+
);
|
|
70676
|
+
return;
|
|
70677
|
+
}
|
|
70678
|
+
let fixSucceeded = false;
|
|
70679
|
+
for (let attempt = 1; attempt <= maxFixRetries; attempt++) {
|
|
70680
|
+
const fixed = await this.attemptExecutorVerificationFix(
|
|
70681
|
+
task,
|
|
70682
|
+
worktreePath,
|
|
70683
|
+
{
|
|
70684
|
+
command: failedCommand,
|
|
70685
|
+
exitCode: failedResult.exitCode,
|
|
70686
|
+
output: failureOutput,
|
|
70687
|
+
type: failedType
|
|
70688
|
+
},
|
|
70689
|
+
settings,
|
|
70690
|
+
attempt,
|
|
70691
|
+
maxFixRetries
|
|
70692
|
+
);
|
|
70693
|
+
if (fixed) {
|
|
70694
|
+
fixSucceeded = true;
|
|
70695
|
+
executorLog.log(`${task.id}: [verification] fix agent succeeded on attempt ${attempt}/${maxFixRetries}`);
|
|
70696
|
+
await this.store.logEntry(
|
|
70697
|
+
task.id,
|
|
70698
|
+
`[verification] Fix agent succeeded on attempt ${attempt}/${maxFixRetries}. Verification now passing.`,
|
|
70699
|
+
void 0,
|
|
70700
|
+
this.currentRunContext
|
|
70701
|
+
);
|
|
70702
|
+
break;
|
|
70703
|
+
}
|
|
70704
|
+
executorLog.log(`${task.id}: [verification] fix agent attempt ${attempt}/${maxFixRetries} failed`);
|
|
70705
|
+
await this.store.logEntry(
|
|
70706
|
+
task.id,
|
|
70707
|
+
`[verification] Fix agent attempt ${attempt}/${maxFixRetries} failed`,
|
|
70708
|
+
void 0,
|
|
70709
|
+
this.currentRunContext
|
|
70710
|
+
);
|
|
70711
|
+
}
|
|
70712
|
+
if (!fixSucceeded) {
|
|
70713
|
+
executorLog.log(`${task.id}: [verification] all fix attempts exhausted (${maxFixRetries}/${maxFixRetries}) \u2014 sending task back`);
|
|
70714
|
+
await this.sendTaskBackForFix(
|
|
70715
|
+
task,
|
|
70716
|
+
worktreePath,
|
|
70717
|
+
`${failedType} command \`${failedCommand}\` failed (exit ${failedResult.exitCode}) after ${maxFixRetries} fix attempts:
|
|
70718
|
+
${summary}`,
|
|
70719
|
+
`Verification (${failedType})`,
|
|
70720
|
+
`Deterministic verification failed after ${maxFixRetries} fix attempts`
|
|
70721
|
+
);
|
|
70722
|
+
return;
|
|
70723
|
+
}
|
|
70724
|
+
}
|
|
70725
|
+
}
|
|
70726
|
+
}
|
|
70289
70727
|
if (executionMode !== "fast") {
|
|
70290
70728
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
70291
70729
|
if (workflowResult === "deferred-paused") {
|
|
@@ -70374,7 +70812,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70374
70812
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
|
|
70375
70813
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
70376
70814
|
}
|
|
70377
|
-
if (worktreePath &&
|
|
70815
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
70378
70816
|
try {
|
|
70379
70817
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
70380
70818
|
await audit.git({ type: "worktree:remove", target: worktreePath });
|
|
@@ -70433,7 +70871,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70433
70871
|
try {
|
|
70434
70872
|
const latestTask = await this.store.getTask(task.id);
|
|
70435
70873
|
await this.resetStepsIfWorkLost(latestTask);
|
|
70436
|
-
if (worktreePath &&
|
|
70874
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
70437
70875
|
try {
|
|
70438
70876
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
70439
70877
|
} catch (wtErr) {
|
|
@@ -70545,7 +70983,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70545
70983
|
const executorFallbackProvider = settings.fallbackProvider;
|
|
70546
70984
|
const executorFallbackModelId = settings.fallbackModelId;
|
|
70547
70985
|
const executorThinkingLevel = detail.thinkingLevel ?? settings.defaultThinkingLevel;
|
|
70548
|
-
const isResuming = !!task.sessionFile &&
|
|
70986
|
+
const isResuming = !!task.sessionFile && existsSync29(task.sessionFile);
|
|
70549
70987
|
const sessionManager = isResuming ? SessionManager2.open(task.sessionFile) : SessionManager2.create(worktreePath);
|
|
70550
70988
|
executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
|
|
70551
70989
|
const executorInstructions = await this.resolveInstructionsForRole("executor");
|
|
@@ -70575,7 +71013,13 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70575
71013
|
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
70576
71014
|
taskId: task.id,
|
|
70577
71015
|
taskTitle: detail.title,
|
|
70578
|
-
onFallbackModelUsed:
|
|
71016
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
71017
|
+
agent: "executor",
|
|
71018
|
+
label: "executor",
|
|
71019
|
+
store: this.store,
|
|
71020
|
+
taskId: task.id,
|
|
71021
|
+
taskTitle: detail.title
|
|
71022
|
+
})
|
|
70579
71023
|
});
|
|
70580
71024
|
if (isResuming) {
|
|
70581
71025
|
executorLog.log(`${task.id}: resumed session from ${task.sessionFile}`);
|
|
@@ -71009,7 +71453,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
71009
71453
|
this.options.onComplete?.(task);
|
|
71010
71454
|
} else {
|
|
71011
71455
|
executorLog.log(`${task.id} paused \u2014 moving to todo`);
|
|
71012
|
-
if (worktreePath &&
|
|
71456
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
71013
71457
|
try {
|
|
71014
71458
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
71015
71459
|
executorLog.log(`Removed old worktree for paused task: ${worktreePath}`);
|
|
@@ -71105,7 +71549,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
71105
71549
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
|
|
71106
71550
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
71107
71551
|
}
|
|
71108
|
-
if (worktreePath &&
|
|
71552
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
71109
71553
|
try {
|
|
71110
71554
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
71111
71555
|
executorLog.log(`Removed old worktree for transient retry: ${worktreePath}`);
|
|
@@ -71160,7 +71604,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
71160
71604
|
try {
|
|
71161
71605
|
const latestTask = await this.store.getTask(task.id);
|
|
71162
71606
|
await this.resetStepsIfWorkLost(latestTask);
|
|
71163
|
-
if (worktreePath &&
|
|
71607
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
71164
71608
|
try {
|
|
71165
71609
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
71166
71610
|
executorLog.log(`Removed old worktree for stuck-killed retry: ${worktreePath}`);
|
|
@@ -71665,7 +72109,7 @@ Take a different approach. Do NOT repeat the rejected strategy. Re-read the step
|
|
|
71665
72109
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
71666
72110
|
*/
|
|
71667
72111
|
async injectWorkflowRevisionInstructions(task, feedback) {
|
|
71668
|
-
const promptPath =
|
|
72112
|
+
const promptPath = join36(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
71669
72113
|
let content;
|
|
71670
72114
|
try {
|
|
71671
72115
|
content = await readFile15(promptPath, "utf-8");
|
|
@@ -71719,6 +72163,217 @@ ${feedback}
|
|
|
71719
72163
|
*
|
|
71720
72164
|
* @returns true if a retry was scheduled, false if retries are exhausted
|
|
71721
72165
|
*/
|
|
72166
|
+
/**
|
|
72167
|
+
* Run deterministic verification (test + build commands) in the task's worktree.
|
|
72168
|
+
* Returns a structured result indicating whether all commands passed.
|
|
72169
|
+
*/
|
|
72170
|
+
async runExecutorDeterministicVerification(task, worktreePath, settings) {
|
|
72171
|
+
const testCommand = settings.testCommand?.trim();
|
|
72172
|
+
const buildCommand2 = settings.buildCommand?.trim();
|
|
72173
|
+
if (!testCommand && !buildCommand2) {
|
|
72174
|
+
executorLog.log(`${task.id}: no test/build commands configured \u2014 skipping verification`);
|
|
72175
|
+
return { allPassed: true };
|
|
72176
|
+
}
|
|
72177
|
+
const parts = [];
|
|
72178
|
+
if (testCommand) parts.push(`test: ${testCommand}`);
|
|
72179
|
+
if (buildCommand2) parts.push(`build: ${buildCommand2}`);
|
|
72180
|
+
executorLog.log(`${task.id}: [verification] running deterministic verification (${parts.join(", ")})`);
|
|
72181
|
+
await this.store.logEntry(
|
|
72182
|
+
task.id,
|
|
72183
|
+
`[verification] Running deterministic verification (${parts.join(", ")})`,
|
|
72184
|
+
void 0,
|
|
72185
|
+
this.currentRunContext
|
|
72186
|
+
);
|
|
72187
|
+
const result = { allPassed: true };
|
|
72188
|
+
if (testCommand) {
|
|
72189
|
+
const testResult = await runVerificationCommand(
|
|
72190
|
+
this.store,
|
|
72191
|
+
worktreePath,
|
|
72192
|
+
task.id,
|
|
72193
|
+
testCommand,
|
|
72194
|
+
"test",
|
|
72195
|
+
void 0,
|
|
72196
|
+
executorLog,
|
|
72197
|
+
"executor"
|
|
72198
|
+
);
|
|
72199
|
+
result.testResult = testResult;
|
|
72200
|
+
if (!testResult.success) {
|
|
72201
|
+
result.allPassed = false;
|
|
72202
|
+
result.failedCommand = "testCommand";
|
|
72203
|
+
executorLog.log(`${task.id}: [verification] test failed (exit ${testResult.exitCode})`);
|
|
72204
|
+
return result;
|
|
72205
|
+
}
|
|
72206
|
+
}
|
|
72207
|
+
if (buildCommand2) {
|
|
72208
|
+
const buildResult = await runVerificationCommand(
|
|
72209
|
+
this.store,
|
|
72210
|
+
worktreePath,
|
|
72211
|
+
task.id,
|
|
72212
|
+
buildCommand2,
|
|
72213
|
+
"build",
|
|
72214
|
+
void 0,
|
|
72215
|
+
executorLog,
|
|
72216
|
+
"executor"
|
|
72217
|
+
);
|
|
72218
|
+
result.buildResult = buildResult;
|
|
72219
|
+
if (!buildResult.success) {
|
|
72220
|
+
result.allPassed = false;
|
|
72221
|
+
result.failedCommand = "buildCommand";
|
|
72222
|
+
executorLog.log(`${task.id}: [verification] build failed (exit ${buildResult.exitCode})`);
|
|
72223
|
+
return result;
|
|
72224
|
+
}
|
|
72225
|
+
}
|
|
72226
|
+
executorLog.log(`${task.id}: [verification] passed`);
|
|
72227
|
+
await this.store.logEntry(
|
|
72228
|
+
task.id,
|
|
72229
|
+
`[verification] Deterministic verification passed`,
|
|
72230
|
+
void 0,
|
|
72231
|
+
this.currentRunContext
|
|
72232
|
+
);
|
|
72233
|
+
return result;
|
|
72234
|
+
}
|
|
72235
|
+
/**
|
|
72236
|
+
* Attempt to fix verification failures by spawning a dedicated AI fix agent.
|
|
72237
|
+
* Follows the pattern established by the merger's attemptInMergeVerificationFix.
|
|
72238
|
+
* Returns true if verification passes after the fix attempt, false otherwise.
|
|
72239
|
+
*/
|
|
72240
|
+
async attemptExecutorVerificationFix(task, worktreePath, failureContext, settings, retryNumber, maxRetries) {
|
|
72241
|
+
try {
|
|
72242
|
+
executorLog.log(`${task.id}: spawning executor verification fix agent (attempt ${retryNumber}/${maxRetries})`);
|
|
72243
|
+
const logger2 = new AgentLogger({
|
|
72244
|
+
store: this.store,
|
|
72245
|
+
taskId: task.id,
|
|
72246
|
+
agent: "executor",
|
|
72247
|
+
persistAgentToolOutput: settings.persistAgentToolOutput,
|
|
72248
|
+
onAgentText: this.options.onAgentText,
|
|
72249
|
+
onAgentTool: this.options.onAgentTool
|
|
72250
|
+
});
|
|
72251
|
+
let skillContext;
|
|
72252
|
+
if (this.options.agentStore) {
|
|
72253
|
+
try {
|
|
72254
|
+
skillContext = await buildSessionSkillContext({
|
|
72255
|
+
agentStore: this.options.agentStore,
|
|
72256
|
+
task,
|
|
72257
|
+
sessionPurpose: "executor",
|
|
72258
|
+
projectRootDir: worktreePath,
|
|
72259
|
+
pluginRunner: this.options.pluginRunner
|
|
72260
|
+
});
|
|
72261
|
+
} catch {
|
|
72262
|
+
}
|
|
72263
|
+
}
|
|
72264
|
+
const { provider: executorProvider, modelId: executorModelId } = resolveExecutorModelPair2(
|
|
72265
|
+
task.modelProvider,
|
|
72266
|
+
task.modelId,
|
|
72267
|
+
settings
|
|
72268
|
+
);
|
|
72269
|
+
const { session } = await createResolvedAgentSession({
|
|
72270
|
+
sessionPurpose: "executor",
|
|
72271
|
+
pluginRunner: this.options.pluginRunner,
|
|
72272
|
+
cwd: worktreePath,
|
|
72273
|
+
// Run in the task's worktree
|
|
72274
|
+
systemPrompt: `You are a verification fix agent running during task execution in a worktree.
|
|
72275
|
+
|
|
72276
|
+
All step-session steps completed successfully but the deterministic verification command failed. Your job is to fix the failing code directly in the working directory.
|
|
72277
|
+
|
|
72278
|
+
## Scope
|
|
72279
|
+
Only fix what is required to make the failing verification pass.
|
|
72280
|
+
Do not refactor, rename broadly, or make opportunistic improvements.
|
|
72281
|
+
|
|
72282
|
+
## Rules
|
|
72283
|
+
1. Read the error output carefully to understand what is failing before editing anything
|
|
72284
|
+
2. Before assuming a code fix is needed, check whether the failure is caused by stale/missing build artifacts in a sibling workspace package \u2014 typical signatures: \`Failed to resolve import "./X.js"\` pointing into another package's \`dist/\`, \`Cannot find module\`, or \`ERR_MODULE_NOT_FOUND\` referencing a workspace-internal path. In that case, rebuild the affected package(s) (e.g. \`pnpm --filter <pkg> build\`, or \`pnpm --filter "<scope>/*" build\` for a group) and re-run verification before editing source files.
|
|
72285
|
+
3. Make targeted fixes to the failing code path
|
|
72286
|
+
4. After fixing, run the verification command to confirm the fix works
|
|
72287
|
+
5. Do NOT make any git commits \u2014 just fix the code
|
|
72288
|
+
6. You MAY modify any files needed to make the verification pass, including files unrelated to this task's original change. Pre-existing build/test breakage is in scope: fix it. Prefer the smallest change that makes verification green.
|
|
72289
|
+
7. If you cannot fix the issue within scope, explain why and what evidence indicates a deeper/root problem`,
|
|
72290
|
+
tools: "coding",
|
|
72291
|
+
onText: logger2.onText,
|
|
72292
|
+
onThinking: logger2.onThinking,
|
|
72293
|
+
onToolStart: logger2.onToolStart,
|
|
72294
|
+
onToolEnd: logger2.onToolEnd,
|
|
72295
|
+
defaultProvider: executorProvider,
|
|
72296
|
+
defaultModelId: executorModelId,
|
|
72297
|
+
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
72298
|
+
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {}
|
|
72299
|
+
});
|
|
72300
|
+
await this.store.logEntry(
|
|
72301
|
+
task.id,
|
|
72302
|
+
`Executor verification fix agent started (model: ${describeModel(session)}, attempt ${retryNumber}/${maxRetries})`,
|
|
72303
|
+
void 0,
|
|
72304
|
+
this.currentRunContext
|
|
72305
|
+
);
|
|
72306
|
+
await this.store.appendAgentLog(
|
|
72307
|
+
task.id,
|
|
72308
|
+
`Fix agent started (model: ${describeModel(session)}, attempt ${retryNumber}/${maxRetries})`,
|
|
72309
|
+
"text",
|
|
72310
|
+
void 0,
|
|
72311
|
+
"executor"
|
|
72312
|
+
);
|
|
72313
|
+
try {
|
|
72314
|
+
const fixPrompt = `Fix the failing ${failureContext.type} verification for task ${task.id}.
|
|
72315
|
+
|
|
72316
|
+
## Failed command
|
|
72317
|
+
Command: \`${failureContext.command}\`
|
|
72318
|
+
Exit code: ${failureContext.exitCode}
|
|
72319
|
+
|
|
72320
|
+
## Error output
|
|
72321
|
+
${failureContext.output.slice(0, VERIFICATION_LOG_MAX_CHARS)}
|
|
72322
|
+
|
|
72323
|
+
## Instructions
|
|
72324
|
+
1. Read the error output and identify the root cause
|
|
72325
|
+
2. Make targeted fixes to resolve the failure
|
|
72326
|
+
3. Run the verification command \`${failureContext.command}\` to confirm your fix works
|
|
72327
|
+
4. If the fix doesn't work, try a different approach
|
|
72328
|
+
5. Do NOT make any git commits`;
|
|
72329
|
+
await withRateLimitRetry(async () => {
|
|
72330
|
+
await promptWithFallback(session, fixPrompt);
|
|
72331
|
+
}, {
|
|
72332
|
+
onRetry: (attempt, delayMs, error) => {
|
|
72333
|
+
const delaySec = Math.round(delayMs / 1e3);
|
|
72334
|
+
executorLog.warn(`\u23F3 ${task.id} executor fix agent rate limited \u2014 retry ${attempt} in ${delaySec}s: ${error.message}`);
|
|
72335
|
+
}
|
|
72336
|
+
});
|
|
72337
|
+
await accumulateSessionTokenUsage(this.store, task.id, session);
|
|
72338
|
+
executorLog.log(`${task.id}: re-running deterministic verification after fix attempt ${retryNumber}/${maxRetries}`);
|
|
72339
|
+
await this.store.logEntry(
|
|
72340
|
+
task.id,
|
|
72341
|
+
`Re-running deterministic verification (attempt ${retryNumber}/${maxRetries})`,
|
|
72342
|
+
void 0,
|
|
72343
|
+
this.currentRunContext
|
|
72344
|
+
);
|
|
72345
|
+
await this.store.appendAgentLog(
|
|
72346
|
+
task.id,
|
|
72347
|
+
`Re-running verification (attempt ${retryNumber}/${maxRetries})`,
|
|
72348
|
+
"text",
|
|
72349
|
+
void 0,
|
|
72350
|
+
"executor"
|
|
72351
|
+
);
|
|
72352
|
+
const reRunResult = await this.runExecutorDeterministicVerification(task, worktreePath, settings);
|
|
72353
|
+
return reRunResult.allPassed;
|
|
72354
|
+
} finally {
|
|
72355
|
+
await logger2.flush();
|
|
72356
|
+
await session.dispose();
|
|
72357
|
+
}
|
|
72358
|
+
} catch (err) {
|
|
72359
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
72360
|
+
executorLog.warn(`${task.id}: executor verification fix agent error: ${errorMessage}`);
|
|
72361
|
+
await this.store.logEntry(
|
|
72362
|
+
task.id,
|
|
72363
|
+
`Executor verification fix agent encountered an error`,
|
|
72364
|
+
errorMessage,
|
|
72365
|
+
this.currentRunContext
|
|
72366
|
+
);
|
|
72367
|
+
await this.store.appendAgentLog(
|
|
72368
|
+
task.id,
|
|
72369
|
+
"Fix agent encountered an error",
|
|
72370
|
+
"tool_error",
|
|
72371
|
+
errorMessage,
|
|
72372
|
+
"executor"
|
|
72373
|
+
);
|
|
72374
|
+
return false;
|
|
72375
|
+
}
|
|
72376
|
+
}
|
|
71722
72377
|
async handleWorkflowStepFailure(task, worktreePath, failureFeedback, stepName) {
|
|
71723
72378
|
this.clearCompletedTaskWatchdog(task.id);
|
|
71724
72379
|
const currentRetries = task.workflowStepRetries ?? 0;
|
|
@@ -71788,7 +72443,7 @@ Please fix the issues so the verification can pass on the next attempt.`,
|
|
|
71788
72443
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
71789
72444
|
*/
|
|
71790
72445
|
async injectWorkflowStepFailureInstructions(task, failureFeedback, stepName, retryCount) {
|
|
71791
|
-
const promptPath =
|
|
72446
|
+
const promptPath = join36(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
71792
72447
|
let content;
|
|
71793
72448
|
try {
|
|
71794
72449
|
content = await readFile15(promptPath, "utf-8");
|
|
@@ -71853,31 +72508,33 @@ ${failureFeedback}
|
|
|
71853
72508
|
* Uses git diff against the stored baseCommitSha to determine what changed.
|
|
71854
72509
|
* Returns an empty array if no changes or if git commands fail.
|
|
71855
72510
|
*/
|
|
72511
|
+
async resolveDiffBaseRef(worktreePath, baseCommitSha) {
|
|
72512
|
+
if (baseCommitSha) return baseCommitSha;
|
|
72513
|
+
try {
|
|
72514
|
+
const { stdout } = await execAsync5(
|
|
72515
|
+
"git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main",
|
|
72516
|
+
{ cwd: worktreePath, encoding: "utf-8" }
|
|
72517
|
+
);
|
|
72518
|
+
const ref = stdout.trim();
|
|
72519
|
+
if (ref) return ref;
|
|
72520
|
+
} catch (mergeBaseErr) {
|
|
72521
|
+
const mergeBaseMsg = mergeBaseErr instanceof Error ? mergeBaseErr.message : String(mergeBaseErr);
|
|
72522
|
+
executorLog.warn(`Failed merge-base lookup for diff base in ${worktreePath}, trying HEAD~1 fallback: ${mergeBaseMsg}`);
|
|
72523
|
+
}
|
|
72524
|
+
try {
|
|
72525
|
+
const { stdout } = await execAsync5("git rev-parse HEAD~1", {
|
|
72526
|
+
cwd: worktreePath,
|
|
72527
|
+
encoding: "utf-8"
|
|
72528
|
+
});
|
|
72529
|
+
return stdout.trim() || void 0;
|
|
72530
|
+
} catch {
|
|
72531
|
+
executorLog.log(`Could not determine base commit for diff in ${worktreePath}`);
|
|
72532
|
+
return void 0;
|
|
72533
|
+
}
|
|
72534
|
+
}
|
|
71856
72535
|
async captureModifiedFiles(worktreePath, baseCommitSha) {
|
|
71857
72536
|
try {
|
|
71858
|
-
|
|
71859
|
-
if (!baseRef) {
|
|
71860
|
-
try {
|
|
71861
|
-
const { stdout: stdout2 } = await execAsync5("git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main", {
|
|
71862
|
-
cwd: worktreePath,
|
|
71863
|
-
encoding: "utf-8"
|
|
71864
|
-
});
|
|
71865
|
-
baseRef = stdout2.trim();
|
|
71866
|
-
} catch (mergeBaseErr) {
|
|
71867
|
-
const mergeBaseMsg = mergeBaseErr instanceof Error ? mergeBaseErr.message : String(mergeBaseErr);
|
|
71868
|
-
executorLog.warn(`Failed merge-base lookup for diff base in ${worktreePath}, trying HEAD~1 fallback: ${mergeBaseMsg}`);
|
|
71869
|
-
try {
|
|
71870
|
-
const { stdout: stdout2 } = await execAsync5("git rev-parse HEAD~1", {
|
|
71871
|
-
cwd: worktreePath,
|
|
71872
|
-
encoding: "utf-8"
|
|
71873
|
-
});
|
|
71874
|
-
baseRef = stdout2.trim();
|
|
71875
|
-
} catch {
|
|
71876
|
-
executorLog.log(`Could not determine base commit for diff in ${worktreePath}`);
|
|
71877
|
-
return [];
|
|
71878
|
-
}
|
|
71879
|
-
}
|
|
71880
|
-
}
|
|
72537
|
+
const baseRef = await this.resolveDiffBaseRef(worktreePath, baseCommitSha);
|
|
71881
72538
|
if (!baseRef) {
|
|
71882
72539
|
return [];
|
|
71883
72540
|
}
|
|
@@ -72113,6 +72770,30 @@ ${failureFeedback}
|
|
|
72113
72770
|
*/
|
|
72114
72771
|
async executeWorkflowStep(task, workflowStep, worktreePath, settings) {
|
|
72115
72772
|
const toolMode = workflowStep.toolMode || "readonly";
|
|
72773
|
+
const scopedFiles = await this.captureModifiedFiles(worktreePath, task.baseCommitSha);
|
|
72774
|
+
let diffShortstat;
|
|
72775
|
+
try {
|
|
72776
|
+
const baseRef = await this.resolveDiffBaseRef(worktreePath, task.baseCommitSha);
|
|
72777
|
+
if (baseRef) {
|
|
72778
|
+
const { stdout } = await execAsync5(`git diff --shortstat ${baseRef}..HEAD`, {
|
|
72779
|
+
cwd: worktreePath,
|
|
72780
|
+
encoding: "utf-8"
|
|
72781
|
+
});
|
|
72782
|
+
diffShortstat = stdout.trim() || void 0;
|
|
72783
|
+
}
|
|
72784
|
+
} catch {
|
|
72785
|
+
}
|
|
72786
|
+
const MAX_SCOPE_FILES = 100;
|
|
72787
|
+
const scopeFileBlock = scopedFiles.length === 0 ? "(no modified files detected for this task \u2014 review the worktree directly, but do NOT browse unrelated files)" : scopedFiles.length > MAX_SCOPE_FILES ? `${scopedFiles.slice(0, MAX_SCOPE_FILES).map((f) => `- ${f}`).join("\n")}
|
|
72788
|
+
- ... (${scopedFiles.length - MAX_SCOPE_FILES} more files truncated)` : scopedFiles.map((f) => `- ${f}`).join("\n");
|
|
72789
|
+
const scopeBlock = `Diff Scope (files changed by THIS task vs base):
|
|
72790
|
+
${scopeFileBlock}${diffShortstat ? `
|
|
72791
|
+
Diff stat: ${diffShortstat}` : ""}
|
|
72792
|
+
|
|
72793
|
+
CRITICAL SCOPING RULES \u2014 read before doing anything else:
|
|
72794
|
+
- Review ONLY the files listed above. Do NOT analyze unmodified files or unrelated parts of the codebase.
|
|
72795
|
+
- If NONE of the files in the diff scope are relevant to your review category (e.g. a UX/design reviewer with no UI/CSS/component files in scope, a security reviewer with no auth/network code in scope, an a11y reviewer with no markup changes), respond IMMEDIATELY with a single short approval line such as "No relevant changes in scope \u2014 approved." and STOP. Do not start exploring the codebase.
|
|
72796
|
+
- Your wall-clock budget is short. Spending it browsing unmodified files will cause this step to time out and block merge.`;
|
|
72116
72797
|
const systemPrompt = `You are a workflow step agent executing: ${workflowStep.name}
|
|
72117
72798
|
|
|
72118
72799
|
Task Context:
|
|
@@ -72120,6 +72801,8 @@ Task Context:
|
|
|
72120
72801
|
- Task Description: ${task.description}
|
|
72121
72802
|
- Worktree: ${worktreePath}
|
|
72122
72803
|
|
|
72804
|
+
${scopeBlock}
|
|
72805
|
+
|
|
72123
72806
|
Your role:
|
|
72124
72807
|
- Execute this workflow step exactly as scoped.
|
|
72125
72808
|
- Prioritize high-impact correctness/risk findings over stylistic nits.
|
|
@@ -72588,7 +73271,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
72588
73271
|
* rather than fail the task permanently.
|
|
72589
73272
|
*/
|
|
72590
73273
|
async resolveWorktreeStartPoint(startPoint, taskId) {
|
|
72591
|
-
const command = isAbsolute11(startPoint) &&
|
|
73274
|
+
const command = isAbsolute11(startPoint) && existsSync29(startPoint) ? `git -C "${startPoint}" rev-parse --verify HEAD^{commit}` : `git rev-parse --verify "${startPoint}^{commit}"`;
|
|
72592
73275
|
try {
|
|
72593
73276
|
const { stdout } = await execAsync5(command, { cwd: this.rootDir });
|
|
72594
73277
|
return stdout.trim() || startPoint;
|
|
@@ -72608,7 +73291,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
72608
73291
|
*/
|
|
72609
73292
|
async tryCreateWorktree(branch, path5, taskId, startPoint, attemptNumber = 0, recoveryDepth = 0) {
|
|
72610
73293
|
await this.assertWorktreePathNotNested(path5, taskId);
|
|
72611
|
-
if (
|
|
73294
|
+
if (existsSync29(path5)) {
|
|
72612
73295
|
const isRegistered = await this.isRegisteredWorktree(path5);
|
|
72613
73296
|
if (!isRegistered) {
|
|
72614
73297
|
await this.store.logEntry(
|
|
@@ -72759,7 +73442,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
72759
73442
|
);
|
|
72760
73443
|
if (shouldGenerateNewName) {
|
|
72761
73444
|
const conflictStartPoint = branch;
|
|
72762
|
-
const newPath =
|
|
73445
|
+
const newPath = join36(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
72763
73446
|
for (let suffix = 2; suffix <= 6; suffix++) {
|
|
72764
73447
|
const suffixedBranch = `${branch}-${suffix}`;
|
|
72765
73448
|
try {
|
|
@@ -73359,7 +74042,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
73359
74042
|
metadata: { type: "spawned", parentTaskId: taskId }
|
|
73360
74043
|
});
|
|
73361
74044
|
const childWorktreeName = generateWorktreeName(this.rootDir);
|
|
73362
|
-
const childWorktreePath =
|
|
74045
|
+
const childWorktreePath = join36(this.rootDir, ".worktrees", childWorktreeName);
|
|
73363
74046
|
const childBranch = `fusion/spawn-${agent.id}`;
|
|
73364
74047
|
await this.createWorktree(childBranch, childWorktreePath, taskId, worktreePath);
|
|
73365
74048
|
await this.options.agentStore.updateAgentState(agent.id, "active");
|
|
@@ -73548,9 +74231,9 @@ var init_node_routing_policy = __esm({
|
|
|
73548
74231
|
});
|
|
73549
74232
|
|
|
73550
74233
|
// ../engine/src/scheduler.ts
|
|
73551
|
-
import { existsSync as
|
|
74234
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
73552
74235
|
import { readFile as readFile16 } from "node:fs/promises";
|
|
73553
|
-
import { basename as basename8, join as
|
|
74236
|
+
import { basename as basename8, join as join37 } from "node:path";
|
|
73554
74237
|
function pathsOverlap2(a, b) {
|
|
73555
74238
|
for (const pa of a) {
|
|
73556
74239
|
const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
|
|
@@ -73720,12 +74403,12 @@ var init_scheduler = __esm({
|
|
|
73720
74403
|
* @returns Object with `valid: true` if checks pass, or `valid: false` with a `reason` string if they fail
|
|
73721
74404
|
*/
|
|
73722
74405
|
async validateTaskFilesystem(id) {
|
|
73723
|
-
const taskDir =
|
|
73724
|
-
if (!
|
|
74406
|
+
const taskDir = join37(this.store.getTasksDir(), id);
|
|
74407
|
+
if (!existsSync30(taskDir)) {
|
|
73725
74408
|
return { valid: false, reason: "missing directory" };
|
|
73726
74409
|
}
|
|
73727
|
-
const promptPath =
|
|
73728
|
-
if (!
|
|
74410
|
+
const promptPath = join37(taskDir, "PROMPT.md");
|
|
74411
|
+
if (!existsSync30(promptPath)) {
|
|
73729
74412
|
return { valid: false, reason: "missing or empty PROMPT.md" };
|
|
73730
74413
|
}
|
|
73731
74414
|
try {
|
|
@@ -73864,7 +74547,7 @@ var init_scheduler = __esm({
|
|
|
73864
74547
|
break;
|
|
73865
74548
|
}
|
|
73866
74549
|
reservedNames.add(worktreeName);
|
|
73867
|
-
return
|
|
74550
|
+
return join37(this.store.getRootDir(), ".worktrees", worktreeName);
|
|
73868
74551
|
}
|
|
73869
74552
|
/**
|
|
73870
74553
|
* Run one scheduling pass.
|
|
@@ -75141,7 +75824,7 @@ var init_mission_execution_loop = __esm({
|
|
|
75141
75824
|
init_pi();
|
|
75142
75825
|
init_agent_session_helpers();
|
|
75143
75826
|
init_logger2();
|
|
75144
|
-
|
|
75827
|
+
init_fallback_model_observer();
|
|
75145
75828
|
loopLog = createLogger2("mission-loop");
|
|
75146
75829
|
VALIDATION_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
75147
75830
|
MissionExecutionLoop = class extends EventEmitter18 {
|
|
@@ -75375,7 +76058,13 @@ Assertions: ${assertions.map((a) => a.title).join(", ")}`,
|
|
|
75375
76058
|
},
|
|
75376
76059
|
taskId: task?.id,
|
|
75377
76060
|
taskTitle: task?.title,
|
|
75378
|
-
onFallbackModelUsed:
|
|
76061
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
76062
|
+
agent: "reviewer",
|
|
76063
|
+
label: "mission validator",
|
|
76064
|
+
store: this.taskStore,
|
|
76065
|
+
taskId: task?.id,
|
|
76066
|
+
taskTitle: task?.title
|
|
76067
|
+
})
|
|
75379
76068
|
});
|
|
75380
76069
|
session = { session: sessionResult.session, sessionFile: sessionResult.sessionFile };
|
|
75381
76070
|
loopLog.log(`Validation session created for feature ${feature.id}`);
|
|
@@ -77183,7 +77872,7 @@ not loop on the same plan across heartbeats without recording why.`;
|
|
|
77183
77872
|
};
|
|
77184
77873
|
}
|
|
77185
77874
|
};
|
|
77186
|
-
const { createResolvedAgentSession:
|
|
77875
|
+
const { createResolvedAgentSession: createResolvedAgentSession3, extractRuntimeHint: extractRuntimeHint2, extractRuntimeModel: extractRuntimeModel2 } = await Promise.resolve().then(() => (init_agent_session_helpers(), agent_session_helpers_exports));
|
|
77187
77876
|
const { buildSessionSkillContextSync: buildSessionSkillContextSync2 } = await Promise.resolve().then(() => (init_session_skill_context(), session_skill_context_exports));
|
|
77188
77877
|
let heartbeatTools;
|
|
77189
77878
|
if (isNoTaskRun) {
|
|
@@ -77254,7 +77943,7 @@ not loop on the same plan across heartbeats without recording why.`;
|
|
|
77254
77943
|
persistAgentToolOutput: memorySettings?.persistAgentToolOutput
|
|
77255
77944
|
});
|
|
77256
77945
|
}
|
|
77257
|
-
const { session } = await
|
|
77946
|
+
const { session } = await createResolvedAgentSession3({
|
|
77258
77947
|
sessionPurpose: "heartbeat",
|
|
77259
77948
|
runtimeHint: extractRuntimeHint2(agent.runtimeConfig),
|
|
77260
77949
|
pluginRunner: this.pluginRunner,
|
|
@@ -78640,7 +79329,7 @@ async function createAiPromptExecutor(cwd) {
|
|
|
78640
79329
|
}
|
|
78641
79330
|
};
|
|
78642
79331
|
}
|
|
78643
|
-
function
|
|
79332
|
+
function truncateOutput2(stdout, stderr) {
|
|
78644
79333
|
const out = stdout ?? "";
|
|
78645
79334
|
const err = stderr ?? "";
|
|
78646
79335
|
let combined = out;
|
|
@@ -78820,7 +79509,7 @@ var init_cron_runner = __esm({
|
|
|
78820
79509
|
maxBuffer: MAX_BUFFER,
|
|
78821
79510
|
shell: defaultShell
|
|
78822
79511
|
});
|
|
78823
|
-
const output =
|
|
79512
|
+
const output = truncateOutput2(stdout, stderr);
|
|
78824
79513
|
log15.log(`\u2713 ${schedule.name} completed (${output.length} bytes output)`);
|
|
78825
79514
|
return {
|
|
78826
79515
|
success: true,
|
|
@@ -78831,7 +79520,7 @@ var init_cron_runner = __esm({
|
|
|
78831
79520
|
} catch (err) {
|
|
78832
79521
|
const stdout = err.stdout ?? "";
|
|
78833
79522
|
const stderr = err.stderr ?? "";
|
|
78834
|
-
const output =
|
|
79523
|
+
const output = truncateOutput2(stdout, stderr);
|
|
78835
79524
|
const errorMessage = err.killed ? `Command timed out after ${(schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS6) / 1e3}s` : err.message ?? String(err);
|
|
78836
79525
|
log15.warn(`\u2717 ${schedule.name} failed: ${errorMessage}`);
|
|
78837
79526
|
return {
|
|
@@ -78876,7 +79565,7 @@ var init_cron_runner = __esm({
|
|
|
78876
79565
|
const result = await runBackupCommand2(fusionDir, settings);
|
|
78877
79566
|
return {
|
|
78878
79567
|
success: result.success,
|
|
78879
|
-
output:
|
|
79568
|
+
output: truncateOutput2(result.output ?? "", ""),
|
|
78880
79569
|
error: result.success ? void 0 : result.output
|
|
78881
79570
|
};
|
|
78882
79571
|
} catch (err) {
|
|
@@ -78917,7 +79606,7 @@ var init_cron_runner = __esm({
|
|
|
78917
79606
|
if (sr.output) outputParts.push(sr.output);
|
|
78918
79607
|
if (sr.error) outputParts.push(`Error: ${sr.error}`);
|
|
78919
79608
|
}
|
|
78920
|
-
const output =
|
|
79609
|
+
const output = truncateOutput2(outputParts.join("\n"), "");
|
|
78921
79610
|
const failedSteps = stepResults.filter((sr) => !sr.success);
|
|
78922
79611
|
const error = failedSteps.length > 0 ? `${failedSteps.length} step(s) failed: ${failedSteps.map((s) => s.stepName).join(", ")}${stoppedEarly ? " (execution stopped)" : ""}` : void 0;
|
|
78923
79612
|
const status = overallSuccess ? "\u2713" : "\u2717";
|
|
@@ -78996,7 +79685,7 @@ var init_cron_runner = __esm({
|
|
|
78996
79685
|
stepName: step.name,
|
|
78997
79686
|
stepIndex,
|
|
78998
79687
|
success: true,
|
|
78999
|
-
output:
|
|
79688
|
+
output: truncateOutput2(stdout, stderr),
|
|
79000
79689
|
startedAt,
|
|
79001
79690
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
79002
79691
|
};
|
|
@@ -79009,7 +79698,7 @@ var init_cron_runner = __esm({
|
|
|
79009
79698
|
stepName: step.name,
|
|
79010
79699
|
stepIndex,
|
|
79011
79700
|
success: false,
|
|
79012
|
-
output:
|
|
79701
|
+
output: truncateOutput2(stdout, stderr),
|
|
79013
79702
|
error: errorMessage,
|
|
79014
79703
|
startedAt,
|
|
79015
79704
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -79159,7 +79848,7 @@ var init_cron_runner = __esm({
|
|
|
79159
79848
|
// ../engine/src/routine-runner.ts
|
|
79160
79849
|
import { exec as exec8 } from "node:child_process";
|
|
79161
79850
|
import { promisify as promisify8 } from "node:util";
|
|
79162
|
-
function
|
|
79851
|
+
function truncateOutput3(stdout, stderr) {
|
|
79163
79852
|
let output = stdout;
|
|
79164
79853
|
if (stderr) {
|
|
79165
79854
|
output += stdout ? "\n--- stderr ---\n" : "";
|
|
@@ -79342,7 +80031,7 @@ var init_routine_runner = __esm({
|
|
|
79342
80031
|
const result = await runBackupCommand2(fusionDir, settings);
|
|
79343
80032
|
return {
|
|
79344
80033
|
success: result.success,
|
|
79345
|
-
output:
|
|
80034
|
+
output: truncateOutput3(result.output ?? "", ""),
|
|
79346
80035
|
error: result.success ? void 0 : result.output,
|
|
79347
80036
|
startedAt,
|
|
79348
80037
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -79366,7 +80055,7 @@ var init_routine_runner = __esm({
|
|
|
79366
80055
|
});
|
|
79367
80056
|
return {
|
|
79368
80057
|
success: true,
|
|
79369
|
-
output:
|
|
80058
|
+
output: truncateOutput3(stdout, stderr),
|
|
79370
80059
|
startedAt,
|
|
79371
80060
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
79372
80061
|
};
|
|
@@ -79377,7 +80066,7 @@ var init_routine_runner = __esm({
|
|
|
79377
80066
|
const error = errObj.killed === true ? `Command timed out after ${(timeoutMs ?? DEFAULT_TIMEOUT_MS7) / 1e3}s` : (err instanceof Error ? err.message : null) ?? String(err);
|
|
79378
80067
|
return {
|
|
79379
80068
|
success: false,
|
|
79380
|
-
output:
|
|
80069
|
+
output: truncateOutput3(stdout, stderr),
|
|
79381
80070
|
error,
|
|
79382
80071
|
startedAt,
|
|
79383
80072
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -79410,7 +80099,7 @@ var init_routine_runner = __esm({
|
|
|
79410
80099
|
const failedSteps = stepResults.filter((sr) => !sr.success);
|
|
79411
80100
|
return {
|
|
79412
80101
|
success: overallSuccess,
|
|
79413
|
-
output:
|
|
80102
|
+
output: truncateOutput3(outputParts.join("\n"), ""),
|
|
79414
80103
|
error: failedSteps.length > 0 ? `${failedSteps.length} step(s) failed: ${failedSteps.map((s) => s.stepName).join(", ")}${stoppedEarly ? " (execution stopped)" : ""}` : void 0,
|
|
79415
80104
|
startedAt,
|
|
79416
80105
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -79445,7 +80134,7 @@ var init_routine_runner = __esm({
|
|
|
79445
80134
|
this.options.aiPromptExecutor(step.prompt, step.modelProvider, step.modelId),
|
|
79446
80135
|
new Promise((_resolve, reject2) => setTimeout(() => reject2(new Error(`AI prompt step timed out after ${timeoutMs / 1e3}s`)), timeoutMs))
|
|
79447
80136
|
]);
|
|
79448
|
-
return { stepId: step.id, stepName: step.name, stepIndex, success: true, output:
|
|
80137
|
+
return { stepId: step.id, stepName: step.name, stepIndex, success: true, output: truncateOutput3(output, ""), startedAt, completedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
79449
80138
|
} catch (err) {
|
|
79450
80139
|
return { stepId: step.id, stepName: step.name, stepIndex, success: false, output: "", error: err instanceof Error ? err.message : String(err), startedAt, completedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
79451
80140
|
}
|
|
@@ -80059,8 +80748,8 @@ var init_stuck_task_detector = __esm({
|
|
|
80059
80748
|
// ../engine/src/self-healing.ts
|
|
80060
80749
|
import { exec as exec9 } from "node:child_process";
|
|
80061
80750
|
import { promisify as promisify9 } from "node:util";
|
|
80062
|
-
import { existsSync as
|
|
80063
|
-
import { isAbsolute as isAbsolute13, join as
|
|
80751
|
+
import { existsSync as existsSync31, readdirSync as readdirSync5, rmSync as rmSync3, statSync as statSync5 } from "node:fs";
|
|
80752
|
+
import { isAbsolute as isAbsolute13, join as join38, relative as relative8, resolve as resolve17 } from "node:path";
|
|
80064
80753
|
function shellQuote(value) {
|
|
80065
80754
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
80066
80755
|
}
|
|
@@ -80456,7 +81145,7 @@ var init_self_healing = __esm({
|
|
|
80456
81145
|
return commit;
|
|
80457
81146
|
}
|
|
80458
81147
|
async cleanupInterruptedMergeArtifacts(task) {
|
|
80459
|
-
if (task.worktree &&
|
|
81148
|
+
if (task.worktree && existsSync31(task.worktree)) {
|
|
80460
81149
|
try {
|
|
80461
81150
|
await execAsync7(`git worktree remove ${shellQuote(task.worktree)} --force`, {
|
|
80462
81151
|
cwd: this.options.rootDir,
|
|
@@ -81077,7 +81766,7 @@ var init_self_healing = __esm({
|
|
|
81077
81766
|
return false;
|
|
81078
81767
|
}
|
|
81079
81768
|
const staleness = now - new Date(t.updatedAt).getTime();
|
|
81080
|
-
const hasWorktree = t.worktree &&
|
|
81769
|
+
const hasWorktree = t.worktree && existsSync31(t.worktree);
|
|
81081
81770
|
const graceMs = hasWorktree ? ORPHANED_WITH_WORKTREE_GRACE_MS : ORPHANED_EXECUTION_RECOVERY_GRACE_MS;
|
|
81082
81771
|
return staleness >= graceMs;
|
|
81083
81772
|
});
|
|
@@ -81086,7 +81775,7 @@ var init_self_healing = __esm({
|
|
|
81086
81775
|
let recovered = 0;
|
|
81087
81776
|
for (const task of orphaned) {
|
|
81088
81777
|
try {
|
|
81089
|
-
const hadWorktree = task.worktree &&
|
|
81778
|
+
const hadWorktree = task.worktree && existsSync31(task.worktree);
|
|
81090
81779
|
const reason = hadWorktree ? "worktree exists but no active session" : "missing worktree/session";
|
|
81091
81780
|
await this.resetStepsIfWorkLost(task);
|
|
81092
81781
|
await this.store.updateTask(task.id, {
|
|
@@ -81232,7 +81921,7 @@ var init_self_healing = __esm({
|
|
|
81232
81921
|
}
|
|
81233
81922
|
}
|
|
81234
81923
|
async hasRecoverableGitWork(task) {
|
|
81235
|
-
if (task.worktree &&
|
|
81924
|
+
if (task.worktree && existsSync31(task.worktree)) {
|
|
81236
81925
|
try {
|
|
81237
81926
|
const { stdout: status } = await execAsync7("git status --porcelain", {
|
|
81238
81927
|
cwd: task.worktree,
|
|
@@ -81417,11 +82106,11 @@ var init_self_healing = __esm({
|
|
|
81417
82106
|
* tracks registered idle worktrees, never these orphans.
|
|
81418
82107
|
*/
|
|
81419
82108
|
async reapUnregisteredOrphans() {
|
|
81420
|
-
const worktreesDir =
|
|
81421
|
-
if (!
|
|
82109
|
+
const worktreesDir = join38(this.options.rootDir, ".worktrees");
|
|
82110
|
+
if (!existsSync31(worktreesDir)) return 0;
|
|
81422
82111
|
let dirs;
|
|
81423
82112
|
try {
|
|
81424
|
-
dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) =>
|
|
82113
|
+
dirs = readdirSync5(worktreesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => join38(worktreesDir, e.name));
|
|
81425
82114
|
} catch (err) {
|
|
81426
82115
|
log17.warn(`Failed to read .worktrees/ for unregistered orphan reap: ${err instanceof Error ? err.message : String(err)}`);
|
|
81427
82116
|
return 0;
|
|
@@ -81526,8 +82215,8 @@ var init_self_healing = __esm({
|
|
|
81526
82215
|
}
|
|
81527
82216
|
/** Remove oldest idle worktrees if total count exceeds 2× maxWorktrees. */
|
|
81528
82217
|
async enforceWorktreeCap() {
|
|
81529
|
-
const worktreesDir =
|
|
81530
|
-
if (!
|
|
82218
|
+
const worktreesDir = join38(this.options.rootDir, ".worktrees");
|
|
82219
|
+
if (!existsSync31(worktreesDir)) return;
|
|
81531
82220
|
try {
|
|
81532
82221
|
const settings = await this.store.getSettings();
|
|
81533
82222
|
const cap = (settings.maxWorktrees ?? 4) * 2;
|
|
@@ -83365,7 +84054,7 @@ var init_ipc_host = __esm({
|
|
|
83365
84054
|
import { EventEmitter as EventEmitter21 } from "node:events";
|
|
83366
84055
|
import { fork } from "node:child_process";
|
|
83367
84056
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
83368
|
-
import { dirname as dirname11, join as
|
|
84057
|
+
import { dirname as dirname11, join as join39 } from "node:path";
|
|
83369
84058
|
var HealthMonitor, ChildProcessRuntime;
|
|
83370
84059
|
var init_child_process_runtime = __esm({
|
|
83371
84060
|
"../engine/src/runtimes/child-process-runtime.ts"() {
|
|
@@ -83527,7 +84216,7 @@ var init_child_process_runtime = __esm({
|
|
|
83527
84216
|
const isCompiled = !import.meta.url.endsWith(".ts");
|
|
83528
84217
|
const currentDir = dirname11(fileURLToPath3(import.meta.url));
|
|
83529
84218
|
const workerFile = isCompiled ? "child-process-worker.js" : "child-process-worker.ts";
|
|
83530
|
-
return
|
|
84219
|
+
return join39(currentDir, workerFile);
|
|
83531
84220
|
}
|
|
83532
84221
|
/**
|
|
83533
84222
|
* Set up event forwarding from IPC host to runtime listeners.
|
|
@@ -87780,6 +88469,8 @@ __export(src_exports2, {
|
|
|
87780
88469
|
describeAgentModel: () => describeAgentModel,
|
|
87781
88470
|
describeModel: () => describeModel,
|
|
87782
88471
|
ensureDefaultHeartbeatProcedureFile: () => ensureDefaultHeartbeatProcedureFile,
|
|
88472
|
+
extractRuntimeHint: () => extractRuntimeHint,
|
|
88473
|
+
extractRuntimeModel: () => extractRuntimeModel,
|
|
87783
88474
|
formatTaskIdentifier: () => formatTaskIdentifier,
|
|
87784
88475
|
getDefaultPiRuntime: () => getDefaultPiRuntime,
|
|
87785
88476
|
getHostExtensionPaths: () => getHostExtensionPaths,
|
|
@@ -92124,7 +92815,7 @@ var init_api_error = __esm({
|
|
|
92124
92815
|
// ../dashboard/src/plugin-routes.ts
|
|
92125
92816
|
import { Router } from "express";
|
|
92126
92817
|
import { access as access5, stat as stat6, readFile as readFile18 } from "node:fs/promises";
|
|
92127
|
-
import { join as
|
|
92818
|
+
import { join as join40, isAbsolute as isAbsolute14, dirname as dirname12, basename as basename9 } from "node:path";
|
|
92128
92819
|
async function resolvePluginManifest(sourcePath) {
|
|
92129
92820
|
try {
|
|
92130
92821
|
await access5(sourcePath);
|
|
@@ -92140,7 +92831,7 @@ async function resolvePluginManifest(sourcePath) {
|
|
|
92140
92831
|
if (!sourceStat.isDirectory()) {
|
|
92141
92832
|
throw badRequest(`Path is not a directory: ${sourcePath}`);
|
|
92142
92833
|
}
|
|
92143
|
-
const directManifestPath =
|
|
92834
|
+
const directManifestPath = join40(sourcePath, "manifest.json");
|
|
92144
92835
|
try {
|
|
92145
92836
|
await access5(directManifestPath);
|
|
92146
92837
|
const manifest = await readAndValidateManifest(directManifestPath);
|
|
@@ -92151,7 +92842,7 @@ async function resolvePluginManifest(sourcePath) {
|
|
|
92151
92842
|
const dirName = basename9(sourcePath).toLowerCase();
|
|
92152
92843
|
if (DIST_DIR_NAMES.has(dirName)) {
|
|
92153
92844
|
const parentDir = dirname12(sourcePath);
|
|
92154
|
-
const parentManifestPath =
|
|
92845
|
+
const parentManifestPath = join40(parentDir, "manifest.json");
|
|
92155
92846
|
try {
|
|
92156
92847
|
await access5(parentManifestPath);
|
|
92157
92848
|
const manifest = await readAndValidateManifest(parentManifestPath);
|
|
@@ -93622,8 +94313,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
93622
94313
|
});
|
|
93623
94314
|
if (retrySpecification) {
|
|
93624
94315
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
93625
|
-
const { join:
|
|
93626
|
-
const promptPath =
|
|
94316
|
+
const { join: join72 } = await import("node:path");
|
|
94317
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
93627
94318
|
await rm6(promptPath, { force: true });
|
|
93628
94319
|
await scopedStore.logEntry(req.params.id, "Retry requested from dashboard (planning retry budget reset)");
|
|
93629
94320
|
const updated2 = await scopedStore.getTask(req.params.id);
|
|
@@ -94088,8 +94779,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94088
94779
|
await scopedStore.logEntry(task.id, "Plan rejected by user", "Specification will be regenerated");
|
|
94089
94780
|
await scopedStore.updateTask(task.id, { status: void 0 });
|
|
94090
94781
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
94091
|
-
const { join:
|
|
94092
|
-
const promptPath =
|
|
94782
|
+
const { join: join72 } = await import("node:path");
|
|
94783
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94093
94784
|
await rm6(promptPath, { force: true });
|
|
94094
94785
|
const updated = await scopedStore.getTask(task.id);
|
|
94095
94786
|
res.json(updated);
|
|
@@ -94359,8 +95050,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94359
95050
|
if (task.column === "triage") {
|
|
94360
95051
|
await scopedStore.logEntry(task.id, "AI spec revision requested", feedback);
|
|
94361
95052
|
const { rm: rm7 } = await import("node:fs/promises");
|
|
94362
|
-
const { join:
|
|
94363
|
-
const promptPath2 =
|
|
95053
|
+
const { join: join73 } = await import("node:path");
|
|
95054
|
+
const promptPath2 = join73(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94364
95055
|
await rm7(promptPath2, { force: true });
|
|
94365
95056
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94366
95057
|
const updated2 = await scopedStore.getTask(task.id);
|
|
@@ -94376,8 +95067,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94376
95067
|
await scopedStore.logEntry(task.id, "AI spec revision requested", feedback);
|
|
94377
95068
|
const updated = await scopedStore.moveTask(task.id, "triage");
|
|
94378
95069
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
94379
|
-
const { join:
|
|
94380
|
-
const promptPath =
|
|
95070
|
+
const { join: join72 } = await import("node:path");
|
|
95071
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94381
95072
|
await rm6(promptPath, { force: true });
|
|
94382
95073
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94383
95074
|
res.json(updated);
|
|
@@ -94397,8 +95088,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94397
95088
|
if (task.column === "triage") {
|
|
94398
95089
|
await scopedStore.logEntry(task.id, "Specification rebuild requested by user");
|
|
94399
95090
|
const { rm: rm7 } = await import("node:fs/promises");
|
|
94400
|
-
const { join:
|
|
94401
|
-
const promptPath2 =
|
|
95091
|
+
const { join: join73 } = await import("node:path");
|
|
95092
|
+
const promptPath2 = join73(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94402
95093
|
await rm7(promptPath2, { force: true });
|
|
94403
95094
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94404
95095
|
const updated2 = await scopedStore.getTask(task.id);
|
|
@@ -94412,8 +95103,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94412
95103
|
await scopedStore.logEntry(task.id, "Specification rebuild requested by user");
|
|
94413
95104
|
const updated = await scopedStore.moveTask(task.id, "triage");
|
|
94414
95105
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
94415
|
-
const { join:
|
|
94416
|
-
const promptPath =
|
|
95106
|
+
const { join: join72 } = await import("node:path");
|
|
95107
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94417
95108
|
await rm6(promptPath, { force: true });
|
|
94418
95109
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94419
95110
|
res.json(updated);
|
|
@@ -95794,14 +96485,15 @@ __export(chat_exports, {
|
|
|
95794
96485
|
__setBuildAgentChatPrompt: () => __setBuildAgentChatPrompt,
|
|
95795
96486
|
__setChatDiagnostics: () => __setChatDiagnostics,
|
|
95796
96487
|
__setCreateFnAgent: () => __setCreateFnAgent2,
|
|
96488
|
+
__setCreateResolvedAgentSession: () => __setCreateResolvedAgentSession,
|
|
95797
96489
|
chatStreamManager: () => chatStreamManager,
|
|
95798
96490
|
checkRateLimit: () => checkRateLimit5,
|
|
95799
96491
|
getRateLimitResetTime: () => getRateLimitResetTime5,
|
|
95800
96492
|
resolveFileReferences: () => resolveFileReferences
|
|
95801
96493
|
});
|
|
95802
96494
|
import { EventEmitter as EventEmitter29 } from "node:events";
|
|
95803
|
-
import { existsSync as
|
|
95804
|
-
import { join as
|
|
96495
|
+
import { existsSync as existsSync32 } from "node:fs";
|
|
96496
|
+
import { join as join41, resolve as resolve19, relative as relative9 } from "node:path";
|
|
95805
96497
|
import { SessionManager as SessionManager3 } from "@mariozechner/pi-coding-agent";
|
|
95806
96498
|
function __getChatDiagnostics() {
|
|
95807
96499
|
return _diagnostics;
|
|
@@ -95831,7 +96523,7 @@ function validateFilePath(basePath, filePath) {
|
|
|
95831
96523
|
throw new Error(`Access denied: Absolute paths not allowed`);
|
|
95832
96524
|
}
|
|
95833
96525
|
const resolvedBase = resolve19(basePath);
|
|
95834
|
-
const resolvedPath = resolve19(
|
|
96526
|
+
const resolvedPath = resolve19(join41(resolvedBase, decodedPath));
|
|
95835
96527
|
const relativePath = relative9(resolvedBase, resolvedPath);
|
|
95836
96528
|
if (relativePath.startsWith("..") || relativePath.startsWith("../") || relativePath === "..") {
|
|
95837
96529
|
throw new Error(`Access denied: Path traversal detected`);
|
|
@@ -95908,6 +96600,9 @@ function getRateLimitResetTime5(ip) {
|
|
|
95908
96600
|
function __setCreateFnAgent2(mock) {
|
|
95909
96601
|
createFnAgent8 = mock;
|
|
95910
96602
|
}
|
|
96603
|
+
function __setCreateResolvedAgentSession(mock) {
|
|
96604
|
+
createResolvedAgentSession2 = mock;
|
|
96605
|
+
}
|
|
95911
96606
|
function __setBuildAgentChatPrompt(mock) {
|
|
95912
96607
|
buildAgentChatPromptFn = mock;
|
|
95913
96608
|
}
|
|
@@ -95915,9 +96610,11 @@ function __resetChatState() {
|
|
|
95915
96610
|
chatStreamManager.reset();
|
|
95916
96611
|
rateLimits5.clear();
|
|
95917
96612
|
buildAgentChatPromptFn = void 0;
|
|
96613
|
+
createFnAgent8 = createFnAgent2;
|
|
96614
|
+
createResolvedAgentSession2 = createResolvedAgentSession;
|
|
95918
96615
|
__setChatDiagnostics(null);
|
|
95919
96616
|
}
|
|
95920
|
-
var createFnAgent8, buildAgentChatPromptFn, defaultDiagnostics, _diagnostics, diagnostics6, CHAT_SYSTEM_PROMPT, RATE_LIMIT_WINDOW_MS5, MAX_MESSAGES_PER_IP_PER_MINUTE, MAX_REFERENCED_FILE_SIZE, rateLimits5, ChatStreamManager, chatStreamManager, ChatManager;
|
|
96617
|
+
var createFnAgent8, createResolvedAgentSession2, buildAgentChatPromptFn, defaultDiagnostics, _diagnostics, diagnostics6, CHAT_SYSTEM_PROMPT, RATE_LIMIT_WINDOW_MS5, MAX_MESSAGES_PER_IP_PER_MINUTE, MAX_REFERENCED_FILE_SIZE, rateLimits5, ChatStreamManager, chatStreamManager, ChatManager;
|
|
95921
96618
|
var init_chat = __esm({
|
|
95922
96619
|
"../dashboard/src/chat.ts"() {
|
|
95923
96620
|
"use strict";
|
|
@@ -95926,6 +96623,7 @@ var init_chat = __esm({
|
|
|
95926
96623
|
init_src2();
|
|
95927
96624
|
init_src2();
|
|
95928
96625
|
createFnAgent8 = createFnAgent2;
|
|
96626
|
+
createResolvedAgentSession2 = createResolvedAgentSession;
|
|
95929
96627
|
defaultDiagnostics = {
|
|
95930
96628
|
log(message, ...args) {
|
|
95931
96629
|
console.log(`[chat] ${message}`, ...args);
|
|
@@ -96044,13 +96742,49 @@ var init_chat = __esm({
|
|
|
96044
96742
|
};
|
|
96045
96743
|
chatStreamManager = new ChatStreamManager();
|
|
96046
96744
|
ChatManager = class {
|
|
96047
|
-
constructor(chatStore, rootDir, agentStore) {
|
|
96745
|
+
constructor(chatStore, rootDir, agentStore, pluginRunner, getSettings) {
|
|
96048
96746
|
this.chatStore = chatStore;
|
|
96049
96747
|
this.rootDir = rootDir;
|
|
96050
96748
|
this.agentStore = agentStore;
|
|
96749
|
+
this.pluginRunner = pluginRunner;
|
|
96750
|
+
this.getSettings = getSettings;
|
|
96051
96751
|
}
|
|
96052
96752
|
agentStoreReady;
|
|
96053
96753
|
activeGenerations = /* @__PURE__ */ new Map();
|
|
96754
|
+
async getChatModelSettings() {
|
|
96755
|
+
if (!this.getSettings) {
|
|
96756
|
+
return {};
|
|
96757
|
+
}
|
|
96758
|
+
try {
|
|
96759
|
+
const settings = await this.getSettings();
|
|
96760
|
+
return {
|
|
96761
|
+
fallbackProvider: settings?.fallbackProvider ?? void 0,
|
|
96762
|
+
fallbackModelId: settings?.fallbackModelId ?? void 0,
|
|
96763
|
+
defaultProvider: settings?.defaultProvider ?? void 0,
|
|
96764
|
+
defaultModelId: settings?.defaultModelId ?? void 0
|
|
96765
|
+
};
|
|
96766
|
+
} catch (err) {
|
|
96767
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
96768
|
+
diagnostics6.warn(`Failed to load chat fallback settings: ${message}`);
|
|
96769
|
+
return {};
|
|
96770
|
+
}
|
|
96771
|
+
}
|
|
96772
|
+
handleFallbackModelUsed(sessionId, payload) {
|
|
96773
|
+
const slashIndex = payload.fallbackModel.indexOf("/");
|
|
96774
|
+
if (slashIndex > 0 && slashIndex < payload.fallbackModel.length - 1) {
|
|
96775
|
+
this.chatStore.updateSession(sessionId, {
|
|
96776
|
+
modelProvider: payload.fallbackModel.slice(0, slashIndex),
|
|
96777
|
+
modelId: payload.fallbackModel.slice(slashIndex + 1)
|
|
96778
|
+
});
|
|
96779
|
+
}
|
|
96780
|
+
diagnostics6.warn(
|
|
96781
|
+
`[fallback] chat ${sessionId} switched from ${payload.primaryModel} to ${payload.fallbackModel} (${payload.triggerPoint})`
|
|
96782
|
+
);
|
|
96783
|
+
chatStreamManager.broadcast(sessionId, {
|
|
96784
|
+
type: "fallback",
|
|
96785
|
+
data: payload
|
|
96786
|
+
});
|
|
96787
|
+
}
|
|
96054
96788
|
/**
|
|
96055
96789
|
* Resolve the per-chat pi/Claude CLI SessionManager.
|
|
96056
96790
|
*
|
|
@@ -96069,7 +96803,7 @@ var init_chat = __esm({
|
|
|
96069
96803
|
* keep the CLI session stable across user messages.
|
|
96070
96804
|
*/
|
|
96071
96805
|
resolveCliSessionManager(session) {
|
|
96072
|
-
if (session.cliSessionFile &&
|
|
96806
|
+
if (session.cliSessionFile && existsSync32(session.cliSessionFile)) {
|
|
96073
96807
|
try {
|
|
96074
96808
|
return SessionManager3.open(session.cliSessionFile);
|
|
96075
96809
|
} catch (err) {
|
|
@@ -96203,6 +96937,7 @@ var init_chat = __esm({
|
|
|
96203
96937
|
let accumulatedText = "";
|
|
96204
96938
|
const toolCallsAccum = [];
|
|
96205
96939
|
const pendingToolStarts = /* @__PURE__ */ new Map();
|
|
96940
|
+
let fallbackInfo;
|
|
96206
96941
|
try {
|
|
96207
96942
|
if (!session) {
|
|
96208
96943
|
chatStreamManager.broadcast(sessionId, {
|
|
@@ -96228,30 +96963,12 @@ var init_chat = __esm({
|
|
|
96228
96963
|
});
|
|
96229
96964
|
return;
|
|
96230
96965
|
}
|
|
96231
|
-
const
|
|
96232
|
-
const
|
|
96966
|
+
const requestedModelProvider = modelProvider ?? session.modelProvider ?? void 0;
|
|
96967
|
+
const requestedModelId = modelId ?? session.modelId ?? void 0;
|
|
96968
|
+
let effectiveModelProvider = requestedModelProvider;
|
|
96969
|
+
let effectiveModelId = requestedModelId;
|
|
96970
|
+
let hasExplicitAgentRuntimeModel = false;
|
|
96233
96971
|
const needsTitle = session.title === null || session.title === void 0 || session.title.trim() === "";
|
|
96234
|
-
if (needsTitle) {
|
|
96235
|
-
(async () => {
|
|
96236
|
-
try {
|
|
96237
|
-
const generated = await summarizeTitle(
|
|
96238
|
-
content.trim(),
|
|
96239
|
-
this.rootDir,
|
|
96240
|
-
effectiveModelProvider,
|
|
96241
|
-
effectiveModelId
|
|
96242
|
-
);
|
|
96243
|
-
const title = generated ?? content.trim().slice(0, 60).trim();
|
|
96244
|
-
if (title) {
|
|
96245
|
-
this.chatStore.updateSession(sessionId, { title });
|
|
96246
|
-
}
|
|
96247
|
-
} catch {
|
|
96248
|
-
const fallback2 = content.trim().slice(0, 60).trim();
|
|
96249
|
-
if (fallback2) {
|
|
96250
|
-
this.chatStore.updateSession(sessionId, { title: fallback2 });
|
|
96251
|
-
}
|
|
96252
|
-
}
|
|
96253
|
-
})();
|
|
96254
|
-
}
|
|
96255
96972
|
await ensureEngineReady5();
|
|
96256
96973
|
if (!createFnAgent8) {
|
|
96257
96974
|
throw new Error("AI agent not available");
|
|
@@ -96282,6 +96999,35 @@ var init_chat = __esm({
|
|
|
96282
96999
|
diagnostics6.warn(`Failed to build enriched system prompt for ${agent.id}: ${message}`);
|
|
96283
97000
|
}
|
|
96284
97001
|
}
|
|
97002
|
+
if (agent) {
|
|
97003
|
+
const runtimeModel = extractRuntimeModel(agent.runtimeConfig);
|
|
97004
|
+
if (runtimeModel.provider && runtimeModel.modelId) {
|
|
97005
|
+
hasExplicitAgentRuntimeModel = true;
|
|
97006
|
+
}
|
|
97007
|
+
effectiveModelProvider ??= runtimeModel.provider;
|
|
97008
|
+
effectiveModelId ??= runtimeModel.modelId;
|
|
97009
|
+
}
|
|
97010
|
+
if (needsTitle) {
|
|
97011
|
+
(async () => {
|
|
97012
|
+
try {
|
|
97013
|
+
const generated = await summarizeTitle(
|
|
97014
|
+
content.trim(),
|
|
97015
|
+
this.rootDir,
|
|
97016
|
+
effectiveModelProvider,
|
|
97017
|
+
effectiveModelId
|
|
97018
|
+
);
|
|
97019
|
+
const title = generated ?? content.trim().slice(0, 60).trim();
|
|
97020
|
+
if (title) {
|
|
97021
|
+
this.chatStore.updateSession(sessionId, { title });
|
|
97022
|
+
}
|
|
97023
|
+
} catch {
|
|
97024
|
+
const fallback2 = content.trim().slice(0, 60).trim();
|
|
97025
|
+
if (fallback2) {
|
|
97026
|
+
this.chatStore.updateSession(sessionId, { title: fallback2 });
|
|
97027
|
+
}
|
|
97028
|
+
}
|
|
97029
|
+
})();
|
|
97030
|
+
}
|
|
96285
97031
|
if (mentions.length > 0) {
|
|
96286
97032
|
const mentionContext = await this.buildMentionContext(mentions, mentionAgents);
|
|
96287
97033
|
if (mentionContext) {
|
|
@@ -96294,7 +97040,10 @@ ${mentionContext}`;
|
|
|
96294
97040
|
const attachmentSummary = attachments && attachments.length > 0 ? `[User attached: ${attachments.map((attachment) => `${attachment.originalName} (${attachment.mimeType}, ${formatAttachmentSize(attachment.size)})`).join(", ")}]` : "";
|
|
96295
97041
|
const promptContent = [attachmentSummary, resolvedContent].filter(Boolean).join("\n\n");
|
|
96296
97042
|
const sessionManager = this.resolveCliSessionManager(session);
|
|
96297
|
-
|
|
97043
|
+
const chatModelSettings = await this.getChatModelSettings();
|
|
97044
|
+
const usesConfiguredDefaultModel = requestedModelProvider === chatModelSettings.defaultProvider && requestedModelId === chatModelSettings.defaultModelId && !!requestedModelProvider && !!requestedModelId;
|
|
97045
|
+
const allowFallback = !hasExplicitAgentRuntimeModel && (!(requestedModelProvider && requestedModelId) || usesConfiguredDefaultModel);
|
|
97046
|
+
const sessionOptions = {
|
|
96298
97047
|
cwd: this.rootDir,
|
|
96299
97048
|
systemPrompt,
|
|
96300
97049
|
tools: "coding",
|
|
@@ -96303,6 +97052,14 @@ ${mentionContext}`;
|
|
|
96303
97052
|
defaultProvider: effectiveModelProvider,
|
|
96304
97053
|
defaultModelId: effectiveModelId
|
|
96305
97054
|
} : {},
|
|
97055
|
+
...allowFallback && chatModelSettings.fallbackProvider && chatModelSettings.fallbackModelId ? {
|
|
97056
|
+
fallbackProvider: chatModelSettings.fallbackProvider,
|
|
97057
|
+
fallbackModelId: chatModelSettings.fallbackModelId
|
|
97058
|
+
} : {},
|
|
97059
|
+
onFallbackModelUsed: (payload) => {
|
|
97060
|
+
fallbackInfo = payload;
|
|
97061
|
+
this.handleFallbackModelUsed(sessionId, payload);
|
|
97062
|
+
},
|
|
96306
97063
|
onThinking: (delta) => {
|
|
96307
97064
|
accumulatedThinking += delta;
|
|
96308
97065
|
chatStreamManager.broadcast(sessionId, {
|
|
@@ -96343,16 +97100,35 @@ ${mentionContext}`;
|
|
|
96343
97100
|
data: { toolName: name, isError, result }
|
|
96344
97101
|
});
|
|
96345
97102
|
}
|
|
96346
|
-
}
|
|
97103
|
+
};
|
|
97104
|
+
const agentRuntimeHint = agent ? extractRuntimeHint(agent.runtimeConfig) : void 0;
|
|
97105
|
+
if (agentRuntimeHint) {
|
|
97106
|
+
agentResult = await createResolvedAgentSession2({
|
|
97107
|
+
sessionPurpose: "executor",
|
|
97108
|
+
runtimeHint: agentRuntimeHint,
|
|
97109
|
+
pluginRunner: this.pluginRunner,
|
|
97110
|
+
...sessionOptions
|
|
97111
|
+
});
|
|
97112
|
+
} else {
|
|
97113
|
+
agentResult = await createFnAgent8(sessionOptions);
|
|
97114
|
+
}
|
|
96347
97115
|
this.activeGenerations.set(sessionId, { abortController, agentResult });
|
|
96348
97116
|
if (abortController.signal.aborted) {
|
|
96349
97117
|
agentResult.session.dispose?.();
|
|
96350
97118
|
return;
|
|
96351
97119
|
}
|
|
96352
|
-
await agentResult.session
|
|
97120
|
+
await promptWithFallback(agentResult.session, promptContent);
|
|
96353
97121
|
if (abortController.signal.aborted) {
|
|
96354
97122
|
return;
|
|
96355
97123
|
}
|
|
97124
|
+
const sessionErrorMessage = agentResult.session.state.errorMessage;
|
|
97125
|
+
if (typeof sessionErrorMessage === "string" && sessionErrorMessage.trim().length > 0 && !accumulatedText && !accumulatedThinking && toolCallsAccum.length === 0) {
|
|
97126
|
+
chatStreamManager.broadcast(sessionId, {
|
|
97127
|
+
type: "error",
|
|
97128
|
+
data: sessionErrorMessage
|
|
97129
|
+
});
|
|
97130
|
+
return;
|
|
97131
|
+
}
|
|
96356
97132
|
let responseText = "";
|
|
96357
97133
|
const lastMessage = agentResult.session.state.messages.filter((m) => m.role === "assistant").pop();
|
|
96358
97134
|
if (lastMessage?.content) {
|
|
@@ -96363,11 +97139,18 @@ ${mentionContext}`;
|
|
|
96363
97139
|
}
|
|
96364
97140
|
}
|
|
96365
97141
|
const finalResponseText = accumulatedText || responseText;
|
|
97142
|
+
const assistantMetadata = {};
|
|
97143
|
+
if (toolCallsAccum.length > 0) {
|
|
97144
|
+
assistantMetadata.toolCalls = toolCallsAccum;
|
|
97145
|
+
}
|
|
97146
|
+
if (fallbackInfo) {
|
|
97147
|
+
assistantMetadata.fallback = fallbackInfo;
|
|
97148
|
+
}
|
|
96366
97149
|
const assistantMessage = this.chatStore.addMessage(sessionId, {
|
|
96367
97150
|
role: "assistant",
|
|
96368
97151
|
content: finalResponseText,
|
|
96369
97152
|
thinkingOutput: accumulatedThinking || void 0,
|
|
96370
|
-
metadata:
|
|
97153
|
+
metadata: Object.keys(assistantMetadata).length > 0 ? assistantMetadata : void 0
|
|
96371
97154
|
});
|
|
96372
97155
|
chatStreamManager.broadcast(sessionId, {
|
|
96373
97156
|
type: "done",
|
|
@@ -96391,6 +97174,7 @@ ${mentionContext}`;
|
|
|
96391
97174
|
thinkingOutput: accumulatedThinking || void 0,
|
|
96392
97175
|
metadata: {
|
|
96393
97176
|
interrupted: true,
|
|
97177
|
+
...fallbackInfo ? { fallback: fallbackInfo } : {},
|
|
96394
97178
|
...toolCallsAccum.length > 0 ? { toolCalls: toolCallsAccum } : {}
|
|
96395
97179
|
}
|
|
96396
97180
|
});
|
|
@@ -96453,7 +97237,7 @@ ${mentionContext}`;
|
|
|
96453
97237
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
96454
97238
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
96455
97239
|
import { mkdir as mkdir13, rm as rm2, writeFile as writeFile13 } from "node:fs/promises";
|
|
96456
|
-
import { basename as basename10, join as
|
|
97240
|
+
import { basename as basename10, join as join42, resolve as resolve20 } from "node:path";
|
|
96457
97241
|
function resolveAttachmentPath(rootDir, sessionId, filename) {
|
|
96458
97242
|
const sessionDir = resolve20(rootDir, ".fusion", "chat-attachments", sessionId);
|
|
96459
97243
|
const safeName = basename10(filename);
|
|
@@ -96711,7 +97495,7 @@ function registerChatRoutes(ctx, deps) {
|
|
|
96711
97495
|
await mkdir13(sessionDir, { recursive: true });
|
|
96712
97496
|
const sanitizedFilename = (file.originalname || "attachment").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
96713
97497
|
const filename = `${Date.now()}-${sanitizedFilename}`;
|
|
96714
|
-
const filePath =
|
|
97498
|
+
const filePath = join42(sessionDir, filename);
|
|
96715
97499
|
await writeFile13(filePath, file.buffer);
|
|
96716
97500
|
const attachment = {
|
|
96717
97501
|
id: `att-${randomUUID19().slice(0, 8)}`,
|
|
@@ -101633,7 +102417,7 @@ var init_remote_auth = __esm({
|
|
|
101633
102417
|
|
|
101634
102418
|
// ../dashboard/src/routes/register-settings-memory-routes.ts
|
|
101635
102419
|
import { execFile as execFile5 } from "node:child_process";
|
|
101636
|
-
import { homedir as
|
|
102420
|
+
import { homedir as homedir7 } from "node:os";
|
|
101637
102421
|
import { promisify as promisify12 } from "node:util";
|
|
101638
102422
|
function registerSettingsMemoryRoutes(ctx, deps) {
|
|
101639
102423
|
const { router, options, store, runtimeLogger, getProjectContext: getProjectContext3, rethrowAsApiError: rethrowAsApiError8 } = ctx;
|
|
@@ -101729,7 +102513,7 @@ function registerSettingsMemoryRoutes(ctx, deps) {
|
|
|
101729
102513
|
try {
|
|
101730
102514
|
await execFileAsync7("mv", [tempPath, globalInstallPath], { timeout: 3e4 });
|
|
101731
102515
|
} catch (error) {
|
|
101732
|
-
const localBinDir = `${
|
|
102516
|
+
const localBinDir = `${homedir7()}/.local/bin`;
|
|
101733
102517
|
const localInstallPath = `${localBinDir}/cloudflared`;
|
|
101734
102518
|
attemptedCommands.push(`mkdir -p ${localBinDir}`);
|
|
101735
102519
|
attemptedCommands.push(`mv ${tempPath} ${localInstallPath}`);
|
|
@@ -103095,7 +103879,7 @@ import * as os3 from "os";
|
|
|
103095
103879
|
import * as path2 from "path";
|
|
103096
103880
|
import * as fs2 from "node:fs";
|
|
103097
103881
|
import { createRequire as createRequire3 } from "node:module";
|
|
103098
|
-
import { join as
|
|
103882
|
+
import { join as join43, dirname as dirname13 } from "node:path";
|
|
103099
103883
|
function getNativePrebuildName() {
|
|
103100
103884
|
const platform4 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
|
|
103101
103885
|
const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
|
|
@@ -103105,12 +103889,12 @@ function findInstalledNodePtyNativeDir() {
|
|
|
103105
103889
|
try {
|
|
103106
103890
|
const packageJsonPath = require2.resolve("node-pty/package.json");
|
|
103107
103891
|
const pkgRoot = dirname13(packageJsonPath);
|
|
103108
|
-
const releaseDir =
|
|
103109
|
-
if (fs2.existsSync(
|
|
103892
|
+
const releaseDir = join43(pkgRoot, "build", "Release");
|
|
103893
|
+
if (fs2.existsSync(join43(releaseDir, "pty.node"))) {
|
|
103110
103894
|
return releaseDir;
|
|
103111
103895
|
}
|
|
103112
|
-
const prebuildDir =
|
|
103113
|
-
if (fs2.existsSync(
|
|
103896
|
+
const prebuildDir = join43(pkgRoot, "prebuilds", getNativePrebuildName());
|
|
103897
|
+
if (fs2.existsSync(join43(prebuildDir, "pty.node"))) {
|
|
103114
103898
|
return prebuildDir;
|
|
103115
103899
|
}
|
|
103116
103900
|
return null;
|
|
@@ -103136,8 +103920,8 @@ function ensureNodePtyNativePermissions() {
|
|
|
103136
103920
|
candidateDirs.add(installedNativeDir);
|
|
103137
103921
|
}
|
|
103138
103922
|
for (const nativeDir of candidateDirs) {
|
|
103139
|
-
const helperPath =
|
|
103140
|
-
const nativeModulePath =
|
|
103923
|
+
const helperPath = join43(nativeDir, "spawn-helper");
|
|
103924
|
+
const nativeModulePath = join43(nativeDir, "pty.node");
|
|
103141
103925
|
try {
|
|
103142
103926
|
fs2.chmodSync(helperPath, 493);
|
|
103143
103927
|
} catch {
|
|
@@ -103155,14 +103939,14 @@ function ensureNodePtyNativePermissions() {
|
|
|
103155
103939
|
function findStagedNativeDir() {
|
|
103156
103940
|
const prebuildName = getNativePrebuildName();
|
|
103157
103941
|
if (process.env.FUSION_RUNTIME_DIR) {
|
|
103158
|
-
const envPath =
|
|
103159
|
-
if (fs2.existsSync(
|
|
103942
|
+
const envPath = join43(process.env.FUSION_RUNTIME_DIR, prebuildName);
|
|
103943
|
+
if (fs2.existsSync(join43(envPath, "pty.node"))) {
|
|
103160
103944
|
return envPath;
|
|
103161
103945
|
}
|
|
103162
103946
|
}
|
|
103163
103947
|
const execDir = dirname13(process.execPath);
|
|
103164
|
-
const nextToBinary =
|
|
103165
|
-
if (fs2.existsSync(
|
|
103948
|
+
const nextToBinary = join43(execDir, "runtime", prebuildName);
|
|
103949
|
+
if (fs2.existsSync(join43(nextToBinary, "pty.node"))) {
|
|
103166
103950
|
return nextToBinary;
|
|
103167
103951
|
}
|
|
103168
103952
|
return null;
|
|
@@ -103182,7 +103966,7 @@ async function loadPtyModule() {
|
|
|
103182
103966
|
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
|
|
103183
103967
|
}
|
|
103184
103968
|
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
|
|
103185
|
-
const nativePath =
|
|
103969
|
+
const nativePath = join43(nativeDir, "pty.node");
|
|
103186
103970
|
if (fs2.existsSync(nativePath)) {
|
|
103187
103971
|
try {
|
|
103188
103972
|
const nativeModule = { exports: {} };
|
|
@@ -108872,7 +109656,7 @@ var init_terminal = __esm({
|
|
|
108872
109656
|
});
|
|
108873
109657
|
|
|
108874
109658
|
// ../dashboard/src/file-service.ts
|
|
108875
|
-
import { join as
|
|
109659
|
+
import { join as join44, resolve as resolve22, relative as relative11, dirname as dirname14, basename as basename12 } from "node:path";
|
|
108876
109660
|
import { readdir as readdir8, readFile as fsReadFile, writeFile as fsWriteFile, stat as stat7, copyFile as fsCopyFile, rename as fsRename, rm as fsRm, mkdir as mkdir14, access as access6 } from "node:fs/promises";
|
|
108877
109661
|
async function getTaskBasePath(store, taskId) {
|
|
108878
109662
|
try {
|
|
@@ -108885,7 +109669,7 @@ async function getTaskBasePath(store, taskId) {
|
|
|
108885
109669
|
}
|
|
108886
109670
|
}
|
|
108887
109671
|
const rootDir = store.getRootDir();
|
|
108888
|
-
return resolve22(
|
|
109672
|
+
return resolve22(join44(rootDir, ".fusion", "tasks", taskId));
|
|
108889
109673
|
} catch (err) {
|
|
108890
109674
|
const error = err;
|
|
108891
109675
|
if (error.code === "ENOENT" || error.message && error.message.includes("not found")) {
|
|
@@ -108912,7 +109696,7 @@ function validatePath(basePath, filePath) {
|
|
|
108912
109696
|
throw new FileServiceError(`Access denied: Absolute paths not allowed`, "EINVAL");
|
|
108913
109697
|
}
|
|
108914
109698
|
const resolvedBase = resolve22(basePath);
|
|
108915
|
-
const resolvedPath = resolve22(
|
|
109699
|
+
const resolvedPath = resolve22(join44(resolvedBase, decodedPath));
|
|
108916
109700
|
const relativePath = relative11(resolvedBase, resolvedPath);
|
|
108917
109701
|
if (relativePath.startsWith("..") || relativePath.startsWith("../") || relativePath === "..") {
|
|
108918
109702
|
throw new FileServiceError(`Access denied: Path traversal detected`, "EINVAL");
|
|
@@ -108941,7 +109725,7 @@ async function listFilesForBasePath(basePath, subPath) {
|
|
|
108941
109725
|
const entries = await readdir8(targetPath, { withFileTypes: true });
|
|
108942
109726
|
const fileNodes = [];
|
|
108943
109727
|
for (const entry of entries) {
|
|
108944
|
-
const entryPath =
|
|
109728
|
+
const entryPath = join44(targetPath, entry.name);
|
|
108945
109729
|
const entryStats = await stat7(entryPath);
|
|
108946
109730
|
fileNodes.push({
|
|
108947
109731
|
name: entry.name,
|
|
@@ -109268,7 +110052,7 @@ async function renameWorkspaceFile(store, workspace, filePath, newName) {
|
|
|
109268
110052
|
}
|
|
109269
110053
|
throw err;
|
|
109270
110054
|
}
|
|
109271
|
-
const destPath =
|
|
110055
|
+
const destPath = join44(dirname14(resolvedPath), newName);
|
|
109272
110056
|
const destRelative = relative11(resolve22(workspaceBase), destPath);
|
|
109273
110057
|
if (destRelative.startsWith("..") || destRelative.startsWith("../") || destRelative === "..") {
|
|
109274
110058
|
throw new FileServiceError("Destination would be outside workspace", "EINVAL");
|
|
@@ -109361,7 +110145,7 @@ function isHiddenPathSegment(name) {
|
|
|
109361
110145
|
return name.startsWith(".");
|
|
109362
110146
|
}
|
|
109363
110147
|
async function walkDirForMarkdown(basePath, currentRelative, results, options) {
|
|
109364
|
-
const currentPath = currentRelative ?
|
|
110148
|
+
const currentPath = currentRelative ? join44(basePath, currentRelative) : basePath;
|
|
109365
110149
|
let entries;
|
|
109366
110150
|
try {
|
|
109367
110151
|
entries = await readdir8(currentPath, { withFileTypes: true });
|
|
@@ -109369,7 +110153,7 @@ async function walkDirForMarkdown(basePath, currentRelative, results, options) {
|
|
|
109369
110153
|
return;
|
|
109370
110154
|
}
|
|
109371
110155
|
for (const entry of entries) {
|
|
109372
|
-
const entryRelativePath = currentRelative ?
|
|
110156
|
+
const entryRelativePath = currentRelative ? join44(currentRelative, entry.name) : entry.name;
|
|
109373
110157
|
if (entry.isDirectory()) {
|
|
109374
110158
|
if (MARKDOWN_SCAN_EXCLUDED_DIRS.has(entry.name)) {
|
|
109375
110159
|
continue;
|
|
@@ -109386,7 +110170,7 @@ async function walkDirForMarkdown(basePath, currentRelative, results, options) {
|
|
|
109386
110170
|
if (!options.showHidden && isHiddenPathSegment(entry.name)) {
|
|
109387
110171
|
continue;
|
|
109388
110172
|
}
|
|
109389
|
-
const fullPath =
|
|
110173
|
+
const fullPath = join44(basePath, entryRelativePath);
|
|
109390
110174
|
let fileStats;
|
|
109391
110175
|
try {
|
|
109392
110176
|
fileStats = await stat7(fullPath);
|
|
@@ -109436,7 +110220,7 @@ async function scanMarkdownFiles(store, options) {
|
|
|
109436
110220
|
return;
|
|
109437
110221
|
}
|
|
109438
110222
|
for (const entry of entries) {
|
|
109439
|
-
const entryRelativePath = relativeDir ?
|
|
110223
|
+
const entryRelativePath = relativeDir ? join44(relativeDir, entry.name) : entry.name;
|
|
109440
110224
|
let shouldRecurse = entry.isDirectory();
|
|
109441
110225
|
if (!shouldRecurse && typeof entry.isSymbolicLink === "function" && entry.isSymbolicLink()) {
|
|
109442
110226
|
let symlinkPath;
|
|
@@ -109526,8 +110310,8 @@ async function searchWorkspaceFiles(store, workspace, query) {
|
|
|
109526
110310
|
if (entry.isDirectory() && EXCLUDED_DIRS.has(entry.name)) {
|
|
109527
110311
|
continue;
|
|
109528
110312
|
}
|
|
109529
|
-
const fullPath =
|
|
109530
|
-
const relPath =
|
|
110313
|
+
const fullPath = join44(dir2, entry.name);
|
|
110314
|
+
const relPath = join44(relativeDir, entry.name);
|
|
109531
110315
|
if (entry.isFile()) {
|
|
109532
110316
|
if (entry.name.toLowerCase().includes(lowerQuery)) {
|
|
109533
110317
|
results.push({
|
|
@@ -109548,8 +110332,8 @@ async function copyDirectoryRecursive(source, destination) {
|
|
|
109548
110332
|
await mkdir14(destination, { recursive: true });
|
|
109549
110333
|
const entries = await readdir8(source, { withFileTypes: true });
|
|
109550
110334
|
for (const entry of entries) {
|
|
109551
|
-
const sourcePath =
|
|
109552
|
-
const destPath =
|
|
110335
|
+
const sourcePath = join44(source, entry.name);
|
|
110336
|
+
const destPath = join44(destination, entry.name);
|
|
109553
110337
|
if (entry.isDirectory()) {
|
|
109554
110338
|
await copyDirectoryRecursive(sourcePath, destPath);
|
|
109555
110339
|
} else {
|
|
@@ -113618,7 +114402,7 @@ var require_BufferList = __commonJS({
|
|
|
113618
114402
|
this.head = this.tail = null;
|
|
113619
114403
|
this.length = 0;
|
|
113620
114404
|
};
|
|
113621
|
-
BufferList.prototype.join = function
|
|
114405
|
+
BufferList.prototype.join = function join72(s) {
|
|
113622
114406
|
if (this.length === 0) return "";
|
|
113623
114407
|
var p = this.head;
|
|
113624
114408
|
var ret = "" + p.data;
|
|
@@ -136127,7 +136911,7 @@ var init_exec_file = __esm({
|
|
|
136127
136911
|
|
|
136128
136912
|
// ../dashboard/src/routes/register-project-routes.ts
|
|
136129
136913
|
import * as fsPromises from "node:fs/promises";
|
|
136130
|
-
import { dirname as dirname15, isAbsolute as isAbsolute17, join as
|
|
136914
|
+
import { dirname as dirname15, isAbsolute as isAbsolute17, join as join45 } from "node:path";
|
|
136131
136915
|
var access9, stat8, mkdir15, readdir9, rm3, registerProjectRoutes;
|
|
136132
136916
|
var init_register_project_routes = __esm({
|
|
136133
136917
|
"../dashboard/src/routes/register-project-routes.ts"() {
|
|
@@ -136350,7 +137134,7 @@ var init_register_project_routes = __esm({
|
|
|
136350
137134
|
}
|
|
136351
137135
|
}
|
|
136352
137136
|
let hasFusionDir = false;
|
|
136353
|
-
const fusionDirPath =
|
|
137137
|
+
const fusionDirPath = join45(normalizedPath, ".fusion");
|
|
136354
137138
|
try {
|
|
136355
137139
|
await access9(fusionDirPath);
|
|
136356
137140
|
hasFusionDir = true;
|
|
@@ -136414,8 +137198,8 @@ var init_register_project_routes = __esm({
|
|
|
136414
137198
|
const entries = await readdir9(searchPath, { withFileTypes: true });
|
|
136415
137199
|
for (const entry of entries) {
|
|
136416
137200
|
if (!entry.isDirectory()) continue;
|
|
136417
|
-
const dirPath =
|
|
136418
|
-
if (isValidSqliteDatabaseFile(
|
|
137201
|
+
const dirPath = join45(searchPath, entry.name);
|
|
137202
|
+
if (isValidSqliteDatabaseFile(join45(dirPath, ".fusion", "fusion.db"))) {
|
|
136419
137203
|
detected.push({
|
|
136420
137204
|
path: dirPath,
|
|
136421
137205
|
suggestedName: entry.name,
|
|
@@ -137429,14 +138213,14 @@ var init_register_docker_provisioning_routes = __esm({
|
|
|
137429
138213
|
|
|
137430
138214
|
// ../dashboard/src/auth-paths.ts
|
|
137431
138215
|
import path3 from "node:path";
|
|
137432
|
-
import { homedir as
|
|
137433
|
-
function getFusionAgentDir2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
138216
|
+
import { homedir as homedir8 } from "node:os";
|
|
138217
|
+
function getFusionAgentDir2(home = process.env.HOME || process.env.USERPROFILE || homedir8()) {
|
|
137434
138218
|
return path3.join(home, ".fusion", "agent");
|
|
137435
138219
|
}
|
|
137436
|
-
function getFusionAuthPath2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
138220
|
+
function getFusionAuthPath2(home = process.env.HOME || process.env.USERPROFILE || homedir8()) {
|
|
137437
138221
|
return path3.join(getFusionAgentDir2(home), "auth.json");
|
|
137438
138222
|
}
|
|
137439
|
-
function getAuthFileCandidates(cwd = process.cwd(), home = process.env.HOME || process.env.USERPROFILE ||
|
|
138223
|
+
function getAuthFileCandidates(cwd = process.cwd(), home = process.env.HOME || process.env.USERPROFILE || homedir8()) {
|
|
137440
138224
|
return [
|
|
137441
138225
|
path3.join(home, ".fusion", "agent", "auth.json"),
|
|
137442
138226
|
path3.join(home, ".fusion", "auth.json"),
|
|
@@ -140739,7 +141523,7 @@ Rules:
|
|
|
140739
141523
|
import { createWriteStream } from "node:fs";
|
|
140740
141524
|
import * as fsPromises2 from "node:fs/promises";
|
|
140741
141525
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
140742
|
-
import { join as
|
|
141526
|
+
import { join as join46, resolve as resolve23 } from "node:path";
|
|
140743
141527
|
import { Readable } from "node:stream";
|
|
140744
141528
|
import { pipeline as streamPipeline } from "node:stream/promises";
|
|
140745
141529
|
function registerAgentImportExportRoutes(ctx) {
|
|
@@ -140780,7 +141564,7 @@ function registerAgentImportExportRoutes(ctx) {
|
|
|
140780
141564
|
} else if (typeof outputDir === "string") {
|
|
140781
141565
|
throw badRequest("outputDir cannot be empty");
|
|
140782
141566
|
} else {
|
|
140783
|
-
resolvedOutputDir = await mkdtemp(
|
|
141567
|
+
resolvedOutputDir = await mkdtemp(join46(tmpdir4(), "fusion-agent-export-"));
|
|
140784
141568
|
}
|
|
140785
141569
|
const result = await exportAgentsToDirectory2(agentsToExport, resolvedOutputDir, {
|
|
140786
141570
|
companyName: typeof companyName === "string" ? companyName : void 0,
|
|
@@ -140907,7 +141691,7 @@ ${body}`;
|
|
|
140907
141691
|
return result;
|
|
140908
141692
|
}
|
|
140909
141693
|
const safeCompanySlug = companySlug ? slugifyPathSegment2(companySlug, "unknown-company") : "unknown-company";
|
|
140910
|
-
const skillsBaseDir =
|
|
141694
|
+
const skillsBaseDir = join46(projectRoot, "skills", "imported", safeCompanySlug);
|
|
140911
141695
|
const usedSlugs = /* @__PURE__ */ new Set();
|
|
140912
141696
|
for (const skill of skills) {
|
|
140913
141697
|
const name = typeof skill.name === "string" && skill.name.trim().length > 0 ? skill.name.trim() : null;
|
|
@@ -140924,8 +141708,8 @@ ${body}`;
|
|
|
140924
141708
|
skillSlug = `${skillSlug}-${counter}`;
|
|
140925
141709
|
}
|
|
140926
141710
|
usedSlugs.add(skillSlug);
|
|
140927
|
-
const skillDir =
|
|
140928
|
-
const skillPath =
|
|
141711
|
+
const skillDir = join46(skillsBaseDir, skillSlug);
|
|
141712
|
+
const skillPath = join46(skillDir, "SKILL.md");
|
|
140929
141713
|
try {
|
|
140930
141714
|
await access10(skillPath);
|
|
140931
141715
|
result.skipped.push(name);
|
|
@@ -141095,8 +141879,8 @@ ${body}`;
|
|
|
141095
141879
|
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/main.tar.gz`;
|
|
141096
141880
|
let tempDir = null;
|
|
141097
141881
|
try {
|
|
141098
|
-
tempDir = await mkdtemp(
|
|
141099
|
-
const archivePath =
|
|
141882
|
+
tempDir = await mkdtemp(join46(tmpdir4(), `fn-agent-import-${importCompanySlug}-`));
|
|
141883
|
+
const archivePath = join46(tempDir, "archive.tar.gz");
|
|
141100
141884
|
const downloadController = new AbortController();
|
|
141101
141885
|
const downloadTimeout = setTimeout(() => downloadController.abort(), 3e4);
|
|
141102
141886
|
let archiveResponse;
|
|
@@ -142377,7 +143161,7 @@ import * as path4 from "node:path";
|
|
|
142377
143161
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
142378
143162
|
import * as https from "node:https";
|
|
142379
143163
|
import * as child_process from "node:child_process";
|
|
142380
|
-
function
|
|
143164
|
+
function getHomeDir6() {
|
|
142381
143165
|
return process.env.HOME || process.env.USERPROFILE || os4.homedir();
|
|
142382
143166
|
}
|
|
142383
143167
|
function execFileAsync5(file, args, options) {
|
|
@@ -142887,8 +143671,8 @@ async function fetchClaudeUsage(authStorage) {
|
|
|
142887
143671
|
}
|
|
142888
143672
|
if (!creds) {
|
|
142889
143673
|
const credPaths = [
|
|
142890
|
-
path4.join(
|
|
142891
|
-
path4.join(
|
|
143674
|
+
path4.join(getHomeDir6(), ".claude", ".credentials.json"),
|
|
143675
|
+
path4.join(getHomeDir6(), ".config", "claude", ".credentials.json")
|
|
142892
143676
|
];
|
|
142893
143677
|
for (const p of credPaths) {
|
|
142894
143678
|
try {
|
|
@@ -143048,7 +143832,7 @@ async function fetchCodexUsage() {
|
|
|
143048
143832
|
status: "no-auth",
|
|
143049
143833
|
windows: []
|
|
143050
143834
|
};
|
|
143051
|
-
const codexHome = process.env.CODEX_HOME || path4.join(
|
|
143835
|
+
const codexHome = process.env.CODEX_HOME || path4.join(getHomeDir6(), ".codex");
|
|
143052
143836
|
const authPath = path4.join(codexHome, "auth.json");
|
|
143053
143837
|
let auth = null;
|
|
143054
143838
|
try {
|
|
@@ -143139,7 +143923,7 @@ async function fetchGeminiUsage() {
|
|
|
143139
143923
|
status: "no-auth",
|
|
143140
143924
|
windows: []
|
|
143141
143925
|
};
|
|
143142
|
-
const oauthPath = path4.join(
|
|
143926
|
+
const oauthPath = path4.join(getHomeDir6(), ".gemini", "oauth_creds.json");
|
|
143143
143927
|
let oauthCreds = null;
|
|
143144
143928
|
try {
|
|
143145
143929
|
oauthCreds = JSON.parse(await readFile20(oauthPath, "utf-8"));
|
|
@@ -143155,7 +143939,7 @@ async function fetchGeminiUsage() {
|
|
|
143155
143939
|
const claims = decodeJwtPayload(oauthCreds.id_token);
|
|
143156
143940
|
if (claims?.email) usage.email = claims.email;
|
|
143157
143941
|
}
|
|
143158
|
-
const settingsPath = path4.join(
|
|
143942
|
+
const settingsPath = path4.join(getHomeDir6(), ".gemini", "settings.json");
|
|
143159
143943
|
try {
|
|
143160
143944
|
const settings = JSON.parse(await readFile20(settingsPath, "utf-8"));
|
|
143161
143945
|
const authType = settings?.security?.auth?.selectedType;
|
|
@@ -143792,6 +144576,13 @@ var init_register_auth_routes = __esm({
|
|
|
143792
144576
|
}
|
|
143793
144577
|
return key.slice(0, 3) + "\u2022\u2022\u2022\u2022\u2022" + key.slice(-4);
|
|
143794
144578
|
}
|
|
144579
|
+
function isExpiredOauthCredential(providerId, storage) {
|
|
144580
|
+
const credential = storage.get?.(providerId);
|
|
144581
|
+
if (!credential || credential.type !== "oauth" || typeof credential.expires !== "number") {
|
|
144582
|
+
return false;
|
|
144583
|
+
}
|
|
144584
|
+
return Date.now() >= credential.expires;
|
|
144585
|
+
}
|
|
143795
144586
|
const loginInProgress = /* @__PURE__ */ new Map();
|
|
143796
144587
|
const OAUTH_SESSION_TTL_MS = 5 * 60 * 1e3;
|
|
143797
144588
|
const oauthSessions = /* @__PURE__ */ new Map();
|
|
@@ -143852,6 +144643,40 @@ var init_register_auth_routes = __esm({
|
|
|
143852
144643
|
path: `${redirectUriUrl.pathname}${redirectUriUrl.search}`
|
|
143853
144644
|
};
|
|
143854
144645
|
}
|
|
144646
|
+
function shouldRewriteOauthRedirect(providerId, origin) {
|
|
144647
|
+
if (!origin || isLocalhostOrigin(origin)) {
|
|
144648
|
+
return false;
|
|
144649
|
+
}
|
|
144650
|
+
if (providerId === "openai-codex") {
|
|
144651
|
+
return false;
|
|
144652
|
+
}
|
|
144653
|
+
return true;
|
|
144654
|
+
}
|
|
144655
|
+
function getManualCodeConfig(providerId, origin) {
|
|
144656
|
+
if (providerId !== "openai-codex") {
|
|
144657
|
+
return void 0;
|
|
144658
|
+
}
|
|
144659
|
+
const remoteDashboard = origin !== void 0 && !isLocalhostOrigin(origin);
|
|
144660
|
+
return {
|
|
144661
|
+
prompt: "Paste the final redirect URL or authorization code",
|
|
144662
|
+
placeholder: "http://localhost:1455/auth/callback?code=...&state=... or just the code",
|
|
144663
|
+
helpText: remoteDashboard ? "After sign-in, OpenAI may redirect to a localhost callback that cannot open from this dashboard host. Copy the full browser URL from the address bar and paste it here." : "If the browser cannot finish the localhost callback automatically, copy the full browser URL from the address bar and paste it here."
|
|
144664
|
+
};
|
|
144665
|
+
}
|
|
144666
|
+
function appendManualCodeHint(instructions, providerId, origin) {
|
|
144667
|
+
const manualCode = getManualCodeConfig(providerId, origin);
|
|
144668
|
+
if (!manualCode) {
|
|
144669
|
+
return instructions;
|
|
144670
|
+
}
|
|
144671
|
+
const hint = manualCode.helpText;
|
|
144672
|
+
if (!hint) {
|
|
144673
|
+
return instructions;
|
|
144674
|
+
}
|
|
144675
|
+
if (!instructions?.trim()) {
|
|
144676
|
+
return hint;
|
|
144677
|
+
}
|
|
144678
|
+
return `${instructions.trim()} ${hint}`;
|
|
144679
|
+
}
|
|
143855
144680
|
router.get("/auth/status", async (_req, res) => {
|
|
143856
144681
|
try {
|
|
143857
144682
|
const storage = getAuthStorage();
|
|
@@ -143860,7 +144685,7 @@ var init_register_auth_routes = __esm({
|
|
|
143860
144685
|
const providers = oauthProviders.map((p) => ({
|
|
143861
144686
|
id: p.id,
|
|
143862
144687
|
name: p.name,
|
|
143863
|
-
authenticated: storage.hasAuth(p.id),
|
|
144688
|
+
authenticated: storage.hasAuth(p.id) && !isExpiredOauthCredential(p.id, storage),
|
|
143864
144689
|
type: "oauth",
|
|
143865
144690
|
loginInProgress: loginInProgress.has(p.id)
|
|
143866
144691
|
}));
|
|
@@ -144114,7 +144939,29 @@ var init_register_auth_routes = __esm({
|
|
|
144114
144939
|
throw badRequest(`Unknown provider: ${provider}`);
|
|
144115
144940
|
}
|
|
144116
144941
|
const abortController = new AbortController();
|
|
144117
|
-
|
|
144942
|
+
let resolveInput = () => {
|
|
144943
|
+
};
|
|
144944
|
+
let rejectInput = () => {
|
|
144945
|
+
};
|
|
144946
|
+
const inputPromise = new Promise((resolve44, reject2) => {
|
|
144947
|
+
resolveInput = resolve44;
|
|
144948
|
+
rejectInput = reject2;
|
|
144949
|
+
});
|
|
144950
|
+
void inputPromise.catch((error) => {
|
|
144951
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
144952
|
+
if (message !== "cancelled") {
|
|
144953
|
+
console.warn(`[auth/login] manual OAuth input promise rejected for ${provider}: ${message}`);
|
|
144954
|
+
}
|
|
144955
|
+
});
|
|
144956
|
+
const pendingLogin = {
|
|
144957
|
+
abortController,
|
|
144958
|
+
inputPromise,
|
|
144959
|
+
resolveInput,
|
|
144960
|
+
rejectInput,
|
|
144961
|
+
inputSubmitted: false,
|
|
144962
|
+
manualCode: getManualCodeConfig(provider, origin)
|
|
144963
|
+
};
|
|
144964
|
+
loginInProgress.set(provider, pendingLogin);
|
|
144118
144965
|
let authResolve;
|
|
144119
144966
|
let authReject;
|
|
144120
144967
|
const authUrlPromise = new Promise((resolve44, reject2) => {
|
|
@@ -144123,12 +144970,16 @@ var init_register_auth_routes = __esm({
|
|
|
144123
144970
|
});
|
|
144124
144971
|
const loginPromise = storage.login(provider, {
|
|
144125
144972
|
onAuth: (info) => {
|
|
144126
|
-
authResolve({
|
|
144127
|
-
|
|
144128
|
-
|
|
144129
|
-
|
|
144130
|
-
return prompt.placeholder || "";
|
|
144973
|
+
authResolve({
|
|
144974
|
+
url: info.url,
|
|
144975
|
+
instructions: appendManualCodeHint(info.instructions, provider, origin)
|
|
144976
|
+
});
|
|
144131
144977
|
},
|
|
144978
|
+
onPrompt: async () => await pendingLogin.inputPromise,
|
|
144979
|
+
// AuthStorage.login() forwards callbacks to provider-specific OAuth
|
|
144980
|
+
// implementations verbatim. openai-codex supports this optional hook
|
|
144981
|
+
// to race pasted codes against the localhost callback server.
|
|
144982
|
+
onManualCodeInput: async () => await pendingLogin.inputPromise,
|
|
144132
144983
|
onProgress: () => {
|
|
144133
144984
|
},
|
|
144134
144985
|
// no-op for web UI
|
|
@@ -144147,7 +144998,7 @@ var init_register_auth_routes = __esm({
|
|
|
144147
144998
|
const authInfo = await authUrlPromise;
|
|
144148
144999
|
clearTimeout(timeout2);
|
|
144149
145000
|
let responseUrl = authInfo.url;
|
|
144150
|
-
if (
|
|
145001
|
+
if (shouldRewriteOauthRedirect(provider, origin)) {
|
|
144151
145002
|
const rewritten = rewriteAuthUrl(authInfo.url, origin);
|
|
144152
145003
|
setOauthSession(rewritten.state, {
|
|
144153
145004
|
port: rewritten.port,
|
|
@@ -144156,7 +145007,11 @@ var init_register_auth_routes = __esm({
|
|
|
144156
145007
|
});
|
|
144157
145008
|
responseUrl = rewritten.url;
|
|
144158
145009
|
}
|
|
144159
|
-
res.json({
|
|
145010
|
+
res.json({
|
|
145011
|
+
url: responseUrl,
|
|
145012
|
+
instructions: authInfo.instructions,
|
|
145013
|
+
manualCode: pendingLogin.manualCode
|
|
145014
|
+
});
|
|
144160
145015
|
} catch (err) {
|
|
144161
145016
|
if (err instanceof ApiError) {
|
|
144162
145017
|
throw err;
|
|
@@ -144178,7 +145033,9 @@ var init_register_auth_routes = __esm({
|
|
|
144178
145033
|
return;
|
|
144179
145034
|
}
|
|
144180
145035
|
loginInProgress.delete(provider);
|
|
144181
|
-
activeLogin.
|
|
145036
|
+
activeLogin.inputSubmitted = true;
|
|
145037
|
+
activeLogin.rejectInput(new Error("cancelled"));
|
|
145038
|
+
activeLogin.abortController.abort();
|
|
144182
145039
|
res.json({ success: true, cancelled: true });
|
|
144183
145040
|
} catch (err) {
|
|
144184
145041
|
if (err instanceof ApiError) {
|
|
@@ -144187,6 +145044,33 @@ var init_register_auth_routes = __esm({
|
|
|
144187
145044
|
rethrowAsApiError8(err);
|
|
144188
145045
|
}
|
|
144189
145046
|
});
|
|
145047
|
+
router.post("/auth/manual-code", (req, res) => {
|
|
145048
|
+
try {
|
|
145049
|
+
const { provider, code } = req.body;
|
|
145050
|
+
if (!provider || typeof provider !== "string") {
|
|
145051
|
+
throw badRequest("provider is required");
|
|
145052
|
+
}
|
|
145053
|
+
if (!code || typeof code !== "string" || !code.trim()) {
|
|
145054
|
+
throw badRequest("code is required");
|
|
145055
|
+
}
|
|
145056
|
+
const activeLogin = loginInProgress.get(provider);
|
|
145057
|
+
if (!activeLogin) {
|
|
145058
|
+
throw conflict(`No login in progress for ${provider}`);
|
|
145059
|
+
}
|
|
145060
|
+
if (activeLogin.inputSubmitted) {
|
|
145061
|
+
res.json({ success: true, submitted: false });
|
|
145062
|
+
return;
|
|
145063
|
+
}
|
|
145064
|
+
activeLogin.inputSubmitted = true;
|
|
145065
|
+
activeLogin.resolveInput(code.trim());
|
|
145066
|
+
res.json({ success: true, submitted: true });
|
|
145067
|
+
} catch (err) {
|
|
145068
|
+
if (err instanceof ApiError) {
|
|
145069
|
+
throw err;
|
|
145070
|
+
}
|
|
145071
|
+
rethrowAsApiError8(err);
|
|
145072
|
+
}
|
|
145073
|
+
});
|
|
144190
145074
|
router.get("/auth/oauth-callback", async (req, res) => {
|
|
144191
145075
|
try {
|
|
144192
145076
|
const error = typeof req.query.error === "string" ? req.query.error : void 0;
|
|
@@ -145518,15 +146402,15 @@ var init_register_runtime_provider_routes = __esm({
|
|
|
145518
146402
|
});
|
|
145519
146403
|
|
|
145520
146404
|
// ../dashboard/src/cli-package-version.ts
|
|
145521
|
-
import { existsSync as
|
|
146405
|
+
import { existsSync as existsSync34, readFileSync as readFileSync12 } from "node:fs";
|
|
145522
146406
|
import { dirname as dirname16, resolve as resolve24 } from "node:path";
|
|
145523
146407
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
145524
146408
|
function readCliPackageVersion(pkgPath) {
|
|
145525
|
-
if (!
|
|
146409
|
+
if (!existsSync34(pkgPath)) {
|
|
145526
146410
|
return null;
|
|
145527
146411
|
}
|
|
145528
146412
|
try {
|
|
145529
|
-
const parsed = JSON.parse(
|
|
146413
|
+
const parsed = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
145530
146414
|
if (parsed.name === CLI_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
145531
146415
|
return {
|
|
145532
146416
|
packageJsonPath: pkgPath,
|
|
@@ -145728,9 +146612,9 @@ var init_register_fn_binary_routes = __esm({
|
|
|
145728
146612
|
});
|
|
145729
146613
|
|
|
145730
146614
|
// ../dashboard/src/update-check.ts
|
|
145731
|
-
import { readFileSync as
|
|
146615
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
145732
146616
|
import { mkdir as mkdir17, rm as rm5, writeFile as writeFile15 } from "node:fs/promises";
|
|
145733
|
-
import { join as
|
|
146617
|
+
import { join as join48 } from "node:path";
|
|
145734
146618
|
function ttlForFrequency(frequency) {
|
|
145735
146619
|
switch (frequency) {
|
|
145736
146620
|
case "manual":
|
|
@@ -145744,7 +146628,7 @@ function ttlForFrequency(frequency) {
|
|
|
145744
146628
|
}
|
|
145745
146629
|
}
|
|
145746
146630
|
function getCachePath(fusionDir) {
|
|
145747
|
-
return
|
|
146631
|
+
return join48(fusionDir, CACHE_FILENAME);
|
|
145748
146632
|
}
|
|
145749
146633
|
function parseVersion(version) {
|
|
145750
146634
|
return version.split(".").slice(0, 3).map((part) => Number.parseInt(part, 10)).map((value) => Number.isFinite(value) ? value : 0);
|
|
@@ -145768,7 +146652,7 @@ function isValidResult(value) {
|
|
|
145768
146652
|
}
|
|
145769
146653
|
function readCachedUpdateCheck(fusionDir) {
|
|
145770
146654
|
try {
|
|
145771
|
-
const raw =
|
|
146655
|
+
const raw = readFileSync13(getCachePath(fusionDir), "utf-8");
|
|
145772
146656
|
const parsed = JSON.parse(raw);
|
|
145773
146657
|
return isValidResult(parsed) ? parsed : null;
|
|
145774
146658
|
} catch {
|
|
@@ -150415,7 +151299,7 @@ var init_todo_routes = __esm({
|
|
|
150415
151299
|
|
|
150416
151300
|
// ../dashboard/src/dev-server-detect.ts
|
|
150417
151301
|
import { glob, readFile as readFile21 } from "node:fs/promises";
|
|
150418
|
-
import { dirname as dirname17, join as
|
|
151302
|
+
import { dirname as dirname17, join as join49, relative as relative12, resolve as resolve25 } from "node:path";
|
|
150419
151303
|
async function readPackageJson(filePath) {
|
|
150420
151304
|
try {
|
|
150421
151305
|
const raw = await readFile21(filePath, "utf-8");
|
|
@@ -150458,7 +151342,7 @@ function scoreCandidate(scriptName, pkg) {
|
|
|
150458
151342
|
async function collectWorkspacePackageJsons(projectRoot) {
|
|
150459
151343
|
const discovered = /* @__PURE__ */ new Set();
|
|
150460
151344
|
try {
|
|
150461
|
-
await readFile21(
|
|
151345
|
+
await readFile21(join49(projectRoot, "pnpm-workspace.yaml"), "utf-8");
|
|
150462
151346
|
} catch {
|
|
150463
151347
|
}
|
|
150464
151348
|
for (const pattern of ["packages/*/package.json", "apps/*/package.json"]) {
|
|
@@ -150492,7 +151376,7 @@ function toSource(projectRoot, packageJsonPath) {
|
|
|
150492
151376
|
async function detectDevServerScripts(projectRoot) {
|
|
150493
151377
|
const root = resolve25(projectRoot);
|
|
150494
151378
|
const candidates = [];
|
|
150495
|
-
const rootPackagePath =
|
|
151379
|
+
const rootPackagePath = join49(root, "package.json");
|
|
150496
151380
|
const rootPackage = await readPackageJson(rootPackagePath);
|
|
150497
151381
|
if (rootPackage) {
|
|
150498
151382
|
for (const script of extractScripts(rootPackage)) {
|
|
@@ -150559,9 +151443,9 @@ var init_dev_server_detect = __esm({
|
|
|
150559
151443
|
|
|
150560
151444
|
// ../dashboard/src/dev-server-store.ts
|
|
150561
151445
|
import { mkdir as mkdir18, readFile as readFile22, writeFile as writeFile16 } from "node:fs/promises";
|
|
150562
|
-
import { dirname as dirname18, join as
|
|
151446
|
+
import { dirname as dirname18, join as join50, resolve as resolve26 } from "node:path";
|
|
150563
151447
|
function devServerFilePath(projectDir) {
|
|
150564
|
-
return
|
|
151448
|
+
return join50(resolve26(projectDir), ".fusion", "dev-server.json");
|
|
150565
151449
|
}
|
|
150566
151450
|
function normalizeState(candidate) {
|
|
150567
151451
|
const defaults = DEV_SERVER_DEFAULT_STATE();
|
|
@@ -151815,7 +152699,7 @@ Your job is to refine task descriptions based on the user's selected refinement
|
|
|
151815
152699
|
|
|
151816
152700
|
// ../dashboard/src/routes.ts
|
|
151817
152701
|
import multer from "multer";
|
|
151818
|
-
import { resolve as resolve27, sep as sep7, join as
|
|
152702
|
+
import { resolve as resolve27, sep as sep7, join as join51, isAbsolute as isAbsolute18 } from "node:path";
|
|
151819
152703
|
import * as nodeFs from "node:fs";
|
|
151820
152704
|
import os5 from "node:os";
|
|
151821
152705
|
import v8 from "node:v8";
|
|
@@ -151843,8 +152727,8 @@ async function getPiPackageManagerAgentDir() {
|
|
|
151843
152727
|
const fusionAgentDir = getFusionAgentDir();
|
|
151844
152728
|
const legacyAgentDir = getLegacyPiAgentDir();
|
|
151845
152729
|
const [fusionSettings, legacySettings, legacyExists, fusionExists] = await Promise.all([
|
|
151846
|
-
readJsonObject3(
|
|
151847
|
-
readJsonObject3(
|
|
152730
|
+
readJsonObject3(join51(fusionAgentDir, "settings.json")),
|
|
152731
|
+
readJsonObject3(join51(legacyAgentDir, "settings.json")),
|
|
151848
152732
|
pathExists(legacyAgentDir),
|
|
151849
152733
|
pathExists(fusionAgentDir)
|
|
151850
152734
|
]);
|
|
@@ -151871,9 +152755,9 @@ async function discoverDashboardPiExtensions(cwd) {
|
|
|
151871
152755
|
const { DefaultPackageManager: DefaultPackageManager5 } = await import("@mariozechner/pi-coding-agent");
|
|
151872
152756
|
const [agentDir, legacyGlobalSettings, fusionGlobalSettings, projectSettings] = await Promise.all([
|
|
151873
152757
|
getPiPackageManagerAgentDir(),
|
|
151874
|
-
readJsonObject3(
|
|
151875
|
-
readJsonObject3(
|
|
151876
|
-
readJsonObject3(
|
|
152758
|
+
readJsonObject3(join51(getLegacyPiAgentDir(), "settings.json")),
|
|
152759
|
+
readJsonObject3(join51(getFusionAgentDir(), "settings.json")),
|
|
152760
|
+
readJsonObject3(join51(cwd, ".fusion", "settings.json"))
|
|
151877
152761
|
]);
|
|
151878
152762
|
const globalSettings = { ...legacyGlobalSettings, ...fusionGlobalSettings };
|
|
151879
152763
|
const mergedSettings = { ...globalSettings, ...projectSettings };
|
|
@@ -154287,7 +155171,7 @@ Description: ${step.description}`
|
|
|
154287
155171
|
return;
|
|
154288
155172
|
}
|
|
154289
155173
|
}
|
|
154290
|
-
const { resolve: resolve44, dirname: dirname34, join:
|
|
155174
|
+
const { resolve: resolve44, dirname: dirname34, join: join72 } = await import("node:path");
|
|
154291
155175
|
const { readdir: readdir12, stat: stat12 } = await import("node:fs/promises");
|
|
154292
155176
|
const rawPath = req.query.path || process.env.HOME || process.env.USERPROFILE || "/";
|
|
154293
155177
|
const showHidden = req.query.showHidden === "true";
|
|
@@ -154312,7 +155196,7 @@ Description: ${step.description}`
|
|
|
154312
155196
|
for (const entry of dirEntries) {
|
|
154313
155197
|
if (!entry.isDirectory()) continue;
|
|
154314
155198
|
if (!showHidden && entry.name.startsWith(".")) continue;
|
|
154315
|
-
const entryPath =
|
|
155199
|
+
const entryPath = join72(resolvedPath, entry.name);
|
|
154316
155200
|
let hasChildren = false;
|
|
154317
155201
|
try {
|
|
154318
155202
|
const subEntries = await readdir12(entryPath, { withFileTypes: true });
|
|
@@ -159920,8 +160804,8 @@ var init_auth_middleware = __esm({
|
|
|
159920
160804
|
// ../dashboard/src/server.ts
|
|
159921
160805
|
import express from "express";
|
|
159922
160806
|
import { randomUUID as randomUUID25 } from "node:crypto";
|
|
159923
|
-
import { join as
|
|
159924
|
-
import { existsSync as
|
|
160807
|
+
import { join as join52, dirname as dirname19 } from "node:path";
|
|
160808
|
+
import { existsSync as existsSync35, readFileSync as readFileSync14 } from "node:fs";
|
|
159925
160809
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
159926
160810
|
import { createSecureServer as createHttp2SecureServer } from "node:http2";
|
|
159927
160811
|
function parseVersion2(version) {
|
|
@@ -160023,11 +160907,11 @@ function loadTlsCredentialsFromEnv(env = process.env) {
|
|
|
160023
160907
|
"FUSION_TLS_* environment is incomplete: set both a cert and a key (inline via FUSION_TLS_CERT/FUSION_TLS_KEY or paths via *_FILE)."
|
|
160024
160908
|
);
|
|
160025
160909
|
}
|
|
160026
|
-
const cert = certInline ? Buffer.from(certInline) :
|
|
160027
|
-
const key = keyInline ? Buffer.from(keyInline) :
|
|
160910
|
+
const cert = certInline ? Buffer.from(certInline) : readFileSync14(certFile);
|
|
160911
|
+
const key = keyInline ? Buffer.from(keyInline) : readFileSync14(keyFile);
|
|
160028
160912
|
const caInline = env.FUSION_TLS_CA;
|
|
160029
160913
|
const caFile = env.FUSION_TLS_CA_FILE;
|
|
160030
|
-
const ca = caInline ? Buffer.from(caInline) : caFile ?
|
|
160914
|
+
const ca = caInline ? Buffer.from(caInline) : caFile ? readFileSync14(caFile) : void 0;
|
|
160031
160915
|
return { cert, key, ca };
|
|
160032
160916
|
}
|
|
160033
160917
|
function createServer(store, options) {
|
|
@@ -160103,11 +160987,11 @@ function createServer(store, options) {
|
|
|
160103
160987
|
getTerminalService(store.getRootDir());
|
|
160104
160988
|
const isHeadless = options?.headless === true;
|
|
160105
160989
|
const execDir = dirname19(process.execPath);
|
|
160106
|
-
const clientDir = process.env.FUSION_CLIENT_DIR ? process.env.FUSION_CLIENT_DIR :
|
|
160990
|
+
const clientDir = process.env.FUSION_CLIENT_DIR ? process.env.FUSION_CLIENT_DIR : existsSync35(join52(execDir, "client", "index.html")) ? join52(execDir, "client") : existsSync35(join52(__dirname, "..", "dist", "client")) ? join52(__dirname, "..", "dist", "client") : join52(__dirname, "..", "client");
|
|
160107
160991
|
if (!isHeadless) {
|
|
160108
160992
|
app.get("/version.json", (_req, res) => {
|
|
160109
160993
|
res.setHeader("Cache-Control", "no-store, max-age=0");
|
|
160110
|
-
res.sendFile(
|
|
160994
|
+
res.sendFile(join52(clientDir, "version.json"), (err) => {
|
|
160111
160995
|
if (err) {
|
|
160112
160996
|
res.status(404).json({ version: null });
|
|
160113
160997
|
}
|
|
@@ -160371,7 +161255,13 @@ data: ${JSON.stringify({ type: event.type, data: event.data })}
|
|
|
160371
161255
|
});
|
|
160372
161256
|
}
|
|
160373
161257
|
const chatAgentStore = new AgentStore({ rootDir: store.getFusionDir() });
|
|
160374
|
-
const chatManager = options?.chatManager ?? new ChatManager(
|
|
161258
|
+
const chatManager = options?.chatManager ?? new ChatManager(
|
|
161259
|
+
chatStore,
|
|
161260
|
+
store.getRootDir(),
|
|
161261
|
+
chatAgentStore,
|
|
161262
|
+
options?.pluginRunner,
|
|
161263
|
+
() => store.getSettings()
|
|
161264
|
+
);
|
|
160375
161265
|
const runAiSessionCleanup = (maxAgeMs, source) => {
|
|
160376
161266
|
const result = aiSessionStore.cleanupStaleSessions(maxAgeMs);
|
|
160377
161267
|
runtimeLogger.info("AI session cleanup summary", {
|
|
@@ -160564,7 +161454,7 @@ data: ${JSON.stringify({ type: event.type, data: event.data })}
|
|
|
160564
161454
|
});
|
|
160565
161455
|
if (!isHeadless) {
|
|
160566
161456
|
app.get("/{*splat}", (_req, res) => {
|
|
160567
|
-
res.sendFile(
|
|
161457
|
+
res.sendFile(join52(clientDir, "index.html"));
|
|
160568
161458
|
});
|
|
160569
161459
|
}
|
|
160570
161460
|
const dashboardApp = app;
|
|
@@ -161019,7 +161909,7 @@ var init_server = __esm({
|
|
|
161019
161909
|
|
|
161020
161910
|
// ../dashboard/src/skills-adapter.ts
|
|
161021
161911
|
import { access as access11, readFile as readFile23, writeFile as writeFile17, mkdir as mkdir19, readdir as readdir10, stat as stat10 } from "node:fs/promises";
|
|
161022
|
-
import { join as
|
|
161912
|
+
import { join as join53, relative as relative13, dirname as dirname20 } from "node:path";
|
|
161023
161913
|
async function pathExists2(path5) {
|
|
161024
161914
|
try {
|
|
161025
161915
|
await access11(path5);
|
|
@@ -161275,7 +162165,7 @@ function createSkillsAdapter(options) {
|
|
|
161275
162165
|
} catch {
|
|
161276
162166
|
skillDir = dirname20(skill.path);
|
|
161277
162167
|
}
|
|
161278
|
-
const skillMdPath =
|
|
162168
|
+
const skillMdPath = join53(skillDir, "SKILL.md");
|
|
161279
162169
|
let skillMd = "";
|
|
161280
162170
|
try {
|
|
161281
162171
|
skillMd = await readFile23(skillMdPath, "utf-8");
|
|
@@ -161483,7 +162373,7 @@ function normalizeEntry(entry) {
|
|
|
161483
162373
|
};
|
|
161484
162374
|
}
|
|
161485
162375
|
function getProjectSettingsPath(rootDir) {
|
|
161486
|
-
return
|
|
162376
|
+
return join53(rootDir, ".fusion", "settings.json");
|
|
161487
162377
|
}
|
|
161488
162378
|
var MIN_PUBLIC_SEARCH_QUERY_LENGTH;
|
|
161489
162379
|
var init_skills_adapter = __esm({
|
|
@@ -161726,33 +162616,33 @@ var init_port_prompt = __esm({
|
|
|
161726
162616
|
});
|
|
161727
162617
|
|
|
161728
162618
|
// src/commands/provider-settings.ts
|
|
161729
|
-
import { existsSync as
|
|
161730
|
-
import { join as
|
|
162619
|
+
import { existsSync as existsSync36, readFileSync as readFileSync15, writeFileSync as writeFileSync2, mkdirSync as mkdirSync6 } from "node:fs";
|
|
162620
|
+
import { join as join54, dirname as dirname21, basename as basename15 } from "node:path";
|
|
161731
162621
|
function siblingAgentDir2(agentDir, siblingRoot) {
|
|
161732
162622
|
if (basename15(agentDir) !== "agent") {
|
|
161733
162623
|
return void 0;
|
|
161734
162624
|
}
|
|
161735
|
-
return
|
|
162625
|
+
return join54(dirname21(dirname21(agentDir)), siblingRoot, "agent");
|
|
161736
162626
|
}
|
|
161737
162627
|
function readJsonObject4(path5) {
|
|
161738
|
-
if (!
|
|
162628
|
+
if (!existsSync36(path5)) {
|
|
161739
162629
|
return {};
|
|
161740
162630
|
}
|
|
161741
162631
|
try {
|
|
161742
|
-
const parsed = JSON.parse(
|
|
162632
|
+
const parsed = JSON.parse(readFileSync15(path5, "utf-8"));
|
|
161743
162633
|
return parsed !== null && typeof parsed === "object" ? parsed : {};
|
|
161744
162634
|
} catch {
|
|
161745
162635
|
return {};
|
|
161746
162636
|
}
|
|
161747
162637
|
}
|
|
161748
162638
|
function createReadOnlyProviderSettingsView(cwd, agentDir) {
|
|
161749
|
-
const fusionAgentDir = agentDir.includes(`${
|
|
161750
|
-
const legacyAgentDir = agentDir.includes(`${
|
|
161751
|
-
const legacyGlobalSettings = legacyAgentDir ? readJsonObject4(
|
|
161752
|
-
const fusionGlobalSettings = fusionAgentDir ? readJsonObject4(
|
|
161753
|
-
const directGlobalSettings = readJsonObject4(
|
|
162639
|
+
const fusionAgentDir = agentDir.includes(`${join54(".fusion", "agent")}`) ? agentDir : siblingAgentDir2(agentDir, ".fusion");
|
|
162640
|
+
const legacyAgentDir = agentDir.includes(`${join54(".pi", "agent")}`) ? agentDir : siblingAgentDir2(agentDir, ".pi");
|
|
162641
|
+
const legacyGlobalSettings = legacyAgentDir ? readJsonObject4(join54(legacyAgentDir, "settings.json")) : {};
|
|
162642
|
+
const fusionGlobalSettings = fusionAgentDir ? readJsonObject4(join54(fusionAgentDir, "settings.json")) : {};
|
|
162643
|
+
const directGlobalSettings = readJsonObject4(join54(agentDir, "settings.json"));
|
|
161754
162644
|
const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
|
|
161755
|
-
const fusionProjectSettings = readJsonObject4(
|
|
162645
|
+
const fusionProjectSettings = readJsonObject4(join54(cwd, ".fusion", "settings.json"));
|
|
161756
162646
|
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
|
161757
162647
|
return {
|
|
161758
162648
|
getGlobalSettings: () => structuredClone(globalSettings),
|
|
@@ -161767,7 +162657,6 @@ var init_provider_settings = __esm({
|
|
|
161767
162657
|
});
|
|
161768
162658
|
|
|
161769
162659
|
// src/commands/provider-auth.ts
|
|
161770
|
-
import { existsSync as existsSync36, readFileSync as readFileSync15 } from "node:fs";
|
|
161771
162660
|
import { getOAuthProvider as getOAuthProvider2 } from "@mariozechner/pi-ai/oauth";
|
|
161772
162661
|
function getProviderDisplayName(providerId) {
|
|
161773
162662
|
const knownProviderNames = new Map(
|
|
@@ -161783,7 +162672,10 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
|
|
|
161783
162672
|
reload: () => mergedAuthStorage.reload(),
|
|
161784
162673
|
getOAuthProviders: () => mergedAuthStorage.getOAuthProviders().filter((provider) => !OAUTH_TO_API_KEY_RECLASSIFICATIONS.has(provider.id)).map((provider) => ({ id: provider.id, name: provider.name })),
|
|
161785
162674
|
hasAuth: (provider) => mergedAuthStorage.hasAuth(provider),
|
|
161786
|
-
login: (providerId, callbacks) => mergedAuthStorage.login(
|
|
162675
|
+
login: (providerId, callbacks) => mergedAuthStorage.login(
|
|
162676
|
+
providerId,
|
|
162677
|
+
callbacks
|
|
162678
|
+
),
|
|
161787
162679
|
logout: (provider) => mergedAuthStorage.logout(provider),
|
|
161788
162680
|
getApiKeyProviders: () => {
|
|
161789
162681
|
const oauthProviderIds = new Set(
|
|
@@ -161822,13 +162714,28 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
|
|
|
161822
162714
|
}
|
|
161823
162715
|
function mergeAuthStorageReads(authStorage, readFallbackAuthStorages = []) {
|
|
161824
162716
|
const readAuthStorages = [authStorage, ...readFallbackAuthStorages];
|
|
161825
|
-
const
|
|
161826
|
-
|
|
161827
|
-
|
|
161828
|
-
|
|
162717
|
+
const selectCredential = (providerId, storages) => {
|
|
162718
|
+
let best;
|
|
162719
|
+
for (const storage of storages) {
|
|
162720
|
+
best = choosePreferredStoredCredential(best, storage.get(providerId));
|
|
161829
162721
|
}
|
|
161830
|
-
return
|
|
162722
|
+
return best;
|
|
161831
162723
|
};
|
|
162724
|
+
const getCredential = (providerId) => selectCredential(providerId, readAuthStorages);
|
|
162725
|
+
const syncFallbackOauthCredentials = () => {
|
|
162726
|
+
const providerIds = new Set(readFallbackAuthStorages.flatMap((storage) => storage.list()));
|
|
162727
|
+
for (const providerId of providerIds) {
|
|
162728
|
+
const current = authStorage.get(providerId);
|
|
162729
|
+
const candidate = selectCredential(providerId, readFallbackAuthStorages);
|
|
162730
|
+
if (!shouldHydrateStoredCredential(current, candidate)) {
|
|
162731
|
+
continue;
|
|
162732
|
+
}
|
|
162733
|
+
if (candidate && (candidate.type === "oauth" || candidate.type === "api_key")) {
|
|
162734
|
+
authStorage.set(providerId, candidate);
|
|
162735
|
+
}
|
|
162736
|
+
}
|
|
162737
|
+
};
|
|
162738
|
+
syncFallbackOauthCredentials();
|
|
161832
162739
|
return new Proxy(authStorage, {
|
|
161833
162740
|
get(target, prop, receiver) {
|
|
161834
162741
|
if (prop === "reload") {
|
|
@@ -161836,6 +162743,7 @@ function mergeAuthStorageReads(authStorage, readFallbackAuthStorages = []) {
|
|
|
161836
162743
|
for (const storage of readAuthStorages) {
|
|
161837
162744
|
storage.reload();
|
|
161838
162745
|
}
|
|
162746
|
+
syncFallbackOauthCredentials();
|
|
161839
162747
|
};
|
|
161840
162748
|
}
|
|
161841
162749
|
if (prop === "get") {
|
|
@@ -161848,13 +162756,17 @@ function mergeAuthStorageReads(authStorage, readFallbackAuthStorages = []) {
|
|
|
161848
162756
|
return (provider) => readAuthStorages.some((storage) => storage.hasAuth(provider));
|
|
161849
162757
|
}
|
|
161850
162758
|
if (prop === "getAll") {
|
|
161851
|
-
return () =>
|
|
161852
|
-
|
|
161853
|
-
|
|
161854
|
-
|
|
161855
|
-
|
|
161856
|
-
|
|
161857
|
-
|
|
162759
|
+
return () => {
|
|
162760
|
+
const providerIds = new Set(readAuthStorages.flatMap((storage) => storage.list()));
|
|
162761
|
+
const merged = {};
|
|
162762
|
+
for (const providerId of providerIds) {
|
|
162763
|
+
const credential = getCredential(providerId);
|
|
162764
|
+
if (credential) {
|
|
162765
|
+
merged[providerId] = credential;
|
|
162766
|
+
}
|
|
162767
|
+
}
|
|
162768
|
+
return merged;
|
|
162769
|
+
};
|
|
161858
162770
|
}
|
|
161859
162771
|
if (prop === "list") {
|
|
161860
162772
|
return () => Array.from(new Set(readAuthStorages.flatMap((storage) => storage.list())));
|
|
@@ -161896,15 +162808,9 @@ function createReadOnlyAuthFileStorage(authPaths) {
|
|
|
161896
162808
|
const reload = () => {
|
|
161897
162809
|
const nextCredentials = {};
|
|
161898
162810
|
for (const authPath of authPaths) {
|
|
161899
|
-
|
|
161900
|
-
|
|
161901
|
-
|
|
161902
|
-
try {
|
|
161903
|
-
const parsed = JSON.parse(readFileSync15(authPath, "utf-8"));
|
|
161904
|
-
for (const [provider, credential] of Object.entries(parsed)) {
|
|
161905
|
-
nextCredentials[provider] ??= credential;
|
|
161906
|
-
}
|
|
161907
|
-
} catch {
|
|
162811
|
+
const parsed = readStoredCredentialsFromAuthFile(authPath);
|
|
162812
|
+
for (const [provider, credential] of Object.entries(parsed)) {
|
|
162813
|
+
nextCredentials[provider] = choosePreferredStoredCredential(nextCredentials[provider], credential) ?? credential;
|
|
161908
162814
|
}
|
|
161909
162815
|
}
|
|
161910
162816
|
credentials = nextCredentials;
|
|
@@ -161925,6 +162831,7 @@ var OAUTH_TO_API_KEY_RECLASSIFICATIONS, BUILT_IN_API_KEY_PROVIDERS, CLI_PROVIDER
|
|
|
161925
162831
|
var init_provider_auth = __esm({
|
|
161926
162832
|
"src/commands/provider-auth.ts"() {
|
|
161927
162833
|
"use strict";
|
|
162834
|
+
init_src();
|
|
161928
162835
|
OAUTH_TO_API_KEY_RECLASSIFICATIONS = /* @__PURE__ */ new Set([
|
|
161929
162836
|
"anthropic"
|
|
161930
162837
|
]);
|
|
@@ -161942,34 +162849,37 @@ var init_provider_auth = __esm({
|
|
|
161942
162849
|
});
|
|
161943
162850
|
|
|
161944
162851
|
// src/commands/auth-paths.ts
|
|
161945
|
-
import { homedir as
|
|
162852
|
+
import { homedir as homedir10 } from "node:os";
|
|
161946
162853
|
import { existsSync as existsSync37, readFileSync as readFileSync16 } from "node:fs";
|
|
161947
|
-
import { join as
|
|
161948
|
-
function getFusionAgentDir3(home = process.env.HOME || process.env.USERPROFILE ||
|
|
161949
|
-
return
|
|
162854
|
+
import { join as join55 } from "node:path";
|
|
162855
|
+
function getFusionAgentDir3(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162856
|
+
return join55(home, ".fusion", "agent");
|
|
162857
|
+
}
|
|
162858
|
+
function getLegacyAgentDir(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162859
|
+
return join55(home, ".pi", "agent");
|
|
161950
162860
|
}
|
|
161951
|
-
function
|
|
161952
|
-
return
|
|
162861
|
+
function getFusionAuthPath3(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162862
|
+
return join55(getFusionAgentDir3(home), "auth.json");
|
|
161953
162863
|
}
|
|
161954
|
-
function
|
|
161955
|
-
return
|
|
162864
|
+
function getCodexCliAuthPath2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162865
|
+
return join55(home, ".codex", "auth.json");
|
|
161956
162866
|
}
|
|
161957
|
-
function getLegacyAuthPaths2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162867
|
+
function getLegacyAuthPaths2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161958
162868
|
return [
|
|
161959
|
-
|
|
161960
|
-
|
|
162869
|
+
join55(home, ".pi", "agent", "auth.json"),
|
|
162870
|
+
join55(home, ".pi", "auth.json")
|
|
161961
162871
|
];
|
|
161962
162872
|
}
|
|
161963
|
-
function getFusionModelsPath2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
161964
|
-
return
|
|
162873
|
+
function getFusionModelsPath2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162874
|
+
return join55(getFusionAgentDir3(home), "models.json");
|
|
161965
162875
|
}
|
|
161966
|
-
function getLegacyModelsPaths2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162876
|
+
function getLegacyModelsPaths2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161967
162877
|
return [
|
|
161968
|
-
|
|
161969
|
-
|
|
162878
|
+
join55(home, ".pi", "agent", "models.json"),
|
|
162879
|
+
join55(home, ".pi", "models.json")
|
|
161970
162880
|
];
|
|
161971
162881
|
}
|
|
161972
|
-
function getModelRegistryModelsPath2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162882
|
+
function getModelRegistryModelsPath2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161973
162883
|
const fusionModelsPath = getFusionModelsPath2(home);
|
|
161974
162884
|
if (existsSync37(fusionModelsPath)) {
|
|
161975
162885
|
return fusionModelsPath;
|
|
@@ -161990,11 +162900,11 @@ function readJsonObject5(path5) {
|
|
|
161990
162900
|
function hasPackageManagerSettings3(settings) {
|
|
161991
162901
|
return Array.isArray(settings.packages) || Array.isArray(settings.npmCommand);
|
|
161992
162902
|
}
|
|
161993
|
-
function getPackageManagerAgentDir2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162903
|
+
function getPackageManagerAgentDir2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161994
162904
|
const fusionAgentDir = getFusionAgentDir3(home);
|
|
161995
162905
|
const legacyAgentDir = getLegacyAgentDir(home);
|
|
161996
|
-
const fusionSettings = readJsonObject5(
|
|
161997
|
-
const legacySettings = readJsonObject5(
|
|
162906
|
+
const fusionSettings = readJsonObject5(join55(fusionAgentDir, "settings.json"));
|
|
162907
|
+
const legacySettings = readJsonObject5(join55(legacyAgentDir, "settings.json"));
|
|
161998
162908
|
if (hasPackageManagerSettings3(fusionSettings) || !existsSync37(legacyAgentDir)) {
|
|
161999
162909
|
return fusionAgentDir;
|
|
162000
162910
|
}
|
|
@@ -162170,7 +163080,7 @@ import {
|
|
|
162170
163080
|
symlinkSync,
|
|
162171
163081
|
unlinkSync
|
|
162172
163082
|
} from "node:fs";
|
|
162173
|
-
import { dirname as dirname23, join as
|
|
163083
|
+
import { dirname as dirname23, join as join56, resolve as resolve29 } from "node:path";
|
|
162174
163084
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
162175
163085
|
function isPiClaudeCliConfigured(globalSettings) {
|
|
162176
163086
|
if (!globalSettings || typeof globalSettings !== "object") {
|
|
@@ -162194,7 +163104,7 @@ function resolveFusionSkillSource() {
|
|
|
162194
163104
|
return existsSync38(candidate) ? candidate : null;
|
|
162195
163105
|
}
|
|
162196
163106
|
function installFusionSkillIntoProject(projectPath, options = {}) {
|
|
162197
|
-
const target =
|
|
163107
|
+
const target = join56(projectPath, ".claude", "skills", FUSION_SKILL_NAME);
|
|
162198
163108
|
if (options.enabled === false) {
|
|
162199
163109
|
return { outcome: "skipped", target, reason: "pi-claude-cli not configured" };
|
|
162200
163110
|
}
|
|
@@ -162219,7 +163129,7 @@ function installFusionSkillIntoProject(projectPath, options = {}) {
|
|
|
162219
163129
|
unlinkSync(target);
|
|
162220
163130
|
replaced = true;
|
|
162221
163131
|
} else {
|
|
162222
|
-
const skillMd =
|
|
163132
|
+
const skillMd = join56(target, "SKILL.md");
|
|
162223
163133
|
if (!existsSync38(skillMd)) {
|
|
162224
163134
|
return {
|
|
162225
163135
|
outcome: "failed",
|
|
@@ -162266,7 +163176,7 @@ function ensureFusionSkillForProjects(projects, options = { enabled: false }) {
|
|
|
162266
163176
|
if (!options.enabled) {
|
|
162267
163177
|
return projects.map((p) => ({
|
|
162268
163178
|
outcome: "skipped",
|
|
162269
|
-
target:
|
|
163179
|
+
target: join56(p.path, ".claude", "skills", FUSION_SKILL_NAME),
|
|
162270
163180
|
reason: "pi-claude-cli not configured"
|
|
162271
163181
|
}));
|
|
162272
163182
|
}
|
|
@@ -162569,10 +163479,10 @@ var init_droid_cli_extension = __esm({
|
|
|
162569
163479
|
|
|
162570
163480
|
// src/update-cache.ts
|
|
162571
163481
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
162572
|
-
import { join as
|
|
163482
|
+
import { join as join57 } from "node:path";
|
|
162573
163483
|
function getCachedUpdateStatus(currentVersion) {
|
|
162574
163484
|
try {
|
|
162575
|
-
const cachePath =
|
|
163485
|
+
const cachePath = join57(resolveGlobalDir(), "update-check.json");
|
|
162576
163486
|
const raw = readFileSync19(cachePath, "utf-8");
|
|
162577
163487
|
const parsed = JSON.parse(raw);
|
|
162578
163488
|
if (parsed.updateAvailable === true && typeof parsed.latestVersion === "string" && parsed.latestVersion.length > 0 && typeof parsed.currentVersion === "string" && parsed.currentVersion.length > 0) {
|
|
@@ -162662,19 +163572,19 @@ var init_self_extension = __esm({
|
|
|
162662
163572
|
// src/plugins/bundled-plugin-install.ts
|
|
162663
163573
|
import { existsSync as existsSync42 } from "node:fs";
|
|
162664
163574
|
import { readFile as readFile24 } from "node:fs/promises";
|
|
162665
|
-
import { dirname as dirname27, join as
|
|
163575
|
+
import { dirname as dirname27, join as join58, resolve as resolve33 } from "node:path";
|
|
162666
163576
|
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
162667
163577
|
function getCandidatePluginPaths() {
|
|
162668
163578
|
const moduleDir = dirname27(fileURLToPath10(import.meta.url));
|
|
162669
163579
|
const cliPackageRoot = resolve33(moduleDir, "..", "..");
|
|
162670
163580
|
return [
|
|
162671
|
-
|
|
162672
|
-
|
|
162673
|
-
|
|
163581
|
+
join58(cliPackageRoot, "dist", "plugins", DEPENDENCY_GRAPH_PLUGIN_ID),
|
|
163582
|
+
join58(cliPackageRoot, "plugins", DEPENDENCY_GRAPH_PLUGIN_ID),
|
|
163583
|
+
join58(cliPackageRoot, "..", "..", "plugins", DEPENDENCY_GRAPH_PLUGIN_ID)
|
|
162674
163584
|
];
|
|
162675
163585
|
}
|
|
162676
163586
|
async function loadManifest(pluginDir) {
|
|
162677
|
-
const manifestPath =
|
|
163587
|
+
const manifestPath = join58(pluginDir, "manifest.json");
|
|
162678
163588
|
const content = await readFile24(manifestPath, "utf-8");
|
|
162679
163589
|
const manifest = JSON.parse(content);
|
|
162680
163590
|
const validation = validatePluginManifest(manifest);
|
|
@@ -162685,7 +163595,7 @@ async function loadManifest(pluginDir) {
|
|
|
162685
163595
|
}
|
|
162686
163596
|
function resolveBundledDependencyGraphPath() {
|
|
162687
163597
|
for (const path5 of getCandidatePluginPaths()) {
|
|
162688
|
-
if (existsSync42(
|
|
163598
|
+
if (existsSync42(join58(path5, "manifest.json"))) {
|
|
162689
163599
|
return path5;
|
|
162690
163600
|
}
|
|
162691
163601
|
}
|
|
@@ -167114,7 +168024,7 @@ __export(dashboard_exports, {
|
|
|
167114
168024
|
promptForPort: () => promptForPort,
|
|
167115
168025
|
runDashboard: () => runDashboard
|
|
167116
168026
|
});
|
|
167117
|
-
import { dirname as dirname28, join as
|
|
168027
|
+
import { dirname as dirname28, join as join59, resolve as pathResolve } from "node:path";
|
|
167118
168028
|
import { execFile as execFileCb } from "node:child_process";
|
|
167119
168029
|
import { promisify as promisify16 } from "node:util";
|
|
167120
168030
|
import { stat as stat11, readdir as readdir11, readFile as fsReadFile3 } from "node:fs/promises";
|
|
@@ -167452,7 +168362,7 @@ async function buildFileListDirectory(projectPath, relativePath) {
|
|
|
167452
168362
|
let size = 0;
|
|
167453
168363
|
let modifiedAt = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
167454
168364
|
try {
|
|
167455
|
-
const s = await stat11(
|
|
168365
|
+
const s = await stat11(join59(absDir, d.name));
|
|
167456
168366
|
size = d.isDirectory() ? 0 : s.size;
|
|
167457
168367
|
modifiedAt = s.mtime.toISOString();
|
|
167458
168368
|
} catch {
|
|
@@ -167864,8 +168774,11 @@ async function runDashboard(port, opts = {}) {
|
|
|
167864
168774
|
rootDir: cwd
|
|
167865
168775
|
});
|
|
167866
168776
|
const authStorage = AuthStorage2.create(getFusionAuthPath3());
|
|
167867
|
-
const
|
|
167868
|
-
|
|
168777
|
+
const supplementalAuthStorage = createReadOnlyAuthFileStorage([
|
|
168778
|
+
...getLegacyAuthPaths2(),
|
|
168779
|
+
getCodexCliAuthPath2()
|
|
168780
|
+
]);
|
|
168781
|
+
const mergedAuthStorage = mergeAuthStorageReads(authStorage, [supplementalAuthStorage]);
|
|
167869
168782
|
const modelRegistry = ModelRegistry3.create(mergedAuthStorage, getModelRegistryModelsPath2());
|
|
167870
168783
|
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(mergedAuthStorage, modelRegistry);
|
|
167871
168784
|
let packageManager;
|
|
@@ -167927,7 +168840,7 @@ async function runDashboard(port, opts = {}) {
|
|
|
167927
168840
|
...droidCliPaths
|
|
167928
168841
|
],
|
|
167929
168842
|
cwd,
|
|
167930
|
-
|
|
168843
|
+
join59(cwd, ".fusion", "disabled-auto-extension-discovery")
|
|
167931
168844
|
);
|
|
167932
168845
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
167933
168846
|
logSink.log(`Failed to load ${path5}: ${error}`, "extensions");
|
|
@@ -169450,7 +170363,7 @@ var serve_exports = {};
|
|
|
169450
170363
|
__export(serve_exports, {
|
|
169451
170364
|
runServe: () => runServe
|
|
169452
170365
|
});
|
|
169453
|
-
import { dirname as dirname29, join as
|
|
170366
|
+
import { dirname as dirname29, join as join60 } from "node:path";
|
|
169454
170367
|
import {
|
|
169455
170368
|
AuthStorage as AuthStorage3,
|
|
169456
170369
|
DefaultPackageManager as DefaultPackageManager3,
|
|
@@ -169703,8 +170616,11 @@ async function runServe(port, opts = {}) {
|
|
|
169703
170616
|
const missionExecutionLoop = cwdEngine.getRuntime().getMissionExecutionLoop();
|
|
169704
170617
|
const automationStore = cwdEngine.getAutomationStore();
|
|
169705
170618
|
const authStorage = AuthStorage3.create(getFusionAuthPath3());
|
|
169706
|
-
const
|
|
169707
|
-
|
|
170619
|
+
const supplementalAuthStorage = createReadOnlyAuthFileStorage([
|
|
170620
|
+
...getLegacyAuthPaths2(),
|
|
170621
|
+
getCodexCliAuthPath2()
|
|
170622
|
+
]);
|
|
170623
|
+
const mergedAuthStorage = mergeAuthStorageReads(authStorage, [supplementalAuthStorage]);
|
|
169708
170624
|
const modelRegistry = ModelRegistry4.create(mergedAuthStorage, getModelRegistryModelsPath2());
|
|
169709
170625
|
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(mergedAuthStorage, modelRegistry);
|
|
169710
170626
|
let packageManager;
|
|
@@ -169766,7 +170682,7 @@ async function runServe(port, opts = {}) {
|
|
|
169766
170682
|
...droidCliPaths
|
|
169767
170683
|
],
|
|
169768
170684
|
cwd,
|
|
169769
|
-
|
|
170685
|
+
join60(cwd, ".fusion", "disabled-auto-extension-discovery")
|
|
169770
170686
|
);
|
|
169771
170687
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
169772
170688
|
console.log(`[extensions] Failed to load ${path5}: ${error}`);
|
|
@@ -170121,7 +171037,7 @@ var daemon_exports = {};
|
|
|
170121
171037
|
__export(daemon_exports, {
|
|
170122
171038
|
runDaemon: () => runDaemon
|
|
170123
171039
|
});
|
|
170124
|
-
import { join as
|
|
171040
|
+
import { join as join61 } from "node:path";
|
|
170125
171041
|
import {
|
|
170126
171042
|
AuthStorage as AuthStorage4,
|
|
170127
171043
|
DefaultPackageManager as DefaultPackageManager4,
|
|
@@ -170367,8 +171283,11 @@ async function runDaemon(opts = {}) {
|
|
|
170367
171283
|
const missionExecutionLoop = cwdEngine.getRuntime().getMissionExecutionLoop();
|
|
170368
171284
|
const automationStore = cwdEngine.getAutomationStore();
|
|
170369
171285
|
const authStorage = AuthStorage4.create(getFusionAuthPath3());
|
|
170370
|
-
const
|
|
170371
|
-
|
|
171286
|
+
const supplementalAuthStorage = createReadOnlyAuthFileStorage([
|
|
171287
|
+
...getLegacyAuthPaths2(),
|
|
171288
|
+
getCodexCliAuthPath2()
|
|
171289
|
+
]);
|
|
171290
|
+
const mergedAuthStorage = mergeAuthStorageReads(authStorage, [supplementalAuthStorage]);
|
|
170372
171291
|
const modelRegistry = ModelRegistry5.create(mergedAuthStorage, getModelRegistryModelsPath2());
|
|
170373
171292
|
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(mergedAuthStorage, modelRegistry);
|
|
170374
171293
|
let packageManager;
|
|
@@ -170428,7 +171347,7 @@ async function runDaemon(opts = {}) {
|
|
|
170428
171347
|
const extensionsResult = await discoverAndLoadExtensions4(
|
|
170429
171348
|
[...reconciledExtensionPaths, ...droidCliPaths],
|
|
170430
171349
|
cwd,
|
|
170431
|
-
|
|
171350
|
+
join61(cwd, ".fusion", "disabled-auto-extension-discovery")
|
|
170432
171351
|
);
|
|
170433
171352
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
170434
171353
|
console.log(`[extensions] Failed to load ${path5}: ${error}`);
|
|
@@ -170649,7 +171568,7 @@ __export(desktop_exports, {
|
|
|
170649
171568
|
});
|
|
170650
171569
|
import { spawn as spawn18 } from "node:child_process";
|
|
170651
171570
|
import { once as once2 } from "node:events";
|
|
170652
|
-
import { join as
|
|
171571
|
+
import { join as join62 } from "node:path";
|
|
170653
171572
|
import { createRequire as createRequire6 } from "node:module";
|
|
170654
171573
|
function runCommand(command, args, cwd) {
|
|
170655
171574
|
return new Promise((resolve44, reject2) => {
|
|
@@ -170728,7 +171647,7 @@ async function runDesktop(options = {}) {
|
|
|
170728
171647
|
}
|
|
170729
171648
|
const runtime = await startDashboardRuntime(rootDir, Boolean(options.paused));
|
|
170730
171649
|
const electronBinary = resolveElectronBinary();
|
|
170731
|
-
const desktopEntry =
|
|
171650
|
+
const desktopEntry = join62(rootDir, "packages", "desktop", "dist", "main.js");
|
|
170732
171651
|
const electronArgs = ["--enable-source-maps", desktopEntry, ...options.dev ? ["--dev"] : []];
|
|
170733
171652
|
const electronEnv = {
|
|
170734
171653
|
...process.env,
|
|
@@ -170814,7 +171733,7 @@ __export(task_exports, {
|
|
|
170814
171733
|
});
|
|
170815
171734
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
170816
171735
|
import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync43, readFileSync as readFileSync21 } from "node:fs";
|
|
170817
|
-
import { basename as basename17, join as
|
|
171736
|
+
import { basename as basename17, join as join63 } from "node:path";
|
|
170818
171737
|
function getGitHubIssueUrl(sourceMetadata) {
|
|
170819
171738
|
if (!sourceMetadata || typeof sourceMetadata !== "object") return void 0;
|
|
170820
171739
|
const issueUrl = sourceMetadata.issueUrl;
|
|
@@ -171127,7 +172046,7 @@ async function runTaskLogs(id, options = {}, projectName) {
|
|
|
171127
172046
|
printEntries(filteredEntries);
|
|
171128
172047
|
if (options.follow) {
|
|
171129
172048
|
const projectPath = projectContext?.projectPath ?? process.cwd();
|
|
171130
|
-
const logPath =
|
|
172049
|
+
const logPath = join63(projectPath, ".fusion", "tasks", id, "agent.log");
|
|
171131
172050
|
if (!existsSync43(logPath)) {
|
|
171132
172051
|
console.log(`
|
|
171133
172052
|
Waiting for log file to be created...`);
|
|
@@ -172405,7 +173324,7 @@ __export(settings_export_exports, {
|
|
|
172405
173324
|
runSettingsExport: () => runSettingsExport
|
|
172406
173325
|
});
|
|
172407
173326
|
import { writeFile as writeFile18 } from "node:fs/promises";
|
|
172408
|
-
import { resolve as resolve34, join as
|
|
173327
|
+
import { resolve as resolve34, join as join64 } from "node:path";
|
|
172409
173328
|
async function runSettingsExport(options = {}) {
|
|
172410
173329
|
const scope = options.scope ?? "both";
|
|
172411
173330
|
const project = options.projectName ? await resolveProject(options.projectName) : void 0;
|
|
@@ -172419,7 +173338,7 @@ async function runSettingsExport(options = {}) {
|
|
|
172419
173338
|
targetPath = resolve34(outputPath);
|
|
172420
173339
|
} else {
|
|
172421
173340
|
const filename = generateExportFilename();
|
|
172422
|
-
targetPath =
|
|
173341
|
+
targetPath = join64(process.cwd(), filename);
|
|
172423
173342
|
}
|
|
172424
173343
|
const jsonContent = JSON.stringify(exportData, null, 2);
|
|
172425
173344
|
await writeFile18(targetPath, jsonContent);
|
|
@@ -173993,14 +174912,14 @@ var init_project = __esm({
|
|
|
173993
174912
|
|
|
173994
174913
|
// src/commands/skill-installation.ts
|
|
173995
174914
|
import { cpSync as cpSync2, existsSync as existsSync47, mkdirSync as mkdirSync8 } from "node:fs";
|
|
173996
|
-
import { homedir as
|
|
173997
|
-
import { dirname as dirname31, join as
|
|
174915
|
+
import { homedir as homedir11 } from "node:os";
|
|
174916
|
+
import { dirname as dirname31, join as join65, resolve as resolve38 } from "node:path";
|
|
173998
174917
|
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
173999
|
-
function getSupportedSkillInstallTargets(homeDir = process.env.HOME || process.env.USERPROFILE ||
|
|
174918
|
+
function getSupportedSkillInstallTargets(homeDir = process.env.HOME || process.env.USERPROFILE || homedir11()) {
|
|
174000
174919
|
return [
|
|
174001
|
-
{ client: "claude", targetDir:
|
|
174002
|
-
{ client: "codex", targetDir:
|
|
174003
|
-
{ client: "gemini", targetDir:
|
|
174920
|
+
{ client: "claude", targetDir: join65(homeDir, ".claude", "skills", FUSION_SKILL_NAME2) },
|
|
174921
|
+
{ client: "codex", targetDir: join65(homeDir, ".codex", "skills", FUSION_SKILL_NAME2) },
|
|
174922
|
+
{ client: "gemini", targetDir: join65(homeDir, ".gemini", "skills", FUSION_SKILL_NAME2) }
|
|
174004
174923
|
];
|
|
174005
174924
|
}
|
|
174006
174925
|
function resolveBundledFusionSkillSource() {
|
|
@@ -174064,13 +174983,13 @@ __export(init_exports, {
|
|
|
174064
174983
|
runInit: () => runInit
|
|
174065
174984
|
});
|
|
174066
174985
|
import { existsSync as existsSync48, mkdirSync as mkdirSync9, writeFileSync as writeFileSync3, readFileSync as readFileSync22 } from "node:fs";
|
|
174067
|
-
import { join as
|
|
174986
|
+
import { join as join66, resolve as resolve39, basename as basename20 } from "node:path";
|
|
174068
174987
|
import { exec as exec13 } from "node:child_process";
|
|
174069
174988
|
import { promisify as promisify18 } from "node:util";
|
|
174070
174989
|
async function runInit(options = {}) {
|
|
174071
174990
|
const cwd = options.path ? resolve39(options.path) : process.cwd();
|
|
174072
|
-
const fusionDir =
|
|
174073
|
-
const dbPath =
|
|
174991
|
+
const fusionDir = join66(cwd, ".fusion");
|
|
174992
|
+
const dbPath = join66(fusionDir, "fusion.db");
|
|
174074
174993
|
const hasDbPath = existsSync48(dbPath);
|
|
174075
174994
|
const hasValidDb = hasDbPath && isValidSqliteDatabaseFile(dbPath);
|
|
174076
174995
|
if (existsSync48(fusionDir) && hasDbPath && hasValidDb) {
|
|
@@ -174166,7 +175085,7 @@ async function runInit(options = {}) {
|
|
|
174166
175085
|
}
|
|
174167
175086
|
}
|
|
174168
175087
|
async function detectProjectName(dir2) {
|
|
174169
|
-
if (!existsSync48(
|
|
175088
|
+
if (!existsSync48(join66(dir2, ".git"))) {
|
|
174170
175089
|
return basename20(dir2) || "my-project";
|
|
174171
175090
|
}
|
|
174172
175091
|
try {
|
|
@@ -174186,7 +175105,7 @@ async function detectProjectName(dir2) {
|
|
|
174186
175105
|
return basename20(dir2) || "my-project";
|
|
174187
175106
|
}
|
|
174188
175107
|
async function addLocalStorageToGitignore(cwd) {
|
|
174189
|
-
const gitignorePath =
|
|
175108
|
+
const gitignorePath = join66(cwd, ".gitignore");
|
|
174190
175109
|
let content = "";
|
|
174191
175110
|
if (existsSync48(gitignorePath)) {
|
|
174192
175111
|
try {
|
|
@@ -174229,7 +175148,7 @@ async function initializeGitRepo(cwd) {
|
|
|
174229
175148
|
}
|
|
174230
175149
|
await ensureGitConfig(cwd, "user.name", "Fusion");
|
|
174231
175150
|
await ensureGitConfig(cwd, "user.email", "noreply@runfusion.ai");
|
|
174232
|
-
const gitkeepPath =
|
|
175151
|
+
const gitkeepPath = join66(cwd, ".gitkeep");
|
|
174233
175152
|
if (!existsSync48(gitkeepPath)) {
|
|
174234
175153
|
writeFileSync3(gitkeepPath, "\n");
|
|
174235
175154
|
}
|
|
@@ -174896,7 +175815,7 @@ __export(plugin_exports, {
|
|
|
174896
175815
|
runPluginUninstall: () => runPluginUninstall
|
|
174897
175816
|
});
|
|
174898
175817
|
import { existsSync as existsSync50 } from "node:fs";
|
|
174899
|
-
import { join as
|
|
175818
|
+
import { join as join67 } from "node:path";
|
|
174900
175819
|
import { readFile as readFile25 } from "node:fs/promises";
|
|
174901
175820
|
import * as readline from "node:readline";
|
|
174902
175821
|
async function getProjectPath6(projectName) {
|
|
@@ -174934,7 +175853,7 @@ async function createPluginLoader(pluginStore, projectName) {
|
|
|
174934
175853
|
return { store: pluginStore, loader };
|
|
174935
175854
|
}
|
|
174936
175855
|
async function loadManifestFromPath(pluginPath) {
|
|
174937
|
-
const manifestPath =
|
|
175856
|
+
const manifestPath = join67(pluginPath, "manifest.json");
|
|
174938
175857
|
if (!existsSync50(manifestPath)) {
|
|
174939
175858
|
throw new Error(`Plugin manifest not found at: ${manifestPath}`);
|
|
174940
175859
|
}
|
|
@@ -175126,7 +176045,7 @@ __export(plugin_scaffold_exports, {
|
|
|
175126
176045
|
runPluginCreate: () => runPluginCreate
|
|
175127
176046
|
});
|
|
175128
176047
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync5, existsSync as existsSync51 } from "node:fs";
|
|
175129
|
-
import { join as
|
|
176048
|
+
import { join as join68 } from "node:path";
|
|
175130
176049
|
function toTitleCase(str) {
|
|
175131
176050
|
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
175132
176051
|
}
|
|
@@ -175259,7 +176178,7 @@ async function runPluginCreate(name, options) {
|
|
|
175259
176178
|
process.exit(1);
|
|
175260
176179
|
}
|
|
175261
176180
|
const targetDir = options?.output ?? name;
|
|
175262
|
-
const targetPath =
|
|
176181
|
+
const targetPath = join68(process.cwd(), targetDir);
|
|
175263
176182
|
if (existsSync51(targetPath)) {
|
|
175264
176183
|
console.error(`Error: Directory '${targetDir}' already exists.`);
|
|
175265
176184
|
console.error("Please choose a different name or remove the existing directory.");
|
|
@@ -175267,16 +176186,16 @@ async function runPluginCreate(name, options) {
|
|
|
175267
176186
|
}
|
|
175268
176187
|
try {
|
|
175269
176188
|
mkdirSync11(targetPath, { recursive: true });
|
|
175270
|
-
mkdirSync11(
|
|
175271
|
-
writeFileSync5(
|
|
175272
|
-
writeFileSync5(
|
|
175273
|
-
writeFileSync5(
|
|
175274
|
-
writeFileSync5(
|
|
176189
|
+
mkdirSync11(join68(targetPath, "src", "__tests__"), { recursive: true });
|
|
176190
|
+
writeFileSync5(join68(targetPath, "package.json"), generatePackageJson(name));
|
|
176191
|
+
writeFileSync5(join68(targetPath, "tsconfig.json"), generateTsconfig());
|
|
176192
|
+
writeFileSync5(join68(targetPath, "vitest.config.ts"), generateVitestConfig());
|
|
176193
|
+
writeFileSync5(join68(targetPath, "src", "index.ts"), generateIndexTs(name));
|
|
175275
176194
|
writeFileSync5(
|
|
175276
|
-
|
|
176195
|
+
join68(targetPath, "src", "__tests__", "index.test.ts"),
|
|
175277
176196
|
generateTestTs(name)
|
|
175278
176197
|
);
|
|
175279
|
-
writeFileSync5(
|
|
176198
|
+
writeFileSync5(join68(targetPath, "README.md"), generateReadme(name));
|
|
175280
176199
|
} catch (err) {
|
|
175281
176200
|
console.error(
|
|
175282
176201
|
`Error creating plugin files: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -175428,7 +176347,7 @@ __export(research_exports, {
|
|
|
175428
176347
|
runResearchShow: () => runResearchShow
|
|
175429
176348
|
});
|
|
175430
176349
|
import { writeFile as writeFile19 } from "node:fs/promises";
|
|
175431
|
-
import { join as
|
|
176350
|
+
import { join as join69, resolve as resolve42 } from "node:path";
|
|
175432
176351
|
async function getStore3(projectName) {
|
|
175433
176352
|
const project = projectName ? await resolveProject(projectName) : void 0;
|
|
175434
176353
|
const store = new TaskStore(project?.projectPath ?? process.cwd());
|
|
@@ -175584,7 +176503,7 @@ async function runResearchExport(options) {
|
|
|
175584
176503
|
}
|
|
175585
176504
|
const content = format === "json" ? JSON.stringify(run, null, 2) : renderMarkdown(run);
|
|
175586
176505
|
const ext = format === "json" ? "json" : "md";
|
|
175587
|
-
const outputPath = options.output ? resolve42(options.output) :
|
|
176506
|
+
const outputPath = options.output ? resolve42(options.output) : join69(process.cwd(), `research-${run.id.toLowerCase()}.${ext}`);
|
|
175588
176507
|
await writeFile19(outputPath, content, "utf8");
|
|
175589
176508
|
store.getResearchStore().createExport(run.id, format, content);
|
|
175590
176509
|
if (options.json) {
|
|
@@ -175649,7 +176568,7 @@ __export(native_patch_exports, {
|
|
|
175649
176568
|
isTerminalAvailable: () => isTerminalAvailable,
|
|
175650
176569
|
setupNativeResolution: () => setupNativeResolution
|
|
175651
176570
|
});
|
|
175652
|
-
import { join as
|
|
176571
|
+
import { join as join70, basename as basename21, dirname as dirname32 } from "node:path";
|
|
175653
176572
|
import { existsSync as existsSync52, copyFileSync, mkdirSync as mkdirSync12, symlinkSync as symlinkSync2, rmSync as rmSync5, lstatSync as lstatSync3, readlinkSync as readlinkSync2 } from "node:fs";
|
|
175654
176573
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
175655
176574
|
function findStagedNativeDir2() {
|
|
@@ -175657,13 +176576,13 @@ function findStagedNativeDir2() {
|
|
|
175657
176576
|
const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
|
|
175658
176577
|
const prebuildName = `${platform4}-${arch}`;
|
|
175659
176578
|
const execDir = dirname32(process.execPath);
|
|
175660
|
-
const nextToBinary =
|
|
175661
|
-
if (existsSync52(
|
|
176579
|
+
const nextToBinary = join70(execDir, "runtime", prebuildName);
|
|
176580
|
+
if (existsSync52(join70(nextToBinary, "pty.node"))) {
|
|
175662
176581
|
return nextToBinary;
|
|
175663
176582
|
}
|
|
175664
176583
|
if (process.env.FUSION_RUNTIME_DIR) {
|
|
175665
|
-
const envPath =
|
|
175666
|
-
if (existsSync52(
|
|
176584
|
+
const envPath = join70(process.env.FUSION_RUNTIME_DIR, prebuildName);
|
|
176585
|
+
if (existsSync52(join70(envPath, "pty.node"))) {
|
|
175667
176586
|
return envPath;
|
|
175668
176587
|
}
|
|
175669
176588
|
}
|
|
@@ -175696,17 +176615,17 @@ function setupNativeResolution() {
|
|
|
175696
176615
|
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
|
|
175697
176616
|
}
|
|
175698
176617
|
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
|
|
175699
|
-
const tmpRoot =
|
|
175700
|
-
const fnDir =
|
|
175701
|
-
const prebuildsDir =
|
|
175702
|
-
const platformDir =
|
|
176618
|
+
const tmpRoot = join70(tmpdir5(), `fn-bunfs-${process.pid}`);
|
|
176619
|
+
const fnDir = join70(tmpRoot, "fn");
|
|
176620
|
+
const prebuildsDir = join70(fnDir, "prebuilds");
|
|
176621
|
+
const platformDir = join70(prebuildsDir, basename21(nativeDir));
|
|
175703
176622
|
try {
|
|
175704
176623
|
cleanupStaleBunfsLinks();
|
|
175705
176624
|
mkdirSync12(platformDir, { recursive: true });
|
|
175706
|
-
const ptyNodeDest =
|
|
175707
|
-
copyFileSync(
|
|
175708
|
-
if (existsSync52(
|
|
175709
|
-
copyFileSync(
|
|
176625
|
+
const ptyNodeDest = join70(platformDir, "pty.node");
|
|
176626
|
+
copyFileSync(join70(nativeDir, "pty.node"), ptyNodeDest);
|
|
176627
|
+
if (existsSync52(join70(nativeDir, "spawn-helper"))) {
|
|
176628
|
+
copyFileSync(join70(nativeDir, "spawn-helper"), join70(platformDir, "spawn-helper"));
|
|
175710
176629
|
}
|
|
175711
176630
|
process.env.FUSION_FAKE_BUNFS_ROOT = tmpRoot;
|
|
175712
176631
|
if (process.platform !== "win32") {
|
|
@@ -175783,7 +176702,7 @@ var init_native_patch = __esm({
|
|
|
175783
176702
|
// src/bin.ts
|
|
175784
176703
|
import { existsSync as existsSync53, mkdtempSync as mkdtempSync2, readFileSync as readFileSync24, symlinkSync as symlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
175785
176704
|
import { createRequire as createRequire7 } from "node:module";
|
|
175786
|
-
import { join as
|
|
176705
|
+
import { join as join71, dirname as dirname33, resolve as resolve43 } from "node:path";
|
|
175787
176706
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
175788
176707
|
import { performance as performance3 } from "node:perf_hooks";
|
|
175789
176708
|
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
@@ -175792,7 +176711,7 @@ function configurePiPackage() {
|
|
|
175792
176711
|
if (process.env.PI_PACKAGE_DIR) {
|
|
175793
176712
|
return;
|
|
175794
176713
|
}
|
|
175795
|
-
const tmp = mkdtempSync2(
|
|
176714
|
+
const tmp = mkdtempSync2(join71(tmpdir6(), "fn-pkg-"));
|
|
175796
176715
|
let packageJson = {
|
|
175797
176716
|
name: "pi",
|
|
175798
176717
|
version: "0.1.0",
|
|
@@ -175804,9 +176723,9 @@ function configurePiPackage() {
|
|
|
175804
176723
|
const piPackageDir = dirname33(piPackagePath);
|
|
175805
176724
|
packageJson = JSON.parse(readFileSync24(piPackagePath, "utf-8"));
|
|
175806
176725
|
for (const entry of ["dist", "docs", "examples", "README.md", "CHANGELOG.md"]) {
|
|
175807
|
-
const source =
|
|
176726
|
+
const source = join71(piPackageDir, entry);
|
|
175808
176727
|
if (existsSync53(source)) {
|
|
175809
|
-
symlinkSync3(source,
|
|
176728
|
+
symlinkSync3(source, join71(tmp, entry));
|
|
175810
176729
|
}
|
|
175811
176730
|
}
|
|
175812
176731
|
} catch {
|
|
@@ -175815,7 +176734,7 @@ function configurePiPackage() {
|
|
|
175815
176734
|
...packageJson.piConfig ?? {},
|
|
175816
176735
|
configDir: ".fusion"
|
|
175817
176736
|
};
|
|
175818
|
-
writeFileSync6(
|
|
176737
|
+
writeFileSync6(join71(tmp, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
175819
176738
|
process.env.PI_PACKAGE_DIR = tmp;
|
|
175820
176739
|
}
|
|
175821
176740
|
configurePiPackage();
|
|
@@ -175843,8 +176762,8 @@ function loadEnvFile(path5) {
|
|
|
175843
176762
|
}
|
|
175844
176763
|
function loadLocalEnv() {
|
|
175845
176764
|
const cwd = process.cwd();
|
|
175846
|
-
loadEnvFile(
|
|
175847
|
-
loadEnvFile(
|
|
176765
|
+
loadEnvFile(join71(cwd, ".env"));
|
|
176766
|
+
loadEnvFile(join71(cwd, ".env.local"));
|
|
175848
176767
|
}
|
|
175849
176768
|
loadLocalEnv();
|
|
175850
176769
|
async function loadCommandHandlers() {
|