@mastra/factory 0.2.2-alpha.0 → 0.2.2-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +22 -2
- package/dist/auth.js.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +411 -244
- package/dist/factory.js.map +1 -1
- package/dist/index.js +411 -244
- package/dist/index.js.map +1 -1
- package/dist/integrations/github/integration.js +85 -37
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +85 -37
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +2 -0
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +28 -8
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +119 -39
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +1 -4
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/surface.js +1 -4
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +7 -1
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/tools.js.map +1 -1
- package/dist/spa-static.d.ts.map +1 -1
- package/dist/spa-static.js +1 -0
- package/dist/spa-static.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +9 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +4 -0
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +4 -0
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +20 -4
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/storage/domains/work-items/metrics.js.map +1 -1
- package/dist/timing.d.ts +15 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +27 -0
- package/dist/timing.js.map +1 -0
- package/dist/workspace.d.ts +2 -3
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +104 -65
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-review/SKILL.md +9 -2
- package/package.json +8 -8
package/dist/factory.js
CHANGED
|
@@ -24558,6 +24558,31 @@ import {
|
|
|
24558
24558
|
isSessionProvider,
|
|
24559
24559
|
isSSOProvider
|
|
24560
24560
|
} from "@mastra/core/server";
|
|
24561
|
+
|
|
24562
|
+
// src/timing.ts
|
|
24563
|
+
async function timedPhase(phase, fn) {
|
|
24564
|
+
const start = performance.now();
|
|
24565
|
+
try {
|
|
24566
|
+
return await fn();
|
|
24567
|
+
} finally {
|
|
24568
|
+
process.stderr.write(`[factory:timing] ${phase} ${Math.round(performance.now() - start)}ms
|
|
24569
|
+
`);
|
|
24570
|
+
}
|
|
24571
|
+
}
|
|
24572
|
+
async function timedAboveThreshold(phase, thresholdMs, fn) {
|
|
24573
|
+
const start = performance.now();
|
|
24574
|
+
try {
|
|
24575
|
+
return await fn();
|
|
24576
|
+
} finally {
|
|
24577
|
+
const elapsed = performance.now() - start;
|
|
24578
|
+
if (elapsed > thresholdMs) {
|
|
24579
|
+
process.stderr.write(`[factory:timing] slow ${phase} ${Math.round(elapsed)}ms
|
|
24580
|
+
`);
|
|
24581
|
+
}
|
|
24582
|
+
}
|
|
24583
|
+
}
|
|
24584
|
+
|
|
24585
|
+
// src/auth.ts
|
|
24561
24586
|
function sanitizeReturnTo(raw) {
|
|
24562
24587
|
if (!raw) return "/";
|
|
24563
24588
|
if (!raw.startsWith("/")) return "/";
|
|
@@ -24846,11 +24871,15 @@ function createFactoryAuthGate(provider) {
|
|
|
24846
24871
|
if (c.req.method === "POST" && path3 === "/web/github/webhook") {
|
|
24847
24872
|
return next();
|
|
24848
24873
|
}
|
|
24849
|
-
if (path3 === "/signin" || path3.startsWith("/assets/")) {
|
|
24874
|
+
if (path3 === "/signin" || path3.startsWith("/assets/") || path3 === "/manifest.webmanifest" || path3 === "/mastra.svg") {
|
|
24850
24875
|
return next();
|
|
24851
24876
|
}
|
|
24852
24877
|
const token = getBearerToken(c.req.header("Authorization"));
|
|
24853
|
-
const user = await
|
|
24878
|
+
const user = await timedAboveThreshold(
|
|
24879
|
+
"auth.gate.authenticate",
|
|
24880
|
+
1e3,
|
|
24881
|
+
() => authenticateRequest(provider, token, c.req.raw)
|
|
24882
|
+
);
|
|
24854
24883
|
if (user) {
|
|
24855
24884
|
await ensureUserOrg(provider, user);
|
|
24856
24885
|
c.set(FACTORY_AUTH_USER_KEY, user);
|
|
@@ -25532,8 +25561,23 @@ async function teardownProjectSandbox(options) {
|
|
|
25532
25561
|
function shellQuote(value) {
|
|
25533
25562
|
return `'` + value.split(`'`).join(`'\\''`) + `'`;
|
|
25534
25563
|
}
|
|
25535
|
-
|
|
25536
|
-
|
|
25564
|
+
var DEFAULT_COMMAND_TIMEOUT_MS = 15 * 6e4;
|
|
25565
|
+
var CHECKOUT_COMMAND_TIMEOUT_MS = 5 * 6e4;
|
|
25566
|
+
async function sh(sandbox, script, options = {}) {
|
|
25567
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_COMMAND_TIMEOUT_MS;
|
|
25568
|
+
let timer;
|
|
25569
|
+
const hangGuard = new Promise((_, reject) => {
|
|
25570
|
+
timer = setTimeout(() => {
|
|
25571
|
+
const phase = options.phase ? ` during ${options.phase}` : "";
|
|
25572
|
+
reject(new Error(`Sandbox command timed out after ${Math.round(timeoutMs / 1e3)}s${phase}.`));
|
|
25573
|
+
}, timeoutMs);
|
|
25574
|
+
timer.unref?.();
|
|
25575
|
+
});
|
|
25576
|
+
try {
|
|
25577
|
+
return await Promise.race([sandbox.executeCommand("sh", ["-c", script], { timeout: timeoutMs }), hangGuard]);
|
|
25578
|
+
} finally {
|
|
25579
|
+
clearTimeout(timer);
|
|
25580
|
+
}
|
|
25537
25581
|
}
|
|
25538
25582
|
var MaterializeError = class extends Error {
|
|
25539
25583
|
constructor(message, code) {
|
|
@@ -25570,7 +25614,8 @@ async function materializeRepo(options) {
|
|
|
25570
25614
|
);
|
|
25571
25615
|
}
|
|
25572
25616
|
const authUrl = tokenUrl(repo, token);
|
|
25573
|
-
const alreadyMaterialized =
|
|
25617
|
+
const alreadyMaterialized = await hasExistingCheckout(sandbox, workdir, repo);
|
|
25618
|
+
let succeeded = false;
|
|
25574
25619
|
try {
|
|
25575
25620
|
if (!alreadyMaterialized) {
|
|
25576
25621
|
reportProgress(onProgress, {
|
|
@@ -25579,7 +25624,8 @@ async function materializeRepo(options) {
|
|
|
25579
25624
|
});
|
|
25580
25625
|
const clone = await sh(
|
|
25581
25626
|
sandbox,
|
|
25582
|
-
`git clone --depth=1 --single-branch --branch ${shellQuote(repoInfo.defaultBranch)} ${shellQuote(authUrl)} ${shellQuote(workdir)}
|
|
25627
|
+
`git clone --depth=1 --single-branch --branch ${shellQuote(repoInfo.defaultBranch)} ${shellQuote(authUrl)} ${shellQuote(workdir)}`,
|
|
25628
|
+
{ phase: "repository clone" }
|
|
25583
25629
|
);
|
|
25584
25630
|
if (clone.exitCode !== 0) {
|
|
25585
25631
|
throw classifyGitFailure(clone, "clone-failed");
|
|
@@ -25590,13 +25636,14 @@ async function materializeRepo(options) {
|
|
|
25590
25636
|
if (setUrl.exitCode !== 0) {
|
|
25591
25637
|
throw new MaterializeError(`Failed to set git remote: ${setUrl.stderr}`, "pull-failed");
|
|
25592
25638
|
}
|
|
25593
|
-
const pull = await sh(sandbox, `git -C ${shellQuote(workdir)} pull --ff-only
|
|
25639
|
+
const pull = await sh(sandbox, `git -C ${shellQuote(workdir)} pull --ff-only`, { phase: "repository pull" });
|
|
25594
25640
|
if (pull.exitCode !== 0) {
|
|
25595
25641
|
throw classifyGitFailure(pull, "pull-failed");
|
|
25596
25642
|
}
|
|
25597
25643
|
}
|
|
25644
|
+
succeeded = true;
|
|
25598
25645
|
} finally {
|
|
25599
|
-
await scrubRemote(sandbox, workdir, repo,
|
|
25646
|
+
await scrubRemote(sandbox, workdir, repo, succeeded);
|
|
25600
25647
|
}
|
|
25601
25648
|
reportProgress(onProgress, { phase: "finalizing", message: "Finalizing workspace\u2026" });
|
|
25602
25649
|
await storage.markMaterialized({ id: sandboxRow.id });
|
|
@@ -25627,7 +25674,8 @@ async function checkoutSessionBranch(sandbox, workdir, {
|
|
|
25627
25674
|
if (setUrl.exitCode !== 0) throw classifyGitFailure(setUrl, "pull-failed");
|
|
25628
25675
|
const fetch2 = await sh(
|
|
25629
25676
|
sandbox,
|
|
25630
|
-
`git -C ${shellQuote(workdir)} fetch origin ${shellQuote(baseBranch)} && git -C ${shellQuote(workdir)} checkout -b ${shellQuote(branch)} FETCH_HEAD
|
|
25677
|
+
`git -C ${shellQuote(workdir)} fetch origin ${shellQuote(baseBranch)} && git -C ${shellQuote(workdir)} checkout -b ${shellQuote(branch)} FETCH_HEAD`,
|
|
25678
|
+
{ timeoutMs: CHECKOUT_COMMAND_TIMEOUT_MS, phase: "branch checkout" }
|
|
25631
25679
|
);
|
|
25632
25680
|
if (fetch2.exitCode !== 0) throw classifyGitFailure(fetch2, "clone-failed");
|
|
25633
25681
|
} finally {
|
|
@@ -25751,7 +25799,7 @@ function computeWorktreePath(repoWorkdir, branch) {
|
|
|
25751
25799
|
}
|
|
25752
25800
|
async function runWorktreeSetup(sandbox, worktreePath, command) {
|
|
25753
25801
|
const result = await sh(sandbox, `cd ${shellQuote(worktreePath)} && { ${command}
|
|
25754
|
-
}
|
|
25802
|
+
}`, { phase: "worktree setup" });
|
|
25755
25803
|
if (result.exitCode !== 0) {
|
|
25756
25804
|
const detail = (result.stderr.trim() || result.stdout.trim()).slice(-2e3);
|
|
25757
25805
|
throw new WorktreeError(`Setup command failed (exit ${result.exitCode}): ${detail}`, "setup-failed");
|
|
@@ -26484,43 +26532,56 @@ function buildGithubRoutes(options) {
|
|
|
26484
26532
|
handler: async (c) => {
|
|
26485
26533
|
const resolved = await resolveOrgTenant(loose(c), auth);
|
|
26486
26534
|
if ("response" in resolved) return resolved.response;
|
|
26487
|
-
const
|
|
26535
|
+
const orgId = resolved.tenant.orgId;
|
|
26536
|
+
const installs = await github.sourceControlStorage.installations.list({ orgId });
|
|
26488
26537
|
const query = (c.req.query("q") ?? "").toLowerCase();
|
|
26489
|
-
const
|
|
26538
|
+
const listed = await Promise.all(
|
|
26539
|
+
installs.map(async (inst) => {
|
|
26540
|
+
try {
|
|
26541
|
+
return { inst, list: await github.listInstallationRepos(Number(inst.externalId)) };
|
|
26542
|
+
} catch (err) {
|
|
26543
|
+
if (err.status !== 404) throw err;
|
|
26544
|
+
console.error(`[Mastra Factory] pruning stale GitHub installation ${inst.externalId} (404 from GitHub)`);
|
|
26545
|
+
await github.sourceControlStorage.installations.delete({ orgId, id: inst.id });
|
|
26546
|
+
return { inst, list: [] };
|
|
26547
|
+
}
|
|
26548
|
+
})
|
|
26549
|
+
);
|
|
26550
|
+
const matches = [];
|
|
26490
26551
|
const seenRepositoryIds = /* @__PURE__ */ new Set();
|
|
26491
|
-
for (const inst of
|
|
26492
|
-
let list;
|
|
26493
|
-
try {
|
|
26494
|
-
list = await github.listInstallationRepos(Number(inst.externalId));
|
|
26495
|
-
} catch (err) {
|
|
26496
|
-
if (err.status !== 404) throw err;
|
|
26497
|
-
console.error(`[Mastra Factory] pruning stale GitHub installation ${inst.externalId} (404 from GitHub)`);
|
|
26498
|
-
await github.sourceControlStorage.installations.delete({ orgId: resolved.tenant.orgId, id: inst.id });
|
|
26499
|
-
continue;
|
|
26500
|
-
}
|
|
26552
|
+
for (const { inst, list } of listed) {
|
|
26501
26553
|
for (const repo of list) {
|
|
26502
26554
|
if (query && !repo.fullName.toLowerCase().includes(query)) continue;
|
|
26503
26555
|
if (seenRepositoryIds.has(repo.id)) continue;
|
|
26504
26556
|
seenRepositoryIds.add(repo.id);
|
|
26505
|
-
|
|
26506
|
-
orgId: resolved.tenant.orgId,
|
|
26507
|
-
input: {
|
|
26508
|
-
installationId: inst.id,
|
|
26509
|
-
externalId: repo.id.toString(),
|
|
26510
|
-
slug: repo.fullName,
|
|
26511
|
-
defaultBranch: isValidGitRef2(repo.defaultBranch) ? repo.defaultBranch : "main",
|
|
26512
|
-
providerMetadata: { private: repo.private, owner: repo.owner }
|
|
26513
|
-
}
|
|
26514
|
-
});
|
|
26515
|
-
repos.push({
|
|
26516
|
-
...repo,
|
|
26517
|
-
installationStorageId: inst.id,
|
|
26518
|
-
repositoryStorageId: repository.id,
|
|
26519
|
-
sandboxProvider: fleet.provider,
|
|
26520
|
-
sandboxWorkdir: fleet.computeWorkdir(repo.fullName)
|
|
26521
|
-
});
|
|
26557
|
+
matches.push({ inst, repo });
|
|
26522
26558
|
}
|
|
26523
26559
|
}
|
|
26560
|
+
const repos = new Array(matches.length);
|
|
26561
|
+
const upsertConcurrency = 10;
|
|
26562
|
+
for (let start = 0; start < matches.length; start += upsertConcurrency) {
|
|
26563
|
+
await Promise.all(
|
|
26564
|
+
matches.slice(start, start + upsertConcurrency).map(async ({ inst, repo }, offset) => {
|
|
26565
|
+
const repository = await github.sourceControlStorage.repositories.upsert({
|
|
26566
|
+
orgId,
|
|
26567
|
+
input: {
|
|
26568
|
+
installationId: inst.id,
|
|
26569
|
+
externalId: repo.id.toString(),
|
|
26570
|
+
slug: repo.fullName,
|
|
26571
|
+
defaultBranch: isValidGitRef2(repo.defaultBranch) ? repo.defaultBranch : "main",
|
|
26572
|
+
providerMetadata: { private: repo.private, owner: repo.owner }
|
|
26573
|
+
}
|
|
26574
|
+
});
|
|
26575
|
+
repos[start + offset] = {
|
|
26576
|
+
...repo,
|
|
26577
|
+
installationStorageId: inst.id,
|
|
26578
|
+
repositoryStorageId: repository.id,
|
|
26579
|
+
sandboxProvider: fleet.provider,
|
|
26580
|
+
sandboxWorkdir: fleet.computeWorkdir(repo.fullName)
|
|
26581
|
+
};
|
|
26582
|
+
})
|
|
26583
|
+
);
|
|
26584
|
+
}
|
|
26524
26585
|
return c.json({ repos });
|
|
26525
26586
|
}
|
|
26526
26587
|
})
|
|
@@ -26851,8 +26912,25 @@ async function loadOrCreateSandboxRow(github, project, userId) {
|
|
|
26851
26912
|
return github.sourceControlStorage.sandboxes.getOrCreate({ projectRepository: project, userId });
|
|
26852
26913
|
}
|
|
26853
26914
|
async function prepareProject(options) {
|
|
26854
|
-
const { github, fleet,
|
|
26855
|
-
|
|
26915
|
+
const { github, fleet, userId, onProgress } = options;
|
|
26916
|
+
let project = options.project;
|
|
26917
|
+
if (project.sandboxProvider !== fleet.provider) {
|
|
26918
|
+
const sandboxWorkdir = fleet.computeWorkdir(project.repository.slug);
|
|
26919
|
+
await github.sourceControlStorage.projectRepositories.update({
|
|
26920
|
+
orgId: project.installation.orgId,
|
|
26921
|
+
id: project.id,
|
|
26922
|
+
input: { sandboxProvider: fleet.provider, sandboxWorkdir }
|
|
26923
|
+
});
|
|
26924
|
+
project = { ...project, sandboxProvider: fleet.provider, sandboxWorkdir };
|
|
26925
|
+
}
|
|
26926
|
+
let sandboxRow = await loadOrCreateSandboxRow(github, project, userId);
|
|
26927
|
+
if (sandboxRow.sandboxWorkdir !== project.sandboxWorkdir) {
|
|
26928
|
+
await github.sourceControlStorage.sandboxes.setWorkdir({
|
|
26929
|
+
id: sandboxRow.id,
|
|
26930
|
+
sandboxWorkdir: project.sandboxWorkdir
|
|
26931
|
+
});
|
|
26932
|
+
sandboxRow = { ...sandboxRow, sandboxWorkdir: project.sandboxWorkdir, materializedAt: null };
|
|
26933
|
+
}
|
|
26856
26934
|
const access = await github.versionControl.getRepositoryAccess({
|
|
26857
26935
|
orgId: project.installation.orgId,
|
|
26858
26936
|
repositoryId: project.repository.id
|
|
@@ -28326,6 +28404,17 @@ function retryDelay(error, fallbackMs) {
|
|
|
28326
28404
|
// src/integrations/platform/github/integration.ts
|
|
28327
28405
|
var PAGE_SIZE = 30;
|
|
28328
28406
|
var API_PREFIX2 = "/v1/server";
|
|
28407
|
+
var INSTALLATION_REPOS_CACHE_TTL_MS = 3e4;
|
|
28408
|
+
var REPOSITORY_ACCESS_CACHE_TTL_MS = 5 * 6e4;
|
|
28409
|
+
var MAX_CACHE_ENTRIES = 1e3;
|
|
28410
|
+
function setBounded(cache, key, value) {
|
|
28411
|
+
cache.delete(key);
|
|
28412
|
+
cache.set(key, value);
|
|
28413
|
+
if (cache.size > MAX_CACHE_ENTRIES) {
|
|
28414
|
+
const oldest = cache.keys().next().value;
|
|
28415
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
28416
|
+
}
|
|
28417
|
+
}
|
|
28329
28418
|
var REPOSITORY_TOKEN_PERMISSIONS = {
|
|
28330
28419
|
contents: "write",
|
|
28331
28420
|
issues: "write",
|
|
@@ -28345,6 +28434,10 @@ var PlatformGithubIntegration = class {
|
|
|
28345
28434
|
#pollingIntervalMs;
|
|
28346
28435
|
#storage;
|
|
28347
28436
|
#integrationStorage;
|
|
28437
|
+
/** installationId → cached repository listing (TTL-bounded). */
|
|
28438
|
+
#installationReposCache = /* @__PURE__ */ new Map();
|
|
28439
|
+
/** `orgId:repositoryId` → cached repository access (TTL-bounded). */
|
|
28440
|
+
#repositoryAccessCache = /* @__PURE__ */ new Map();
|
|
28348
28441
|
intake = {
|
|
28349
28442
|
resolveIntakeDispatch: (input) => this.#resolveIntakeDispatch(input),
|
|
28350
28443
|
listSources: async ({ orgId, userId }) => {
|
|
@@ -28475,6 +28568,10 @@ var PlatformGithubIntegration = class {
|
|
|
28475
28568
|
)
|
|
28476
28569
|
),
|
|
28477
28570
|
getRepositoryAccess: async ({ orgId, repositoryId }) => {
|
|
28571
|
+
const cacheKey = `${orgId}:${repositoryId}`;
|
|
28572
|
+
const cached = this.#repositoryAccessCache.get(cacheKey);
|
|
28573
|
+
if (cached && cached.expiresAt > Date.now()) return cached.access;
|
|
28574
|
+
this.#repositoryAccessCache.delete(cacheKey);
|
|
28478
28575
|
const repository = await this.storage.repositories.get({ orgId, id: repositoryId });
|
|
28479
28576
|
if (!repository) throw new Error("Version-control repository not found.");
|
|
28480
28577
|
const cloneUrl = `https://github.com/${repository.slug}.git`;
|
|
@@ -28488,10 +28585,15 @@ var PlatformGithubIntegration = class {
|
|
|
28488
28585
|
`${API_PREFIX2}/github-app/installations/${installationId}/token`,
|
|
28489
28586
|
{ repositories: [repositoryName], permissions: REPOSITORY_TOKEN_PERMISSIONS }
|
|
28490
28587
|
);
|
|
28491
|
-
|
|
28588
|
+
const access = {
|
|
28492
28589
|
cloneUrl,
|
|
28493
28590
|
authorization: { scheme: "bearer", token: token.token }
|
|
28494
28591
|
};
|
|
28592
|
+
setBounded(this.#repositoryAccessCache, cacheKey, {
|
|
28593
|
+
access,
|
|
28594
|
+
expiresAt: Date.now() + REPOSITORY_ACCESS_CACHE_TTL_MS
|
|
28595
|
+
});
|
|
28596
|
+
return access;
|
|
28495
28597
|
},
|
|
28496
28598
|
listPullRequests: (input) => this.#listPullRequests(input),
|
|
28497
28599
|
getPullRequest: (input) => this.#getPullRequest(input),
|
|
@@ -28786,8 +28888,16 @@ var PlatformGithubIntegration = class {
|
|
|
28786
28888
|
}
|
|
28787
28889
|
}
|
|
28788
28890
|
async listInstallationRepos(installationId) {
|
|
28891
|
+
const cached = this.#installationReposCache.get(installationId);
|
|
28892
|
+
if (cached && cached.expiresAt > Date.now()) return cached.repos;
|
|
28893
|
+
this.#installationReposCache.delete(installationId);
|
|
28789
28894
|
const result = await this.#client.request("GET", `${API_PREFIX2}/github-app/installations/${installationId}/repositories`);
|
|
28790
|
-
|
|
28895
|
+
const repos = result.repositories.map((repository) => ({ ...repository, installationId }));
|
|
28896
|
+
setBounded(this.#installationReposCache, installationId, {
|
|
28897
|
+
repos,
|
|
28898
|
+
expiresAt: Date.now() + INSTALLATION_REPOS_CACHE_TTL_MS
|
|
28899
|
+
});
|
|
28900
|
+
return repos;
|
|
28791
28901
|
}
|
|
28792
28902
|
async mintInstallationToken(installationId) {
|
|
28793
28903
|
const repositories = await this.listInstallationRepos(installationId);
|
|
@@ -31922,7 +32032,7 @@ var ConfigRoutes = class extends Route {
|
|
|
31922
32032
|
const record = await context.storage.get({ orgId: context.orgId, userId: context.userId });
|
|
31923
32033
|
if (!resourceId) return c.json({ config: readStoredOMConfig(record) });
|
|
31924
32034
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31925
|
-
if (!session) return c.json({
|
|
32035
|
+
if (!session) return c.json({ config: readStoredOMConfig(record) });
|
|
31926
32036
|
await hydrateSessionMemorySettings(session, record);
|
|
31927
32037
|
return c.json({ config: readOMConfig(session) });
|
|
31928
32038
|
} catch (error) {
|
|
@@ -31956,7 +32066,6 @@ var ConfigRoutes = class extends Route {
|
|
|
31956
32066
|
if ("response" in context) return context.response;
|
|
31957
32067
|
try {
|
|
31958
32068
|
const session = resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0;
|
|
31959
|
-
if (resourceId && !session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31960
32069
|
const otherRole = session ? role === "observer" ? session.om.reflector : session.om.observer : void 0;
|
|
31961
32070
|
const otherRoleCurrentModelId = otherRole?.modelId() ?? null;
|
|
31962
32071
|
await session?.om[role].switchModel({ modelId });
|
|
@@ -31998,7 +32107,6 @@ var ConfigRoutes = class extends Route {
|
|
|
31998
32107
|
if ("response" in context) return context.response;
|
|
31999
32108
|
try {
|
|
32000
32109
|
const session = resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0;
|
|
32001
|
-
if (resourceId && !session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
32002
32110
|
if (observation !== void 0 && session) {
|
|
32003
32111
|
await session.state.set({ observationThreshold: observation });
|
|
32004
32112
|
await session.thread.setSetting({ key: "observationThreshold", value: observation });
|
|
@@ -32043,7 +32151,6 @@ var ConfigRoutes = class extends Route {
|
|
|
32043
32151
|
if ("response" in context) return context.response;
|
|
32044
32152
|
try {
|
|
32045
32153
|
const session = resourceId ? await controller.getSessionByResource?.(resourceId, scope) : void 0;
|
|
32046
|
-
if (resourceId && !session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
32047
32154
|
if (session) {
|
|
32048
32155
|
await session.state.set({ observeAttachments: value });
|
|
32049
32156
|
await session.thread.setSetting({ key: "observeAttachments", value });
|
|
@@ -33827,7 +33934,8 @@ var FACTORY_GOVERNANCE_SCHEMAS = [
|
|
|
33827
33934
|
name: "factory_deferred_decisions_tenant_key_unique",
|
|
33828
33935
|
columns: ["org_id", "factory_project_id", "idempotency_key"]
|
|
33829
33936
|
}
|
|
33830
|
-
]
|
|
33937
|
+
],
|
|
33938
|
+
indexes: [{ name: "factory_deferred_decisions_claim_idx", columns: ["status", "created_at"] }]
|
|
33831
33939
|
},
|
|
33832
33940
|
{
|
|
33833
33941
|
name: "factory_run_bindings",
|
|
@@ -33844,7 +33952,17 @@ var FACTORY_GOVERNANCE_SCHEMAS = [
|
|
|
33844
33952
|
status: { type: "text" },
|
|
33845
33953
|
created_at: { type: "timestamp" },
|
|
33846
33954
|
revoked_at: { type: "timestamp", nullable: true }
|
|
33847
|
-
}
|
|
33955
|
+
},
|
|
33956
|
+
indexes: [
|
|
33957
|
+
// Exact-address lookups run on every processor message; status filter is
|
|
33958
|
+
// applied on top of the address columns.
|
|
33959
|
+
{
|
|
33960
|
+
name: "factory_run_bindings_session_idx",
|
|
33961
|
+
columns: ["factory_project_id", "thread_id", "resource_id", "session_id"]
|
|
33962
|
+
},
|
|
33963
|
+
// Restart reconciler enumerates active bindings across all tenants.
|
|
33964
|
+
{ name: "factory_run_bindings_status_idx", columns: ["status"] }
|
|
33965
|
+
]
|
|
33848
33966
|
},
|
|
33849
33967
|
{
|
|
33850
33968
|
name: "factory_tool_result_cursors",
|
|
@@ -33881,7 +33999,8 @@ var FACTORY_GOVERNANCE_SCHEMAS = [
|
|
|
33881
33999
|
name: "factory_pending_starts_tenant_kickoff_unique",
|
|
33882
34000
|
columns: ["org_id", "factory_project_id", "kickoff_key"]
|
|
33883
34001
|
}
|
|
33884
|
-
]
|
|
34002
|
+
],
|
|
34003
|
+
indexes: [{ name: "factory_pending_starts_claim_idx", columns: ["status", "created_at"] }]
|
|
33885
34004
|
}
|
|
33886
34005
|
];
|
|
33887
34006
|
function toBinding(row) {
|
|
@@ -33962,7 +34081,11 @@ var WorkItemsStorage = class extends FactoryStorageDomain3 {
|
|
|
33962
34081
|
}
|
|
33963
34082
|
async #claimLeases(table, input, map) {
|
|
33964
34083
|
const claim = () => this.storage.withTransaction(async (ops) => {
|
|
33965
|
-
const candidates = await ops.findMany(
|
|
34084
|
+
const candidates = await ops.findMany(
|
|
34085
|
+
table,
|
|
34086
|
+
{ status: { in: ["pending", "retry", "leased"] } },
|
|
34087
|
+
{ orderBy: [["created_at", "asc"]], limit: Math.max(input.limit * 5, 50) }
|
|
34088
|
+
);
|
|
33966
34089
|
const claimed = [];
|
|
33967
34090
|
for (const candidate of candidates) {
|
|
33968
34091
|
if (claimed.length >= input.limit) break;
|
|
@@ -36448,6 +36571,12 @@ var FactoryDecisionDispatcher = class {
|
|
|
36448
36571
|
(candidate) => candidate.id === record.bindingId && candidate.status === "active"
|
|
36449
36572
|
);
|
|
36450
36573
|
if (!binding) throw new Error("Prepared Factory binding is unavailable or revoked.");
|
|
36574
|
+
const item = await this.#storage.get({ orgId: record.orgId, id: binding.workItemId });
|
|
36575
|
+
const startedBy = item?.sessions[binding.role]?.startedBy;
|
|
36576
|
+
if (!startedBy) throw new Error(`Factory binding ${binding.id} has no authenticated session owner.`);
|
|
36577
|
+
await this.#primeCredentials?.({ orgId: record.orgId, userId: startedBy });
|
|
36578
|
+
const requestContext = new RequestContext2();
|
|
36579
|
+
requestContext.set("user", { workosId: startedBy, organizationId: record.orgId });
|
|
36451
36580
|
const session = await this.#requireSession(binding);
|
|
36452
36581
|
await awaitNotification(
|
|
36453
36582
|
await session.sendNotificationSignal(
|
|
@@ -36460,7 +36589,7 @@ var FactoryDecisionDispatcher = class {
|
|
|
36460
36589
|
sourceId: record.id,
|
|
36461
36590
|
dedupeKey: `factory-kickoff:${record.kickoffKey}`
|
|
36462
36591
|
},
|
|
36463
|
-
{ ifActive: { behavior: "deliver" }, ifIdle: { behavior: "wake" } }
|
|
36592
|
+
{ ifActive: { behavior: "deliver" }, ifIdle: { behavior: "wake" }, requestContext }
|
|
36464
36593
|
),
|
|
36465
36594
|
true
|
|
36466
36595
|
);
|
|
@@ -36941,6 +37070,7 @@ var MIME = {
|
|
|
36941
37070
|
".mjs": "text/javascript; charset=utf-8",
|
|
36942
37071
|
".css": "text/css; charset=utf-8",
|
|
36943
37072
|
".json": "application/json",
|
|
37073
|
+
".webmanifest": "application/manifest+json",
|
|
36944
37074
|
".map": "application/json",
|
|
36945
37075
|
".svg": "image/svg+xml",
|
|
36946
37076
|
".png": "image/png",
|
|
@@ -38714,6 +38844,10 @@ var SourceControlStorage = class extends FactoryStorageDomain11 {
|
|
|
38714
38844
|
}
|
|
38715
38845
|
},
|
|
38716
38846
|
getById: ({ id }) => getSandbox(id),
|
|
38847
|
+
setWorkdir: async ({ id, sandboxWorkdir }) => {
|
|
38848
|
+
await requireSandbox(id);
|
|
38849
|
+
await db().updateMany(SANDBOXES, { id }, { sandbox_workdir: sandboxWorkdir, materialized_at: null });
|
|
38850
|
+
},
|
|
38717
38851
|
setSandboxId: async ({ id, sandboxId }) => {
|
|
38718
38852
|
await requireSandbox(id);
|
|
38719
38853
|
await db().updateMany(SANDBOXES, { id }, { sandbox_id: sandboxId });
|
|
@@ -38924,6 +39058,7 @@ function createWorkspaceFactory(options = {}) {
|
|
|
38924
39058
|
const { sandbox: sandboxConfig, github, fleet, workItems } = options;
|
|
38925
39059
|
const isLocalSandbox = sandboxConfig?.machine instanceof LocalSandbox;
|
|
38926
39060
|
const githubTokenInjectors = /* @__PURE__ */ new Map();
|
|
39061
|
+
const inflightMaterializations = /* @__PURE__ */ new Map();
|
|
38927
39062
|
return async ({ requestContext, mastra, skillExtension }) => {
|
|
38928
39063
|
const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
|
|
38929
39064
|
const ctx = requestContext.get("controller");
|
|
@@ -38991,65 +39126,84 @@ function createWorkspaceFactory(options = {}) {
|
|
|
38991
39126
|
}
|
|
38992
39127
|
} catch {
|
|
38993
39128
|
}
|
|
38994
|
-
const
|
|
38995
|
-
|
|
38996
|
-
|
|
38997
|
-
|
|
38998
|
-
|
|
38999
|
-
|
|
39000
|
-
|
|
39001
|
-
|
|
39002
|
-
|
|
39003
|
-
|
|
39004
|
-
|
|
39005
|
-
|
|
39006
|
-
|
|
39007
|
-
|
|
39008
|
-
|
|
39009
|
-
const ghCliToken = await getGithubPat(() => github.integrationStorage, session.orgId, patKind) ?? token;
|
|
39010
|
-
const sandbox = await fleet.ensureSandbox(
|
|
39011
|
-
binding,
|
|
39012
|
-
{ GH_TOKEN: ghCliToken },
|
|
39013
|
-
void 0,
|
|
39014
|
-
isLocalSandbox ? { workingDirectory: workdir } : {}
|
|
39015
|
-
);
|
|
39016
|
-
await materializeRepo({
|
|
39017
|
-
row: { id: session.id, sandboxWorkdir: workdir, materializedAt: session.materializedAt },
|
|
39018
|
-
repoInfo: { repoFullName, defaultBranch: repository.defaultBranch },
|
|
39019
|
-
sandbox,
|
|
39020
|
-
token,
|
|
39021
|
-
storage: storage.sessions
|
|
39022
|
-
});
|
|
39023
|
-
await checkoutSessionBranch(sandbox, workdir, {
|
|
39024
|
-
branch: session.branch,
|
|
39025
|
-
baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,
|
|
39026
|
-
token,
|
|
39027
|
-
repoFullName
|
|
39028
|
-
});
|
|
39029
|
-
if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
|
|
39030
|
-
const injectGithubToken2 = (freshToken) => {
|
|
39031
|
-
if (!sandbox.setEnvironmentVariable) {
|
|
39032
|
-
throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
|
|
39129
|
+
const materialize = async () => {
|
|
39130
|
+
const access = await github.versionControl.getRepositoryAccess({
|
|
39131
|
+
orgId: session.orgId,
|
|
39132
|
+
repositoryId: repository.id
|
|
39133
|
+
});
|
|
39134
|
+
const token = access.authorization?.token;
|
|
39135
|
+
if (!token) throw new Error("Repository access did not include a bearer token for the Factory session");
|
|
39136
|
+
let patKind = "default";
|
|
39137
|
+
if (workItems) {
|
|
39138
|
+
try {
|
|
39139
|
+
const address = getFactorySessionAddress(requestContext);
|
|
39140
|
+
const runBinding = address ? await workItems.findRunBindingBySession(address) : null;
|
|
39141
|
+
if (runBinding?.role === "review" && runBinding.orgId === session.orgId) patKind = "reviewer";
|
|
39142
|
+
} catch {
|
|
39143
|
+
}
|
|
39033
39144
|
}
|
|
39034
|
-
|
|
39035
|
-
const
|
|
39036
|
-
|
|
39145
|
+
const ghCliToken = await getGithubPat(() => github.integrationStorage, session.orgId, patKind) ?? token;
|
|
39146
|
+
const sandbox = await fleet.ensureSandbox(
|
|
39147
|
+
binding,
|
|
39148
|
+
{ GH_TOKEN: ghCliToken },
|
|
39149
|
+
void 0,
|
|
39150
|
+
isLocalSandbox ? { workingDirectory: workdir } : {}
|
|
39151
|
+
);
|
|
39152
|
+
await materializeRepo({
|
|
39153
|
+
row: { id: session.id, sandboxWorkdir: workdir, materializedAt: session.materializedAt },
|
|
39154
|
+
repoInfo: { repoFullName, defaultBranch: repository.defaultBranch },
|
|
39155
|
+
sandbox,
|
|
39156
|
+
token,
|
|
39157
|
+
storage: storage.sessions
|
|
39158
|
+
});
|
|
39159
|
+
await checkoutSessionBranch(sandbox, workdir, {
|
|
39160
|
+
branch: session.branch,
|
|
39161
|
+
baseBranch: session.baseBranch || projectRepository.branch || repository.defaultBranch,
|
|
39162
|
+
token,
|
|
39163
|
+
repoFullName
|
|
39164
|
+
});
|
|
39165
|
+
if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
|
|
39166
|
+
const injectGithubToken2 = (freshToken) => {
|
|
39167
|
+
if (!sandbox.setEnvironmentVariable) {
|
|
39168
|
+
throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
|
|
39169
|
+
}
|
|
39170
|
+
sandbox.setEnvironmentVariable("GH_TOKEN", freshToken);
|
|
39171
|
+
const registered = githubTokenInjectors.get(workspaceId);
|
|
39172
|
+
if (registered) registered.ghToken = freshToken;
|
|
39173
|
+
};
|
|
39174
|
+
githubTokenInjectors.set(workspaceId, { inject: injectGithubToken2, patKind, ghToken: ghCliToken });
|
|
39175
|
+
registerGithubTokenInjector(requestContext, injectGithubToken2);
|
|
39176
|
+
registerGithubPatKind(requestContext, patKind);
|
|
39177
|
+
const filesystem = new SandboxFilesystem2({ sandbox, workdir });
|
|
39178
|
+
const projectSkillPaths = [path2.join(configDir, "skills"), ".claude/skills", ".agents/skills"];
|
|
39179
|
+
const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
|
|
39180
|
+
return new Workspace({
|
|
39181
|
+
id: workspaceId,
|
|
39182
|
+
name: "Mastra Code Factory Session Workspace",
|
|
39183
|
+
filesystem,
|
|
39184
|
+
sandbox,
|
|
39185
|
+
tools: MASTRACODE_WORKSPACE_TOOLS,
|
|
39186
|
+
skills: skillPaths,
|
|
39187
|
+
skillSource: effectiveSkillExtension?.createSource(filesystem, projectSkillPaths) ?? filesystem
|
|
39188
|
+
});
|
|
39037
39189
|
};
|
|
39038
|
-
|
|
39039
|
-
|
|
39040
|
-
|
|
39041
|
-
|
|
39042
|
-
|
|
39043
|
-
|
|
39044
|
-
|
|
39045
|
-
|
|
39046
|
-
|
|
39047
|
-
|
|
39048
|
-
|
|
39049
|
-
|
|
39050
|
-
|
|
39051
|
-
|
|
39052
|
-
}
|
|
39190
|
+
const inflight = inflightMaterializations.get(workspaceId);
|
|
39191
|
+
if (inflight) {
|
|
39192
|
+
const workspace = await inflight;
|
|
39193
|
+
const registered = githubTokenInjectors.get(workspaceId);
|
|
39194
|
+
if (registered) {
|
|
39195
|
+
registerGithubTokenInjector(requestContext, registered.inject);
|
|
39196
|
+
registerGithubPatKind(requestContext, registered.patKind);
|
|
39197
|
+
}
|
|
39198
|
+
return workspace;
|
|
39199
|
+
}
|
|
39200
|
+
const materialization = materialize();
|
|
39201
|
+
inflightMaterializations.set(workspaceId, materialization);
|
|
39202
|
+
try {
|
|
39203
|
+
return await materialization;
|
|
39204
|
+
} finally {
|
|
39205
|
+
inflightMaterializations.delete(workspaceId);
|
|
39206
|
+
}
|
|
39053
39207
|
};
|
|
39054
39208
|
}
|
|
39055
39209
|
var getFactoryWorkspace = createWorkspaceFactory();
|
|
@@ -39194,9 +39348,12 @@ var MastraFactory = class {
|
|
|
39194
39348
|
registerSandboxReattach(fleet);
|
|
39195
39349
|
const stateSigner = createStateSigner(this.#config.stateSecret);
|
|
39196
39350
|
if (auth && hasAuthInit(auth)) {
|
|
39197
|
-
await
|
|
39351
|
+
await timedPhase(
|
|
39352
|
+
"prepare.auth.init",
|
|
39353
|
+
() => auth.init({ database: storage.authDatabase?.(), publicUrl: publicOrigin, allowedOrigins })
|
|
39354
|
+
);
|
|
39198
39355
|
}
|
|
39199
|
-
await storage.init();
|
|
39356
|
+
await timedPhase("prepare.storage.init", () => storage.init());
|
|
39200
39357
|
if (auth) {
|
|
39201
39358
|
registerTenantCredentialResolver(modelCredentialsStorage);
|
|
39202
39359
|
}
|
|
@@ -39264,153 +39421,160 @@ var MastraFactory = class {
|
|
|
39264
39421
|
const toolIntegrations = integrationRegistrations.filter(
|
|
39265
39422
|
({ integration }) => integration.agentTools || integration.sessionTools
|
|
39266
39423
|
);
|
|
39267
|
-
const prepared = await
|
|
39268
|
-
|
|
39269
|
-
|
|
39270
|
-
|
|
39271
|
-
|
|
39272
|
-
|
|
39273
|
-
|
|
39274
|
-
|
|
39275
|
-
|
|
39276
|
-
|
|
39277
|
-
|
|
39278
|
-
|
|
39279
|
-
|
|
39280
|
-
|
|
39281
|
-
|
|
39282
|
-
|
|
39283
|
-
|
|
39284
|
-
|
|
39285
|
-
|
|
39286
|
-
|
|
39287
|
-
|
|
39288
|
-
|
|
39289
|
-
|
|
39290
|
-
|
|
39291
|
-
|
|
39292
|
-
|
|
39293
|
-
|
|
39424
|
+
const prepared = await timedPhase(
|
|
39425
|
+
"prepare.controllerMount",
|
|
39426
|
+
() => prepareAgentControllerMount({
|
|
39427
|
+
controllerId: CONTROLLER_ID,
|
|
39428
|
+
workspace: createWorkspaceFactory({
|
|
39429
|
+
...this.#config.sandbox ? { sandbox: this.#config.sandbox } : {},
|
|
39430
|
+
...githubIntegration ? { github: githubIntegration } : {},
|
|
39431
|
+
...workItemsStorage ? { workItems: workItemsStorage } : {},
|
|
39432
|
+
fleet
|
|
39433
|
+
}),
|
|
39434
|
+
disableGithubSignals: true,
|
|
39435
|
+
// Memory settings live in the factory's `memory-settings` app table (per
|
|
39436
|
+
// org/user), so the host machine's TUI settings.json must not seed them.
|
|
39437
|
+
disableSettingsOmSeed: true,
|
|
39438
|
+
storage: storage.getMastraStorage(),
|
|
39439
|
+
...mastraStorageBackend ? { storageBackend: mastraStorageBackend } : {},
|
|
39440
|
+
...factoryProcessor ? { inputProcessors: [factoryProcessor] } : {},
|
|
39441
|
+
...vector ? { vector } : {},
|
|
39442
|
+
...toolIntegrations.length > 0 || workItemsStorage && transitionService ? {
|
|
39443
|
+
extraTools: async ({ requestContext }) => {
|
|
39444
|
+
const tools = {};
|
|
39445
|
+
const toolOwners = /* @__PURE__ */ new Map();
|
|
39446
|
+
const mergeTools = (ownerId, contributed) => {
|
|
39447
|
+
for (const [name, tool] of Object.entries(contributed)) {
|
|
39448
|
+
const owner = toolOwners.get(name);
|
|
39449
|
+
if (owner) {
|
|
39450
|
+
throw new Error(
|
|
39451
|
+
`MastraFactory: integration tool '${name}' from '${ownerId}' conflicts with '${owner}'.`
|
|
39452
|
+
);
|
|
39453
|
+
}
|
|
39454
|
+
toolOwners.set(name, ownerId);
|
|
39455
|
+
tools[name] = tool;
|
|
39294
39456
|
}
|
|
39295
|
-
|
|
39296
|
-
|
|
39457
|
+
};
|
|
39458
|
+
if (workItemsStorage && transitionService) {
|
|
39459
|
+
mergeTools(
|
|
39460
|
+
"factory",
|
|
39461
|
+
await createFactoryTransitionTools({
|
|
39462
|
+
requestContext,
|
|
39463
|
+
storage: workItemsStorage,
|
|
39464
|
+
transitionService
|
|
39465
|
+
})
|
|
39466
|
+
);
|
|
39297
39467
|
}
|
|
39298
|
-
|
|
39299
|
-
|
|
39300
|
-
|
|
39301
|
-
|
|
39302
|
-
|
|
39303
|
-
|
|
39304
|
-
|
|
39305
|
-
|
|
39306
|
-
|
|
39307
|
-
|
|
39308
|
-
|
|
39309
|
-
|
|
39310
|
-
|
|
39468
|
+
for (const { integration, ready, ensureReady } of toolIntegrations) {
|
|
39469
|
+
if (!ready && ensureReady) {
|
|
39470
|
+
try {
|
|
39471
|
+
await ensureReady();
|
|
39472
|
+
} catch {
|
|
39473
|
+
continue;
|
|
39474
|
+
}
|
|
39475
|
+
}
|
|
39476
|
+
if (integration.agentTools) {
|
|
39477
|
+
mergeTools(integration.id, await integration.agentTools({ requestContext }));
|
|
39478
|
+
}
|
|
39479
|
+
if (integration.sessionTools) {
|
|
39480
|
+
mergeTools(integration.id, integration.sessionTools({ requestContext }));
|
|
39311
39481
|
}
|
|
39312
39482
|
}
|
|
39313
|
-
|
|
39314
|
-
|
|
39315
|
-
|
|
39316
|
-
|
|
39317
|
-
|
|
39318
|
-
|
|
39483
|
+
return tools;
|
|
39484
|
+
}
|
|
39485
|
+
} : {},
|
|
39486
|
+
postToolObserver: async (toolContext) => {
|
|
39487
|
+
const requestContext = toolContext.context?.requestContext;
|
|
39488
|
+
if (requestContext) {
|
|
39489
|
+
await observeAgentGitAction({
|
|
39490
|
+
audit: auditDomain,
|
|
39491
|
+
toolContext: { ...toolContext, context: requestContext }
|
|
39492
|
+
});
|
|
39319
39493
|
}
|
|
39320
|
-
|
|
39321
|
-
|
|
39322
|
-
|
|
39323
|
-
|
|
39324
|
-
|
|
39325
|
-
|
|
39326
|
-
|
|
39494
|
+
await Promise.all(
|
|
39495
|
+
integrations.map(async (integration) => {
|
|
39496
|
+
if (!integration.postToolObserver) return;
|
|
39497
|
+
try {
|
|
39498
|
+
await integration.postToolObserver({ toolContext, requestContext });
|
|
39499
|
+
} catch (error) {
|
|
39500
|
+
console.warn(`[factory] Integration '${integration.id}' post-tool observer failed:`, error);
|
|
39501
|
+
}
|
|
39502
|
+
})
|
|
39503
|
+
);
|
|
39504
|
+
},
|
|
39505
|
+
...pubsub ? { pubsub, crossProcessPubSub: true } : {},
|
|
39506
|
+
buildApiRoutes: ({ controller, authStorage }) => [
|
|
39507
|
+
// Public `/auth/*` routes (login/callback/logout/me). Folded in as
|
|
39508
|
+
// `apiRoutes` (not plain Hono routes) because the entry can't touch the
|
|
39509
|
+
// Hono app the deployer generates. `requiresAuth: false`; the gate
|
|
39510
|
+
// skips `/auth/*`.
|
|
39511
|
+
...auth ? buildAuthRoutes(auth, { publicUrl: publicOrigin }) : [],
|
|
39512
|
+
// Custom `/web/*` routes (fs / config / integrations / factory / audit).
|
|
39513
|
+
...assembleFactoryApiRoutes({
|
|
39514
|
+
controllerId: CONTROLLER_ID,
|
|
39515
|
+
controller,
|
|
39516
|
+
auth: routeAuth,
|
|
39517
|
+
authStorage,
|
|
39327
39518
|
audit: auditDomain,
|
|
39328
|
-
|
|
39329
|
-
|
|
39330
|
-
|
|
39331
|
-
|
|
39332
|
-
|
|
39333
|
-
|
|
39334
|
-
|
|
39335
|
-
|
|
39336
|
-
|
|
39337
|
-
|
|
39519
|
+
publicOrigin,
|
|
39520
|
+
stateSigner,
|
|
39521
|
+
fleet,
|
|
39522
|
+
factoryStorage: storage,
|
|
39523
|
+
integrationStorage,
|
|
39524
|
+
sourceControlStorage,
|
|
39525
|
+
domains,
|
|
39526
|
+
integrations: integrationRegistrations,
|
|
39527
|
+
intakeReady,
|
|
39528
|
+
factoryReady,
|
|
39529
|
+
rules,
|
|
39530
|
+
factoryTransitionService: transitionService,
|
|
39531
|
+
onFactoryRuntime: ({ transitionService: runtimeTransitionService, prepareBinding }) => {
|
|
39532
|
+
this.#dispatcher ??= new FactoryDecisionDispatcher({
|
|
39533
|
+
controller,
|
|
39534
|
+
transitionService: runtimeTransitionService,
|
|
39535
|
+
storage: storage.getDomain("work-items"),
|
|
39536
|
+
reconcileToolResults: () => factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),
|
|
39537
|
+
prepareBinding,
|
|
39538
|
+
primeCredentials: (tenant) => primeTenantCredentials({ tenant, credentials: modelCredentialsStorage })
|
|
39539
|
+
});
|
|
39338
39540
|
}
|
|
39339
|
-
})
|
|
39340
|
-
|
|
39341
|
-
|
|
39342
|
-
|
|
39343
|
-
|
|
39344
|
-
|
|
39345
|
-
|
|
39346
|
-
|
|
39347
|
-
|
|
39348
|
-
|
|
39349
|
-
|
|
39350
|
-
|
|
39351
|
-
|
|
39352
|
-
|
|
39353
|
-
|
|
39354
|
-
|
|
39355
|
-
|
|
39356
|
-
|
|
39357
|
-
stateSigner,
|
|
39358
|
-
fleet,
|
|
39359
|
-
factoryStorage: storage,
|
|
39360
|
-
integrationStorage,
|
|
39361
|
-
sourceControlStorage,
|
|
39362
|
-
domains,
|
|
39363
|
-
integrations: integrationRegistrations,
|
|
39364
|
-
intakeReady,
|
|
39365
|
-
factoryReady,
|
|
39366
|
-
rules,
|
|
39367
|
-
factoryTransitionService: transitionService,
|
|
39368
|
-
onFactoryRuntime: ({ transitionService: runtimeTransitionService, prepareBinding }) => {
|
|
39369
|
-
this.#dispatcher ??= new FactoryDecisionDispatcher({
|
|
39370
|
-
controller,
|
|
39371
|
-
transitionService: runtimeTransitionService,
|
|
39372
|
-
storage: storage.getDomain("work-items"),
|
|
39373
|
-
reconcileToolResults: () => factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve(),
|
|
39374
|
-
prepareBinding,
|
|
39375
|
-
primeCredentials: (tenant) => primeTenantCredentials({ tenant, credentials: modelCredentialsStorage })
|
|
39376
|
-
});
|
|
39541
|
+
}),
|
|
39542
|
+
...projectRoutes.routes(),
|
|
39543
|
+
...auditDomain.routes()
|
|
39544
|
+
],
|
|
39545
|
+
buildServerConfig: () => {
|
|
39546
|
+
const cors = allowedOrigins.length ? { cors: { origin: allowedOrigins, credentials: true } } : {};
|
|
39547
|
+
const onError = { onError: handleServerError };
|
|
39548
|
+
const uiDist = resolveUiDistDir();
|
|
39549
|
+
const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];
|
|
39550
|
+
if (!auth) {
|
|
39551
|
+
return {
|
|
39552
|
+
middleware: [
|
|
39553
|
+
createCustomProvidersPrimer({ auth: routeAuth, storage: customProvidersStorage, authEnabled: false }),
|
|
39554
|
+
...spa
|
|
39555
|
+
],
|
|
39556
|
+
...cors,
|
|
39557
|
+
...onError
|
|
39558
|
+
};
|
|
39377
39559
|
}
|
|
39378
|
-
}),
|
|
39379
|
-
...projectRoutes.routes(),
|
|
39380
|
-
...auditDomain.routes()
|
|
39381
|
-
],
|
|
39382
|
-
buildServerConfig: () => {
|
|
39383
|
-
const cors = allowedOrigins.length ? { cors: { origin: allowedOrigins, credentials: true } } : {};
|
|
39384
|
-
const onError = { onError: handleServerError };
|
|
39385
|
-
const uiDist = resolveUiDistDir();
|
|
39386
|
-
const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];
|
|
39387
|
-
if (!auth) {
|
|
39388
39560
|
return {
|
|
39561
|
+
auth,
|
|
39389
39562
|
middleware: [
|
|
39390
|
-
|
|
39563
|
+
createFactoryAuthGate(auth),
|
|
39564
|
+
createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),
|
|
39565
|
+
createCustomProvidersPrimer({
|
|
39566
|
+
auth: routeAuth,
|
|
39567
|
+
storage: customProvidersStorage,
|
|
39568
|
+
authEnabled: Boolean(auth)
|
|
39569
|
+
}),
|
|
39391
39570
|
...spa
|
|
39392
39571
|
],
|
|
39393
39572
|
...cors,
|
|
39394
39573
|
...onError
|
|
39395
39574
|
};
|
|
39396
39575
|
}
|
|
39397
|
-
|
|
39398
|
-
|
|
39399
|
-
middleware: [
|
|
39400
|
-
createFactoryAuthGate(auth),
|
|
39401
|
-
createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),
|
|
39402
|
-
createCustomProvidersPrimer({
|
|
39403
|
-
auth: routeAuth,
|
|
39404
|
-
storage: customProvidersStorage,
|
|
39405
|
-
authEnabled: Boolean(auth)
|
|
39406
|
-
}),
|
|
39407
|
-
...spa
|
|
39408
|
-
],
|
|
39409
|
-
...cors,
|
|
39410
|
-
...onError
|
|
39411
|
-
};
|
|
39412
|
-
}
|
|
39413
|
-
});
|
|
39576
|
+
})
|
|
39577
|
+
);
|
|
39414
39578
|
this.#prepared = prepared;
|
|
39415
39579
|
this.#factoryProcessor = factoryProcessor;
|
|
39416
39580
|
const integrationWorkers = integrationRegistrations.filter(({ integration, ready }) => ready && integration.workers).flatMap(
|
|
@@ -39451,8 +39615,11 @@ var MastraFactory = class {
|
|
|
39451
39615
|
if (!this.#prepared) {
|
|
39452
39616
|
throw new Error("MastraFactory.finalize() called before prepare()");
|
|
39453
39617
|
}
|
|
39454
|
-
await this.#prepared.finalize();
|
|
39455
|
-
await
|
|
39618
|
+
await timedPhase("finalize.controller", () => this.#prepared.finalize());
|
|
39619
|
+
await timedPhase(
|
|
39620
|
+
"finalize.reconcileBoundThreads",
|
|
39621
|
+
() => this.#factoryProcessor?.reconcileAllBoundThreads() ?? Promise.resolve()
|
|
39622
|
+
);
|
|
39456
39623
|
this.#dispatcher?.start();
|
|
39457
39624
|
}
|
|
39458
39625
|
/** Stop Factory-owned background dispatch before the host process shuts down. */
|