@runfusion/fusion 0.17.0 → 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 +1729 -789
- package/dist/client/assets/{AgentDetailView-DGqT1oDt.js → AgentDetailView-17J-F0Rl.js} +3 -3
- package/dist/client/assets/{AgentsView-BmemrfrO.js → AgentsView-sbBkb7Wd.js} +45 -45
- package/dist/client/assets/ChatView-BR5cvK_B.js +1 -0
- package/dist/client/assets/{DevServerView-C3Q0XqDA.js → DevServerView-GFFVXHVP.js} +1 -1
- package/dist/client/assets/{DirectoryPicker-BZWVA9ND.js → DirectoryPicker-WPDSBdT6.js} +1 -1
- package/dist/client/assets/{DocumentsView-DO48ivSq.js → DocumentsView-BHpDsIIt.js} +1 -1
- package/dist/client/assets/{InsightsView-CAngTfMf.js → InsightsView-Bxu0TJkt.js} +1 -1
- package/dist/client/assets/{MemoryView-B3rNcAOW.js → MemoryView-CmnzZorw.js} +2 -2
- package/dist/client/assets/{NodesView-BnV1LWa8.js → NodesView-CO9_4hCr.js} +4 -4
- package/dist/client/assets/{PiExtensionsManager-C3_Lw4sa.js → PiExtensionsManager-4e3MlD62.js} +3 -3
- package/dist/client/assets/{PluginManager-Vv3nzrJ1.js → PluginManager-DGN2rvOY.js} +1 -1
- package/dist/client/assets/ResearchView-Dsa6Gykl.js +1 -0
- package/dist/client/assets/{RoadmapsView-BiIpE-b8.js → RoadmapsView-jHTOK0RQ.js} +2 -2
- package/dist/client/assets/{SettingsModal-CK4w8Ztb.js → SettingsModal-4Z8ZJMzD.js} +1 -1
- package/dist/client/assets/SettingsModal-D0kuJpBA.js +31 -0
- package/dist/client/assets/{SetupWizardModal-Dw6N4UvY.js → SetupWizardModal-Bhumd4Rf.js} +1 -1
- package/dist/client/assets/{SkillsView-C1196wgA.js → SkillsView-MHweJTz4.js} +1 -1
- package/dist/client/assets/{folder-open-WVtgE4k3.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-MSImEC8V.js → star-7L86NZrT.js} +1 -1
- package/dist/client/assets/{upload-Dmvy3xXd.js → upload-DsAS6tno.js} +1 -1
- package/dist/client/assets/{users-CncYvHNf.js → users-D3u6f2Rz.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/client/version.json +1 -1
- package/dist/extension.js +1239 -527
- 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-CZQUBFlV.js +0 -1
- package/dist/client/assets/ResearchView-Dfdsuc21.js +0 -1
- package/dist/client/assets/SettingsModal-BN00HYJ2.js +0 -31
- package/dist/client/assets/index-Bv0TGzDH.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();
|
|
55650
|
+
}
|
|
55651
|
+
function getFusionAuthPath(home = getHomeDir5()) {
|
|
55652
|
+
return join24(home, ".fusion", "agent", "auth.json");
|
|
55449
55653
|
}
|
|
55450
|
-
function
|
|
55451
|
-
return
|
|
55654
|
+
function getFusionModelsPath(home = getHomeDir5()) {
|
|
55655
|
+
return join24(home, ".fusion", "agent", "models.json");
|
|
55452
55656
|
}
|
|
55453
|
-
function
|
|
55454
|
-
return
|
|
55657
|
+
function getLegacyAuthPaths(home = getHomeDir5()) {
|
|
55658
|
+
return [
|
|
55659
|
+
join24(home, ".pi", "agent", "auth.json"),
|
|
55660
|
+
join24(home, ".pi", "auth.json")
|
|
55661
|
+
];
|
|
55455
55662
|
}
|
|
55456
|
-
function
|
|
55663
|
+
function getSupplementalAuthPaths(home = getHomeDir5()) {
|
|
55457
55664
|
return [
|
|
55458
|
-
|
|
55459
|
-
|
|
55665
|
+
...getLegacyAuthPaths(home),
|
|
55666
|
+
getCodexCliAuthPath(home)
|
|
55460
55667
|
];
|
|
55461
55668
|
}
|
|
55462
|
-
function getLegacyModelsPaths(home =
|
|
55669
|
+
function getLegacyModelsPaths(home = getHomeDir5()) {
|
|
55463
55670
|
return [
|
|
55464
|
-
|
|
55465
|
-
|
|
55671
|
+
join24(home, ".pi", "agent", "models.json"),
|
|
55672
|
+
join24(home, ".pi", "models.json")
|
|
55466
55673
|
];
|
|
55467
55674
|
}
|
|
55468
|
-
function getModelRegistryModelsPath(home =
|
|
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
|
}
|
|
@@ -58774,6 +59008,7 @@ __export(agent_session_helpers_exports, {
|
|
|
58774
59008
|
createResolvedAgentSession: () => createResolvedAgentSession,
|
|
58775
59009
|
describeAgentModel: () => describeAgentModel,
|
|
58776
59010
|
extractRuntimeHint: () => extractRuntimeHint,
|
|
59011
|
+
extractRuntimeModel: () => extractRuntimeModel,
|
|
58777
59012
|
promptWithAutoRetry: () => promptWithAutoRetry
|
|
58778
59013
|
});
|
|
58779
59014
|
function extractRuntimeHint(runtimeConfig) {
|
|
@@ -58784,6 +59019,24 @@ function extractRuntimeHint(runtimeConfig) {
|
|
|
58784
59019
|
const normalizedHint = hint.trim();
|
|
58785
59020
|
return normalizedHint.length > 0 ? normalizedHint : void 0;
|
|
58786
59021
|
}
|
|
59022
|
+
function extractRuntimeModel(runtimeConfig) {
|
|
59023
|
+
const combined = typeof runtimeConfig?.model === "string" ? runtimeConfig.model.trim() : "";
|
|
59024
|
+
if (combined) {
|
|
59025
|
+
const slashIdx = combined.indexOf("/");
|
|
59026
|
+
if (slashIdx > 0 && slashIdx < combined.length - 1) {
|
|
59027
|
+
return {
|
|
59028
|
+
provider: combined.slice(0, slashIdx).trim() || void 0,
|
|
59029
|
+
modelId: combined.slice(slashIdx + 1).trim() || void 0
|
|
59030
|
+
};
|
|
59031
|
+
}
|
|
59032
|
+
}
|
|
59033
|
+
const provider = typeof runtimeConfig?.modelProvider === "string" ? runtimeConfig.modelProvider.trim() : "";
|
|
59034
|
+
const modelId = typeof runtimeConfig?.modelId === "string" ? runtimeConfig.modelId.trim() : "";
|
|
59035
|
+
return {
|
|
59036
|
+
provider: provider || void 0,
|
|
59037
|
+
modelId: modelId || void 0
|
|
59038
|
+
};
|
|
59039
|
+
}
|
|
58787
59040
|
async function createResolvedAgentSession(options) {
|
|
58788
59041
|
const { sessionPurpose, pluginRunner, runtimeHint, ...runtimeOptions } = options;
|
|
58789
59042
|
const context = buildRuntimeResolutionContext(sessionPurpose, pluginRunner, runtimeHint);
|
|
@@ -60037,6 +60290,38 @@ var init_notifier = __esm({
|
|
|
60037
60290
|
}
|
|
60038
60291
|
});
|
|
60039
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
|
+
|
|
60040
60325
|
// ../engine/src/reviewer.ts
|
|
60041
60326
|
async function reviewStep(cwd, taskId, stepNumber, stepName, reviewType, promptContent, baseline, options = {}) {
|
|
60042
60327
|
let liveSettings = options.settings;
|
|
@@ -60167,7 +60452,13 @@ async function reviewStep(cwd, taskId, stepNumber, stepName, reviewType, promptC
|
|
|
60167
60452
|
...skillContext?.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
60168
60453
|
taskId: options.taskId,
|
|
60169
60454
|
taskTitle: options.taskTitle,
|
|
60170
|
-
onFallbackModelUsed:
|
|
60455
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
60456
|
+
agent: "reviewer",
|
|
60457
|
+
label: "reviewer",
|
|
60458
|
+
store: options.store,
|
|
60459
|
+
taskId: options.taskId,
|
|
60460
|
+
taskTitle: options.taskTitle
|
|
60461
|
+
}),
|
|
60171
60462
|
beforeSpawnSession: async () => {
|
|
60172
60463
|
if (!options.store) return;
|
|
60173
60464
|
let finalSettings;
|
|
@@ -60354,7 +60645,7 @@ var init_reviewer = __esm({
|
|
|
60354
60645
|
init_logger2();
|
|
60355
60646
|
init_usage_limit_detector();
|
|
60356
60647
|
init_agent_instructions();
|
|
60357
|
-
|
|
60648
|
+
init_fallback_model_observer();
|
|
60358
60649
|
init_agent_tools();
|
|
60359
60650
|
REVIEWER_SYSTEM_PROMPT = `You are an independent code and plan reviewer.
|
|
60360
60651
|
|
|
@@ -60715,7 +61006,7 @@ var init_recovery_policy = __esm({
|
|
|
60715
61006
|
// ../engine/src/triage.ts
|
|
60716
61007
|
import { Type as Type2 } from "@mariozechner/pi-ai";
|
|
60717
61008
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
60718
|
-
import { join as
|
|
61009
|
+
import { join as join29 } from "node:path";
|
|
60719
61010
|
function extractPromptDeclaredTitle(prompt, taskId) {
|
|
60720
61011
|
const headingMatch = prompt.match(/^#\s+Task:\s+([A-Z]+-\d+)\s+-\s+(.+)$/m);
|
|
60721
61012
|
if (!headingMatch) return null;
|
|
@@ -60744,9 +61035,9 @@ async function readAttachmentContents(rootDir, taskId, attachments) {
|
|
|
60744
61035
|
return { attachmentContents, imageContents };
|
|
60745
61036
|
}
|
|
60746
61037
|
const { readFile: readFile26 } = await import("node:fs/promises");
|
|
60747
|
-
const { join:
|
|
61038
|
+
const { join: join72 } = await import("node:path");
|
|
60748
61039
|
for (const att of attachments) {
|
|
60749
|
-
const filePath =
|
|
61040
|
+
const filePath = join72(
|
|
60750
61041
|
rootDir,
|
|
60751
61042
|
".fusion",
|
|
60752
61043
|
"tasks",
|
|
@@ -60971,7 +61262,7 @@ var init_triage = __esm({
|
|
|
60971
61262
|
init_concurrency();
|
|
60972
61263
|
init_agent_logger();
|
|
60973
61264
|
init_agent_instructions();
|
|
60974
|
-
|
|
61265
|
+
init_fallback_model_observer();
|
|
60975
61266
|
init_logger2();
|
|
60976
61267
|
init_usage_limit_detector();
|
|
60977
61268
|
init_transient_error_detector();
|
|
@@ -61581,7 +61872,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
61581
61872
|
return false;
|
|
61582
61873
|
}
|
|
61583
61874
|
const settings = await this.store.getSettings();
|
|
61584
|
-
const promptPath =
|
|
61875
|
+
const promptPath = join29(this.rootDir, ".fusion", "tasks", task.id, "PROMPT.md");
|
|
61585
61876
|
const written = await readFile14(promptPath, "utf-8").catch((err) => {
|
|
61586
61877
|
const msg = err instanceof Error ? err.message : String(err);
|
|
61587
61878
|
planLog.warn(`${task.id}: failed to read PROMPT.md during approved-spec recovery (${promptPath}): ${msg}`);
|
|
@@ -61811,7 +62102,13 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
61811
62102
|
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
61812
62103
|
taskId: task.id,
|
|
61813
62104
|
taskTitle: task.title,
|
|
61814
|
-
onFallbackModelUsed:
|
|
62105
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
62106
|
+
agent: "triage",
|
|
62107
|
+
label: "triage",
|
|
62108
|
+
store: this.store,
|
|
62109
|
+
taskId: task.id,
|
|
62110
|
+
taskTitle: task.title
|
|
62111
|
+
})
|
|
61815
62112
|
});
|
|
61816
62113
|
const modelDesc = describeModel(session);
|
|
61817
62114
|
planLog.log(`${task.id}: using model ${modelDesc}`);
|
|
@@ -61954,7 +62251,13 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
61954
62251
|
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
61955
62252
|
taskId: task.id,
|
|
61956
62253
|
taskTitle: task.title,
|
|
61957
|
-
onFallbackModelUsed:
|
|
62254
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
62255
|
+
agent: "triage",
|
|
62256
|
+
label: "triage",
|
|
62257
|
+
store: this.store,
|
|
62258
|
+
taskId: task.id,
|
|
62259
|
+
taskTitle: task.title
|
|
62260
|
+
})
|
|
61958
62261
|
});
|
|
61959
62262
|
session = fallbackResult.session;
|
|
61960
62263
|
const fallbackModelDesc = describeModel(session);
|
|
@@ -62039,7 +62342,7 @@ Write the PROMPT.md directly using the write tool, then call \`fn_review_spec()\
|
|
|
62039
62342
|
return;
|
|
62040
62343
|
}
|
|
62041
62344
|
const written = await readFile14(
|
|
62042
|
-
|
|
62345
|
+
join29(this.rootDir, promptPath),
|
|
62043
62346
|
"utf-8"
|
|
62044
62347
|
).catch((err) => {
|
|
62045
62348
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -62358,9 +62661,9 @@ Remove or replace these ids and call fn_task_create again.`
|
|
|
62358
62661
|
}
|
|
62359
62662
|
try {
|
|
62360
62663
|
const { readFile: readFile26 } = await import("node:fs/promises");
|
|
62361
|
-
const { join:
|
|
62664
|
+
const { join: join72 } = await import("node:path");
|
|
62362
62665
|
const promptContent = await readFile26(
|
|
62363
|
-
|
|
62666
|
+
join72(rootDir, promptPath),
|
|
62364
62667
|
"utf-8"
|
|
62365
62668
|
).catch((err) => {
|
|
62366
62669
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -62588,160 +62891,8 @@ Take a completely different approach to writing this specification. Do NOT repea
|
|
|
62588
62891
|
}
|
|
62589
62892
|
});
|
|
62590
62893
|
|
|
62591
|
-
// ../engine/src/
|
|
62592
|
-
|
|
62593
|
-
__export(session_token_usage_exports, {
|
|
62594
|
-
accumulateSessionTokenUsage: () => accumulateSessionTokenUsage
|
|
62595
|
-
});
|
|
62596
|
-
function readSessionStats(session) {
|
|
62597
|
-
const accessor = session.getSessionStats;
|
|
62598
|
-
if (typeof accessor !== "function") return void 0;
|
|
62599
|
-
try {
|
|
62600
|
-
return accessor.call(session);
|
|
62601
|
-
} catch {
|
|
62602
|
-
return void 0;
|
|
62603
|
-
}
|
|
62604
|
-
}
|
|
62605
|
-
async function accumulateSessionTokenUsage(store, taskId, session) {
|
|
62606
|
-
try {
|
|
62607
|
-
const stats = readSessionStats(session);
|
|
62608
|
-
const tokens = stats?.tokens;
|
|
62609
|
-
if (!tokens) return;
|
|
62610
|
-
const currentInput = (tokens.input ?? 0) + (tokens.cacheWrite ?? 0);
|
|
62611
|
-
const currentOutput = tokens.output ?? 0;
|
|
62612
|
-
const currentCached = tokens.cacheRead ?? 0;
|
|
62613
|
-
const baseline = sessionBaselines.get(session) ?? { input: 0, output: 0, cached: 0 };
|
|
62614
|
-
const inputDelta = Math.max(0, currentInput - baseline.input);
|
|
62615
|
-
const outputDelta = Math.max(0, currentOutput - baseline.output);
|
|
62616
|
-
const cachedDelta = Math.max(0, currentCached - baseline.cached);
|
|
62617
|
-
sessionBaselines.set(session, {
|
|
62618
|
-
input: currentInput,
|
|
62619
|
-
output: currentOutput,
|
|
62620
|
-
cached: currentCached
|
|
62621
|
-
});
|
|
62622
|
-
if (inputDelta === 0 && outputDelta === 0 && cachedDelta === 0) return;
|
|
62623
|
-
const task = await store.getTask(taskId);
|
|
62624
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
62625
|
-
const newInput = (task.tokenUsage?.inputTokens ?? 0) + inputDelta;
|
|
62626
|
-
const newOutput = (task.tokenUsage?.outputTokens ?? 0) + outputDelta;
|
|
62627
|
-
const newCached = (task.tokenUsage?.cachedTokens ?? 0) + cachedDelta;
|
|
62628
|
-
await store.updateTask(taskId, {
|
|
62629
|
-
tokenUsage: {
|
|
62630
|
-
inputTokens: newInput,
|
|
62631
|
-
outputTokens: newOutput,
|
|
62632
|
-
cachedTokens: newCached,
|
|
62633
|
-
totalTokens: newInput + newOutput + newCached,
|
|
62634
|
-
firstUsedAt: task.tokenUsage?.firstUsedAt ?? now,
|
|
62635
|
-
lastUsedAt: now
|
|
62636
|
-
}
|
|
62637
|
-
});
|
|
62638
|
-
} catch (err) {
|
|
62639
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
62640
|
-
log14.warn(`${taskId}: session token usage accumulate failed: ${message}`);
|
|
62641
|
-
}
|
|
62642
|
-
}
|
|
62643
|
-
var log14, sessionBaselines;
|
|
62644
|
-
var init_session_token_usage = __esm({
|
|
62645
|
-
"../engine/src/session-token-usage.ts"() {
|
|
62646
|
-
"use strict";
|
|
62647
|
-
init_logger2();
|
|
62648
|
-
log14 = createLogger2("session-token-usage");
|
|
62649
|
-
sessionBaselines = /* @__PURE__ */ new WeakMap();
|
|
62650
|
-
}
|
|
62651
|
-
});
|
|
62652
|
-
|
|
62653
|
-
// ../engine/src/run-audit.ts
|
|
62654
|
-
function createRunAuditor(store, context) {
|
|
62655
|
-
if (!context) {
|
|
62656
|
-
return {
|
|
62657
|
-
git: async () => {
|
|
62658
|
-
},
|
|
62659
|
-
database: async () => {
|
|
62660
|
-
},
|
|
62661
|
-
filesystem: async () => {
|
|
62662
|
-
}
|
|
62663
|
-
};
|
|
62664
|
-
}
|
|
62665
|
-
const hasRecordAuditEvent = typeof store.recordRunAuditEvent === "function";
|
|
62666
|
-
if (!hasRecordAuditEvent) {
|
|
62667
|
-
return {
|
|
62668
|
-
git: async () => {
|
|
62669
|
-
},
|
|
62670
|
-
database: async () => {
|
|
62671
|
-
},
|
|
62672
|
-
filesystem: async () => {
|
|
62673
|
-
}
|
|
62674
|
-
};
|
|
62675
|
-
}
|
|
62676
|
-
return {
|
|
62677
|
-
git: async (input) => {
|
|
62678
|
-
const eventInput = {
|
|
62679
|
-
taskId: context.taskId,
|
|
62680
|
-
agentId: context.agentId,
|
|
62681
|
-
runId: context.runId,
|
|
62682
|
-
domain: "git",
|
|
62683
|
-
mutationType: input.type,
|
|
62684
|
-
target: input.target,
|
|
62685
|
-
metadata: {
|
|
62686
|
-
phase: context.phase,
|
|
62687
|
-
...context.source ? { source: context.source } : {},
|
|
62688
|
-
...input.metadata
|
|
62689
|
-
}
|
|
62690
|
-
};
|
|
62691
|
-
await store.recordRunAuditEvent(eventInput);
|
|
62692
|
-
},
|
|
62693
|
-
database: async (input) => {
|
|
62694
|
-
const inferredTaskId = input.target.startsWith("FN-") || input.target.startsWith("KB-") ? input.target : context.taskId;
|
|
62695
|
-
const eventInput = {
|
|
62696
|
-
taskId: inferredTaskId,
|
|
62697
|
-
agentId: context.agentId,
|
|
62698
|
-
runId: context.runId,
|
|
62699
|
-
domain: "database",
|
|
62700
|
-
mutationType: input.type,
|
|
62701
|
-
target: input.target,
|
|
62702
|
-
metadata: {
|
|
62703
|
-
phase: context.phase,
|
|
62704
|
-
...context.source ? { source: context.source } : {},
|
|
62705
|
-
...input.metadata
|
|
62706
|
-
}
|
|
62707
|
-
};
|
|
62708
|
-
await store.recordRunAuditEvent(eventInput);
|
|
62709
|
-
},
|
|
62710
|
-
filesystem: async (input) => {
|
|
62711
|
-
const eventInput = {
|
|
62712
|
-
taskId: context.taskId,
|
|
62713
|
-
agentId: context.agentId,
|
|
62714
|
-
runId: context.runId,
|
|
62715
|
-
domain: "filesystem",
|
|
62716
|
-
mutationType: input.type,
|
|
62717
|
-
target: input.target,
|
|
62718
|
-
metadata: {
|
|
62719
|
-
phase: context.phase,
|
|
62720
|
-
...context.source ? { source: context.source } : {},
|
|
62721
|
-
...input.metadata
|
|
62722
|
-
}
|
|
62723
|
-
};
|
|
62724
|
-
await store.recordRunAuditEvent(eventInput);
|
|
62725
|
-
}
|
|
62726
|
-
};
|
|
62727
|
-
}
|
|
62728
|
-
function generateSyntheticRunId(prefix, taskId) {
|
|
62729
|
-
const timestamp = Date.now();
|
|
62730
|
-
const random = Math.random().toString(36).slice(2, 6);
|
|
62731
|
-
return `${prefix}-${taskId}-${timestamp}-${random}`;
|
|
62732
|
-
}
|
|
62733
|
-
var init_run_audit = __esm({
|
|
62734
|
-
"../engine/src/run-audit.ts"() {
|
|
62735
|
-
"use strict";
|
|
62736
|
-
}
|
|
62737
|
-
});
|
|
62738
|
-
|
|
62739
|
-
// ../engine/src/merger.ts
|
|
62740
|
-
import { execSync, exec as exec3, spawn as spawn3 } from "node:child_process";
|
|
62741
|
-
import { promisify as promisify4 } from "node:util";
|
|
62742
|
-
import { existsSync as existsSync23 } from "node:fs";
|
|
62743
|
-
import { join as join29 } from "node:path";
|
|
62744
|
-
import { Type as Type3 } from "typebox";
|
|
62894
|
+
// ../engine/src/verification-utils.ts
|
|
62895
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
62745
62896
|
async function execWithProcessGroup(command, options) {
|
|
62746
62897
|
return new Promise((resolve44, reject2) => {
|
|
62747
62898
|
if (options.signal?.aborted) {
|
|
@@ -62853,6 +63004,11 @@ function truncateWithEllipsis(text, maxChars) {
|
|
|
62853
63004
|
return `${text.slice(0, maxChars)}
|
|
62854
63005
|
... (truncated)`;
|
|
62855
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
|
+
}
|
|
62856
63012
|
function summarizeVerificationOutput(output, type) {
|
|
62857
63013
|
const lines = output.split("\n");
|
|
62858
63014
|
let summaryLine = null;
|
|
@@ -62918,6 +63074,13 @@ function summarizeVerificationOutput(output, type) {
|
|
|
62918
63074
|
failureNames.add(truncated);
|
|
62919
63075
|
}
|
|
62920
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
|
+
}
|
|
62921
63084
|
const parts = [];
|
|
62922
63085
|
if (summaryLine) {
|
|
62923
63086
|
parts.push(summaryLine);
|
|
@@ -62935,29 +63098,292 @@ function summarizeVerificationOutput(output, type) {
|
|
|
62935
63098
|
parts.push(` \u2022 ... and ${names.length - 5} more failures`);
|
|
62936
63099
|
}
|
|
62937
63100
|
}
|
|
62938
|
-
if (parts.length
|
|
62939
|
-
|
|
62940
|
-
|
|
62941
|
-
|
|
62942
|
-
|
|
62943
|
-
|
|
62944
|
-
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)}
|
|
62945
63107
|
${footer}`;
|
|
62946
63108
|
}
|
|
62947
|
-
|
|
62948
|
-
return `${trimmed}
|
|
63109
|
+
return parts.join("\n") + `
|
|
62949
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
|
+
);
|
|
62950
63120
|
}
|
|
62951
|
-
|
|
62952
|
-
|
|
62953
|
-
|
|
62954
|
-
|
|
62955
|
-
|
|
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;
|
|
62956
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
|
+
);
|
|
62957
63220
|
}
|
|
62958
|
-
return
|
|
62959
|
-
${footer}`;
|
|
63221
|
+
return result;
|
|
62960
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";
|
|
62961
63387
|
function truncateWorkflowScriptOutput(output) {
|
|
62962
63388
|
if (output.length <= WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS) return output;
|
|
62963
63389
|
return `... output truncated to last ${WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS} characters ...
|
|
@@ -63001,7 +63427,7 @@ async function getStagedFiles(cwd) {
|
|
|
63001
63427
|
}
|
|
63002
63428
|
}
|
|
63003
63429
|
function hasInstallState(rootDir) {
|
|
63004
|
-
return
|
|
63430
|
+
return existsSync24(join30(rootDir, "node_modules")) || existsSync24(join30(rootDir, ".pnp.cjs"));
|
|
63005
63431
|
}
|
|
63006
63432
|
function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
63007
63433
|
if (!installStatePresent) return true;
|
|
@@ -63010,10 +63436,10 @@ function shouldSyncDependenciesForMerge(stagedFiles, installStatePresent) {
|
|
|
63010
63436
|
);
|
|
63011
63437
|
}
|
|
63012
63438
|
function getDependencySyncCommand(rootDir) {
|
|
63013
|
-
if (
|
|
63014
|
-
if (
|
|
63015
|
-
if (
|
|
63016
|
-
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"))) {
|
|
63017
63443
|
return "bun install --frozen-lockfile";
|
|
63018
63444
|
}
|
|
63019
63445
|
return null;
|
|
@@ -63046,8 +63472,8 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
63046
63472
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63047
63473
|
};
|
|
63048
63474
|
}
|
|
63049
|
-
if (
|
|
63050
|
-
if (
|
|
63475
|
+
if (existsSync24(join30(rootDir, "pnpm-lock.yaml"))) {
|
|
63476
|
+
if (existsSync24(join30(rootDir, "pnpm-workspace.yaml"))) {
|
|
63051
63477
|
mergerLog.warn(
|
|
63052
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\`.`
|
|
63053
63479
|
);
|
|
@@ -63058,21 +63484,21 @@ function inferDefaultTestCommand(rootDir, explicitTestCommand, explicitBuildComm
|
|
|
63058
63484
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63059
63485
|
};
|
|
63060
63486
|
}
|
|
63061
|
-
if (
|
|
63487
|
+
if (existsSync24(join30(rootDir, "yarn.lock"))) {
|
|
63062
63488
|
return {
|
|
63063
63489
|
command: "yarn test",
|
|
63064
63490
|
testSource: "inferred",
|
|
63065
63491
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63066
63492
|
};
|
|
63067
63493
|
}
|
|
63068
|
-
if (
|
|
63494
|
+
if (existsSync24(join30(rootDir, "bun.lock")) || existsSync24(join30(rootDir, "bun.lockb"))) {
|
|
63069
63495
|
return {
|
|
63070
63496
|
command: "bun test",
|
|
63071
63497
|
testSource: "inferred",
|
|
63072
63498
|
buildSource: explicitBuildCommand?.trim() ? "explicit" : void 0
|
|
63073
63499
|
};
|
|
63074
63500
|
}
|
|
63075
|
-
if (
|
|
63501
|
+
if (existsSync24(join30(rootDir, "package-lock.json"))) {
|
|
63076
63502
|
return {
|
|
63077
63503
|
command: "npm test",
|
|
63078
63504
|
testSource: "inferred",
|
|
@@ -63109,7 +63535,7 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
63109
63535
|
await store.logEntry(taskId, deterministicVerificationMessage);
|
|
63110
63536
|
await store.appendAgentLog(taskId, deterministicVerificationMessage, "text", void 0, "merger");
|
|
63111
63537
|
if (hasTestCommand) {
|
|
63112
|
-
const testResult = await
|
|
63538
|
+
const testResult = await runVerificationCommand2(
|
|
63113
63539
|
store,
|
|
63114
63540
|
rootDir,
|
|
63115
63541
|
taskId,
|
|
@@ -63140,7 +63566,7 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
63140
63566
|
}
|
|
63141
63567
|
}
|
|
63142
63568
|
if (hasBuildCommand) {
|
|
63143
|
-
const buildResult = await
|
|
63569
|
+
const buildResult = await runVerificationCommand2(
|
|
63144
63570
|
store,
|
|
63145
63571
|
rootDir,
|
|
63146
63572
|
taskId,
|
|
@@ -63175,98 +63601,9 @@ async function runDeterministicVerification(store, rootDir, taskId, testCommand,
|
|
|
63175
63601
|
await store.appendAgentLog(taskId, "Deterministic merge verification passed", "text", void 0, "merger");
|
|
63176
63602
|
return result;
|
|
63177
63603
|
}
|
|
63178
|
-
async function
|
|
63604
|
+
async function runVerificationCommand2(store, rootDir, taskId, command, type, signal) {
|
|
63179
63605
|
throwIfAborted(signal, taskId);
|
|
63180
|
-
|
|
63181
|
-
await store.logEntry(taskId, `[verification] Running ${type} command: ${command}`);
|
|
63182
|
-
await store.appendAgentLog(taskId, `Running ${type} command`, "tool", command, "merger");
|
|
63183
|
-
const result = {
|
|
63184
|
-
command,
|
|
63185
|
-
exitCode: null,
|
|
63186
|
-
stdout: "",
|
|
63187
|
-
stderr: "",
|
|
63188
|
-
success: false
|
|
63189
|
-
};
|
|
63190
|
-
const verificationStartedAt = Date.now();
|
|
63191
|
-
try {
|
|
63192
|
-
const { stdout, stderr, bufferOverflow } = await execWithProcessGroup(command, {
|
|
63193
|
-
cwd: rootDir,
|
|
63194
|
-
timeout: VERIFICATION_COMMAND_TIMEOUT_MS,
|
|
63195
|
-
maxBuffer: VERIFICATION_COMMAND_MAX_BUFFER,
|
|
63196
|
-
signal
|
|
63197
|
-
});
|
|
63198
|
-
throwIfAborted(signal, taskId);
|
|
63199
|
-
result.stdout = stdout?.toString?.() || "";
|
|
63200
|
-
result.stderr = stderr?.toString?.() || "";
|
|
63201
|
-
result.exitCode = 0;
|
|
63202
|
-
result.success = true;
|
|
63203
|
-
const verificationDurationMs = Date.now() - verificationStartedAt;
|
|
63204
|
-
const timingDetail = `${verificationDurationMs}ms`;
|
|
63205
|
-
if (bufferOverflow) {
|
|
63206
|
-
mergerLog.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`);
|
|
63207
|
-
await store.logEntry(
|
|
63208
|
-
taskId,
|
|
63209
|
-
`[timing] [verification] ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`
|
|
63210
|
-
);
|
|
63211
|
-
await store.appendAgentLog(
|
|
63212
|
-
taskId,
|
|
63213
|
-
`${type} command succeeded (exit 0)`,
|
|
63214
|
-
"tool_result",
|
|
63215
|
-
timingDetail,
|
|
63216
|
-
"merger"
|
|
63217
|
-
);
|
|
63218
|
-
} else {
|
|
63219
|
-
mergerLog.log(`${taskId}: ${type} command succeeded in ${verificationDurationMs}ms`);
|
|
63220
|
-
await store.logEntry(taskId, `[timing] [verification] ${type} command succeeded (exit 0) in ${verificationDurationMs}ms`);
|
|
63221
|
-
await store.appendAgentLog(
|
|
63222
|
-
taskId,
|
|
63223
|
-
`${type} command succeeded (exit 0)`,
|
|
63224
|
-
"tool_result",
|
|
63225
|
-
timingDetail,
|
|
63226
|
-
"merger"
|
|
63227
|
-
);
|
|
63228
|
-
}
|
|
63229
|
-
return result;
|
|
63230
|
-
} catch (error) {
|
|
63231
|
-
throwIfAborted(signal, taskId);
|
|
63232
|
-
const verificationDurationMs = Date.now() - verificationStartedAt;
|
|
63233
|
-
result.stdout = error?.stdout?.toString?.() || "";
|
|
63234
|
-
result.stderr = error?.stderr?.toString?.() || "";
|
|
63235
|
-
result.exitCode = typeof error?.status === "number" ? error.status : typeof error?.code === "number" ? error.code : null;
|
|
63236
|
-
const maxBufferExceeded = error?.code === "ENOBUFS" || error?.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER" || String(error?.message ?? "").includes("maxBuffer");
|
|
63237
|
-
result.success = maxBufferExceeded && result.exitCode === 0;
|
|
63238
|
-
if (result.success) {
|
|
63239
|
-
mergerLog.log(`${taskId}: ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`);
|
|
63240
|
-
await store.logEntry(
|
|
63241
|
-
taskId,
|
|
63242
|
-
`[timing] [verification] ${type} command succeeded (exit 0, output exceeded buffer) in ${verificationDurationMs}ms`
|
|
63243
|
-
);
|
|
63244
|
-
await store.appendAgentLog(
|
|
63245
|
-
taskId,
|
|
63246
|
-
`${type} command succeeded (exit 0)`,
|
|
63247
|
-
"tool_result",
|
|
63248
|
-
`${verificationDurationMs}ms`,
|
|
63249
|
-
"merger"
|
|
63250
|
-
);
|
|
63251
|
-
return result;
|
|
63252
|
-
}
|
|
63253
|
-
const output = result.stderr || result.stdout || error?.message || "Unknown error";
|
|
63254
|
-
const summary = summarizeVerificationOutput(output, type);
|
|
63255
|
-
mergerLog.error(`${taskId}: ${type} command failed (exit ${result.exitCode}) in ${verificationDurationMs}ms; output captured in task log`);
|
|
63256
|
-
await store.logEntry(
|
|
63257
|
-
taskId,
|
|
63258
|
-
`[timing] [verification] ${type} command failed (exit ${result.exitCode}) after ${verificationDurationMs}ms:
|
|
63259
|
-
${summary}`
|
|
63260
|
-
);
|
|
63261
|
-
await store.appendAgentLog(
|
|
63262
|
-
taskId,
|
|
63263
|
-
`${type} command failed (exit ${result.exitCode})`,
|
|
63264
|
-
"tool_error",
|
|
63265
|
-
summary,
|
|
63266
|
-
"merger"
|
|
63267
|
-
);
|
|
63268
|
-
}
|
|
63269
|
-
return result;
|
|
63606
|
+
return runVerificationCommand(store, rootDir, taskId, command, type, signal, mergerLog, "merger");
|
|
63270
63607
|
}
|
|
63271
63608
|
async function attemptInMergeVerificationFix(store, rootDir, taskId, failureContext, settings, options, mergeRunContext, fixAttemptNumber, _testCommand, _buildCommand) {
|
|
63272
63609
|
try {
|
|
@@ -63329,9 +63666,20 @@ Do not refactor, rename broadly, or make opportunistic improvements.
|
|
|
63329
63666
|
onToolEnd: logger2.onToolEnd,
|
|
63330
63667
|
defaultProvider: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultProviderOverride : settings.defaultProvider,
|
|
63331
63668
|
defaultModelId: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultModelIdOverride : settings.defaultModelId,
|
|
63669
|
+
fallbackProvider: settings.fallbackProvider,
|
|
63670
|
+
fallbackModelId: settings.fallbackModelId,
|
|
63332
63671
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
63333
63672
|
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
63334
|
-
...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
|
+
})
|
|
63335
63683
|
});
|
|
63336
63684
|
const runId = mergeRunContext?.runId;
|
|
63337
63685
|
const agentId = mergeRunContext?.agentId ?? "merger";
|
|
@@ -63384,7 +63732,7 @@ ${failureContext.output.slice(0, VERIFICATION_LOG_MAX_CHARS)}
|
|
|
63384
63732
|
void 0,
|
|
63385
63733
|
"merger"
|
|
63386
63734
|
);
|
|
63387
|
-
const reRunResult = await
|
|
63735
|
+
const reRunResult = await runVerificationCommand2(
|
|
63388
63736
|
store,
|
|
63389
63737
|
rootDir,
|
|
63390
63738
|
taskId,
|
|
@@ -64126,9 +64474,16 @@ You are assisting with a paused \`git pull --rebase\`.
|
|
|
64126
64474
|
onToolEnd: agentLogger.onToolEnd,
|
|
64127
64475
|
defaultProvider: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultProviderOverride : settings.defaultProvider,
|
|
64128
64476
|
defaultModelId: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultModelIdOverride : settings.defaultModelId,
|
|
64477
|
+
fallbackProvider: settings.fallbackProvider,
|
|
64478
|
+
fallbackModelId: settings.fallbackModelId,
|
|
64129
64479
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
64130
64480
|
taskId,
|
|
64131
|
-
onFallbackModelUsed:
|
|
64481
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
64482
|
+
agent: "merger",
|
|
64483
|
+
label: "rebase conflict resolver",
|
|
64484
|
+
store,
|
|
64485
|
+
taskId
|
|
64486
|
+
})
|
|
64132
64487
|
});
|
|
64133
64488
|
const prompt = [
|
|
64134
64489
|
`Resolve rebase conflicts for task ${taskId}.`,
|
|
@@ -64320,7 +64675,7 @@ async function pushToRemoteAfterMerge(store, rootDir, taskId, settings, options)
|
|
|
64320
64675
|
}
|
|
64321
64676
|
async function createPostMergeWorktree(rootDir, taskId) {
|
|
64322
64677
|
const randomSuffix = Math.random().toString(36).slice(2, 10);
|
|
64323
|
-
const postMergeWorktree =
|
|
64678
|
+
const postMergeWorktree = join30(rootDir, ".worktrees", `post-merge-${taskId}-${randomSuffix}`);
|
|
64324
64679
|
try {
|
|
64325
64680
|
await execAsync2(`git worktree add ${quoteArg(postMergeWorktree)} HEAD`, { cwd: rootDir });
|
|
64326
64681
|
return postMergeWorktree;
|
|
@@ -65261,7 +65616,7 @@ async function aiMergeTask(store, rootDir, taskId, options = {}) {
|
|
|
65261
65616
|
}
|
|
65262
65617
|
}
|
|
65263
65618
|
throwIfAborted(options.signal, taskId);
|
|
65264
|
-
if (worktreePath &&
|
|
65619
|
+
if (worktreePath && existsSync24(worktreePath)) {
|
|
65265
65620
|
const otherUser = await findWorktreeUser(store, worktreePath, taskId);
|
|
65266
65621
|
if (otherUser) {
|
|
65267
65622
|
mergerLog.log(`Worktree retained \u2014 still needed by ${otherUser}`);
|
|
@@ -65915,9 +66270,20 @@ async function runAiAgentForCommit(params) {
|
|
|
65915
66270
|
onToolEnd: agentLogger.onToolEnd,
|
|
65916
66271
|
defaultProvider: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultProviderOverride : settings.defaultProvider,
|
|
65917
66272
|
defaultModelId: settings.defaultProviderOverride && settings.defaultModelIdOverride ? settings.defaultModelIdOverride : settings.defaultModelId,
|
|
66273
|
+
fallbackProvider: settings.fallbackProvider,
|
|
66274
|
+
fallbackModelId: settings.fallbackModelId,
|
|
65918
66275
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
65919
66276
|
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
65920
|
-
...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
|
+
})
|
|
65921
66287
|
});
|
|
65922
66288
|
options.onSession?.(session);
|
|
65923
66289
|
try {
|
|
@@ -66348,7 +66714,14 @@ If issues are found that need attention, describe them clearly and include concr
|
|
|
66348
66714
|
fallbackModelId: settings.fallbackModelId,
|
|
66349
66715
|
defaultThinkingLevel: settings.defaultThinkingLevel,
|
|
66350
66716
|
// Skill selection: use assigned agent skills if available, otherwise role fallback
|
|
66351
|
-
...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
|
+
})
|
|
66352
66725
|
});
|
|
66353
66726
|
mergerLog.log(`${taskId}: [post-merge] workflow step '${workflowStep.name}' using model ${describeModel(session)}${useOverride ? " (workflow step override)" : ""}`);
|
|
66354
66727
|
await store.logEntry(taskId, `[post-merge] Workflow step '${workflowStep.name}' using model: ${describeModel(session)}${useOverride ? " (workflow step override)" : ""}`);
|
|
@@ -66384,15 +66757,17 @@ async function completeTask(store, taskId, result) {
|
|
|
66384
66757
|
result.task = task;
|
|
66385
66758
|
store.emit("task:merged", result);
|
|
66386
66759
|
}
|
|
66387
|
-
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;
|
|
66388
66761
|
var init_merger = __esm({
|
|
66389
66762
|
"../engine/src/merger.ts"() {
|
|
66390
66763
|
"use strict";
|
|
66764
|
+
init_verification_utils();
|
|
66765
|
+
init_verification_utils();
|
|
66391
66766
|
init_src();
|
|
66392
66767
|
init_pi();
|
|
66393
66768
|
init_session_token_usage();
|
|
66394
66769
|
init_agent_session_helpers();
|
|
66395
|
-
|
|
66770
|
+
init_fallback_model_observer();
|
|
66396
66771
|
init_session_skill_context();
|
|
66397
66772
|
init_agent_logger();
|
|
66398
66773
|
init_logger2();
|
|
@@ -66438,9 +66813,6 @@ var init_merger = __esm({
|
|
|
66438
66813
|
"bun.lock",
|
|
66439
66814
|
"packages/*/package.json"
|
|
66440
66815
|
];
|
|
66441
|
-
VERIFICATION_COMMAND_MAX_BUFFER = 50 * 1024 * 1024;
|
|
66442
|
-
VERIFICATION_COMMAND_TIMEOUT_MS = 6e5;
|
|
66443
|
-
VERIFICATION_LOG_MAX_CHARS = 2e4;
|
|
66444
66816
|
WORKFLOW_SCRIPT_OUTPUT_MAX_CHARS = 4e3;
|
|
66445
66817
|
PULL_REBASE_TIMEOUT_MS = 12e4;
|
|
66446
66818
|
PUSH_TIMEOUT_MS = 6e4;
|
|
@@ -66465,8 +66837,8 @@ var init_merger = __esm({
|
|
|
66465
66837
|
|
|
66466
66838
|
// ../engine/src/worktree-names.ts
|
|
66467
66839
|
import { readdirSync as readdirSync3 } from "node:fs";
|
|
66468
|
-
import { join as
|
|
66469
|
-
import { existsSync as
|
|
66840
|
+
import { join as join31 } from "node:path";
|
|
66841
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
66470
66842
|
function slugify2(str) {
|
|
66471
66843
|
return str.toLowerCase().replace(/[^a-z0-9\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
66472
66844
|
}
|
|
@@ -66477,7 +66849,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
|
|
|
66477
66849
|
const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
|
|
66478
66850
|
const noun = NOUNS[Math.floor(Math.random() * NOUNS.length)];
|
|
66479
66851
|
const baseName = `${adjective}-${noun}`;
|
|
66480
|
-
const worktreesDir =
|
|
66852
|
+
const worktreesDir = join31(rootDir, ".worktrees");
|
|
66481
66853
|
const existing = getExistingWorktreeNames(worktreesDir);
|
|
66482
66854
|
for (const reserved of reservedNames) {
|
|
66483
66855
|
existing.add(reserved);
|
|
@@ -66492,7 +66864,7 @@ function generateReservedWorktreeName(rootDir, reservedNames = /* @__PURE__ */ n
|
|
|
66492
66864
|
return `${baseName}-${suffix}`;
|
|
66493
66865
|
}
|
|
66494
66866
|
function getExistingWorktreeNames(worktreesDir) {
|
|
66495
|
-
if (!
|
|
66867
|
+
if (!existsSync25(worktreesDir)) {
|
|
66496
66868
|
return /* @__PURE__ */ new Set();
|
|
66497
66869
|
}
|
|
66498
66870
|
try {
|
|
@@ -66629,8 +67001,8 @@ __export(worktree_pool_exports, {
|
|
|
66629
67001
|
});
|
|
66630
67002
|
import { exec as exec4 } from "node:child_process";
|
|
66631
67003
|
import { promisify as promisify5 } from "node:util";
|
|
66632
|
-
import { existsSync as
|
|
66633
|
-
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";
|
|
66634
67006
|
function getExecStdout(result) {
|
|
66635
67007
|
if (typeof result === "string") return result;
|
|
66636
67008
|
if (result && typeof result === "object" && "stdout" in result) {
|
|
@@ -66676,10 +67048,10 @@ async function isRegisteredGitWorktree2(rootDir, worktreePath) {
|
|
|
66676
67048
|
return (await getRegisteredWorktreePaths(rootDir)).has(resolve15(worktreePath));
|
|
66677
67049
|
}
|
|
66678
67050
|
function hasRequiredWorktreeFiles(worktreePath) {
|
|
66679
|
-
return
|
|
67051
|
+
return existsSync26(join32(worktreePath, ".git")) && existsSync26(join32(worktreePath, "package.json"));
|
|
66680
67052
|
}
|
|
66681
67053
|
async function isUsableTaskWorktree(rootDir, worktreePath) {
|
|
66682
|
-
return
|
|
67054
|
+
return existsSync26(worktreePath) && await isRegisteredGitWorktree2(rootDir, worktreePath) && hasRequiredWorktreeFiles(worktreePath);
|
|
66683
67055
|
}
|
|
66684
67056
|
function isInsideWorktreesDir(rootDir, worktreePath) {
|
|
66685
67057
|
const worktreesDir = resolve15(rootDir, ".worktrees");
|
|
@@ -66688,14 +67060,14 @@ function isInsideWorktreesDir(rootDir, worktreePath) {
|
|
|
66688
67060
|
return rel !== "" && !rel.startsWith("..") && !isAbsolute9(rel);
|
|
66689
67061
|
}
|
|
66690
67062
|
async function scanIdleWorktrees(rootDir, store) {
|
|
66691
|
-
const worktreesDir =
|
|
66692
|
-
if (!
|
|
67063
|
+
const worktreesDir = join32(rootDir, ".worktrees");
|
|
67064
|
+
if (!existsSync26(worktreesDir)) {
|
|
66693
67065
|
return [];
|
|
66694
67066
|
}
|
|
66695
67067
|
let dirs;
|
|
66696
67068
|
try {
|
|
66697
67069
|
const entries = readdirSync4(worktreesDir, { withFileTypes: true });
|
|
66698
|
-
dirs = entries.filter((e) => e.isDirectory()).map((e) =>
|
|
67070
|
+
dirs = entries.filter((e) => e.isDirectory()).map((e) => join32(worktreesDir, e.name));
|
|
66699
67071
|
} catch (err) {
|
|
66700
67072
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
66701
67073
|
worktreePoolLog.warn(`Failed to read .worktrees/ directory: ${errorMessage}`);
|
|
@@ -66718,16 +67090,16 @@ async function scanIdleWorktrees(rootDir, store) {
|
|
|
66718
67090
|
return registeredDirs.filter((dir2) => !activeWorktrees.has(resolve15(dir2)));
|
|
66719
67091
|
}
|
|
66720
67092
|
async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
66721
|
-
const worktreesDir =
|
|
66722
|
-
if (!
|
|
67093
|
+
const worktreesDir = join32(rootDir, ".worktrees");
|
|
67094
|
+
if (!existsSync26(worktreesDir)) {
|
|
66723
67095
|
return 0;
|
|
66724
67096
|
}
|
|
66725
67097
|
const orphaned = await scanIdleWorktrees(rootDir, store);
|
|
66726
67098
|
const registeredWorktrees = await getRegisteredWorktreePaths(rootDir);
|
|
66727
67099
|
let dirs = [];
|
|
66728
|
-
if (
|
|
67100
|
+
if (existsSync26(worktreesDir)) {
|
|
66729
67101
|
try {
|
|
66730
|
-
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));
|
|
66731
67103
|
} catch (err) {
|
|
66732
67104
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
66733
67105
|
worktreePoolLog.warn(`Failed to read .worktrees/ directory for cleanup: ${errorMessage}`);
|
|
@@ -66759,8 +67131,8 @@ async function cleanupOrphanedWorktrees(rootDir, store) {
|
|
|
66759
67131
|
return cleaned;
|
|
66760
67132
|
}
|
|
66761
67133
|
async function reapOrphanWorktrees(projectRoot) {
|
|
66762
|
-
const worktreesDir =
|
|
66763
|
-
if (!
|
|
67134
|
+
const worktreesDir = join32(projectRoot, ".worktrees");
|
|
67135
|
+
if (!existsSync26(worktreesDir)) {
|
|
66764
67136
|
return 0;
|
|
66765
67137
|
}
|
|
66766
67138
|
let entries;
|
|
@@ -66768,11 +67140,11 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
66768
67140
|
entries = readdirSync4(worktreesDir, { withFileTypes: true }).filter((e) => {
|
|
66769
67141
|
if (!e.isDirectory()) return false;
|
|
66770
67142
|
try {
|
|
66771
|
-
return lstatSync(
|
|
67143
|
+
return lstatSync(join32(worktreesDir, e.name)).isDirectory() && !lstatSync(join32(worktreesDir, e.name)).isSymbolicLink();
|
|
66772
67144
|
} catch {
|
|
66773
67145
|
return false;
|
|
66774
67146
|
}
|
|
66775
|
-
}).map((e) => ({ name: e.name, fullPath:
|
|
67147
|
+
}).map((e) => ({ name: e.name, fullPath: join32(worktreesDir, e.name) }));
|
|
66776
67148
|
} catch (err) {
|
|
66777
67149
|
const msg = err instanceof Error ? err.message : String(err);
|
|
66778
67150
|
worktreePoolLog.warn(`reapOrphanWorktrees: failed to read .worktrees/ \u2014 ${msg}`);
|
|
@@ -66791,8 +67163,8 @@ async function reapOrphanWorktrees(projectRoot) {
|
|
|
66791
67163
|
if (registered.has(resolvedFull)) {
|
|
66792
67164
|
continue;
|
|
66793
67165
|
}
|
|
66794
|
-
const dotGit =
|
|
66795
|
-
if (
|
|
67166
|
+
const dotGit = join32(resolvedFull, ".git");
|
|
67167
|
+
if (existsSync26(dotGit)) {
|
|
66796
67168
|
worktreePoolLog.log(`reapOrphanWorktrees: skipping ${name} (has .git entry but not in registered list \u2014 may be partially registered)`);
|
|
66797
67169
|
continue;
|
|
66798
67170
|
}
|
|
@@ -66852,7 +67224,7 @@ var init_worktree_pool = __esm({
|
|
|
66852
67224
|
acquire() {
|
|
66853
67225
|
for (const path5 of this.idle) {
|
|
66854
67226
|
this.idle.delete(path5);
|
|
66855
|
-
if (
|
|
67227
|
+
if (existsSync26(path5)) {
|
|
66856
67228
|
return path5;
|
|
66857
67229
|
}
|
|
66858
67230
|
worktreePoolLog.log(`Pruned stale entry: ${path5}`);
|
|
@@ -66899,7 +67271,7 @@ var init_worktree_pool = __esm({
|
|
|
66899
67271
|
*/
|
|
66900
67272
|
rehydrate(idlePaths) {
|
|
66901
67273
|
for (const path5 of idlePaths) {
|
|
66902
|
-
if (
|
|
67274
|
+
if (existsSync26(path5)) {
|
|
66903
67275
|
this.idle.add(path5);
|
|
66904
67276
|
} else {
|
|
66905
67277
|
worktreePoolLog.log(`Rehydrate skipped (not on disk): ${path5}`);
|
|
@@ -66955,7 +67327,7 @@ var init_worktree_pool = __esm({
|
|
|
66955
67327
|
throw err;
|
|
66956
67328
|
}
|
|
66957
67329
|
const conflictingPath = match[1];
|
|
66958
|
-
if (!
|
|
67330
|
+
if (!existsSync26(conflictingPath)) {
|
|
66959
67331
|
await execAsync3("git worktree prune", { cwd: worktreePath });
|
|
66960
67332
|
await execAsync3(checkoutCmd, { cwd: worktreePath });
|
|
66961
67333
|
return branchName;
|
|
@@ -67032,8 +67404,8 @@ var init_token_cap_detector = __esm({
|
|
|
67032
67404
|
// ../engine/src/step-session-executor.ts
|
|
67033
67405
|
import { exec as exec5 } from "node:child_process";
|
|
67034
67406
|
import { promisify as promisify6 } from "node:util";
|
|
67035
|
-
import { existsSync as
|
|
67036
|
-
import { join as
|
|
67407
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
67408
|
+
import { join as join33 } from "node:path";
|
|
67037
67409
|
function parseStepFileScopes(prompt) {
|
|
67038
67410
|
const result = /* @__PURE__ */ new Map();
|
|
67039
67411
|
if (!prompt) return result;
|
|
@@ -67320,7 +67692,7 @@ var init_step_session_executor = __esm({
|
|
|
67320
67692
|
init_worktree_names();
|
|
67321
67693
|
init_agent_logger();
|
|
67322
67694
|
init_logger2();
|
|
67323
|
-
|
|
67695
|
+
init_fallback_model_observer();
|
|
67324
67696
|
init_context_limit_detector();
|
|
67325
67697
|
init_usage_limit_detector();
|
|
67326
67698
|
init_agent_tools();
|
|
@@ -67437,7 +67809,7 @@ var init_step_session_executor = __esm({
|
|
|
67437
67809
|
}
|
|
67438
67810
|
for (const [stepIdx, worktreePath] of this.parallelWorktrees) {
|
|
67439
67811
|
try {
|
|
67440
|
-
if (
|
|
67812
|
+
if (existsSync27(worktreePath)) {
|
|
67441
67813
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
67442
67814
|
cwd: this.options.rootDir
|
|
67443
67815
|
});
|
|
@@ -67602,7 +67974,13 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67602
67974
|
...this.options.skillSelection ? { skillSelection: this.options.skillSelection } : {},
|
|
67603
67975
|
taskId: taskDetail.id,
|
|
67604
67976
|
taskTitle: taskDetail.title,
|
|
67605
|
-
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
|
+
})
|
|
67606
67984
|
});
|
|
67607
67985
|
session = createResult.session;
|
|
67608
67986
|
const handle = {
|
|
@@ -67782,7 +68160,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67782
68160
|
for (const [stepIdx, worktreePath] of worktreePaths) {
|
|
67783
68161
|
if (worktreePath !== this.options.worktreePath) {
|
|
67784
68162
|
try {
|
|
67785
|
-
if (
|
|
68163
|
+
if (existsSync27(worktreePath)) {
|
|
67786
68164
|
await execAsync4(`git worktree remove "${worktreePath}" --force`, {
|
|
67787
68165
|
cwd: this.options.rootDir
|
|
67788
68166
|
});
|
|
@@ -67812,7 +68190,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67812
68190
|
async createStepWorktree(stepIndex) {
|
|
67813
68191
|
const { rootDir } = this.options;
|
|
67814
68192
|
const name = generateWorktreeName(rootDir);
|
|
67815
|
-
const worktreePath =
|
|
68193
|
+
const worktreePath = join33(rootDir, ".worktrees", name);
|
|
67816
68194
|
const branchName = `fusion/step-${stepIndex}-${name}`;
|
|
67817
68195
|
stepExecLog.log(`Creating worktree for step ${stepIndex}: ${worktreePath} (branch: ${branchName})`);
|
|
67818
68196
|
try {
|
|
@@ -67887,7 +68265,7 @@ Follow instructions precisely and avoid unrelated changes.`,
|
|
|
67887
68265
|
|
|
67888
68266
|
// ../engine/src/spec-staleness.ts
|
|
67889
68267
|
import { stat as stat5 } from "node:fs/promises";
|
|
67890
|
-
import { join as
|
|
68268
|
+
import { join as join34 } from "node:path";
|
|
67891
68269
|
async function evaluateSpecStaleness(options) {
|
|
67892
68270
|
const { settings, promptPath, nowMs } = options;
|
|
67893
68271
|
if (settings.specStalenessEnabled !== true) {
|
|
@@ -67927,7 +68305,7 @@ async function evaluateSpecStaleness(options) {
|
|
|
67927
68305
|
};
|
|
67928
68306
|
}
|
|
67929
68307
|
function getPromptPath(tasksDir, taskId) {
|
|
67930
|
-
return
|
|
68308
|
+
return join34(tasksDir, taskId, "PROMPT.md");
|
|
67931
68309
|
}
|
|
67932
68310
|
var DEFAULT_SPEC_STALENESS_MAX_AGE_MS;
|
|
67933
68311
|
var init_spec_staleness = __esm({
|
|
@@ -67958,8 +68336,8 @@ var init_task_completion = __esm({
|
|
|
67958
68336
|
|
|
67959
68337
|
// ../engine/src/run-verification-tool.ts
|
|
67960
68338
|
import { spawn as spawn4 } from "node:child_process";
|
|
67961
|
-
import { existsSync as
|
|
67962
|
-
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";
|
|
67963
68341
|
import { Type as Type4 } from "@mariozechner/pi-ai";
|
|
67964
68342
|
function createBuffer() {
|
|
67965
68343
|
return { headChunks: [], headBytes: 0, tailChunks: [], tailBytes: 0, totalBytes: 0 };
|
|
@@ -67992,7 +68370,7 @@ function flattenBuffer(buf) {
|
|
|
67992
68370
|
|
|
67993
68371
|
` + tail;
|
|
67994
68372
|
}
|
|
67995
|
-
async function
|
|
68373
|
+
async function runVerificationCommand3(opts) {
|
|
67996
68374
|
const { command, cwd, timeoutMs, expectFailure = false, onHeartbeat, onLine } = opts;
|
|
67997
68375
|
const startMs = Date.now();
|
|
67998
68376
|
const warnings = [];
|
|
@@ -68132,7 +68510,7 @@ function createRunVerificationTool(opts) {
|
|
|
68132
68510
|
if (params.cwd && isAbsolute10(params.cwd)) {
|
|
68133
68511
|
resolvedCwd = params.cwd;
|
|
68134
68512
|
} else if (params.cwd) {
|
|
68135
|
-
resolvedCwd =
|
|
68513
|
+
resolvedCwd = join35(worktreePath, params.cwd);
|
|
68136
68514
|
} else {
|
|
68137
68515
|
resolvedCwd = worktreePath;
|
|
68138
68516
|
}
|
|
@@ -68147,8 +68525,8 @@ function createRunVerificationTool(opts) {
|
|
|
68147
68525
|
}
|
|
68148
68526
|
let effectiveCommand = command;
|
|
68149
68527
|
if (command.trimStart().startsWith("pnpm --filter")) {
|
|
68150
|
-
const modulesYaml =
|
|
68151
|
-
if (!
|
|
68528
|
+
const modulesYaml = join35(rootDir, "node_modules", ".modules.yaml");
|
|
68529
|
+
if (!existsSync28(modulesYaml)) {
|
|
68152
68530
|
const installCmd = "pnpm install --prefer-offline";
|
|
68153
68531
|
const msg = `node_modules/.modules.yaml not found in workspace root \u2014 auto-prepending \`${installCmd}\` before running the command.`;
|
|
68154
68532
|
warnings.push(msg);
|
|
@@ -68159,7 +68537,7 @@ function createRunVerificationTool(opts) {
|
|
|
68159
68537
|
log19.info(
|
|
68160
68538
|
`[fn_run_verification] ${taskId}: scope=${scope} timeout=${timeoutSec}s cwd=${resolvedCwd} cmd=${effectiveCommand}`
|
|
68161
68539
|
);
|
|
68162
|
-
const result = await
|
|
68540
|
+
const result = await runVerificationCommand3({
|
|
68163
68541
|
command: effectiveCommand,
|
|
68164
68542
|
cwd: resolvedCwd,
|
|
68165
68543
|
timeoutMs,
|
|
@@ -68259,8 +68637,8 @@ var init_run_verification_tool = __esm({
|
|
|
68259
68637
|
// ../engine/src/executor.ts
|
|
68260
68638
|
import { exec as exec6 } from "node:child_process";
|
|
68261
68639
|
import { promisify as promisify7 } from "node:util";
|
|
68262
|
-
import { isAbsolute as isAbsolute11, join as
|
|
68263
|
-
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";
|
|
68264
68642
|
import { readFile as readFile15, writeFile as writeFile12 } from "node:fs/promises";
|
|
68265
68643
|
import { Type as Type5 } from "@mariozechner/pi-ai";
|
|
68266
68644
|
import { ModelRegistry as ModelRegistry2, SessionManager as SessionManager2 } from "@mariozechner/pi-coding-agent";
|
|
@@ -68554,6 +68932,7 @@ var init_executor = __esm({
|
|
|
68554
68932
|
"use strict";
|
|
68555
68933
|
init_src();
|
|
68556
68934
|
init_merger();
|
|
68935
|
+
init_verification_utils();
|
|
68557
68936
|
init_worktree_names();
|
|
68558
68937
|
init_pi();
|
|
68559
68938
|
init_session_token_usage();
|
|
@@ -68578,7 +68957,7 @@ var init_executor = __esm({
|
|
|
68578
68957
|
init_task_completion();
|
|
68579
68958
|
init_auth_storage();
|
|
68580
68959
|
init_run_verification_tool();
|
|
68581
|
-
|
|
68960
|
+
init_fallback_model_observer();
|
|
68582
68961
|
init_agent_logger();
|
|
68583
68962
|
init_agent_tools();
|
|
68584
68963
|
execAsync5 = promisify7(exec6);
|
|
@@ -69725,7 +70104,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
69725
70104
|
);
|
|
69726
70105
|
return false;
|
|
69727
70106
|
}
|
|
69728
|
-
if (task.worktree &&
|
|
70107
|
+
if (task.worktree && existsSync29(task.worktree)) {
|
|
69729
70108
|
const modifiedFiles = await this.captureModifiedFiles(task.worktree, task.baseCommitSha);
|
|
69730
70109
|
if (modifiedFiles.length > 0) {
|
|
69731
70110
|
await this.store.updateTask(task.id, { modifiedFiles });
|
|
@@ -69905,7 +70284,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
69905
70284
|
if (task.dependencies.length === 0) return null;
|
|
69906
70285
|
for (const depId of task.dependencies) {
|
|
69907
70286
|
const dep = allTasks.find((t) => t.id === depId);
|
|
69908
|
-
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)) {
|
|
69909
70288
|
return dep.worktree;
|
|
69910
70289
|
}
|
|
69911
70290
|
}
|
|
@@ -69956,7 +70335,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
69956
70335
|
const activeMergeStatuses = /* @__PURE__ */ new Set(["merging", "merging-pr"]);
|
|
69957
70336
|
const isActiveTask = activeColumns.has(task.column) || activeMergeStatuses.has(task.status ?? "");
|
|
69958
70337
|
if (!isActiveTask) {
|
|
69959
|
-
const tasksDir =
|
|
70338
|
+
const tasksDir = join36(this.store.getFusionDir(), "tasks");
|
|
69960
70339
|
const promptPath = getPromptPath(tasksDir, task.id);
|
|
69961
70340
|
const staleness = await evaluateSpecStaleness({ settings, promptPath });
|
|
69962
70341
|
if (staleness.isStale) {
|
|
@@ -70003,7 +70382,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70003
70382
|
worktreeName = generateWorktreeName(this.rootDir);
|
|
70004
70383
|
break;
|
|
70005
70384
|
}
|
|
70006
|
-
worktreePath =
|
|
70385
|
+
worktreePath = join36(this.rootDir, ".worktrees", worktreeName);
|
|
70007
70386
|
}
|
|
70008
70387
|
let stuckRequeue = null;
|
|
70009
70388
|
let taskDone = false;
|
|
@@ -70027,7 +70406,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70027
70406
|
);
|
|
70028
70407
|
}
|
|
70029
70408
|
const branchName = task.branch || `fusion/${task.id.toLowerCase()}`;
|
|
70030
|
-
let isResume =
|
|
70409
|
+
let isResume = existsSync29(worktreePath);
|
|
70031
70410
|
let acquiredFromPool = false;
|
|
70032
70411
|
const baseBranch = task.baseBranch || null;
|
|
70033
70412
|
if (task.worktree && isResume && !await isUsableTaskWorktree(this.rootDir, worktreePath)) {
|
|
@@ -70040,8 +70419,8 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70040
70419
|
this.currentRunContext
|
|
70041
70420
|
);
|
|
70042
70421
|
await this.store.updateTask(task.id, { worktree: null, branch: null });
|
|
70043
|
-
worktreePath =
|
|
70044
|
-
isResume =
|
|
70422
|
+
worktreePath = join36(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
70423
|
+
isResume = existsSync29(worktreePath);
|
|
70045
70424
|
}
|
|
70046
70425
|
if (!isResume) {
|
|
70047
70426
|
if (this.options.pool && settings.recycleWorktrees) {
|
|
@@ -70267,6 +70646,84 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70267
70646
|
if (await this.shouldDeferCompletionForGlobalPause(task.id, "before workflow steps after step-session completion")) {
|
|
70268
70647
|
return;
|
|
70269
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
|
+
}
|
|
70270
70727
|
if (executionMode !== "fast") {
|
|
70271
70728
|
const workflowResult = await this.runWorkflowSteps(task, worktreePath, settings);
|
|
70272
70729
|
if (workflowResult === "deferred-paused") {
|
|
@@ -70355,7 +70812,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70355
70812
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
|
|
70356
70813
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
70357
70814
|
}
|
|
70358
|
-
if (worktreePath &&
|
|
70815
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
70359
70816
|
try {
|
|
70360
70817
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
70361
70818
|
await audit.git({ type: "worktree:remove", target: worktreePath });
|
|
@@ -70414,7 +70871,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70414
70871
|
try {
|
|
70415
70872
|
const latestTask = await this.store.getTask(task.id);
|
|
70416
70873
|
await this.resetStepsIfWorkLost(latestTask);
|
|
70417
|
-
if (worktreePath &&
|
|
70874
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
70418
70875
|
try {
|
|
70419
70876
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
70420
70877
|
} catch (wtErr) {
|
|
@@ -70526,7 +70983,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70526
70983
|
const executorFallbackProvider = settings.fallbackProvider;
|
|
70527
70984
|
const executorFallbackModelId = settings.fallbackModelId;
|
|
70528
70985
|
const executorThinkingLevel = detail.thinkingLevel ?? settings.defaultThinkingLevel;
|
|
70529
|
-
const isResuming = !!task.sessionFile &&
|
|
70986
|
+
const isResuming = !!task.sessionFile && existsSync29(task.sessionFile);
|
|
70530
70987
|
const sessionManager = isResuming ? SessionManager2.open(task.sessionFile) : SessionManager2.create(worktreePath);
|
|
70531
70988
|
executorLog.log(`${task.id}: creating agent session (provider=${executorProvider ?? "default"}, model=${executorModelId ?? "default"}, resuming=${isResuming})`);
|
|
70532
70989
|
const executorInstructions = await this.resolveInstructionsForRole("executor");
|
|
@@ -70556,7 +71013,13 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70556
71013
|
...skillContext.skillSelectionContext ? { skillSelection: skillContext.skillSelectionContext } : {},
|
|
70557
71014
|
taskId: task.id,
|
|
70558
71015
|
taskTitle: detail.title,
|
|
70559
|
-
onFallbackModelUsed:
|
|
71016
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
71017
|
+
agent: "executor",
|
|
71018
|
+
label: "executor",
|
|
71019
|
+
store: this.store,
|
|
71020
|
+
taskId: task.id,
|
|
71021
|
+
taskTitle: detail.title
|
|
71022
|
+
})
|
|
70560
71023
|
});
|
|
70561
71024
|
if (isResuming) {
|
|
70562
71025
|
executorLog.log(`${task.id}: resumed session from ${task.sessionFile}`);
|
|
@@ -70990,7 +71453,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
70990
71453
|
this.options.onComplete?.(task);
|
|
70991
71454
|
} else {
|
|
70992
71455
|
executorLog.log(`${task.id} paused \u2014 moving to todo`);
|
|
70993
|
-
if (worktreePath &&
|
|
71456
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
70994
71457
|
try {
|
|
70995
71458
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
70996
71459
|
executorLog.log(`Removed old worktree for paused task: ${worktreePath}`);
|
|
@@ -71086,7 +71549,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
71086
71549
|
executorLog.warn(`\u26A1 ${task.id} transient error \u2014 retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}: ${errorMessage}`);
|
|
71087
71550
|
await this.store.logEntry(task.id, `Transient error (retry ${attempt}/${MAX_RECOVERY_RETRIES} in ${delay3}): ${errorMessage}`, void 0, this.currentRunContext);
|
|
71088
71551
|
}
|
|
71089
|
-
if (worktreePath &&
|
|
71552
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
71090
71553
|
try {
|
|
71091
71554
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
71092
71555
|
executorLog.log(`Removed old worktree for transient retry: ${worktreePath}`);
|
|
@@ -71141,7 +71604,7 @@ The tool prevents your session from being killed by the inactivity watchdog duri
|
|
|
71141
71604
|
try {
|
|
71142
71605
|
const latestTask = await this.store.getTask(task.id);
|
|
71143
71606
|
await this.resetStepsIfWorkLost(latestTask);
|
|
71144
|
-
if (worktreePath &&
|
|
71607
|
+
if (worktreePath && existsSync29(worktreePath)) {
|
|
71145
71608
|
try {
|
|
71146
71609
|
await execAsync5(`git worktree remove "${worktreePath}" --force`, { cwd: this.rootDir });
|
|
71147
71610
|
executorLog.log(`Removed old worktree for stuck-killed retry: ${worktreePath}`);
|
|
@@ -71646,7 +72109,7 @@ Take a different approach. Do NOT repeat the rejected strategy. Re-read the step
|
|
|
71646
72109
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
71647
72110
|
*/
|
|
71648
72111
|
async injectWorkflowRevisionInstructions(task, feedback) {
|
|
71649
|
-
const promptPath =
|
|
72112
|
+
const promptPath = join36(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
71650
72113
|
let content;
|
|
71651
72114
|
try {
|
|
71652
72115
|
content = await readFile15(promptPath, "utf-8");
|
|
@@ -71700,6 +72163,217 @@ ${feedback}
|
|
|
71700
72163
|
*
|
|
71701
72164
|
* @returns true if a retry was scheduled, false if retries are exhausted
|
|
71702
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
|
+
}
|
|
71703
72377
|
async handleWorkflowStepFailure(task, worktreePath, failureFeedback, stepName) {
|
|
71704
72378
|
this.clearCompletedTaskWatchdog(task.id);
|
|
71705
72379
|
const currentRetries = task.workflowStepRetries ?? 0;
|
|
@@ -71769,7 +72443,7 @@ Please fix the issues so the verification can pass on the next attempt.`,
|
|
|
71769
72443
|
* The section is replaced entirely to avoid accumulation of old feedback.
|
|
71770
72444
|
*/
|
|
71771
72445
|
async injectWorkflowStepFailureInstructions(task, failureFeedback, stepName, retryCount) {
|
|
71772
|
-
const promptPath =
|
|
72446
|
+
const promptPath = join36(this.store.getFusionDir(), "tasks", task.id, "PROMPT.md");
|
|
71773
72447
|
let content;
|
|
71774
72448
|
try {
|
|
71775
72449
|
content = await readFile15(promptPath, "utf-8");
|
|
@@ -71834,31 +72508,33 @@ ${failureFeedback}
|
|
|
71834
72508
|
* Uses git diff against the stored baseCommitSha to determine what changed.
|
|
71835
72509
|
* Returns an empty array if no changes or if git commands fail.
|
|
71836
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
|
+
}
|
|
71837
72535
|
async captureModifiedFiles(worktreePath, baseCommitSha) {
|
|
71838
72536
|
try {
|
|
71839
|
-
|
|
71840
|
-
if (!baseRef) {
|
|
71841
|
-
try {
|
|
71842
|
-
const { stdout: stdout2 } = await execAsync5("git merge-base HEAD origin/main 2>/dev/null || git merge-base HEAD main", {
|
|
71843
|
-
cwd: worktreePath,
|
|
71844
|
-
encoding: "utf-8"
|
|
71845
|
-
});
|
|
71846
|
-
baseRef = stdout2.trim();
|
|
71847
|
-
} catch (mergeBaseErr) {
|
|
71848
|
-
const mergeBaseMsg = mergeBaseErr instanceof Error ? mergeBaseErr.message : String(mergeBaseErr);
|
|
71849
|
-
executorLog.warn(`Failed merge-base lookup for diff base in ${worktreePath}, trying HEAD~1 fallback: ${mergeBaseMsg}`);
|
|
71850
|
-
try {
|
|
71851
|
-
const { stdout: stdout2 } = await execAsync5("git rev-parse HEAD~1", {
|
|
71852
|
-
cwd: worktreePath,
|
|
71853
|
-
encoding: "utf-8"
|
|
71854
|
-
});
|
|
71855
|
-
baseRef = stdout2.trim();
|
|
71856
|
-
} catch {
|
|
71857
|
-
executorLog.log(`Could not determine base commit for diff in ${worktreePath}`);
|
|
71858
|
-
return [];
|
|
71859
|
-
}
|
|
71860
|
-
}
|
|
71861
|
-
}
|
|
72537
|
+
const baseRef = await this.resolveDiffBaseRef(worktreePath, baseCommitSha);
|
|
71862
72538
|
if (!baseRef) {
|
|
71863
72539
|
return [];
|
|
71864
72540
|
}
|
|
@@ -72094,6 +72770,30 @@ ${failureFeedback}
|
|
|
72094
72770
|
*/
|
|
72095
72771
|
async executeWorkflowStep(task, workflowStep, worktreePath, settings) {
|
|
72096
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.`;
|
|
72097
72797
|
const systemPrompt = `You are a workflow step agent executing: ${workflowStep.name}
|
|
72098
72798
|
|
|
72099
72799
|
Task Context:
|
|
@@ -72101,6 +72801,8 @@ Task Context:
|
|
|
72101
72801
|
- Task Description: ${task.description}
|
|
72102
72802
|
- Worktree: ${worktreePath}
|
|
72103
72803
|
|
|
72804
|
+
${scopeBlock}
|
|
72805
|
+
|
|
72104
72806
|
Your role:
|
|
72105
72807
|
- Execute this workflow step exactly as scoped.
|
|
72106
72808
|
- Prioritize high-impact correctness/risk findings over stylistic nits.
|
|
@@ -72569,7 +73271,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
72569
73271
|
* rather than fail the task permanently.
|
|
72570
73272
|
*/
|
|
72571
73273
|
async resolveWorktreeStartPoint(startPoint, taskId) {
|
|
72572
|
-
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}"`;
|
|
72573
73275
|
try {
|
|
72574
73276
|
const { stdout } = await execAsync5(command, { cwd: this.rootDir });
|
|
72575
73277
|
return stdout.trim() || startPoint;
|
|
@@ -72589,7 +73291,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
72589
73291
|
*/
|
|
72590
73292
|
async tryCreateWorktree(branch, path5, taskId, startPoint, attemptNumber = 0, recoveryDepth = 0) {
|
|
72591
73293
|
await this.assertWorktreePathNotNested(path5, taskId);
|
|
72592
|
-
if (
|
|
73294
|
+
if (existsSync29(path5)) {
|
|
72593
73295
|
const isRegistered = await this.isRegisteredWorktree(path5);
|
|
72594
73296
|
if (!isRegistered) {
|
|
72595
73297
|
await this.store.logEntry(
|
|
@@ -72740,7 +73442,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
72740
73442
|
);
|
|
72741
73443
|
if (shouldGenerateNewName) {
|
|
72742
73444
|
const conflictStartPoint = branch;
|
|
72743
|
-
const newPath =
|
|
73445
|
+
const newPath = join36(this.rootDir, ".worktrees", generateWorktreeName(this.rootDir));
|
|
72744
73446
|
for (let suffix = 2; suffix <= 6; suffix++) {
|
|
72745
73447
|
const suffixedBranch = `${branch}-${suffix}`;
|
|
72746
73448
|
try {
|
|
@@ -73340,7 +74042,7 @@ Review the work done in this worktree and evaluate it against the criteria in yo
|
|
|
73340
74042
|
metadata: { type: "spawned", parentTaskId: taskId }
|
|
73341
74043
|
});
|
|
73342
74044
|
const childWorktreeName = generateWorktreeName(this.rootDir);
|
|
73343
|
-
const childWorktreePath =
|
|
74045
|
+
const childWorktreePath = join36(this.rootDir, ".worktrees", childWorktreeName);
|
|
73344
74046
|
const childBranch = `fusion/spawn-${agent.id}`;
|
|
73345
74047
|
await this.createWorktree(childBranch, childWorktreePath, taskId, worktreePath);
|
|
73346
74048
|
await this.options.agentStore.updateAgentState(agent.id, "active");
|
|
@@ -73529,9 +74231,9 @@ var init_node_routing_policy = __esm({
|
|
|
73529
74231
|
});
|
|
73530
74232
|
|
|
73531
74233
|
// ../engine/src/scheduler.ts
|
|
73532
|
-
import { existsSync as
|
|
74234
|
+
import { existsSync as existsSync30 } from "node:fs";
|
|
73533
74235
|
import { readFile as readFile16 } from "node:fs/promises";
|
|
73534
|
-
import { basename as basename8, join as
|
|
74236
|
+
import { basename as basename8, join as join37 } from "node:path";
|
|
73535
74237
|
function pathsOverlap2(a, b) {
|
|
73536
74238
|
for (const pa of a) {
|
|
73537
74239
|
const prefixA = pa.endsWith("/*") ? pa.slice(0, -1) : null;
|
|
@@ -73701,12 +74403,12 @@ var init_scheduler = __esm({
|
|
|
73701
74403
|
* @returns Object with `valid: true` if checks pass, or `valid: false` with a `reason` string if they fail
|
|
73702
74404
|
*/
|
|
73703
74405
|
async validateTaskFilesystem(id) {
|
|
73704
|
-
const taskDir =
|
|
73705
|
-
if (!
|
|
74406
|
+
const taskDir = join37(this.store.getTasksDir(), id);
|
|
74407
|
+
if (!existsSync30(taskDir)) {
|
|
73706
74408
|
return { valid: false, reason: "missing directory" };
|
|
73707
74409
|
}
|
|
73708
|
-
const promptPath =
|
|
73709
|
-
if (!
|
|
74410
|
+
const promptPath = join37(taskDir, "PROMPT.md");
|
|
74411
|
+
if (!existsSync30(promptPath)) {
|
|
73710
74412
|
return { valid: false, reason: "missing or empty PROMPT.md" };
|
|
73711
74413
|
}
|
|
73712
74414
|
try {
|
|
@@ -73845,7 +74547,7 @@ var init_scheduler = __esm({
|
|
|
73845
74547
|
break;
|
|
73846
74548
|
}
|
|
73847
74549
|
reservedNames.add(worktreeName);
|
|
73848
|
-
return
|
|
74550
|
+
return join37(this.store.getRootDir(), ".worktrees", worktreeName);
|
|
73849
74551
|
}
|
|
73850
74552
|
/**
|
|
73851
74553
|
* Run one scheduling pass.
|
|
@@ -75122,7 +75824,7 @@ var init_mission_execution_loop = __esm({
|
|
|
75122
75824
|
init_pi();
|
|
75123
75825
|
init_agent_session_helpers();
|
|
75124
75826
|
init_logger2();
|
|
75125
|
-
|
|
75827
|
+
init_fallback_model_observer();
|
|
75126
75828
|
loopLog = createLogger2("mission-loop");
|
|
75127
75829
|
VALIDATION_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
75128
75830
|
MissionExecutionLoop = class extends EventEmitter18 {
|
|
@@ -75356,7 +76058,13 @@ Assertions: ${assertions.map((a) => a.title).join(", ")}`,
|
|
|
75356
76058
|
},
|
|
75357
76059
|
taskId: task?.id,
|
|
75358
76060
|
taskTitle: task?.title,
|
|
75359
|
-
onFallbackModelUsed:
|
|
76061
|
+
onFallbackModelUsed: createFallbackModelObserver({
|
|
76062
|
+
agent: "reviewer",
|
|
76063
|
+
label: "mission validator",
|
|
76064
|
+
store: this.taskStore,
|
|
76065
|
+
taskId: task?.id,
|
|
76066
|
+
taskTitle: task?.title
|
|
76067
|
+
})
|
|
75360
76068
|
});
|
|
75361
76069
|
session = { session: sessionResult.session, sessionFile: sessionResult.sessionFile };
|
|
75362
76070
|
loopLog.log(`Validation session created for feature ${feature.id}`);
|
|
@@ -77164,7 +77872,7 @@ not loop on the same plan across heartbeats without recording why.`;
|
|
|
77164
77872
|
};
|
|
77165
77873
|
}
|
|
77166
77874
|
};
|
|
77167
|
-
const { createResolvedAgentSession:
|
|
77875
|
+
const { createResolvedAgentSession: createResolvedAgentSession3, extractRuntimeHint: extractRuntimeHint2, extractRuntimeModel: extractRuntimeModel2 } = await Promise.resolve().then(() => (init_agent_session_helpers(), agent_session_helpers_exports));
|
|
77168
77876
|
const { buildSessionSkillContextSync: buildSessionSkillContextSync2 } = await Promise.resolve().then(() => (init_session_skill_context(), session_skill_context_exports));
|
|
77169
77877
|
let heartbeatTools;
|
|
77170
77878
|
if (isNoTaskRun) {
|
|
@@ -77235,7 +77943,7 @@ not loop on the same plan across heartbeats without recording why.`;
|
|
|
77235
77943
|
persistAgentToolOutput: memorySettings?.persistAgentToolOutput
|
|
77236
77944
|
});
|
|
77237
77945
|
}
|
|
77238
|
-
const { session } = await
|
|
77946
|
+
const { session } = await createResolvedAgentSession3({
|
|
77239
77947
|
sessionPurpose: "heartbeat",
|
|
77240
77948
|
runtimeHint: extractRuntimeHint2(agent.runtimeConfig),
|
|
77241
77949
|
pluginRunner: this.pluginRunner,
|
|
@@ -77243,8 +77951,10 @@ not loop on the same plan across heartbeats without recording why.`;
|
|
|
77243
77951
|
systemPrompt,
|
|
77244
77952
|
tools: "readonly",
|
|
77245
77953
|
customTools: heartbeatTools,
|
|
77246
|
-
|
|
77247
|
-
|
|
77954
|
+
...(() => {
|
|
77955
|
+
const { provider, modelId } = extractRuntimeModel2(agent.runtimeConfig);
|
|
77956
|
+
return { defaultProvider: provider, defaultModelId: modelId };
|
|
77957
|
+
})(),
|
|
77248
77958
|
onText: (delta) => {
|
|
77249
77959
|
outputLength += delta.length;
|
|
77250
77960
|
appendStdoutExcerpt(delta);
|
|
@@ -78619,7 +79329,7 @@ async function createAiPromptExecutor(cwd) {
|
|
|
78619
79329
|
}
|
|
78620
79330
|
};
|
|
78621
79331
|
}
|
|
78622
|
-
function
|
|
79332
|
+
function truncateOutput2(stdout, stderr) {
|
|
78623
79333
|
const out = stdout ?? "";
|
|
78624
79334
|
const err = stderr ?? "";
|
|
78625
79335
|
let combined = out;
|
|
@@ -78799,7 +79509,7 @@ var init_cron_runner = __esm({
|
|
|
78799
79509
|
maxBuffer: MAX_BUFFER,
|
|
78800
79510
|
shell: defaultShell
|
|
78801
79511
|
});
|
|
78802
|
-
const output =
|
|
79512
|
+
const output = truncateOutput2(stdout, stderr);
|
|
78803
79513
|
log15.log(`\u2713 ${schedule.name} completed (${output.length} bytes output)`);
|
|
78804
79514
|
return {
|
|
78805
79515
|
success: true,
|
|
@@ -78810,7 +79520,7 @@ var init_cron_runner = __esm({
|
|
|
78810
79520
|
} catch (err) {
|
|
78811
79521
|
const stdout = err.stdout ?? "";
|
|
78812
79522
|
const stderr = err.stderr ?? "";
|
|
78813
|
-
const output =
|
|
79523
|
+
const output = truncateOutput2(stdout, stderr);
|
|
78814
79524
|
const errorMessage = err.killed ? `Command timed out after ${(schedule.timeoutMs ?? DEFAULT_TIMEOUT_MS6) / 1e3}s` : err.message ?? String(err);
|
|
78815
79525
|
log15.warn(`\u2717 ${schedule.name} failed: ${errorMessage}`);
|
|
78816
79526
|
return {
|
|
@@ -78855,7 +79565,7 @@ var init_cron_runner = __esm({
|
|
|
78855
79565
|
const result = await runBackupCommand2(fusionDir, settings);
|
|
78856
79566
|
return {
|
|
78857
79567
|
success: result.success,
|
|
78858
|
-
output:
|
|
79568
|
+
output: truncateOutput2(result.output ?? "", ""),
|
|
78859
79569
|
error: result.success ? void 0 : result.output
|
|
78860
79570
|
};
|
|
78861
79571
|
} catch (err) {
|
|
@@ -78896,7 +79606,7 @@ var init_cron_runner = __esm({
|
|
|
78896
79606
|
if (sr.output) outputParts.push(sr.output);
|
|
78897
79607
|
if (sr.error) outputParts.push(`Error: ${sr.error}`);
|
|
78898
79608
|
}
|
|
78899
|
-
const output =
|
|
79609
|
+
const output = truncateOutput2(outputParts.join("\n"), "");
|
|
78900
79610
|
const failedSteps = stepResults.filter((sr) => !sr.success);
|
|
78901
79611
|
const error = failedSteps.length > 0 ? `${failedSteps.length} step(s) failed: ${failedSteps.map((s) => s.stepName).join(", ")}${stoppedEarly ? " (execution stopped)" : ""}` : void 0;
|
|
78902
79612
|
const status = overallSuccess ? "\u2713" : "\u2717";
|
|
@@ -78975,7 +79685,7 @@ var init_cron_runner = __esm({
|
|
|
78975
79685
|
stepName: step.name,
|
|
78976
79686
|
stepIndex,
|
|
78977
79687
|
success: true,
|
|
78978
|
-
output:
|
|
79688
|
+
output: truncateOutput2(stdout, stderr),
|
|
78979
79689
|
startedAt,
|
|
78980
79690
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
78981
79691
|
};
|
|
@@ -78988,7 +79698,7 @@ var init_cron_runner = __esm({
|
|
|
78988
79698
|
stepName: step.name,
|
|
78989
79699
|
stepIndex,
|
|
78990
79700
|
success: false,
|
|
78991
|
-
output:
|
|
79701
|
+
output: truncateOutput2(stdout, stderr),
|
|
78992
79702
|
error: errorMessage,
|
|
78993
79703
|
startedAt,
|
|
78994
79704
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -79138,7 +79848,7 @@ var init_cron_runner = __esm({
|
|
|
79138
79848
|
// ../engine/src/routine-runner.ts
|
|
79139
79849
|
import { exec as exec8 } from "node:child_process";
|
|
79140
79850
|
import { promisify as promisify8 } from "node:util";
|
|
79141
|
-
function
|
|
79851
|
+
function truncateOutput3(stdout, stderr) {
|
|
79142
79852
|
let output = stdout;
|
|
79143
79853
|
if (stderr) {
|
|
79144
79854
|
output += stdout ? "\n--- stderr ---\n" : "";
|
|
@@ -79321,7 +80031,7 @@ var init_routine_runner = __esm({
|
|
|
79321
80031
|
const result = await runBackupCommand2(fusionDir, settings);
|
|
79322
80032
|
return {
|
|
79323
80033
|
success: result.success,
|
|
79324
|
-
output:
|
|
80034
|
+
output: truncateOutput3(result.output ?? "", ""),
|
|
79325
80035
|
error: result.success ? void 0 : result.output,
|
|
79326
80036
|
startedAt,
|
|
79327
80037
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -79345,7 +80055,7 @@ var init_routine_runner = __esm({
|
|
|
79345
80055
|
});
|
|
79346
80056
|
return {
|
|
79347
80057
|
success: true,
|
|
79348
|
-
output:
|
|
80058
|
+
output: truncateOutput3(stdout, stderr),
|
|
79349
80059
|
startedAt,
|
|
79350
80060
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
79351
80061
|
};
|
|
@@ -79356,7 +80066,7 @@ var init_routine_runner = __esm({
|
|
|
79356
80066
|
const error = errObj.killed === true ? `Command timed out after ${(timeoutMs ?? DEFAULT_TIMEOUT_MS7) / 1e3}s` : (err instanceof Error ? err.message : null) ?? String(err);
|
|
79357
80067
|
return {
|
|
79358
80068
|
success: false,
|
|
79359
|
-
output:
|
|
80069
|
+
output: truncateOutput3(stdout, stderr),
|
|
79360
80070
|
error,
|
|
79361
80071
|
startedAt,
|
|
79362
80072
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -79389,7 +80099,7 @@ var init_routine_runner = __esm({
|
|
|
79389
80099
|
const failedSteps = stepResults.filter((sr) => !sr.success);
|
|
79390
80100
|
return {
|
|
79391
80101
|
success: overallSuccess,
|
|
79392
|
-
output:
|
|
80102
|
+
output: truncateOutput3(outputParts.join("\n"), ""),
|
|
79393
80103
|
error: failedSteps.length > 0 ? `${failedSteps.length} step(s) failed: ${failedSteps.map((s) => s.stepName).join(", ")}${stoppedEarly ? " (execution stopped)" : ""}` : void 0,
|
|
79394
80104
|
startedAt,
|
|
79395
80105
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -79424,7 +80134,7 @@ var init_routine_runner = __esm({
|
|
|
79424
80134
|
this.options.aiPromptExecutor(step.prompt, step.modelProvider, step.modelId),
|
|
79425
80135
|
new Promise((_resolve, reject2) => setTimeout(() => reject2(new Error(`AI prompt step timed out after ${timeoutMs / 1e3}s`)), timeoutMs))
|
|
79426
80136
|
]);
|
|
79427
|
-
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() };
|
|
79428
80138
|
} catch (err) {
|
|
79429
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() };
|
|
79430
80140
|
}
|
|
@@ -80038,8 +80748,8 @@ var init_stuck_task_detector = __esm({
|
|
|
80038
80748
|
// ../engine/src/self-healing.ts
|
|
80039
80749
|
import { exec as exec9 } from "node:child_process";
|
|
80040
80750
|
import { promisify as promisify9 } from "node:util";
|
|
80041
|
-
import { existsSync as
|
|
80042
|
-
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";
|
|
80043
80753
|
function shellQuote(value) {
|
|
80044
80754
|
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
80045
80755
|
}
|
|
@@ -80435,7 +81145,7 @@ var init_self_healing = __esm({
|
|
|
80435
81145
|
return commit;
|
|
80436
81146
|
}
|
|
80437
81147
|
async cleanupInterruptedMergeArtifacts(task) {
|
|
80438
|
-
if (task.worktree &&
|
|
81148
|
+
if (task.worktree && existsSync31(task.worktree)) {
|
|
80439
81149
|
try {
|
|
80440
81150
|
await execAsync7(`git worktree remove ${shellQuote(task.worktree)} --force`, {
|
|
80441
81151
|
cwd: this.options.rootDir,
|
|
@@ -81056,7 +81766,7 @@ var init_self_healing = __esm({
|
|
|
81056
81766
|
return false;
|
|
81057
81767
|
}
|
|
81058
81768
|
const staleness = now - new Date(t.updatedAt).getTime();
|
|
81059
|
-
const hasWorktree = t.worktree &&
|
|
81769
|
+
const hasWorktree = t.worktree && existsSync31(t.worktree);
|
|
81060
81770
|
const graceMs = hasWorktree ? ORPHANED_WITH_WORKTREE_GRACE_MS : ORPHANED_EXECUTION_RECOVERY_GRACE_MS;
|
|
81061
81771
|
return staleness >= graceMs;
|
|
81062
81772
|
});
|
|
@@ -81065,7 +81775,7 @@ var init_self_healing = __esm({
|
|
|
81065
81775
|
let recovered = 0;
|
|
81066
81776
|
for (const task of orphaned) {
|
|
81067
81777
|
try {
|
|
81068
|
-
const hadWorktree = task.worktree &&
|
|
81778
|
+
const hadWorktree = task.worktree && existsSync31(task.worktree);
|
|
81069
81779
|
const reason = hadWorktree ? "worktree exists but no active session" : "missing worktree/session";
|
|
81070
81780
|
await this.resetStepsIfWorkLost(task);
|
|
81071
81781
|
await this.store.updateTask(task.id, {
|
|
@@ -81211,7 +81921,7 @@ var init_self_healing = __esm({
|
|
|
81211
81921
|
}
|
|
81212
81922
|
}
|
|
81213
81923
|
async hasRecoverableGitWork(task) {
|
|
81214
|
-
if (task.worktree &&
|
|
81924
|
+
if (task.worktree && existsSync31(task.worktree)) {
|
|
81215
81925
|
try {
|
|
81216
81926
|
const { stdout: status } = await execAsync7("git status --porcelain", {
|
|
81217
81927
|
cwd: task.worktree,
|
|
@@ -81396,11 +82106,11 @@ var init_self_healing = __esm({
|
|
|
81396
82106
|
* tracks registered idle worktrees, never these orphans.
|
|
81397
82107
|
*/
|
|
81398
82108
|
async reapUnregisteredOrphans() {
|
|
81399
|
-
const worktreesDir =
|
|
81400
|
-
if (!
|
|
82109
|
+
const worktreesDir = join38(this.options.rootDir, ".worktrees");
|
|
82110
|
+
if (!existsSync31(worktreesDir)) return 0;
|
|
81401
82111
|
let dirs;
|
|
81402
82112
|
try {
|
|
81403
|
-
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));
|
|
81404
82114
|
} catch (err) {
|
|
81405
82115
|
log17.warn(`Failed to read .worktrees/ for unregistered orphan reap: ${err instanceof Error ? err.message : String(err)}`);
|
|
81406
82116
|
return 0;
|
|
@@ -81505,8 +82215,8 @@ var init_self_healing = __esm({
|
|
|
81505
82215
|
}
|
|
81506
82216
|
/** Remove oldest idle worktrees if total count exceeds 2× maxWorktrees. */
|
|
81507
82217
|
async enforceWorktreeCap() {
|
|
81508
|
-
const worktreesDir =
|
|
81509
|
-
if (!
|
|
82218
|
+
const worktreesDir = join38(this.options.rootDir, ".worktrees");
|
|
82219
|
+
if (!existsSync31(worktreesDir)) return;
|
|
81510
82220
|
try {
|
|
81511
82221
|
const settings = await this.store.getSettings();
|
|
81512
82222
|
const cap = (settings.maxWorktrees ?? 4) * 2;
|
|
@@ -83344,7 +84054,7 @@ var init_ipc_host = __esm({
|
|
|
83344
84054
|
import { EventEmitter as EventEmitter21 } from "node:events";
|
|
83345
84055
|
import { fork } from "node:child_process";
|
|
83346
84056
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
83347
|
-
import { dirname as dirname11, join as
|
|
84057
|
+
import { dirname as dirname11, join as join39 } from "node:path";
|
|
83348
84058
|
var HealthMonitor, ChildProcessRuntime;
|
|
83349
84059
|
var init_child_process_runtime = __esm({
|
|
83350
84060
|
"../engine/src/runtimes/child-process-runtime.ts"() {
|
|
@@ -83506,7 +84216,7 @@ var init_child_process_runtime = __esm({
|
|
|
83506
84216
|
const isCompiled = !import.meta.url.endsWith(".ts");
|
|
83507
84217
|
const currentDir = dirname11(fileURLToPath3(import.meta.url));
|
|
83508
84218
|
const workerFile = isCompiled ? "child-process-worker.js" : "child-process-worker.ts";
|
|
83509
|
-
return
|
|
84219
|
+
return join39(currentDir, workerFile);
|
|
83510
84220
|
}
|
|
83511
84221
|
/**
|
|
83512
84222
|
* Set up event forwarding from IPC host to runtime listeners.
|
|
@@ -87759,6 +88469,8 @@ __export(src_exports2, {
|
|
|
87759
88469
|
describeAgentModel: () => describeAgentModel,
|
|
87760
88470
|
describeModel: () => describeModel,
|
|
87761
88471
|
ensureDefaultHeartbeatProcedureFile: () => ensureDefaultHeartbeatProcedureFile,
|
|
88472
|
+
extractRuntimeHint: () => extractRuntimeHint,
|
|
88473
|
+
extractRuntimeModel: () => extractRuntimeModel,
|
|
87762
88474
|
formatTaskIdentifier: () => formatTaskIdentifier,
|
|
87763
88475
|
getDefaultPiRuntime: () => getDefaultPiRuntime,
|
|
87764
88476
|
getHostExtensionPaths: () => getHostExtensionPaths,
|
|
@@ -92103,7 +92815,7 @@ var init_api_error = __esm({
|
|
|
92103
92815
|
// ../dashboard/src/plugin-routes.ts
|
|
92104
92816
|
import { Router } from "express";
|
|
92105
92817
|
import { access as access5, stat as stat6, readFile as readFile18 } from "node:fs/promises";
|
|
92106
|
-
import { join as
|
|
92818
|
+
import { join as join40, isAbsolute as isAbsolute14, dirname as dirname12, basename as basename9 } from "node:path";
|
|
92107
92819
|
async function resolvePluginManifest(sourcePath) {
|
|
92108
92820
|
try {
|
|
92109
92821
|
await access5(sourcePath);
|
|
@@ -92119,7 +92831,7 @@ async function resolvePluginManifest(sourcePath) {
|
|
|
92119
92831
|
if (!sourceStat.isDirectory()) {
|
|
92120
92832
|
throw badRequest(`Path is not a directory: ${sourcePath}`);
|
|
92121
92833
|
}
|
|
92122
|
-
const directManifestPath =
|
|
92834
|
+
const directManifestPath = join40(sourcePath, "manifest.json");
|
|
92123
92835
|
try {
|
|
92124
92836
|
await access5(directManifestPath);
|
|
92125
92837
|
const manifest = await readAndValidateManifest(directManifestPath);
|
|
@@ -92130,7 +92842,7 @@ async function resolvePluginManifest(sourcePath) {
|
|
|
92130
92842
|
const dirName = basename9(sourcePath).toLowerCase();
|
|
92131
92843
|
if (DIST_DIR_NAMES.has(dirName)) {
|
|
92132
92844
|
const parentDir = dirname12(sourcePath);
|
|
92133
|
-
const parentManifestPath =
|
|
92845
|
+
const parentManifestPath = join40(parentDir, "manifest.json");
|
|
92134
92846
|
try {
|
|
92135
92847
|
await access5(parentManifestPath);
|
|
92136
92848
|
const manifest = await readAndValidateManifest(parentManifestPath);
|
|
@@ -93601,8 +94313,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
93601
94313
|
});
|
|
93602
94314
|
if (retrySpecification) {
|
|
93603
94315
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
93604
|
-
const { join:
|
|
93605
|
-
const promptPath =
|
|
94316
|
+
const { join: join72 } = await import("node:path");
|
|
94317
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
93606
94318
|
await rm6(promptPath, { force: true });
|
|
93607
94319
|
await scopedStore.logEntry(req.params.id, "Retry requested from dashboard (planning retry budget reset)");
|
|
93608
94320
|
const updated2 = await scopedStore.getTask(req.params.id);
|
|
@@ -94067,8 +94779,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94067
94779
|
await scopedStore.logEntry(task.id, "Plan rejected by user", "Specification will be regenerated");
|
|
94068
94780
|
await scopedStore.updateTask(task.id, { status: void 0 });
|
|
94069
94781
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
94070
|
-
const { join:
|
|
94071
|
-
const promptPath =
|
|
94782
|
+
const { join: join72 } = await import("node:path");
|
|
94783
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94072
94784
|
await rm6(promptPath, { force: true });
|
|
94073
94785
|
const updated = await scopedStore.getTask(task.id);
|
|
94074
94786
|
res.json(updated);
|
|
@@ -94338,8 +95050,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94338
95050
|
if (task.column === "triage") {
|
|
94339
95051
|
await scopedStore.logEntry(task.id, "AI spec revision requested", feedback);
|
|
94340
95052
|
const { rm: rm7 } = await import("node:fs/promises");
|
|
94341
|
-
const { join:
|
|
94342
|
-
const promptPath2 =
|
|
95053
|
+
const { join: join73 } = await import("node:path");
|
|
95054
|
+
const promptPath2 = join73(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94343
95055
|
await rm7(promptPath2, { force: true });
|
|
94344
95056
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94345
95057
|
const updated2 = await scopedStore.getTask(task.id);
|
|
@@ -94355,8 +95067,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94355
95067
|
await scopedStore.logEntry(task.id, "AI spec revision requested", feedback);
|
|
94356
95068
|
const updated = await scopedStore.moveTask(task.id, "triage");
|
|
94357
95069
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
94358
|
-
const { join:
|
|
94359
|
-
const promptPath =
|
|
95070
|
+
const { join: join72 } = await import("node:path");
|
|
95071
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94360
95072
|
await rm6(promptPath, { force: true });
|
|
94361
95073
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94362
95074
|
res.json(updated);
|
|
@@ -94376,8 +95088,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94376
95088
|
if (task.column === "triage") {
|
|
94377
95089
|
await scopedStore.logEntry(task.id, "Specification rebuild requested by user");
|
|
94378
95090
|
const { rm: rm7 } = await import("node:fs/promises");
|
|
94379
|
-
const { join:
|
|
94380
|
-
const promptPath2 =
|
|
95091
|
+
const { join: join73 } = await import("node:path");
|
|
95092
|
+
const promptPath2 = join73(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94381
95093
|
await rm7(promptPath2, { force: true });
|
|
94382
95094
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94383
95095
|
const updated2 = await scopedStore.getTask(task.id);
|
|
@@ -94391,8 +95103,8 @@ function registerTaskWorkflowRoutes(ctx, deps) {
|
|
|
94391
95103
|
await scopedStore.logEntry(task.id, "Specification rebuild requested by user");
|
|
94392
95104
|
const updated = await scopedStore.moveTask(task.id, "triage");
|
|
94393
95105
|
const { rm: rm6 } = await import("node:fs/promises");
|
|
94394
|
-
const { join:
|
|
94395
|
-
const promptPath =
|
|
95106
|
+
const { join: join72 } = await import("node:path");
|
|
95107
|
+
const promptPath = join72(scopedStore.getRootDir(), ".fusion", "tasks", task.id, "PROMPT.md");
|
|
94396
95108
|
await rm6(promptPath, { force: true });
|
|
94397
95109
|
await scopedStore.updateTask(task.id, { status: "needs-replan" });
|
|
94398
95110
|
res.json(updated);
|
|
@@ -95773,14 +96485,15 @@ __export(chat_exports, {
|
|
|
95773
96485
|
__setBuildAgentChatPrompt: () => __setBuildAgentChatPrompt,
|
|
95774
96486
|
__setChatDiagnostics: () => __setChatDiagnostics,
|
|
95775
96487
|
__setCreateFnAgent: () => __setCreateFnAgent2,
|
|
96488
|
+
__setCreateResolvedAgentSession: () => __setCreateResolvedAgentSession,
|
|
95776
96489
|
chatStreamManager: () => chatStreamManager,
|
|
95777
96490
|
checkRateLimit: () => checkRateLimit5,
|
|
95778
96491
|
getRateLimitResetTime: () => getRateLimitResetTime5,
|
|
95779
96492
|
resolveFileReferences: () => resolveFileReferences
|
|
95780
96493
|
});
|
|
95781
96494
|
import { EventEmitter as EventEmitter29 } from "node:events";
|
|
95782
|
-
import { existsSync as
|
|
95783
|
-
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";
|
|
95784
96497
|
import { SessionManager as SessionManager3 } from "@mariozechner/pi-coding-agent";
|
|
95785
96498
|
function __getChatDiagnostics() {
|
|
95786
96499
|
return _diagnostics;
|
|
@@ -95810,7 +96523,7 @@ function validateFilePath(basePath, filePath) {
|
|
|
95810
96523
|
throw new Error(`Access denied: Absolute paths not allowed`);
|
|
95811
96524
|
}
|
|
95812
96525
|
const resolvedBase = resolve19(basePath);
|
|
95813
|
-
const resolvedPath = resolve19(
|
|
96526
|
+
const resolvedPath = resolve19(join41(resolvedBase, decodedPath));
|
|
95814
96527
|
const relativePath = relative9(resolvedBase, resolvedPath);
|
|
95815
96528
|
if (relativePath.startsWith("..") || relativePath.startsWith("../") || relativePath === "..") {
|
|
95816
96529
|
throw new Error(`Access denied: Path traversal detected`);
|
|
@@ -95887,6 +96600,9 @@ function getRateLimitResetTime5(ip) {
|
|
|
95887
96600
|
function __setCreateFnAgent2(mock) {
|
|
95888
96601
|
createFnAgent8 = mock;
|
|
95889
96602
|
}
|
|
96603
|
+
function __setCreateResolvedAgentSession(mock) {
|
|
96604
|
+
createResolvedAgentSession2 = mock;
|
|
96605
|
+
}
|
|
95890
96606
|
function __setBuildAgentChatPrompt(mock) {
|
|
95891
96607
|
buildAgentChatPromptFn = mock;
|
|
95892
96608
|
}
|
|
@@ -95894,9 +96610,11 @@ function __resetChatState() {
|
|
|
95894
96610
|
chatStreamManager.reset();
|
|
95895
96611
|
rateLimits5.clear();
|
|
95896
96612
|
buildAgentChatPromptFn = void 0;
|
|
96613
|
+
createFnAgent8 = createFnAgent2;
|
|
96614
|
+
createResolvedAgentSession2 = createResolvedAgentSession;
|
|
95897
96615
|
__setChatDiagnostics(null);
|
|
95898
96616
|
}
|
|
95899
|
-
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;
|
|
95900
96618
|
var init_chat = __esm({
|
|
95901
96619
|
"../dashboard/src/chat.ts"() {
|
|
95902
96620
|
"use strict";
|
|
@@ -95905,6 +96623,7 @@ var init_chat = __esm({
|
|
|
95905
96623
|
init_src2();
|
|
95906
96624
|
init_src2();
|
|
95907
96625
|
createFnAgent8 = createFnAgent2;
|
|
96626
|
+
createResolvedAgentSession2 = createResolvedAgentSession;
|
|
95908
96627
|
defaultDiagnostics = {
|
|
95909
96628
|
log(message, ...args) {
|
|
95910
96629
|
console.log(`[chat] ${message}`, ...args);
|
|
@@ -96023,13 +96742,49 @@ var init_chat = __esm({
|
|
|
96023
96742
|
};
|
|
96024
96743
|
chatStreamManager = new ChatStreamManager();
|
|
96025
96744
|
ChatManager = class {
|
|
96026
|
-
constructor(chatStore, rootDir, agentStore) {
|
|
96745
|
+
constructor(chatStore, rootDir, agentStore, pluginRunner, getSettings) {
|
|
96027
96746
|
this.chatStore = chatStore;
|
|
96028
96747
|
this.rootDir = rootDir;
|
|
96029
96748
|
this.agentStore = agentStore;
|
|
96749
|
+
this.pluginRunner = pluginRunner;
|
|
96750
|
+
this.getSettings = getSettings;
|
|
96030
96751
|
}
|
|
96031
96752
|
agentStoreReady;
|
|
96032
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
|
+
}
|
|
96033
96788
|
/**
|
|
96034
96789
|
* Resolve the per-chat pi/Claude CLI SessionManager.
|
|
96035
96790
|
*
|
|
@@ -96048,7 +96803,7 @@ var init_chat = __esm({
|
|
|
96048
96803
|
* keep the CLI session stable across user messages.
|
|
96049
96804
|
*/
|
|
96050
96805
|
resolveCliSessionManager(session) {
|
|
96051
|
-
if (session.cliSessionFile &&
|
|
96806
|
+
if (session.cliSessionFile && existsSync32(session.cliSessionFile)) {
|
|
96052
96807
|
try {
|
|
96053
96808
|
return SessionManager3.open(session.cliSessionFile);
|
|
96054
96809
|
} catch (err) {
|
|
@@ -96182,6 +96937,7 @@ var init_chat = __esm({
|
|
|
96182
96937
|
let accumulatedText = "";
|
|
96183
96938
|
const toolCallsAccum = [];
|
|
96184
96939
|
const pendingToolStarts = /* @__PURE__ */ new Map();
|
|
96940
|
+
let fallbackInfo;
|
|
96185
96941
|
try {
|
|
96186
96942
|
if (!session) {
|
|
96187
96943
|
chatStreamManager.broadcast(sessionId, {
|
|
@@ -96207,30 +96963,12 @@ var init_chat = __esm({
|
|
|
96207
96963
|
});
|
|
96208
96964
|
return;
|
|
96209
96965
|
}
|
|
96210
|
-
const
|
|
96211
|
-
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;
|
|
96212
96971
|
const needsTitle = session.title === null || session.title === void 0 || session.title.trim() === "";
|
|
96213
|
-
if (needsTitle) {
|
|
96214
|
-
(async () => {
|
|
96215
|
-
try {
|
|
96216
|
-
const generated = await summarizeTitle(
|
|
96217
|
-
content.trim(),
|
|
96218
|
-
this.rootDir,
|
|
96219
|
-
effectiveModelProvider,
|
|
96220
|
-
effectiveModelId
|
|
96221
|
-
);
|
|
96222
|
-
const title = generated ?? content.trim().slice(0, 60).trim();
|
|
96223
|
-
if (title) {
|
|
96224
|
-
this.chatStore.updateSession(sessionId, { title });
|
|
96225
|
-
}
|
|
96226
|
-
} catch {
|
|
96227
|
-
const fallback2 = content.trim().slice(0, 60).trim();
|
|
96228
|
-
if (fallback2) {
|
|
96229
|
-
this.chatStore.updateSession(sessionId, { title: fallback2 });
|
|
96230
|
-
}
|
|
96231
|
-
}
|
|
96232
|
-
})();
|
|
96233
|
-
}
|
|
96234
96972
|
await ensureEngineReady5();
|
|
96235
96973
|
if (!createFnAgent8) {
|
|
96236
96974
|
throw new Error("AI agent not available");
|
|
@@ -96261,6 +96999,35 @@ var init_chat = __esm({
|
|
|
96261
96999
|
diagnostics6.warn(`Failed to build enriched system prompt for ${agent.id}: ${message}`);
|
|
96262
97000
|
}
|
|
96263
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
|
+
}
|
|
96264
97031
|
if (mentions.length > 0) {
|
|
96265
97032
|
const mentionContext = await this.buildMentionContext(mentions, mentionAgents);
|
|
96266
97033
|
if (mentionContext) {
|
|
@@ -96273,7 +97040,10 @@ ${mentionContext}`;
|
|
|
96273
97040
|
const attachmentSummary = attachments && attachments.length > 0 ? `[User attached: ${attachments.map((attachment) => `${attachment.originalName} (${attachment.mimeType}, ${formatAttachmentSize(attachment.size)})`).join(", ")}]` : "";
|
|
96274
97041
|
const promptContent = [attachmentSummary, resolvedContent].filter(Boolean).join("\n\n");
|
|
96275
97042
|
const sessionManager = this.resolveCliSessionManager(session);
|
|
96276
|
-
|
|
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 = {
|
|
96277
97047
|
cwd: this.rootDir,
|
|
96278
97048
|
systemPrompt,
|
|
96279
97049
|
tools: "coding",
|
|
@@ -96282,6 +97052,14 @@ ${mentionContext}`;
|
|
|
96282
97052
|
defaultProvider: effectiveModelProvider,
|
|
96283
97053
|
defaultModelId: effectiveModelId
|
|
96284
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
|
+
},
|
|
96285
97063
|
onThinking: (delta) => {
|
|
96286
97064
|
accumulatedThinking += delta;
|
|
96287
97065
|
chatStreamManager.broadcast(sessionId, {
|
|
@@ -96322,16 +97100,35 @@ ${mentionContext}`;
|
|
|
96322
97100
|
data: { toolName: name, isError, result }
|
|
96323
97101
|
});
|
|
96324
97102
|
}
|
|
96325
|
-
}
|
|
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
|
+
}
|
|
96326
97115
|
this.activeGenerations.set(sessionId, { abortController, agentResult });
|
|
96327
97116
|
if (abortController.signal.aborted) {
|
|
96328
97117
|
agentResult.session.dispose?.();
|
|
96329
97118
|
return;
|
|
96330
97119
|
}
|
|
96331
|
-
await agentResult.session
|
|
97120
|
+
await promptWithFallback(agentResult.session, promptContent);
|
|
96332
97121
|
if (abortController.signal.aborted) {
|
|
96333
97122
|
return;
|
|
96334
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
|
+
}
|
|
96335
97132
|
let responseText = "";
|
|
96336
97133
|
const lastMessage = agentResult.session.state.messages.filter((m) => m.role === "assistant").pop();
|
|
96337
97134
|
if (lastMessage?.content) {
|
|
@@ -96342,11 +97139,18 @@ ${mentionContext}`;
|
|
|
96342
97139
|
}
|
|
96343
97140
|
}
|
|
96344
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
|
+
}
|
|
96345
97149
|
const assistantMessage = this.chatStore.addMessage(sessionId, {
|
|
96346
97150
|
role: "assistant",
|
|
96347
97151
|
content: finalResponseText,
|
|
96348
97152
|
thinkingOutput: accumulatedThinking || void 0,
|
|
96349
|
-
metadata:
|
|
97153
|
+
metadata: Object.keys(assistantMetadata).length > 0 ? assistantMetadata : void 0
|
|
96350
97154
|
});
|
|
96351
97155
|
chatStreamManager.broadcast(sessionId, {
|
|
96352
97156
|
type: "done",
|
|
@@ -96370,6 +97174,7 @@ ${mentionContext}`;
|
|
|
96370
97174
|
thinkingOutput: accumulatedThinking || void 0,
|
|
96371
97175
|
metadata: {
|
|
96372
97176
|
interrupted: true,
|
|
97177
|
+
...fallbackInfo ? { fallback: fallbackInfo } : {},
|
|
96373
97178
|
...toolCallsAccum.length > 0 ? { toolCalls: toolCallsAccum } : {}
|
|
96374
97179
|
}
|
|
96375
97180
|
});
|
|
@@ -96432,7 +97237,7 @@ ${mentionContext}`;
|
|
|
96432
97237
|
import { randomUUID as randomUUID19 } from "node:crypto";
|
|
96433
97238
|
import { createReadStream as createReadStream2 } from "node:fs";
|
|
96434
97239
|
import { mkdir as mkdir13, rm as rm2, writeFile as writeFile13 } from "node:fs/promises";
|
|
96435
|
-
import { basename as basename10, join as
|
|
97240
|
+
import { basename as basename10, join as join42, resolve as resolve20 } from "node:path";
|
|
96436
97241
|
function resolveAttachmentPath(rootDir, sessionId, filename) {
|
|
96437
97242
|
const sessionDir = resolve20(rootDir, ".fusion", "chat-attachments", sessionId);
|
|
96438
97243
|
const safeName = basename10(filename);
|
|
@@ -96690,7 +97495,7 @@ function registerChatRoutes(ctx, deps) {
|
|
|
96690
97495
|
await mkdir13(sessionDir, { recursive: true });
|
|
96691
97496
|
const sanitizedFilename = (file.originalname || "attachment").replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
96692
97497
|
const filename = `${Date.now()}-${sanitizedFilename}`;
|
|
96693
|
-
const filePath =
|
|
97498
|
+
const filePath = join42(sessionDir, filename);
|
|
96694
97499
|
await writeFile13(filePath, file.buffer);
|
|
96695
97500
|
const attachment = {
|
|
96696
97501
|
id: `att-${randomUUID19().slice(0, 8)}`,
|
|
@@ -101612,7 +102417,7 @@ var init_remote_auth = __esm({
|
|
|
101612
102417
|
|
|
101613
102418
|
// ../dashboard/src/routes/register-settings-memory-routes.ts
|
|
101614
102419
|
import { execFile as execFile5 } from "node:child_process";
|
|
101615
|
-
import { homedir as
|
|
102420
|
+
import { homedir as homedir7 } from "node:os";
|
|
101616
102421
|
import { promisify as promisify12 } from "node:util";
|
|
101617
102422
|
function registerSettingsMemoryRoutes(ctx, deps) {
|
|
101618
102423
|
const { router, options, store, runtimeLogger, getProjectContext: getProjectContext3, rethrowAsApiError: rethrowAsApiError8 } = ctx;
|
|
@@ -101708,7 +102513,7 @@ function registerSettingsMemoryRoutes(ctx, deps) {
|
|
|
101708
102513
|
try {
|
|
101709
102514
|
await execFileAsync7("mv", [tempPath, globalInstallPath], { timeout: 3e4 });
|
|
101710
102515
|
} catch (error) {
|
|
101711
|
-
const localBinDir = `${
|
|
102516
|
+
const localBinDir = `${homedir7()}/.local/bin`;
|
|
101712
102517
|
const localInstallPath = `${localBinDir}/cloudflared`;
|
|
101713
102518
|
attemptedCommands.push(`mkdir -p ${localBinDir}`);
|
|
101714
102519
|
attemptedCommands.push(`mv ${tempPath} ${localInstallPath}`);
|
|
@@ -103074,7 +103879,7 @@ import * as os3 from "os";
|
|
|
103074
103879
|
import * as path2 from "path";
|
|
103075
103880
|
import * as fs2 from "node:fs";
|
|
103076
103881
|
import { createRequire as createRequire3 } from "node:module";
|
|
103077
|
-
import { join as
|
|
103882
|
+
import { join as join43, dirname as dirname13 } from "node:path";
|
|
103078
103883
|
function getNativePrebuildName() {
|
|
103079
103884
|
const platform4 = process.platform === "darwin" ? "darwin" : process.platform === "linux" ? "linux" : process.platform === "win32" ? "win32" : "unknown";
|
|
103080
103885
|
const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
|
|
@@ -103084,12 +103889,12 @@ function findInstalledNodePtyNativeDir() {
|
|
|
103084
103889
|
try {
|
|
103085
103890
|
const packageJsonPath = require2.resolve("node-pty/package.json");
|
|
103086
103891
|
const pkgRoot = dirname13(packageJsonPath);
|
|
103087
|
-
const releaseDir =
|
|
103088
|
-
if (fs2.existsSync(
|
|
103892
|
+
const releaseDir = join43(pkgRoot, "build", "Release");
|
|
103893
|
+
if (fs2.existsSync(join43(releaseDir, "pty.node"))) {
|
|
103089
103894
|
return releaseDir;
|
|
103090
103895
|
}
|
|
103091
|
-
const prebuildDir =
|
|
103092
|
-
if (fs2.existsSync(
|
|
103896
|
+
const prebuildDir = join43(pkgRoot, "prebuilds", getNativePrebuildName());
|
|
103897
|
+
if (fs2.existsSync(join43(prebuildDir, "pty.node"))) {
|
|
103093
103898
|
return prebuildDir;
|
|
103094
103899
|
}
|
|
103095
103900
|
return null;
|
|
@@ -103115,8 +103920,8 @@ function ensureNodePtyNativePermissions() {
|
|
|
103115
103920
|
candidateDirs.add(installedNativeDir);
|
|
103116
103921
|
}
|
|
103117
103922
|
for (const nativeDir of candidateDirs) {
|
|
103118
|
-
const helperPath =
|
|
103119
|
-
const nativeModulePath =
|
|
103923
|
+
const helperPath = join43(nativeDir, "spawn-helper");
|
|
103924
|
+
const nativeModulePath = join43(nativeDir, "pty.node");
|
|
103120
103925
|
try {
|
|
103121
103926
|
fs2.chmodSync(helperPath, 493);
|
|
103122
103927
|
} catch {
|
|
@@ -103134,14 +103939,14 @@ function ensureNodePtyNativePermissions() {
|
|
|
103134
103939
|
function findStagedNativeDir() {
|
|
103135
103940
|
const prebuildName = getNativePrebuildName();
|
|
103136
103941
|
if (process.env.FUSION_RUNTIME_DIR) {
|
|
103137
|
-
const envPath =
|
|
103138
|
-
if (fs2.existsSync(
|
|
103942
|
+
const envPath = join43(process.env.FUSION_RUNTIME_DIR, prebuildName);
|
|
103943
|
+
if (fs2.existsSync(join43(envPath, "pty.node"))) {
|
|
103139
103944
|
return envPath;
|
|
103140
103945
|
}
|
|
103141
103946
|
}
|
|
103142
103947
|
const execDir = dirname13(process.execPath);
|
|
103143
|
-
const nextToBinary =
|
|
103144
|
-
if (fs2.existsSync(
|
|
103948
|
+
const nextToBinary = join43(execDir, "runtime", prebuildName);
|
|
103949
|
+
if (fs2.existsSync(join43(nextToBinary, "pty.node"))) {
|
|
103145
103950
|
return nextToBinary;
|
|
103146
103951
|
}
|
|
103147
103952
|
return null;
|
|
@@ -103161,7 +103966,7 @@ async function loadPtyModule() {
|
|
|
103161
103966
|
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
|
|
103162
103967
|
}
|
|
103163
103968
|
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
|
|
103164
|
-
const nativePath =
|
|
103969
|
+
const nativePath = join43(nativeDir, "pty.node");
|
|
103165
103970
|
if (fs2.existsSync(nativePath)) {
|
|
103166
103971
|
try {
|
|
103167
103972
|
const nativeModule = { exports: {} };
|
|
@@ -108851,7 +109656,7 @@ var init_terminal = __esm({
|
|
|
108851
109656
|
});
|
|
108852
109657
|
|
|
108853
109658
|
// ../dashboard/src/file-service.ts
|
|
108854
|
-
import { join as
|
|
109659
|
+
import { join as join44, resolve as resolve22, relative as relative11, dirname as dirname14, basename as basename12 } from "node:path";
|
|
108855
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";
|
|
108856
109661
|
async function getTaskBasePath(store, taskId) {
|
|
108857
109662
|
try {
|
|
@@ -108864,7 +109669,7 @@ async function getTaskBasePath(store, taskId) {
|
|
|
108864
109669
|
}
|
|
108865
109670
|
}
|
|
108866
109671
|
const rootDir = store.getRootDir();
|
|
108867
|
-
return resolve22(
|
|
109672
|
+
return resolve22(join44(rootDir, ".fusion", "tasks", taskId));
|
|
108868
109673
|
} catch (err) {
|
|
108869
109674
|
const error = err;
|
|
108870
109675
|
if (error.code === "ENOENT" || error.message && error.message.includes("not found")) {
|
|
@@ -108891,7 +109696,7 @@ function validatePath(basePath, filePath) {
|
|
|
108891
109696
|
throw new FileServiceError(`Access denied: Absolute paths not allowed`, "EINVAL");
|
|
108892
109697
|
}
|
|
108893
109698
|
const resolvedBase = resolve22(basePath);
|
|
108894
|
-
const resolvedPath = resolve22(
|
|
109699
|
+
const resolvedPath = resolve22(join44(resolvedBase, decodedPath));
|
|
108895
109700
|
const relativePath = relative11(resolvedBase, resolvedPath);
|
|
108896
109701
|
if (relativePath.startsWith("..") || relativePath.startsWith("../") || relativePath === "..") {
|
|
108897
109702
|
throw new FileServiceError(`Access denied: Path traversal detected`, "EINVAL");
|
|
@@ -108920,7 +109725,7 @@ async function listFilesForBasePath(basePath, subPath) {
|
|
|
108920
109725
|
const entries = await readdir8(targetPath, { withFileTypes: true });
|
|
108921
109726
|
const fileNodes = [];
|
|
108922
109727
|
for (const entry of entries) {
|
|
108923
|
-
const entryPath =
|
|
109728
|
+
const entryPath = join44(targetPath, entry.name);
|
|
108924
109729
|
const entryStats = await stat7(entryPath);
|
|
108925
109730
|
fileNodes.push({
|
|
108926
109731
|
name: entry.name,
|
|
@@ -109247,7 +110052,7 @@ async function renameWorkspaceFile(store, workspace, filePath, newName) {
|
|
|
109247
110052
|
}
|
|
109248
110053
|
throw err;
|
|
109249
110054
|
}
|
|
109250
|
-
const destPath =
|
|
110055
|
+
const destPath = join44(dirname14(resolvedPath), newName);
|
|
109251
110056
|
const destRelative = relative11(resolve22(workspaceBase), destPath);
|
|
109252
110057
|
if (destRelative.startsWith("..") || destRelative.startsWith("../") || destRelative === "..") {
|
|
109253
110058
|
throw new FileServiceError("Destination would be outside workspace", "EINVAL");
|
|
@@ -109340,7 +110145,7 @@ function isHiddenPathSegment(name) {
|
|
|
109340
110145
|
return name.startsWith(".");
|
|
109341
110146
|
}
|
|
109342
110147
|
async function walkDirForMarkdown(basePath, currentRelative, results, options) {
|
|
109343
|
-
const currentPath = currentRelative ?
|
|
110148
|
+
const currentPath = currentRelative ? join44(basePath, currentRelative) : basePath;
|
|
109344
110149
|
let entries;
|
|
109345
110150
|
try {
|
|
109346
110151
|
entries = await readdir8(currentPath, { withFileTypes: true });
|
|
@@ -109348,7 +110153,7 @@ async function walkDirForMarkdown(basePath, currentRelative, results, options) {
|
|
|
109348
110153
|
return;
|
|
109349
110154
|
}
|
|
109350
110155
|
for (const entry of entries) {
|
|
109351
|
-
const entryRelativePath = currentRelative ?
|
|
110156
|
+
const entryRelativePath = currentRelative ? join44(currentRelative, entry.name) : entry.name;
|
|
109352
110157
|
if (entry.isDirectory()) {
|
|
109353
110158
|
if (MARKDOWN_SCAN_EXCLUDED_DIRS.has(entry.name)) {
|
|
109354
110159
|
continue;
|
|
@@ -109365,7 +110170,7 @@ async function walkDirForMarkdown(basePath, currentRelative, results, options) {
|
|
|
109365
110170
|
if (!options.showHidden && isHiddenPathSegment(entry.name)) {
|
|
109366
110171
|
continue;
|
|
109367
110172
|
}
|
|
109368
|
-
const fullPath =
|
|
110173
|
+
const fullPath = join44(basePath, entryRelativePath);
|
|
109369
110174
|
let fileStats;
|
|
109370
110175
|
try {
|
|
109371
110176
|
fileStats = await stat7(fullPath);
|
|
@@ -109415,7 +110220,7 @@ async function scanMarkdownFiles(store, options) {
|
|
|
109415
110220
|
return;
|
|
109416
110221
|
}
|
|
109417
110222
|
for (const entry of entries) {
|
|
109418
|
-
const entryRelativePath = relativeDir ?
|
|
110223
|
+
const entryRelativePath = relativeDir ? join44(relativeDir, entry.name) : entry.name;
|
|
109419
110224
|
let shouldRecurse = entry.isDirectory();
|
|
109420
110225
|
if (!shouldRecurse && typeof entry.isSymbolicLink === "function" && entry.isSymbolicLink()) {
|
|
109421
110226
|
let symlinkPath;
|
|
@@ -109505,8 +110310,8 @@ async function searchWorkspaceFiles(store, workspace, query) {
|
|
|
109505
110310
|
if (entry.isDirectory() && EXCLUDED_DIRS.has(entry.name)) {
|
|
109506
110311
|
continue;
|
|
109507
110312
|
}
|
|
109508
|
-
const fullPath =
|
|
109509
|
-
const relPath =
|
|
110313
|
+
const fullPath = join44(dir2, entry.name);
|
|
110314
|
+
const relPath = join44(relativeDir, entry.name);
|
|
109510
110315
|
if (entry.isFile()) {
|
|
109511
110316
|
if (entry.name.toLowerCase().includes(lowerQuery)) {
|
|
109512
110317
|
results.push({
|
|
@@ -109527,8 +110332,8 @@ async function copyDirectoryRecursive(source, destination) {
|
|
|
109527
110332
|
await mkdir14(destination, { recursive: true });
|
|
109528
110333
|
const entries = await readdir8(source, { withFileTypes: true });
|
|
109529
110334
|
for (const entry of entries) {
|
|
109530
|
-
const sourcePath =
|
|
109531
|
-
const destPath =
|
|
110335
|
+
const sourcePath = join44(source, entry.name);
|
|
110336
|
+
const destPath = join44(destination, entry.name);
|
|
109532
110337
|
if (entry.isDirectory()) {
|
|
109533
110338
|
await copyDirectoryRecursive(sourcePath, destPath);
|
|
109534
110339
|
} else {
|
|
@@ -113597,7 +114402,7 @@ var require_BufferList = __commonJS({
|
|
|
113597
114402
|
this.head = this.tail = null;
|
|
113598
114403
|
this.length = 0;
|
|
113599
114404
|
};
|
|
113600
|
-
BufferList.prototype.join = function
|
|
114405
|
+
BufferList.prototype.join = function join72(s) {
|
|
113601
114406
|
if (this.length === 0) return "";
|
|
113602
114407
|
var p = this.head;
|
|
113603
114408
|
var ret = "" + p.data;
|
|
@@ -136106,7 +136911,7 @@ var init_exec_file = __esm({
|
|
|
136106
136911
|
|
|
136107
136912
|
// ../dashboard/src/routes/register-project-routes.ts
|
|
136108
136913
|
import * as fsPromises from "node:fs/promises";
|
|
136109
|
-
import { dirname as dirname15, isAbsolute as isAbsolute17, join as
|
|
136914
|
+
import { dirname as dirname15, isAbsolute as isAbsolute17, join as join45 } from "node:path";
|
|
136110
136915
|
var access9, stat8, mkdir15, readdir9, rm3, registerProjectRoutes;
|
|
136111
136916
|
var init_register_project_routes = __esm({
|
|
136112
136917
|
"../dashboard/src/routes/register-project-routes.ts"() {
|
|
@@ -136329,7 +137134,7 @@ var init_register_project_routes = __esm({
|
|
|
136329
137134
|
}
|
|
136330
137135
|
}
|
|
136331
137136
|
let hasFusionDir = false;
|
|
136332
|
-
const fusionDirPath =
|
|
137137
|
+
const fusionDirPath = join45(normalizedPath, ".fusion");
|
|
136333
137138
|
try {
|
|
136334
137139
|
await access9(fusionDirPath);
|
|
136335
137140
|
hasFusionDir = true;
|
|
@@ -136393,8 +137198,8 @@ var init_register_project_routes = __esm({
|
|
|
136393
137198
|
const entries = await readdir9(searchPath, { withFileTypes: true });
|
|
136394
137199
|
for (const entry of entries) {
|
|
136395
137200
|
if (!entry.isDirectory()) continue;
|
|
136396
|
-
const dirPath =
|
|
136397
|
-
if (isValidSqliteDatabaseFile(
|
|
137201
|
+
const dirPath = join45(searchPath, entry.name);
|
|
137202
|
+
if (isValidSqliteDatabaseFile(join45(dirPath, ".fusion", "fusion.db"))) {
|
|
136398
137203
|
detected.push({
|
|
136399
137204
|
path: dirPath,
|
|
136400
137205
|
suggestedName: entry.name,
|
|
@@ -137408,14 +138213,14 @@ var init_register_docker_provisioning_routes = __esm({
|
|
|
137408
138213
|
|
|
137409
138214
|
// ../dashboard/src/auth-paths.ts
|
|
137410
138215
|
import path3 from "node:path";
|
|
137411
|
-
import { homedir as
|
|
137412
|
-
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()) {
|
|
137413
138218
|
return path3.join(home, ".fusion", "agent");
|
|
137414
138219
|
}
|
|
137415
|
-
function getFusionAuthPath2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
138220
|
+
function getFusionAuthPath2(home = process.env.HOME || process.env.USERPROFILE || homedir8()) {
|
|
137416
138221
|
return path3.join(getFusionAgentDir2(home), "auth.json");
|
|
137417
138222
|
}
|
|
137418
|
-
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()) {
|
|
137419
138224
|
return [
|
|
137420
138225
|
path3.join(home, ".fusion", "agent", "auth.json"),
|
|
137421
138226
|
path3.join(home, ".fusion", "auth.json"),
|
|
@@ -140718,7 +141523,7 @@ Rules:
|
|
|
140718
141523
|
import { createWriteStream } from "node:fs";
|
|
140719
141524
|
import * as fsPromises2 from "node:fs/promises";
|
|
140720
141525
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
140721
|
-
import { join as
|
|
141526
|
+
import { join as join46, resolve as resolve23 } from "node:path";
|
|
140722
141527
|
import { Readable } from "node:stream";
|
|
140723
141528
|
import { pipeline as streamPipeline } from "node:stream/promises";
|
|
140724
141529
|
function registerAgentImportExportRoutes(ctx) {
|
|
@@ -140759,7 +141564,7 @@ function registerAgentImportExportRoutes(ctx) {
|
|
|
140759
141564
|
} else if (typeof outputDir === "string") {
|
|
140760
141565
|
throw badRequest("outputDir cannot be empty");
|
|
140761
141566
|
} else {
|
|
140762
|
-
resolvedOutputDir = await mkdtemp(
|
|
141567
|
+
resolvedOutputDir = await mkdtemp(join46(tmpdir4(), "fusion-agent-export-"));
|
|
140763
141568
|
}
|
|
140764
141569
|
const result = await exportAgentsToDirectory2(agentsToExport, resolvedOutputDir, {
|
|
140765
141570
|
companyName: typeof companyName === "string" ? companyName : void 0,
|
|
@@ -140886,7 +141691,7 @@ ${body}`;
|
|
|
140886
141691
|
return result;
|
|
140887
141692
|
}
|
|
140888
141693
|
const safeCompanySlug = companySlug ? slugifyPathSegment2(companySlug, "unknown-company") : "unknown-company";
|
|
140889
|
-
const skillsBaseDir =
|
|
141694
|
+
const skillsBaseDir = join46(projectRoot, "skills", "imported", safeCompanySlug);
|
|
140890
141695
|
const usedSlugs = /* @__PURE__ */ new Set();
|
|
140891
141696
|
for (const skill of skills) {
|
|
140892
141697
|
const name = typeof skill.name === "string" && skill.name.trim().length > 0 ? skill.name.trim() : null;
|
|
@@ -140903,8 +141708,8 @@ ${body}`;
|
|
|
140903
141708
|
skillSlug = `${skillSlug}-${counter}`;
|
|
140904
141709
|
}
|
|
140905
141710
|
usedSlugs.add(skillSlug);
|
|
140906
|
-
const skillDir =
|
|
140907
|
-
const skillPath =
|
|
141711
|
+
const skillDir = join46(skillsBaseDir, skillSlug);
|
|
141712
|
+
const skillPath = join46(skillDir, "SKILL.md");
|
|
140908
141713
|
try {
|
|
140909
141714
|
await access10(skillPath);
|
|
140910
141715
|
result.skipped.push(name);
|
|
@@ -141074,8 +141879,8 @@ ${body}`;
|
|
|
141074
141879
|
const archiveUrl = `https://github.com/${repoOwner}/${repoName}/archive/refs/heads/main.tar.gz`;
|
|
141075
141880
|
let tempDir = null;
|
|
141076
141881
|
try {
|
|
141077
|
-
tempDir = await mkdtemp(
|
|
141078
|
-
const archivePath =
|
|
141882
|
+
tempDir = await mkdtemp(join46(tmpdir4(), `fn-agent-import-${importCompanySlug}-`));
|
|
141883
|
+
const archivePath = join46(tempDir, "archive.tar.gz");
|
|
141079
141884
|
const downloadController = new AbortController();
|
|
141080
141885
|
const downloadTimeout = setTimeout(() => downloadController.abort(), 3e4);
|
|
141081
141886
|
let archiveResponse;
|
|
@@ -142356,7 +143161,7 @@ import * as path4 from "node:path";
|
|
|
142356
143161
|
import { readFile as readFile20 } from "node:fs/promises";
|
|
142357
143162
|
import * as https from "node:https";
|
|
142358
143163
|
import * as child_process from "node:child_process";
|
|
142359
|
-
function
|
|
143164
|
+
function getHomeDir6() {
|
|
142360
143165
|
return process.env.HOME || process.env.USERPROFILE || os4.homedir();
|
|
142361
143166
|
}
|
|
142362
143167
|
function execFileAsync5(file, args, options) {
|
|
@@ -142866,8 +143671,8 @@ async function fetchClaudeUsage(authStorage) {
|
|
|
142866
143671
|
}
|
|
142867
143672
|
if (!creds) {
|
|
142868
143673
|
const credPaths = [
|
|
142869
|
-
path4.join(
|
|
142870
|
-
path4.join(
|
|
143674
|
+
path4.join(getHomeDir6(), ".claude", ".credentials.json"),
|
|
143675
|
+
path4.join(getHomeDir6(), ".config", "claude", ".credentials.json")
|
|
142871
143676
|
];
|
|
142872
143677
|
for (const p of credPaths) {
|
|
142873
143678
|
try {
|
|
@@ -143027,7 +143832,7 @@ async function fetchCodexUsage() {
|
|
|
143027
143832
|
status: "no-auth",
|
|
143028
143833
|
windows: []
|
|
143029
143834
|
};
|
|
143030
|
-
const codexHome = process.env.CODEX_HOME || path4.join(
|
|
143835
|
+
const codexHome = process.env.CODEX_HOME || path4.join(getHomeDir6(), ".codex");
|
|
143031
143836
|
const authPath = path4.join(codexHome, "auth.json");
|
|
143032
143837
|
let auth = null;
|
|
143033
143838
|
try {
|
|
@@ -143118,7 +143923,7 @@ async function fetchGeminiUsage() {
|
|
|
143118
143923
|
status: "no-auth",
|
|
143119
143924
|
windows: []
|
|
143120
143925
|
};
|
|
143121
|
-
const oauthPath = path4.join(
|
|
143926
|
+
const oauthPath = path4.join(getHomeDir6(), ".gemini", "oauth_creds.json");
|
|
143122
143927
|
let oauthCreds = null;
|
|
143123
143928
|
try {
|
|
143124
143929
|
oauthCreds = JSON.parse(await readFile20(oauthPath, "utf-8"));
|
|
@@ -143134,7 +143939,7 @@ async function fetchGeminiUsage() {
|
|
|
143134
143939
|
const claims = decodeJwtPayload(oauthCreds.id_token);
|
|
143135
143940
|
if (claims?.email) usage.email = claims.email;
|
|
143136
143941
|
}
|
|
143137
|
-
const settingsPath = path4.join(
|
|
143942
|
+
const settingsPath = path4.join(getHomeDir6(), ".gemini", "settings.json");
|
|
143138
143943
|
try {
|
|
143139
143944
|
const settings = JSON.parse(await readFile20(settingsPath, "utf-8"));
|
|
143140
143945
|
const authType = settings?.security?.auth?.selectedType;
|
|
@@ -143771,6 +144576,13 @@ var init_register_auth_routes = __esm({
|
|
|
143771
144576
|
}
|
|
143772
144577
|
return key.slice(0, 3) + "\u2022\u2022\u2022\u2022\u2022" + key.slice(-4);
|
|
143773
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
|
+
}
|
|
143774
144586
|
const loginInProgress = /* @__PURE__ */ new Map();
|
|
143775
144587
|
const OAUTH_SESSION_TTL_MS = 5 * 60 * 1e3;
|
|
143776
144588
|
const oauthSessions = /* @__PURE__ */ new Map();
|
|
@@ -143831,6 +144643,40 @@ var init_register_auth_routes = __esm({
|
|
|
143831
144643
|
path: `${redirectUriUrl.pathname}${redirectUriUrl.search}`
|
|
143832
144644
|
};
|
|
143833
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
|
+
}
|
|
143834
144680
|
router.get("/auth/status", async (_req, res) => {
|
|
143835
144681
|
try {
|
|
143836
144682
|
const storage = getAuthStorage();
|
|
@@ -143839,7 +144685,7 @@ var init_register_auth_routes = __esm({
|
|
|
143839
144685
|
const providers = oauthProviders.map((p) => ({
|
|
143840
144686
|
id: p.id,
|
|
143841
144687
|
name: p.name,
|
|
143842
|
-
authenticated: storage.hasAuth(p.id),
|
|
144688
|
+
authenticated: storage.hasAuth(p.id) && !isExpiredOauthCredential(p.id, storage),
|
|
143843
144689
|
type: "oauth",
|
|
143844
144690
|
loginInProgress: loginInProgress.has(p.id)
|
|
143845
144691
|
}));
|
|
@@ -144093,7 +144939,29 @@ var init_register_auth_routes = __esm({
|
|
|
144093
144939
|
throw badRequest(`Unknown provider: ${provider}`);
|
|
144094
144940
|
}
|
|
144095
144941
|
const abortController = new AbortController();
|
|
144096
|
-
|
|
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);
|
|
144097
144965
|
let authResolve;
|
|
144098
144966
|
let authReject;
|
|
144099
144967
|
const authUrlPromise = new Promise((resolve44, reject2) => {
|
|
@@ -144102,12 +144970,16 @@ var init_register_auth_routes = __esm({
|
|
|
144102
144970
|
});
|
|
144103
144971
|
const loginPromise = storage.login(provider, {
|
|
144104
144972
|
onAuth: (info) => {
|
|
144105
|
-
authResolve({
|
|
144106
|
-
|
|
144107
|
-
|
|
144108
|
-
|
|
144109
|
-
return prompt.placeholder || "";
|
|
144973
|
+
authResolve({
|
|
144974
|
+
url: info.url,
|
|
144975
|
+
instructions: appendManualCodeHint(info.instructions, provider, origin)
|
|
144976
|
+
});
|
|
144110
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,
|
|
144111
144983
|
onProgress: () => {
|
|
144112
144984
|
},
|
|
144113
144985
|
// no-op for web UI
|
|
@@ -144126,7 +144998,7 @@ var init_register_auth_routes = __esm({
|
|
|
144126
144998
|
const authInfo = await authUrlPromise;
|
|
144127
144999
|
clearTimeout(timeout2);
|
|
144128
145000
|
let responseUrl = authInfo.url;
|
|
144129
|
-
if (
|
|
145001
|
+
if (shouldRewriteOauthRedirect(provider, origin)) {
|
|
144130
145002
|
const rewritten = rewriteAuthUrl(authInfo.url, origin);
|
|
144131
145003
|
setOauthSession(rewritten.state, {
|
|
144132
145004
|
port: rewritten.port,
|
|
@@ -144135,7 +145007,11 @@ var init_register_auth_routes = __esm({
|
|
|
144135
145007
|
});
|
|
144136
145008
|
responseUrl = rewritten.url;
|
|
144137
145009
|
}
|
|
144138
|
-
res.json({
|
|
145010
|
+
res.json({
|
|
145011
|
+
url: responseUrl,
|
|
145012
|
+
instructions: authInfo.instructions,
|
|
145013
|
+
manualCode: pendingLogin.manualCode
|
|
145014
|
+
});
|
|
144139
145015
|
} catch (err) {
|
|
144140
145016
|
if (err instanceof ApiError) {
|
|
144141
145017
|
throw err;
|
|
@@ -144157,7 +145033,9 @@ var init_register_auth_routes = __esm({
|
|
|
144157
145033
|
return;
|
|
144158
145034
|
}
|
|
144159
145035
|
loginInProgress.delete(provider);
|
|
144160
|
-
activeLogin.
|
|
145036
|
+
activeLogin.inputSubmitted = true;
|
|
145037
|
+
activeLogin.rejectInput(new Error("cancelled"));
|
|
145038
|
+
activeLogin.abortController.abort();
|
|
144161
145039
|
res.json({ success: true, cancelled: true });
|
|
144162
145040
|
} catch (err) {
|
|
144163
145041
|
if (err instanceof ApiError) {
|
|
@@ -144166,6 +145044,33 @@ var init_register_auth_routes = __esm({
|
|
|
144166
145044
|
rethrowAsApiError8(err);
|
|
144167
145045
|
}
|
|
144168
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
|
+
});
|
|
144169
145074
|
router.get("/auth/oauth-callback", async (req, res) => {
|
|
144170
145075
|
try {
|
|
144171
145076
|
const error = typeof req.query.error === "string" ? req.query.error : void 0;
|
|
@@ -145497,15 +146402,15 @@ var init_register_runtime_provider_routes = __esm({
|
|
|
145497
146402
|
});
|
|
145498
146403
|
|
|
145499
146404
|
// ../dashboard/src/cli-package-version.ts
|
|
145500
|
-
import { existsSync as
|
|
146405
|
+
import { existsSync as existsSync34, readFileSync as readFileSync12 } from "node:fs";
|
|
145501
146406
|
import { dirname as dirname16, resolve as resolve24 } from "node:path";
|
|
145502
146407
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
145503
146408
|
function readCliPackageVersion(pkgPath) {
|
|
145504
|
-
if (!
|
|
146409
|
+
if (!existsSync34(pkgPath)) {
|
|
145505
146410
|
return null;
|
|
145506
146411
|
}
|
|
145507
146412
|
try {
|
|
145508
|
-
const parsed = JSON.parse(
|
|
146413
|
+
const parsed = JSON.parse(readFileSync12(pkgPath, "utf-8"));
|
|
145509
146414
|
if (parsed.name === CLI_PACKAGE_NAME && typeof parsed.version === "string" && parsed.version.length > 0) {
|
|
145510
146415
|
return {
|
|
145511
146416
|
packageJsonPath: pkgPath,
|
|
@@ -145707,9 +146612,9 @@ var init_register_fn_binary_routes = __esm({
|
|
|
145707
146612
|
});
|
|
145708
146613
|
|
|
145709
146614
|
// ../dashboard/src/update-check.ts
|
|
145710
|
-
import { readFileSync as
|
|
146615
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
145711
146616
|
import { mkdir as mkdir17, rm as rm5, writeFile as writeFile15 } from "node:fs/promises";
|
|
145712
|
-
import { join as
|
|
146617
|
+
import { join as join48 } from "node:path";
|
|
145713
146618
|
function ttlForFrequency(frequency) {
|
|
145714
146619
|
switch (frequency) {
|
|
145715
146620
|
case "manual":
|
|
@@ -145723,7 +146628,7 @@ function ttlForFrequency(frequency) {
|
|
|
145723
146628
|
}
|
|
145724
146629
|
}
|
|
145725
146630
|
function getCachePath(fusionDir) {
|
|
145726
|
-
return
|
|
146631
|
+
return join48(fusionDir, CACHE_FILENAME);
|
|
145727
146632
|
}
|
|
145728
146633
|
function parseVersion(version) {
|
|
145729
146634
|
return version.split(".").slice(0, 3).map((part) => Number.parseInt(part, 10)).map((value) => Number.isFinite(value) ? value : 0);
|
|
@@ -145747,7 +146652,7 @@ function isValidResult(value) {
|
|
|
145747
146652
|
}
|
|
145748
146653
|
function readCachedUpdateCheck(fusionDir) {
|
|
145749
146654
|
try {
|
|
145750
|
-
const raw =
|
|
146655
|
+
const raw = readFileSync13(getCachePath(fusionDir), "utf-8");
|
|
145751
146656
|
const parsed = JSON.parse(raw);
|
|
145752
146657
|
return isValidResult(parsed) ? parsed : null;
|
|
145753
146658
|
} catch {
|
|
@@ -150394,7 +151299,7 @@ var init_todo_routes = __esm({
|
|
|
150394
151299
|
|
|
150395
151300
|
// ../dashboard/src/dev-server-detect.ts
|
|
150396
151301
|
import { glob, readFile as readFile21 } from "node:fs/promises";
|
|
150397
|
-
import { dirname as dirname17, join as
|
|
151302
|
+
import { dirname as dirname17, join as join49, relative as relative12, resolve as resolve25 } from "node:path";
|
|
150398
151303
|
async function readPackageJson(filePath) {
|
|
150399
151304
|
try {
|
|
150400
151305
|
const raw = await readFile21(filePath, "utf-8");
|
|
@@ -150437,7 +151342,7 @@ function scoreCandidate(scriptName, pkg) {
|
|
|
150437
151342
|
async function collectWorkspacePackageJsons(projectRoot) {
|
|
150438
151343
|
const discovered = /* @__PURE__ */ new Set();
|
|
150439
151344
|
try {
|
|
150440
|
-
await readFile21(
|
|
151345
|
+
await readFile21(join49(projectRoot, "pnpm-workspace.yaml"), "utf-8");
|
|
150441
151346
|
} catch {
|
|
150442
151347
|
}
|
|
150443
151348
|
for (const pattern of ["packages/*/package.json", "apps/*/package.json"]) {
|
|
@@ -150471,7 +151376,7 @@ function toSource(projectRoot, packageJsonPath) {
|
|
|
150471
151376
|
async function detectDevServerScripts(projectRoot) {
|
|
150472
151377
|
const root = resolve25(projectRoot);
|
|
150473
151378
|
const candidates = [];
|
|
150474
|
-
const rootPackagePath =
|
|
151379
|
+
const rootPackagePath = join49(root, "package.json");
|
|
150475
151380
|
const rootPackage = await readPackageJson(rootPackagePath);
|
|
150476
151381
|
if (rootPackage) {
|
|
150477
151382
|
for (const script of extractScripts(rootPackage)) {
|
|
@@ -150538,9 +151443,9 @@ var init_dev_server_detect = __esm({
|
|
|
150538
151443
|
|
|
150539
151444
|
// ../dashboard/src/dev-server-store.ts
|
|
150540
151445
|
import { mkdir as mkdir18, readFile as readFile22, writeFile as writeFile16 } from "node:fs/promises";
|
|
150541
|
-
import { dirname as dirname18, join as
|
|
151446
|
+
import { dirname as dirname18, join as join50, resolve as resolve26 } from "node:path";
|
|
150542
151447
|
function devServerFilePath(projectDir) {
|
|
150543
|
-
return
|
|
151448
|
+
return join50(resolve26(projectDir), ".fusion", "dev-server.json");
|
|
150544
151449
|
}
|
|
150545
151450
|
function normalizeState(candidate) {
|
|
150546
151451
|
const defaults = DEV_SERVER_DEFAULT_STATE();
|
|
@@ -151794,7 +152699,7 @@ Your job is to refine task descriptions based on the user's selected refinement
|
|
|
151794
152699
|
|
|
151795
152700
|
// ../dashboard/src/routes.ts
|
|
151796
152701
|
import multer from "multer";
|
|
151797
|
-
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";
|
|
151798
152703
|
import * as nodeFs from "node:fs";
|
|
151799
152704
|
import os5 from "node:os";
|
|
151800
152705
|
import v8 from "node:v8";
|
|
@@ -151822,8 +152727,8 @@ async function getPiPackageManagerAgentDir() {
|
|
|
151822
152727
|
const fusionAgentDir = getFusionAgentDir();
|
|
151823
152728
|
const legacyAgentDir = getLegacyPiAgentDir();
|
|
151824
152729
|
const [fusionSettings, legacySettings, legacyExists, fusionExists] = await Promise.all([
|
|
151825
|
-
readJsonObject3(
|
|
151826
|
-
readJsonObject3(
|
|
152730
|
+
readJsonObject3(join51(fusionAgentDir, "settings.json")),
|
|
152731
|
+
readJsonObject3(join51(legacyAgentDir, "settings.json")),
|
|
151827
152732
|
pathExists(legacyAgentDir),
|
|
151828
152733
|
pathExists(fusionAgentDir)
|
|
151829
152734
|
]);
|
|
@@ -151850,9 +152755,9 @@ async function discoverDashboardPiExtensions(cwd) {
|
|
|
151850
152755
|
const { DefaultPackageManager: DefaultPackageManager5 } = await import("@mariozechner/pi-coding-agent");
|
|
151851
152756
|
const [agentDir, legacyGlobalSettings, fusionGlobalSettings, projectSettings] = await Promise.all([
|
|
151852
152757
|
getPiPackageManagerAgentDir(),
|
|
151853
|
-
readJsonObject3(
|
|
151854
|
-
readJsonObject3(
|
|
151855
|
-
readJsonObject3(
|
|
152758
|
+
readJsonObject3(join51(getLegacyPiAgentDir(), "settings.json")),
|
|
152759
|
+
readJsonObject3(join51(getFusionAgentDir(), "settings.json")),
|
|
152760
|
+
readJsonObject3(join51(cwd, ".fusion", "settings.json"))
|
|
151856
152761
|
]);
|
|
151857
152762
|
const globalSettings = { ...legacyGlobalSettings, ...fusionGlobalSettings };
|
|
151858
152763
|
const mergedSettings = { ...globalSettings, ...projectSettings };
|
|
@@ -154266,7 +155171,7 @@ Description: ${step.description}`
|
|
|
154266
155171
|
return;
|
|
154267
155172
|
}
|
|
154268
155173
|
}
|
|
154269
|
-
const { resolve: resolve44, dirname: dirname34, join:
|
|
155174
|
+
const { resolve: resolve44, dirname: dirname34, join: join72 } = await import("node:path");
|
|
154270
155175
|
const { readdir: readdir12, stat: stat12 } = await import("node:fs/promises");
|
|
154271
155176
|
const rawPath = req.query.path || process.env.HOME || process.env.USERPROFILE || "/";
|
|
154272
155177
|
const showHidden = req.query.showHidden === "true";
|
|
@@ -154291,7 +155196,7 @@ Description: ${step.description}`
|
|
|
154291
155196
|
for (const entry of dirEntries) {
|
|
154292
155197
|
if (!entry.isDirectory()) continue;
|
|
154293
155198
|
if (!showHidden && entry.name.startsWith(".")) continue;
|
|
154294
|
-
const entryPath =
|
|
155199
|
+
const entryPath = join72(resolvedPath, entry.name);
|
|
154295
155200
|
let hasChildren = false;
|
|
154296
155201
|
try {
|
|
154297
155202
|
const subEntries = await readdir12(entryPath, { withFileTypes: true });
|
|
@@ -159899,8 +160804,8 @@ var init_auth_middleware = __esm({
|
|
|
159899
160804
|
// ../dashboard/src/server.ts
|
|
159900
160805
|
import express from "express";
|
|
159901
160806
|
import { randomUUID as randomUUID25 } from "node:crypto";
|
|
159902
|
-
import { join as
|
|
159903
|
-
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";
|
|
159904
160809
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
159905
160810
|
import { createSecureServer as createHttp2SecureServer } from "node:http2";
|
|
159906
160811
|
function parseVersion2(version) {
|
|
@@ -160002,11 +160907,11 @@ function loadTlsCredentialsFromEnv(env = process.env) {
|
|
|
160002
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)."
|
|
160003
160908
|
);
|
|
160004
160909
|
}
|
|
160005
|
-
const cert = certInline ? Buffer.from(certInline) :
|
|
160006
|
-
const key = keyInline ? Buffer.from(keyInline) :
|
|
160910
|
+
const cert = certInline ? Buffer.from(certInline) : readFileSync14(certFile);
|
|
160911
|
+
const key = keyInline ? Buffer.from(keyInline) : readFileSync14(keyFile);
|
|
160007
160912
|
const caInline = env.FUSION_TLS_CA;
|
|
160008
160913
|
const caFile = env.FUSION_TLS_CA_FILE;
|
|
160009
|
-
const ca = caInline ? Buffer.from(caInline) : caFile ?
|
|
160914
|
+
const ca = caInline ? Buffer.from(caInline) : caFile ? readFileSync14(caFile) : void 0;
|
|
160010
160915
|
return { cert, key, ca };
|
|
160011
160916
|
}
|
|
160012
160917
|
function createServer(store, options) {
|
|
@@ -160082,11 +160987,11 @@ function createServer(store, options) {
|
|
|
160082
160987
|
getTerminalService(store.getRootDir());
|
|
160083
160988
|
const isHeadless = options?.headless === true;
|
|
160084
160989
|
const execDir = dirname19(process.execPath);
|
|
160085
|
-
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");
|
|
160086
160991
|
if (!isHeadless) {
|
|
160087
160992
|
app.get("/version.json", (_req, res) => {
|
|
160088
160993
|
res.setHeader("Cache-Control", "no-store, max-age=0");
|
|
160089
|
-
res.sendFile(
|
|
160994
|
+
res.sendFile(join52(clientDir, "version.json"), (err) => {
|
|
160090
160995
|
if (err) {
|
|
160091
160996
|
res.status(404).json({ version: null });
|
|
160092
160997
|
}
|
|
@@ -160350,7 +161255,13 @@ data: ${JSON.stringify({ type: event.type, data: event.data })}
|
|
|
160350
161255
|
});
|
|
160351
161256
|
}
|
|
160352
161257
|
const chatAgentStore = new AgentStore({ rootDir: store.getFusionDir() });
|
|
160353
|
-
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
|
+
);
|
|
160354
161265
|
const runAiSessionCleanup = (maxAgeMs, source) => {
|
|
160355
161266
|
const result = aiSessionStore.cleanupStaleSessions(maxAgeMs);
|
|
160356
161267
|
runtimeLogger.info("AI session cleanup summary", {
|
|
@@ -160543,7 +161454,7 @@ data: ${JSON.stringify({ type: event.type, data: event.data })}
|
|
|
160543
161454
|
});
|
|
160544
161455
|
if (!isHeadless) {
|
|
160545
161456
|
app.get("/{*splat}", (_req, res) => {
|
|
160546
|
-
res.sendFile(
|
|
161457
|
+
res.sendFile(join52(clientDir, "index.html"));
|
|
160547
161458
|
});
|
|
160548
161459
|
}
|
|
160549
161460
|
const dashboardApp = app;
|
|
@@ -160998,7 +161909,7 @@ var init_server = __esm({
|
|
|
160998
161909
|
|
|
160999
161910
|
// ../dashboard/src/skills-adapter.ts
|
|
161000
161911
|
import { access as access11, readFile as readFile23, writeFile as writeFile17, mkdir as mkdir19, readdir as readdir10, stat as stat10 } from "node:fs/promises";
|
|
161001
|
-
import { join as
|
|
161912
|
+
import { join as join53, relative as relative13, dirname as dirname20 } from "node:path";
|
|
161002
161913
|
async function pathExists2(path5) {
|
|
161003
161914
|
try {
|
|
161004
161915
|
await access11(path5);
|
|
@@ -161254,7 +162165,7 @@ function createSkillsAdapter(options) {
|
|
|
161254
162165
|
} catch {
|
|
161255
162166
|
skillDir = dirname20(skill.path);
|
|
161256
162167
|
}
|
|
161257
|
-
const skillMdPath =
|
|
162168
|
+
const skillMdPath = join53(skillDir, "SKILL.md");
|
|
161258
162169
|
let skillMd = "";
|
|
161259
162170
|
try {
|
|
161260
162171
|
skillMd = await readFile23(skillMdPath, "utf-8");
|
|
@@ -161462,7 +162373,7 @@ function normalizeEntry(entry) {
|
|
|
161462
162373
|
};
|
|
161463
162374
|
}
|
|
161464
162375
|
function getProjectSettingsPath(rootDir) {
|
|
161465
|
-
return
|
|
162376
|
+
return join53(rootDir, ".fusion", "settings.json");
|
|
161466
162377
|
}
|
|
161467
162378
|
var MIN_PUBLIC_SEARCH_QUERY_LENGTH;
|
|
161468
162379
|
var init_skills_adapter = __esm({
|
|
@@ -161705,33 +162616,33 @@ var init_port_prompt = __esm({
|
|
|
161705
162616
|
});
|
|
161706
162617
|
|
|
161707
162618
|
// src/commands/provider-settings.ts
|
|
161708
|
-
import { existsSync as
|
|
161709
|
-
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";
|
|
161710
162621
|
function siblingAgentDir2(agentDir, siblingRoot) {
|
|
161711
162622
|
if (basename15(agentDir) !== "agent") {
|
|
161712
162623
|
return void 0;
|
|
161713
162624
|
}
|
|
161714
|
-
return
|
|
162625
|
+
return join54(dirname21(dirname21(agentDir)), siblingRoot, "agent");
|
|
161715
162626
|
}
|
|
161716
162627
|
function readJsonObject4(path5) {
|
|
161717
|
-
if (!
|
|
162628
|
+
if (!existsSync36(path5)) {
|
|
161718
162629
|
return {};
|
|
161719
162630
|
}
|
|
161720
162631
|
try {
|
|
161721
|
-
const parsed = JSON.parse(
|
|
162632
|
+
const parsed = JSON.parse(readFileSync15(path5, "utf-8"));
|
|
161722
162633
|
return parsed !== null && typeof parsed === "object" ? parsed : {};
|
|
161723
162634
|
} catch {
|
|
161724
162635
|
return {};
|
|
161725
162636
|
}
|
|
161726
162637
|
}
|
|
161727
162638
|
function createReadOnlyProviderSettingsView(cwd, agentDir) {
|
|
161728
|
-
const fusionAgentDir = agentDir.includes(`${
|
|
161729
|
-
const legacyAgentDir = agentDir.includes(`${
|
|
161730
|
-
const legacyGlobalSettings = legacyAgentDir ? readJsonObject4(
|
|
161731
|
-
const fusionGlobalSettings = fusionAgentDir ? readJsonObject4(
|
|
161732
|
-
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"));
|
|
161733
162644
|
const globalSettings = { ...legacyGlobalSettings, ...directGlobalSettings, ...fusionGlobalSettings };
|
|
161734
|
-
const fusionProjectSettings = readJsonObject4(
|
|
162645
|
+
const fusionProjectSettings = readJsonObject4(join54(cwd, ".fusion", "settings.json"));
|
|
161735
162646
|
const mergedSettings = { ...globalSettings, ...fusionProjectSettings };
|
|
161736
162647
|
return {
|
|
161737
162648
|
getGlobalSettings: () => structuredClone(globalSettings),
|
|
@@ -161746,7 +162657,6 @@ var init_provider_settings = __esm({
|
|
|
161746
162657
|
});
|
|
161747
162658
|
|
|
161748
162659
|
// src/commands/provider-auth.ts
|
|
161749
|
-
import { existsSync as existsSync36, readFileSync as readFileSync15 } from "node:fs";
|
|
161750
162660
|
import { getOAuthProvider as getOAuthProvider2 } from "@mariozechner/pi-ai/oauth";
|
|
161751
162661
|
function getProviderDisplayName(providerId) {
|
|
161752
162662
|
const knownProviderNames = new Map(
|
|
@@ -161762,7 +162672,10 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
|
|
|
161762
162672
|
reload: () => mergedAuthStorage.reload(),
|
|
161763
162673
|
getOAuthProviders: () => mergedAuthStorage.getOAuthProviders().filter((provider) => !OAUTH_TO_API_KEY_RECLASSIFICATIONS.has(provider.id)).map((provider) => ({ id: provider.id, name: provider.name })),
|
|
161764
162674
|
hasAuth: (provider) => mergedAuthStorage.hasAuth(provider),
|
|
161765
|
-
login: (providerId, callbacks) => mergedAuthStorage.login(
|
|
162675
|
+
login: (providerId, callbacks) => mergedAuthStorage.login(
|
|
162676
|
+
providerId,
|
|
162677
|
+
callbacks
|
|
162678
|
+
),
|
|
161766
162679
|
logout: (provider) => mergedAuthStorage.logout(provider),
|
|
161767
162680
|
getApiKeyProviders: () => {
|
|
161768
162681
|
const oauthProviderIds = new Set(
|
|
@@ -161801,13 +162714,28 @@ function wrapAuthStorageWithApiKeyProviders(authStorage, modelRegistry, readFall
|
|
|
161801
162714
|
}
|
|
161802
162715
|
function mergeAuthStorageReads(authStorage, readFallbackAuthStorages = []) {
|
|
161803
162716
|
const readAuthStorages = [authStorage, ...readFallbackAuthStorages];
|
|
161804
|
-
const
|
|
161805
|
-
|
|
161806
|
-
|
|
161807
|
-
|
|
162717
|
+
const selectCredential = (providerId, storages) => {
|
|
162718
|
+
let best;
|
|
162719
|
+
for (const storage of storages) {
|
|
162720
|
+
best = choosePreferredStoredCredential(best, storage.get(providerId));
|
|
161808
162721
|
}
|
|
161809
|
-
return
|
|
162722
|
+
return best;
|
|
161810
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();
|
|
161811
162739
|
return new Proxy(authStorage, {
|
|
161812
162740
|
get(target, prop, receiver) {
|
|
161813
162741
|
if (prop === "reload") {
|
|
@@ -161815,6 +162743,7 @@ function mergeAuthStorageReads(authStorage, readFallbackAuthStorages = []) {
|
|
|
161815
162743
|
for (const storage of readAuthStorages) {
|
|
161816
162744
|
storage.reload();
|
|
161817
162745
|
}
|
|
162746
|
+
syncFallbackOauthCredentials();
|
|
161818
162747
|
};
|
|
161819
162748
|
}
|
|
161820
162749
|
if (prop === "get") {
|
|
@@ -161827,13 +162756,17 @@ function mergeAuthStorageReads(authStorage, readFallbackAuthStorages = []) {
|
|
|
161827
162756
|
return (provider) => readAuthStorages.some((storage) => storage.hasAuth(provider));
|
|
161828
162757
|
}
|
|
161829
162758
|
if (prop === "getAll") {
|
|
161830
|
-
return () =>
|
|
161831
|
-
|
|
161832
|
-
|
|
161833
|
-
|
|
161834
|
-
|
|
161835
|
-
|
|
161836
|
-
|
|
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
|
+
};
|
|
161837
162770
|
}
|
|
161838
162771
|
if (prop === "list") {
|
|
161839
162772
|
return () => Array.from(new Set(readAuthStorages.flatMap((storage) => storage.list())));
|
|
@@ -161875,15 +162808,9 @@ function createReadOnlyAuthFileStorage(authPaths) {
|
|
|
161875
162808
|
const reload = () => {
|
|
161876
162809
|
const nextCredentials = {};
|
|
161877
162810
|
for (const authPath of authPaths) {
|
|
161878
|
-
|
|
161879
|
-
|
|
161880
|
-
|
|
161881
|
-
try {
|
|
161882
|
-
const parsed = JSON.parse(readFileSync15(authPath, "utf-8"));
|
|
161883
|
-
for (const [provider, credential] of Object.entries(parsed)) {
|
|
161884
|
-
nextCredentials[provider] ??= credential;
|
|
161885
|
-
}
|
|
161886
|
-
} catch {
|
|
162811
|
+
const parsed = readStoredCredentialsFromAuthFile(authPath);
|
|
162812
|
+
for (const [provider, credential] of Object.entries(parsed)) {
|
|
162813
|
+
nextCredentials[provider] = choosePreferredStoredCredential(nextCredentials[provider], credential) ?? credential;
|
|
161887
162814
|
}
|
|
161888
162815
|
}
|
|
161889
162816
|
credentials = nextCredentials;
|
|
@@ -161904,6 +162831,7 @@ var OAUTH_TO_API_KEY_RECLASSIFICATIONS, BUILT_IN_API_KEY_PROVIDERS, CLI_PROVIDER
|
|
|
161904
162831
|
var init_provider_auth = __esm({
|
|
161905
162832
|
"src/commands/provider-auth.ts"() {
|
|
161906
162833
|
"use strict";
|
|
162834
|
+
init_src();
|
|
161907
162835
|
OAUTH_TO_API_KEY_RECLASSIFICATIONS = /* @__PURE__ */ new Set([
|
|
161908
162836
|
"anthropic"
|
|
161909
162837
|
]);
|
|
@@ -161921,34 +162849,37 @@ var init_provider_auth = __esm({
|
|
|
161921
162849
|
});
|
|
161922
162850
|
|
|
161923
162851
|
// src/commands/auth-paths.ts
|
|
161924
|
-
import { homedir as
|
|
162852
|
+
import { homedir as homedir10 } from "node:os";
|
|
161925
162853
|
import { existsSync as existsSync37, readFileSync as readFileSync16 } from "node:fs";
|
|
161926
|
-
import { join as
|
|
161927
|
-
function getFusionAgentDir3(home = process.env.HOME || process.env.USERPROFILE ||
|
|
161928
|
-
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");
|
|
161929
162860
|
}
|
|
161930
|
-
function
|
|
161931
|
-
return
|
|
162861
|
+
function getFusionAuthPath3(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162862
|
+
return join55(getFusionAgentDir3(home), "auth.json");
|
|
161932
162863
|
}
|
|
161933
|
-
function
|
|
161934
|
-
return
|
|
162864
|
+
function getCodexCliAuthPath2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162865
|
+
return join55(home, ".codex", "auth.json");
|
|
161935
162866
|
}
|
|
161936
|
-
function getLegacyAuthPaths2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162867
|
+
function getLegacyAuthPaths2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161937
162868
|
return [
|
|
161938
|
-
|
|
161939
|
-
|
|
162869
|
+
join55(home, ".pi", "agent", "auth.json"),
|
|
162870
|
+
join55(home, ".pi", "auth.json")
|
|
161940
162871
|
];
|
|
161941
162872
|
}
|
|
161942
|
-
function getFusionModelsPath2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
161943
|
-
return
|
|
162873
|
+
function getFusionModelsPath2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
162874
|
+
return join55(getFusionAgentDir3(home), "models.json");
|
|
161944
162875
|
}
|
|
161945
|
-
function getLegacyModelsPaths2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162876
|
+
function getLegacyModelsPaths2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161946
162877
|
return [
|
|
161947
|
-
|
|
161948
|
-
|
|
162878
|
+
join55(home, ".pi", "agent", "models.json"),
|
|
162879
|
+
join55(home, ".pi", "models.json")
|
|
161949
162880
|
];
|
|
161950
162881
|
}
|
|
161951
|
-
function getModelRegistryModelsPath2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162882
|
+
function getModelRegistryModelsPath2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161952
162883
|
const fusionModelsPath = getFusionModelsPath2(home);
|
|
161953
162884
|
if (existsSync37(fusionModelsPath)) {
|
|
161954
162885
|
return fusionModelsPath;
|
|
@@ -161969,11 +162900,11 @@ function readJsonObject5(path5) {
|
|
|
161969
162900
|
function hasPackageManagerSettings3(settings) {
|
|
161970
162901
|
return Array.isArray(settings.packages) || Array.isArray(settings.npmCommand);
|
|
161971
162902
|
}
|
|
161972
|
-
function getPackageManagerAgentDir2(home = process.env.HOME || process.env.USERPROFILE ||
|
|
162903
|
+
function getPackageManagerAgentDir2(home = process.env.HOME || process.env.USERPROFILE || homedir10()) {
|
|
161973
162904
|
const fusionAgentDir = getFusionAgentDir3(home);
|
|
161974
162905
|
const legacyAgentDir = getLegacyAgentDir(home);
|
|
161975
|
-
const fusionSettings = readJsonObject5(
|
|
161976
|
-
const legacySettings = readJsonObject5(
|
|
162906
|
+
const fusionSettings = readJsonObject5(join55(fusionAgentDir, "settings.json"));
|
|
162907
|
+
const legacySettings = readJsonObject5(join55(legacyAgentDir, "settings.json"));
|
|
161977
162908
|
if (hasPackageManagerSettings3(fusionSettings) || !existsSync37(legacyAgentDir)) {
|
|
161978
162909
|
return fusionAgentDir;
|
|
161979
162910
|
}
|
|
@@ -162149,7 +163080,7 @@ import {
|
|
|
162149
163080
|
symlinkSync,
|
|
162150
163081
|
unlinkSync
|
|
162151
163082
|
} from "node:fs";
|
|
162152
|
-
import { dirname as dirname23, join as
|
|
163083
|
+
import { dirname as dirname23, join as join56, resolve as resolve29 } from "node:path";
|
|
162153
163084
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
162154
163085
|
function isPiClaudeCliConfigured(globalSettings) {
|
|
162155
163086
|
if (!globalSettings || typeof globalSettings !== "object") {
|
|
@@ -162173,7 +163104,7 @@ function resolveFusionSkillSource() {
|
|
|
162173
163104
|
return existsSync38(candidate) ? candidate : null;
|
|
162174
163105
|
}
|
|
162175
163106
|
function installFusionSkillIntoProject(projectPath, options = {}) {
|
|
162176
|
-
const target =
|
|
163107
|
+
const target = join56(projectPath, ".claude", "skills", FUSION_SKILL_NAME);
|
|
162177
163108
|
if (options.enabled === false) {
|
|
162178
163109
|
return { outcome: "skipped", target, reason: "pi-claude-cli not configured" };
|
|
162179
163110
|
}
|
|
@@ -162198,7 +163129,7 @@ function installFusionSkillIntoProject(projectPath, options = {}) {
|
|
|
162198
163129
|
unlinkSync(target);
|
|
162199
163130
|
replaced = true;
|
|
162200
163131
|
} else {
|
|
162201
|
-
const skillMd =
|
|
163132
|
+
const skillMd = join56(target, "SKILL.md");
|
|
162202
163133
|
if (!existsSync38(skillMd)) {
|
|
162203
163134
|
return {
|
|
162204
163135
|
outcome: "failed",
|
|
@@ -162245,7 +163176,7 @@ function ensureFusionSkillForProjects(projects, options = { enabled: false }) {
|
|
|
162245
163176
|
if (!options.enabled) {
|
|
162246
163177
|
return projects.map((p) => ({
|
|
162247
163178
|
outcome: "skipped",
|
|
162248
|
-
target:
|
|
163179
|
+
target: join56(p.path, ".claude", "skills", FUSION_SKILL_NAME),
|
|
162249
163180
|
reason: "pi-claude-cli not configured"
|
|
162250
163181
|
}));
|
|
162251
163182
|
}
|
|
@@ -162548,10 +163479,10 @@ var init_droid_cli_extension = __esm({
|
|
|
162548
163479
|
|
|
162549
163480
|
// src/update-cache.ts
|
|
162550
163481
|
import { readFileSync as readFileSync19 } from "node:fs";
|
|
162551
|
-
import { join as
|
|
163482
|
+
import { join as join57 } from "node:path";
|
|
162552
163483
|
function getCachedUpdateStatus(currentVersion) {
|
|
162553
163484
|
try {
|
|
162554
|
-
const cachePath =
|
|
163485
|
+
const cachePath = join57(resolveGlobalDir(), "update-check.json");
|
|
162555
163486
|
const raw = readFileSync19(cachePath, "utf-8");
|
|
162556
163487
|
const parsed = JSON.parse(raw);
|
|
162557
163488
|
if (parsed.updateAvailable === true && typeof parsed.latestVersion === "string" && parsed.latestVersion.length > 0 && typeof parsed.currentVersion === "string" && parsed.currentVersion.length > 0) {
|
|
@@ -162641,19 +163572,19 @@ var init_self_extension = __esm({
|
|
|
162641
163572
|
// src/plugins/bundled-plugin-install.ts
|
|
162642
163573
|
import { existsSync as existsSync42 } from "node:fs";
|
|
162643
163574
|
import { readFile as readFile24 } from "node:fs/promises";
|
|
162644
|
-
import { dirname as dirname27, join as
|
|
163575
|
+
import { dirname as dirname27, join as join58, resolve as resolve33 } from "node:path";
|
|
162645
163576
|
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
162646
163577
|
function getCandidatePluginPaths() {
|
|
162647
163578
|
const moduleDir = dirname27(fileURLToPath10(import.meta.url));
|
|
162648
163579
|
const cliPackageRoot = resolve33(moduleDir, "..", "..");
|
|
162649
163580
|
return [
|
|
162650
|
-
|
|
162651
|
-
|
|
162652
|
-
|
|
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)
|
|
162653
163584
|
];
|
|
162654
163585
|
}
|
|
162655
163586
|
async function loadManifest(pluginDir) {
|
|
162656
|
-
const manifestPath =
|
|
163587
|
+
const manifestPath = join58(pluginDir, "manifest.json");
|
|
162657
163588
|
const content = await readFile24(manifestPath, "utf-8");
|
|
162658
163589
|
const manifest = JSON.parse(content);
|
|
162659
163590
|
const validation = validatePluginManifest(manifest);
|
|
@@ -162664,7 +163595,7 @@ async function loadManifest(pluginDir) {
|
|
|
162664
163595
|
}
|
|
162665
163596
|
function resolveBundledDependencyGraphPath() {
|
|
162666
163597
|
for (const path5 of getCandidatePluginPaths()) {
|
|
162667
|
-
if (existsSync42(
|
|
163598
|
+
if (existsSync42(join58(path5, "manifest.json"))) {
|
|
162668
163599
|
return path5;
|
|
162669
163600
|
}
|
|
162670
163601
|
}
|
|
@@ -167093,7 +168024,7 @@ __export(dashboard_exports, {
|
|
|
167093
168024
|
promptForPort: () => promptForPort,
|
|
167094
168025
|
runDashboard: () => runDashboard
|
|
167095
168026
|
});
|
|
167096
|
-
import { dirname as dirname28, join as
|
|
168027
|
+
import { dirname as dirname28, join as join59, resolve as pathResolve } from "node:path";
|
|
167097
168028
|
import { execFile as execFileCb } from "node:child_process";
|
|
167098
168029
|
import { promisify as promisify16 } from "node:util";
|
|
167099
168030
|
import { stat as stat11, readdir as readdir11, readFile as fsReadFile3 } from "node:fs/promises";
|
|
@@ -167431,7 +168362,7 @@ async function buildFileListDirectory(projectPath, relativePath) {
|
|
|
167431
168362
|
let size = 0;
|
|
167432
168363
|
let modifiedAt = (/* @__PURE__ */ new Date(0)).toISOString();
|
|
167433
168364
|
try {
|
|
167434
|
-
const s = await stat11(
|
|
168365
|
+
const s = await stat11(join59(absDir, d.name));
|
|
167435
168366
|
size = d.isDirectory() ? 0 : s.size;
|
|
167436
168367
|
modifiedAt = s.mtime.toISOString();
|
|
167437
168368
|
} catch {
|
|
@@ -167843,8 +168774,11 @@ async function runDashboard(port, opts = {}) {
|
|
|
167843
168774
|
rootDir: cwd
|
|
167844
168775
|
});
|
|
167845
168776
|
const authStorage = AuthStorage2.create(getFusionAuthPath3());
|
|
167846
|
-
const
|
|
167847
|
-
|
|
168777
|
+
const supplementalAuthStorage = createReadOnlyAuthFileStorage([
|
|
168778
|
+
...getLegacyAuthPaths2(),
|
|
168779
|
+
getCodexCliAuthPath2()
|
|
168780
|
+
]);
|
|
168781
|
+
const mergedAuthStorage = mergeAuthStorageReads(authStorage, [supplementalAuthStorage]);
|
|
167848
168782
|
const modelRegistry = ModelRegistry3.create(mergedAuthStorage, getModelRegistryModelsPath2());
|
|
167849
168783
|
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(mergedAuthStorage, modelRegistry);
|
|
167850
168784
|
let packageManager;
|
|
@@ -167906,7 +168840,7 @@ async function runDashboard(port, opts = {}) {
|
|
|
167906
168840
|
...droidCliPaths
|
|
167907
168841
|
],
|
|
167908
168842
|
cwd,
|
|
167909
|
-
|
|
168843
|
+
join59(cwd, ".fusion", "disabled-auto-extension-discovery")
|
|
167910
168844
|
);
|
|
167911
168845
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
167912
168846
|
logSink.log(`Failed to load ${path5}: ${error}`, "extensions");
|
|
@@ -169429,7 +170363,7 @@ var serve_exports = {};
|
|
|
169429
170363
|
__export(serve_exports, {
|
|
169430
170364
|
runServe: () => runServe
|
|
169431
170365
|
});
|
|
169432
|
-
import { dirname as dirname29, join as
|
|
170366
|
+
import { dirname as dirname29, join as join60 } from "node:path";
|
|
169433
170367
|
import {
|
|
169434
170368
|
AuthStorage as AuthStorage3,
|
|
169435
170369
|
DefaultPackageManager as DefaultPackageManager3,
|
|
@@ -169682,8 +170616,11 @@ async function runServe(port, opts = {}) {
|
|
|
169682
170616
|
const missionExecutionLoop = cwdEngine.getRuntime().getMissionExecutionLoop();
|
|
169683
170617
|
const automationStore = cwdEngine.getAutomationStore();
|
|
169684
170618
|
const authStorage = AuthStorage3.create(getFusionAuthPath3());
|
|
169685
|
-
const
|
|
169686
|
-
|
|
170619
|
+
const supplementalAuthStorage = createReadOnlyAuthFileStorage([
|
|
170620
|
+
...getLegacyAuthPaths2(),
|
|
170621
|
+
getCodexCliAuthPath2()
|
|
170622
|
+
]);
|
|
170623
|
+
const mergedAuthStorage = mergeAuthStorageReads(authStorage, [supplementalAuthStorage]);
|
|
169687
170624
|
const modelRegistry = ModelRegistry4.create(mergedAuthStorage, getModelRegistryModelsPath2());
|
|
169688
170625
|
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(mergedAuthStorage, modelRegistry);
|
|
169689
170626
|
let packageManager;
|
|
@@ -169745,7 +170682,7 @@ async function runServe(port, opts = {}) {
|
|
|
169745
170682
|
...droidCliPaths
|
|
169746
170683
|
],
|
|
169747
170684
|
cwd,
|
|
169748
|
-
|
|
170685
|
+
join60(cwd, ".fusion", "disabled-auto-extension-discovery")
|
|
169749
170686
|
);
|
|
169750
170687
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
169751
170688
|
console.log(`[extensions] Failed to load ${path5}: ${error}`);
|
|
@@ -170100,7 +171037,7 @@ var daemon_exports = {};
|
|
|
170100
171037
|
__export(daemon_exports, {
|
|
170101
171038
|
runDaemon: () => runDaemon
|
|
170102
171039
|
});
|
|
170103
|
-
import { join as
|
|
171040
|
+
import { join as join61 } from "node:path";
|
|
170104
171041
|
import {
|
|
170105
171042
|
AuthStorage as AuthStorage4,
|
|
170106
171043
|
DefaultPackageManager as DefaultPackageManager4,
|
|
@@ -170346,8 +171283,11 @@ async function runDaemon(opts = {}) {
|
|
|
170346
171283
|
const missionExecutionLoop = cwdEngine.getRuntime().getMissionExecutionLoop();
|
|
170347
171284
|
const automationStore = cwdEngine.getAutomationStore();
|
|
170348
171285
|
const authStorage = AuthStorage4.create(getFusionAuthPath3());
|
|
170349
|
-
const
|
|
170350
|
-
|
|
171286
|
+
const supplementalAuthStorage = createReadOnlyAuthFileStorage([
|
|
171287
|
+
...getLegacyAuthPaths2(),
|
|
171288
|
+
getCodexCliAuthPath2()
|
|
171289
|
+
]);
|
|
171290
|
+
const mergedAuthStorage = mergeAuthStorageReads(authStorage, [supplementalAuthStorage]);
|
|
170351
171291
|
const modelRegistry = ModelRegistry5.create(mergedAuthStorage, getModelRegistryModelsPath2());
|
|
170352
171292
|
const dashboardAuthStorage = wrapAuthStorageWithApiKeyProviders(mergedAuthStorage, modelRegistry);
|
|
170353
171293
|
let packageManager;
|
|
@@ -170407,7 +171347,7 @@ async function runDaemon(opts = {}) {
|
|
|
170407
171347
|
const extensionsResult = await discoverAndLoadExtensions4(
|
|
170408
171348
|
[...reconciledExtensionPaths, ...droidCliPaths],
|
|
170409
171349
|
cwd,
|
|
170410
|
-
|
|
171350
|
+
join61(cwd, ".fusion", "disabled-auto-extension-discovery")
|
|
170411
171351
|
);
|
|
170412
171352
|
for (const { path: path5, error } of extensionsResult.errors) {
|
|
170413
171353
|
console.log(`[extensions] Failed to load ${path5}: ${error}`);
|
|
@@ -170628,7 +171568,7 @@ __export(desktop_exports, {
|
|
|
170628
171568
|
});
|
|
170629
171569
|
import { spawn as spawn18 } from "node:child_process";
|
|
170630
171570
|
import { once as once2 } from "node:events";
|
|
170631
|
-
import { join as
|
|
171571
|
+
import { join as join62 } from "node:path";
|
|
170632
171572
|
import { createRequire as createRequire6 } from "node:module";
|
|
170633
171573
|
function runCommand(command, args, cwd) {
|
|
170634
171574
|
return new Promise((resolve44, reject2) => {
|
|
@@ -170707,7 +171647,7 @@ async function runDesktop(options = {}) {
|
|
|
170707
171647
|
}
|
|
170708
171648
|
const runtime = await startDashboardRuntime(rootDir, Boolean(options.paused));
|
|
170709
171649
|
const electronBinary = resolveElectronBinary();
|
|
170710
|
-
const desktopEntry =
|
|
171650
|
+
const desktopEntry = join62(rootDir, "packages", "desktop", "dist", "main.js");
|
|
170711
171651
|
const electronArgs = ["--enable-source-maps", desktopEntry, ...options.dev ? ["--dev"] : []];
|
|
170712
171652
|
const electronEnv = {
|
|
170713
171653
|
...process.env,
|
|
@@ -170793,7 +171733,7 @@ __export(task_exports, {
|
|
|
170793
171733
|
});
|
|
170794
171734
|
import { createInterface as createInterface3 } from "node:readline/promises";
|
|
170795
171735
|
import { watchFile, unwatchFile, statSync as statSync6, existsSync as existsSync43, readFileSync as readFileSync21 } from "node:fs";
|
|
170796
|
-
import { basename as basename17, join as
|
|
171736
|
+
import { basename as basename17, join as join63 } from "node:path";
|
|
170797
171737
|
function getGitHubIssueUrl(sourceMetadata) {
|
|
170798
171738
|
if (!sourceMetadata || typeof sourceMetadata !== "object") return void 0;
|
|
170799
171739
|
const issueUrl = sourceMetadata.issueUrl;
|
|
@@ -171106,7 +172046,7 @@ async function runTaskLogs(id, options = {}, projectName) {
|
|
|
171106
172046
|
printEntries(filteredEntries);
|
|
171107
172047
|
if (options.follow) {
|
|
171108
172048
|
const projectPath = projectContext?.projectPath ?? process.cwd();
|
|
171109
|
-
const logPath =
|
|
172049
|
+
const logPath = join63(projectPath, ".fusion", "tasks", id, "agent.log");
|
|
171110
172050
|
if (!existsSync43(logPath)) {
|
|
171111
172051
|
console.log(`
|
|
171112
172052
|
Waiting for log file to be created...`);
|
|
@@ -172384,7 +173324,7 @@ __export(settings_export_exports, {
|
|
|
172384
173324
|
runSettingsExport: () => runSettingsExport
|
|
172385
173325
|
});
|
|
172386
173326
|
import { writeFile as writeFile18 } from "node:fs/promises";
|
|
172387
|
-
import { resolve as resolve34, join as
|
|
173327
|
+
import { resolve as resolve34, join as join64 } from "node:path";
|
|
172388
173328
|
async function runSettingsExport(options = {}) {
|
|
172389
173329
|
const scope = options.scope ?? "both";
|
|
172390
173330
|
const project = options.projectName ? await resolveProject(options.projectName) : void 0;
|
|
@@ -172398,7 +173338,7 @@ async function runSettingsExport(options = {}) {
|
|
|
172398
173338
|
targetPath = resolve34(outputPath);
|
|
172399
173339
|
} else {
|
|
172400
173340
|
const filename = generateExportFilename();
|
|
172401
|
-
targetPath =
|
|
173341
|
+
targetPath = join64(process.cwd(), filename);
|
|
172402
173342
|
}
|
|
172403
173343
|
const jsonContent = JSON.stringify(exportData, null, 2);
|
|
172404
173344
|
await writeFile18(targetPath, jsonContent);
|
|
@@ -173972,14 +174912,14 @@ var init_project = __esm({
|
|
|
173972
174912
|
|
|
173973
174913
|
// src/commands/skill-installation.ts
|
|
173974
174914
|
import { cpSync as cpSync2, existsSync as existsSync47, mkdirSync as mkdirSync8 } from "node:fs";
|
|
173975
|
-
import { homedir as
|
|
173976
|
-
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";
|
|
173977
174917
|
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
173978
|
-
function getSupportedSkillInstallTargets(homeDir = process.env.HOME || process.env.USERPROFILE ||
|
|
174918
|
+
function getSupportedSkillInstallTargets(homeDir = process.env.HOME || process.env.USERPROFILE || homedir11()) {
|
|
173979
174919
|
return [
|
|
173980
|
-
{ client: "claude", targetDir:
|
|
173981
|
-
{ client: "codex", targetDir:
|
|
173982
|
-
{ 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) }
|
|
173983
174923
|
];
|
|
173984
174924
|
}
|
|
173985
174925
|
function resolveBundledFusionSkillSource() {
|
|
@@ -174043,13 +174983,13 @@ __export(init_exports, {
|
|
|
174043
174983
|
runInit: () => runInit
|
|
174044
174984
|
});
|
|
174045
174985
|
import { existsSync as existsSync48, mkdirSync as mkdirSync9, writeFileSync as writeFileSync3, readFileSync as readFileSync22 } from "node:fs";
|
|
174046
|
-
import { join as
|
|
174986
|
+
import { join as join66, resolve as resolve39, basename as basename20 } from "node:path";
|
|
174047
174987
|
import { exec as exec13 } from "node:child_process";
|
|
174048
174988
|
import { promisify as promisify18 } from "node:util";
|
|
174049
174989
|
async function runInit(options = {}) {
|
|
174050
174990
|
const cwd = options.path ? resolve39(options.path) : process.cwd();
|
|
174051
|
-
const fusionDir =
|
|
174052
|
-
const dbPath =
|
|
174991
|
+
const fusionDir = join66(cwd, ".fusion");
|
|
174992
|
+
const dbPath = join66(fusionDir, "fusion.db");
|
|
174053
174993
|
const hasDbPath = existsSync48(dbPath);
|
|
174054
174994
|
const hasValidDb = hasDbPath && isValidSqliteDatabaseFile(dbPath);
|
|
174055
174995
|
if (existsSync48(fusionDir) && hasDbPath && hasValidDb) {
|
|
@@ -174145,7 +175085,7 @@ async function runInit(options = {}) {
|
|
|
174145
175085
|
}
|
|
174146
175086
|
}
|
|
174147
175087
|
async function detectProjectName(dir2) {
|
|
174148
|
-
if (!existsSync48(
|
|
175088
|
+
if (!existsSync48(join66(dir2, ".git"))) {
|
|
174149
175089
|
return basename20(dir2) || "my-project";
|
|
174150
175090
|
}
|
|
174151
175091
|
try {
|
|
@@ -174165,7 +175105,7 @@ async function detectProjectName(dir2) {
|
|
|
174165
175105
|
return basename20(dir2) || "my-project";
|
|
174166
175106
|
}
|
|
174167
175107
|
async function addLocalStorageToGitignore(cwd) {
|
|
174168
|
-
const gitignorePath =
|
|
175108
|
+
const gitignorePath = join66(cwd, ".gitignore");
|
|
174169
175109
|
let content = "";
|
|
174170
175110
|
if (existsSync48(gitignorePath)) {
|
|
174171
175111
|
try {
|
|
@@ -174208,7 +175148,7 @@ async function initializeGitRepo(cwd) {
|
|
|
174208
175148
|
}
|
|
174209
175149
|
await ensureGitConfig(cwd, "user.name", "Fusion");
|
|
174210
175150
|
await ensureGitConfig(cwd, "user.email", "noreply@runfusion.ai");
|
|
174211
|
-
const gitkeepPath =
|
|
175151
|
+
const gitkeepPath = join66(cwd, ".gitkeep");
|
|
174212
175152
|
if (!existsSync48(gitkeepPath)) {
|
|
174213
175153
|
writeFileSync3(gitkeepPath, "\n");
|
|
174214
175154
|
}
|
|
@@ -174875,7 +175815,7 @@ __export(plugin_exports, {
|
|
|
174875
175815
|
runPluginUninstall: () => runPluginUninstall
|
|
174876
175816
|
});
|
|
174877
175817
|
import { existsSync as existsSync50 } from "node:fs";
|
|
174878
|
-
import { join as
|
|
175818
|
+
import { join as join67 } from "node:path";
|
|
174879
175819
|
import { readFile as readFile25 } from "node:fs/promises";
|
|
174880
175820
|
import * as readline from "node:readline";
|
|
174881
175821
|
async function getProjectPath6(projectName) {
|
|
@@ -174913,7 +175853,7 @@ async function createPluginLoader(pluginStore, projectName) {
|
|
|
174913
175853
|
return { store: pluginStore, loader };
|
|
174914
175854
|
}
|
|
174915
175855
|
async function loadManifestFromPath(pluginPath) {
|
|
174916
|
-
const manifestPath =
|
|
175856
|
+
const manifestPath = join67(pluginPath, "manifest.json");
|
|
174917
175857
|
if (!existsSync50(manifestPath)) {
|
|
174918
175858
|
throw new Error(`Plugin manifest not found at: ${manifestPath}`);
|
|
174919
175859
|
}
|
|
@@ -175105,7 +176045,7 @@ __export(plugin_scaffold_exports, {
|
|
|
175105
176045
|
runPluginCreate: () => runPluginCreate
|
|
175106
176046
|
});
|
|
175107
176047
|
import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync5, existsSync as existsSync51 } from "node:fs";
|
|
175108
|
-
import { join as
|
|
176048
|
+
import { join as join68 } from "node:path";
|
|
175109
176049
|
function toTitleCase(str) {
|
|
175110
176050
|
return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
175111
176051
|
}
|
|
@@ -175238,7 +176178,7 @@ async function runPluginCreate(name, options) {
|
|
|
175238
176178
|
process.exit(1);
|
|
175239
176179
|
}
|
|
175240
176180
|
const targetDir = options?.output ?? name;
|
|
175241
|
-
const targetPath =
|
|
176181
|
+
const targetPath = join68(process.cwd(), targetDir);
|
|
175242
176182
|
if (existsSync51(targetPath)) {
|
|
175243
176183
|
console.error(`Error: Directory '${targetDir}' already exists.`);
|
|
175244
176184
|
console.error("Please choose a different name or remove the existing directory.");
|
|
@@ -175246,16 +176186,16 @@ async function runPluginCreate(name, options) {
|
|
|
175246
176186
|
}
|
|
175247
176187
|
try {
|
|
175248
176188
|
mkdirSync11(targetPath, { recursive: true });
|
|
175249
|
-
mkdirSync11(
|
|
175250
|
-
writeFileSync5(
|
|
175251
|
-
writeFileSync5(
|
|
175252
|
-
writeFileSync5(
|
|
175253
|
-
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));
|
|
175254
176194
|
writeFileSync5(
|
|
175255
|
-
|
|
176195
|
+
join68(targetPath, "src", "__tests__", "index.test.ts"),
|
|
175256
176196
|
generateTestTs(name)
|
|
175257
176197
|
);
|
|
175258
|
-
writeFileSync5(
|
|
176198
|
+
writeFileSync5(join68(targetPath, "README.md"), generateReadme(name));
|
|
175259
176199
|
} catch (err) {
|
|
175260
176200
|
console.error(
|
|
175261
176201
|
`Error creating plugin files: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -175407,7 +176347,7 @@ __export(research_exports, {
|
|
|
175407
176347
|
runResearchShow: () => runResearchShow
|
|
175408
176348
|
});
|
|
175409
176349
|
import { writeFile as writeFile19 } from "node:fs/promises";
|
|
175410
|
-
import { join as
|
|
176350
|
+
import { join as join69, resolve as resolve42 } from "node:path";
|
|
175411
176351
|
async function getStore3(projectName) {
|
|
175412
176352
|
const project = projectName ? await resolveProject(projectName) : void 0;
|
|
175413
176353
|
const store = new TaskStore(project?.projectPath ?? process.cwd());
|
|
@@ -175563,7 +176503,7 @@ async function runResearchExport(options) {
|
|
|
175563
176503
|
}
|
|
175564
176504
|
const content = format === "json" ? JSON.stringify(run, null, 2) : renderMarkdown(run);
|
|
175565
176505
|
const ext = format === "json" ? "json" : "md";
|
|
175566
|
-
const outputPath = options.output ? resolve42(options.output) :
|
|
176506
|
+
const outputPath = options.output ? resolve42(options.output) : join69(process.cwd(), `research-${run.id.toLowerCase()}.${ext}`);
|
|
175567
176507
|
await writeFile19(outputPath, content, "utf8");
|
|
175568
176508
|
store.getResearchStore().createExport(run.id, format, content);
|
|
175569
176509
|
if (options.json) {
|
|
@@ -175628,7 +176568,7 @@ __export(native_patch_exports, {
|
|
|
175628
176568
|
isTerminalAvailable: () => isTerminalAvailable,
|
|
175629
176569
|
setupNativeResolution: () => setupNativeResolution
|
|
175630
176570
|
});
|
|
175631
|
-
import { join as
|
|
176571
|
+
import { join as join70, basename as basename21, dirname as dirname32 } from "node:path";
|
|
175632
176572
|
import { existsSync as existsSync52, copyFileSync, mkdirSync as mkdirSync12, symlinkSync as symlinkSync2, rmSync as rmSync5, lstatSync as lstatSync3, readlinkSync as readlinkSync2 } from "node:fs";
|
|
175633
176573
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
175634
176574
|
function findStagedNativeDir2() {
|
|
@@ -175636,13 +176576,13 @@ function findStagedNativeDir2() {
|
|
|
175636
176576
|
const arch = process.arch === "arm64" ? "arm64" : process.arch === "x64" ? "x64" : "unknown";
|
|
175637
176577
|
const prebuildName = `${platform4}-${arch}`;
|
|
175638
176578
|
const execDir = dirname32(process.execPath);
|
|
175639
|
-
const nextToBinary =
|
|
175640
|
-
if (existsSync52(
|
|
176579
|
+
const nextToBinary = join70(execDir, "runtime", prebuildName);
|
|
176580
|
+
if (existsSync52(join70(nextToBinary, "pty.node"))) {
|
|
175641
176581
|
return nextToBinary;
|
|
175642
176582
|
}
|
|
175643
176583
|
if (process.env.FUSION_RUNTIME_DIR) {
|
|
175644
|
-
const envPath =
|
|
175645
|
-
if (existsSync52(
|
|
176584
|
+
const envPath = join70(process.env.FUSION_RUNTIME_DIR, prebuildName);
|
|
176585
|
+
if (existsSync52(join70(envPath, "pty.node"))) {
|
|
175646
176586
|
return envPath;
|
|
175647
176587
|
}
|
|
175648
176588
|
}
|
|
@@ -175675,17 +176615,17 @@ function setupNativeResolution() {
|
|
|
175675
176615
|
process.env.NODE_PTY_SPAWN_HELPER_DIR = nativeDir;
|
|
175676
176616
|
}
|
|
175677
176617
|
process.env.FUSION_NATIVE_ASSETS_PATH = nativeDir;
|
|
175678
|
-
const tmpRoot =
|
|
175679
|
-
const fnDir =
|
|
175680
|
-
const prebuildsDir =
|
|
175681
|
-
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));
|
|
175682
176622
|
try {
|
|
175683
176623
|
cleanupStaleBunfsLinks();
|
|
175684
176624
|
mkdirSync12(platformDir, { recursive: true });
|
|
175685
|
-
const ptyNodeDest =
|
|
175686
|
-
copyFileSync(
|
|
175687
|
-
if (existsSync52(
|
|
175688
|
-
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"));
|
|
175689
176629
|
}
|
|
175690
176630
|
process.env.FUSION_FAKE_BUNFS_ROOT = tmpRoot;
|
|
175691
176631
|
if (process.platform !== "win32") {
|
|
@@ -175762,7 +176702,7 @@ var init_native_patch = __esm({
|
|
|
175762
176702
|
// src/bin.ts
|
|
175763
176703
|
import { existsSync as existsSync53, mkdtempSync as mkdtempSync2, readFileSync as readFileSync24, symlinkSync as symlinkSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
175764
176704
|
import { createRequire as createRequire7 } from "node:module";
|
|
175765
|
-
import { join as
|
|
176705
|
+
import { join as join71, dirname as dirname33, resolve as resolve43 } from "node:path";
|
|
175766
176706
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
175767
176707
|
import { performance as performance3 } from "node:perf_hooks";
|
|
175768
176708
|
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
@@ -175771,7 +176711,7 @@ function configurePiPackage() {
|
|
|
175771
176711
|
if (process.env.PI_PACKAGE_DIR) {
|
|
175772
176712
|
return;
|
|
175773
176713
|
}
|
|
175774
|
-
const tmp = mkdtempSync2(
|
|
176714
|
+
const tmp = mkdtempSync2(join71(tmpdir6(), "fn-pkg-"));
|
|
175775
176715
|
let packageJson = {
|
|
175776
176716
|
name: "pi",
|
|
175777
176717
|
version: "0.1.0",
|
|
@@ -175783,9 +176723,9 @@ function configurePiPackage() {
|
|
|
175783
176723
|
const piPackageDir = dirname33(piPackagePath);
|
|
175784
176724
|
packageJson = JSON.parse(readFileSync24(piPackagePath, "utf-8"));
|
|
175785
176725
|
for (const entry of ["dist", "docs", "examples", "README.md", "CHANGELOG.md"]) {
|
|
175786
|
-
const source =
|
|
176726
|
+
const source = join71(piPackageDir, entry);
|
|
175787
176727
|
if (existsSync53(source)) {
|
|
175788
|
-
symlinkSync3(source,
|
|
176728
|
+
symlinkSync3(source, join71(tmp, entry));
|
|
175789
176729
|
}
|
|
175790
176730
|
}
|
|
175791
176731
|
} catch {
|
|
@@ -175794,7 +176734,7 @@ function configurePiPackage() {
|
|
|
175794
176734
|
...packageJson.piConfig ?? {},
|
|
175795
176735
|
configDir: ".fusion"
|
|
175796
176736
|
};
|
|
175797
|
-
writeFileSync6(
|
|
176737
|
+
writeFileSync6(join71(tmp, "package.json"), JSON.stringify(packageJson, null, 2) + "\n");
|
|
175798
176738
|
process.env.PI_PACKAGE_DIR = tmp;
|
|
175799
176739
|
}
|
|
175800
176740
|
configurePiPackage();
|
|
@@ -175822,8 +176762,8 @@ function loadEnvFile(path5) {
|
|
|
175822
176762
|
}
|
|
175823
176763
|
function loadLocalEnv() {
|
|
175824
176764
|
const cwd = process.cwd();
|
|
175825
|
-
loadEnvFile(
|
|
175826
|
-
loadEnvFile(
|
|
176765
|
+
loadEnvFile(join71(cwd, ".env"));
|
|
176766
|
+
loadEnvFile(join71(cwd, ".env.local"));
|
|
175827
176767
|
}
|
|
175828
176768
|
loadLocalEnv();
|
|
175829
176769
|
async function loadCommandHandlers() {
|