@mastra/factory 0.25.0-alpha.5 → 0.25.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/boards/work-transition-policy.d.ts.map +1 -1
- package/dist/boards/work-transition-policy.js +3 -3
- package/dist/boards/work-transition-policy.js.map +1 -1
- package/dist/default-thinking-level.d.ts +3 -0
- package/dist/default-thinking-level.d.ts.map +1 -0
- package/dist/default-thinking-level.js +5 -0
- package/dist/default-thinking-level.js.map +1 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +19 -5
- package/dist/factory.js.map +1 -1
- package/dist/integrations/gitlab/integration.d.ts +4 -0
- package/dist/integrations/gitlab/integration.d.ts.map +1 -1
- package/dist/integrations/gitlab/integration.js +10 -1
- package/dist/integrations/gitlab/integration.js.map +1 -1
- package/dist/integrations/gitlab/routes.d.ts +6 -0
- package/dist/integrations/gitlab/routes.d.ts.map +1 -1
- package/dist/integrations/gitlab/routes.js +153 -1
- package/dist/integrations/gitlab/routes.js.map +1 -1
- package/dist/integrations/gitlab/rules.d.ts.map +1 -1
- package/dist/integrations/gitlab/rules.js +4 -1
- package/dist/integrations/gitlab/rules.js.map +1 -1
- package/dist/integrations/jira/api.d.ts +1 -1
- package/dist/integrations/jira/api.d.ts.map +1 -1
- package/dist/integrations/jira/api.js +1 -1
- package/dist/integrations/jira/api.js.map +1 -1
- package/dist/integrations/jira/integration.d.ts +3 -1
- package/dist/integrations/jira/integration.d.ts.map +1 -1
- package/dist/integrations/jira/integration.js +17 -2
- package/dist/integrations/jira/integration.js.map +1 -1
- package/dist/integrations/pull-request-head.d.ts +1 -0
- package/dist/integrations/pull-request-head.d.ts.map +1 -1
- package/dist/integrations/pull-request-head.js +2 -1
- package/dist/integrations/pull-request-head.js.map +1 -1
- package/dist/integrations/slack/channel-reply-processor.d.ts +1 -0
- package/dist/integrations/slack/channel-reply-processor.d.ts.map +1 -1
- package/dist/integrations/slack/channel-reply-processor.js +11 -3
- package/dist/integrations/slack/channel-reply-processor.js.map +1 -1
- package/dist/integrations/slack/channel-session-link-processor.d.ts +1 -0
- package/dist/integrations/slack/channel-session-link-processor.d.ts.map +1 -1
- package/dist/integrations/slack/channel-session-link-processor.js +1 -1
- package/dist/integrations/slack/channel-session-link-processor.js.map +1 -1
- package/dist/integrations/slack/reply-tool.d.ts.map +1 -1
- package/dist/integrations/slack/reply-tool.js +3 -11
- package/dist/integrations/slack/reply-tool.js.map +1 -1
- package/dist/integrations/slack/run-error-card.d.ts +3 -0
- package/dist/integrations/slack/run-error-card.d.ts.map +1 -0
- package/dist/integrations/slack/run-error-card.js +44 -0
- package/dist/integrations/slack/run-error-card.js.map +1 -0
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +5 -1
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/links.d.ts +24 -0
- package/dist/links.d.ts.map +1 -0
- package/dist/links.js +40 -0
- package/dist/links.js.map +1 -0
- package/dist/routes/config.d.ts +3 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +22 -1
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/intake.js +5 -2
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/surface.js +1 -1
- package/dist/routes/surface.js.map +1 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +8 -1
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/run-error.d.ts +13 -0
- package/dist/run-error.d.ts.map +1 -0
- package/dist/run-error.js +81 -0
- package/dist/run-error.js.map +1 -0
- package/dist/session/thinking-default.d.ts +1 -1
- package/dist/session/thinking-default.d.ts.map +1 -1
- package/dist/session/thinking-default.js +10 -11
- package/dist/session/thinking-default.js.map +1 -1
- package/dist/storage/domains/intake/base.d.ts +5 -0
- package/dist/storage/domains/intake/base.d.ts.map +1 -1
- package/dist/storage/domains/intake/base.js +9 -0
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/projects/base.d.ts +0 -1
- package/dist/storage/domains/projects/base.d.ts.map +1 -1
- package/dist/storage/domains/projects/base.js +4 -4
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/tools/read-tools.d.ts.map +1 -1
- package/dist/tools/read-tools.js +18 -2
- package/dist/tools/read-tools.js.map +1 -1
- package/dist/tools/run-watch.d.ts +0 -2
- package/dist/tools/run-watch.d.ts.map +1 -1
- package/dist/tools/run-watch.js +3 -42
- package/dist/tools/run-watch.js.map +1 -1
- package/dist/tools/scope.js +1 -1
- package/dist/tools/scope.js.map +1 -1
- package/dist/tools/session-tools.d.ts.map +1 -1
- package/dist/tools/session-tools.js +21 -5
- package/dist/tools/session-tools.js.map +1 -1
- package/dist/tools/work-item-tools.d.ts.map +1 -1
- package/dist/tools/work-item-tools.js +7 -1
- package/dist/tools/work-item-tools.js.map +1 -1
- package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
- package/factory-skills/factory-plan/SKILL.md +1 -1
- package/factory-skills/factory-rereview/SKILL.md +2 -1
- package/factory-skills/factory-review/SKILL.md +2 -1
- package/factory-skills/factory-self-review/SKILL.md +1 -1
- package/factory-skills/factory-triage/SKILL.md +46 -23
- package/package.json +12 -12
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { PROVIDER_CALL_ERROR_NAME, classifyProviderError, providerErrorReason, providerModelId } from "@mastra/core/error";
|
|
2
|
+
import { PROVIDER_AUTH_REQUIRED_ERROR } from "@mastra/code-sdk/auth/provider-auth-error";
|
|
3
|
+
// src/run-error.ts
|
|
4
|
+
const UNEXPECTED_ERROR = "An unexpected error stopped the run. Copy the error details to investigate.";
|
|
5
|
+
const NO_REASON_AVAILABLE = "The provider rejected the request. A specific reason isn’t available here. Copy the error details and check the server logs.";
|
|
6
|
+
function describeRunError(error) {
|
|
7
|
+
return {
|
|
8
|
+
...explainRunError(error),
|
|
9
|
+
model: providerModelId(error)
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function explainRunError(error) {
|
|
13
|
+
if (error.name === PROVIDER_AUTH_REQUIRED_ERROR) return {
|
|
14
|
+
title: "Provider sign-in required",
|
|
15
|
+
message: error.message,
|
|
16
|
+
recoveries: ["credentials", "model"]
|
|
17
|
+
};
|
|
18
|
+
switch (classifyProviderError(error)) {
|
|
19
|
+
case "quota": return {
|
|
20
|
+
title: "Provider quota exhausted",
|
|
21
|
+
message: afterProviderReason(error, "Check the provider account’s billing or usage allowance, or use another credential or model."),
|
|
22
|
+
recoveries: ["credentials", "model"]
|
|
23
|
+
};
|
|
24
|
+
case "auth": return {
|
|
25
|
+
title: "Provider access failed",
|
|
26
|
+
message: afterProviderReason(error, "Review the credentials and model access configured for this session."),
|
|
27
|
+
recoveries: ["credentials", "model"]
|
|
28
|
+
};
|
|
29
|
+
case "context_overflow": return {
|
|
30
|
+
title: "Conversation exceeds the model’s limit",
|
|
31
|
+
message: "Choose a model with a larger context window or start a new session with a shorter prompt.",
|
|
32
|
+
recoveries: ["model"]
|
|
33
|
+
};
|
|
34
|
+
case "model_rejected": return {
|
|
35
|
+
title: "Model not available",
|
|
36
|
+
message: afterProviderReason(error, "Choose another model for this session.", "The provider rejected this model."),
|
|
37
|
+
recoveries: ["model"]
|
|
38
|
+
};
|
|
39
|
+
case "tool_history_rejected": return {
|
|
40
|
+
title: "Conversation history rejected",
|
|
41
|
+
message: "The provider could not match a tool call with its result. Copy the error details so this session can be investigated.",
|
|
42
|
+
recoveries: []
|
|
43
|
+
};
|
|
44
|
+
case "invalid_request": return {
|
|
45
|
+
title: "Model request rejected",
|
|
46
|
+
message: providerErrorReason(error) ?? NO_REASON_AVAILABLE,
|
|
47
|
+
recoveries: []
|
|
48
|
+
};
|
|
49
|
+
case "timeout": return {
|
|
50
|
+
title: "Request timed out",
|
|
51
|
+
message: "The request took too long to complete. Send a message to continue the run.",
|
|
52
|
+
recoveries: []
|
|
53
|
+
};
|
|
54
|
+
case "rate_limited": return {
|
|
55
|
+
title: "Provider rate limit reached",
|
|
56
|
+
message: "Wait a moment, then send a message to continue the run.",
|
|
57
|
+
recoveries: []
|
|
58
|
+
};
|
|
59
|
+
case "unavailable": return {
|
|
60
|
+
title: "Model provider temporarily unavailable",
|
|
61
|
+
message: "The provider could not complete the request. Send a message to continue the run.",
|
|
62
|
+
recoveries: []
|
|
63
|
+
};
|
|
64
|
+
case "unknown": return describeUnclassifiedError(error);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function afterProviderReason(error, next, fallback) {
|
|
68
|
+
const reason = providerErrorReason(error) ?? fallback;
|
|
69
|
+
return reason ? `${reason.replace(/\.?$/, ".")} ${next}` : next;
|
|
70
|
+
}
|
|
71
|
+
function describeUnclassifiedError(error) {
|
|
72
|
+
const message = providerErrorReason(error) ?? (error.message || UNEXPECTED_ERROR);
|
|
73
|
+
return {
|
|
74
|
+
title: PROVIDER_CALL_ERROR_NAME.test(error.name) ? "Model request failed" : "Run stopped",
|
|
75
|
+
message,
|
|
76
|
+
recoveries: []
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export { describeRunError };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=run-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-error.js","names":[],"sources":["../src/run-error.ts"],"sourcesContent":["import { PROVIDER_AUTH_REQUIRED_ERROR } from '@mastra/code-sdk/auth/provider-auth-error';\nimport {\n classifyProviderError,\n PROVIDER_CALL_ERROR_NAME,\n providerErrorReason,\n providerModelId,\n} from '@mastra/core/error';\n\nexport type RunErrorRecovery = 'credentials' | 'model';\n\nexport interface RunErrorDescription {\n title: string;\n message: string;\n recoveries: RunErrorRecovery[];\n model?: string;\n}\n\nexport interface RunErrorIdentity {\n name: string;\n message: string;\n}\n\nconst UNEXPECTED_ERROR = 'An unexpected error stopped the run. Copy the error details to investigate.';\nconst NO_REASON_AVAILABLE =\n 'The provider rejected the request. A specific reason isn’t available here. Copy the error details and check the server logs.';\n\nexport function describeRunError(error: RunErrorIdentity): RunErrorDescription {\n return { ...explainRunError(error), model: providerModelId(error) };\n}\n\nfunction explainRunError(error: RunErrorIdentity): Omit<RunErrorDescription, 'model'> {\n if (error.name === PROVIDER_AUTH_REQUIRED_ERROR) {\n return { title: 'Provider sign-in required', message: error.message, recoveries: ['credentials', 'model'] };\n }\n switch (classifyProviderError(error)) {\n case 'quota':\n return {\n title: 'Provider quota exhausted',\n message: afterProviderReason(\n error,\n 'Check the provider account’s billing or usage allowance, or use another credential or model.',\n ),\n recoveries: ['credentials', 'model'],\n };\n case 'auth':\n return {\n title: 'Provider access failed',\n message: afterProviderReason(error, 'Review the credentials and model access configured for this session.'),\n recoveries: ['credentials', 'model'],\n };\n case 'context_overflow':\n return {\n title: 'Conversation exceeds the model’s limit',\n message: 'Choose a model with a larger context window or start a new session with a shorter prompt.',\n recoveries: ['model'],\n };\n case 'model_rejected':\n return {\n title: 'Model not available',\n message: afterProviderReason(\n error,\n 'Choose another model for this session.',\n 'The provider rejected this model.',\n ),\n recoveries: ['model'],\n };\n case 'tool_history_rejected':\n return {\n title: 'Conversation history rejected',\n message:\n 'The provider could not match a tool call with its result. Copy the error details so this session can be investigated.',\n recoveries: [],\n };\n case 'invalid_request':\n return {\n title: 'Model request rejected',\n message: providerErrorReason(error) ?? NO_REASON_AVAILABLE,\n recoveries: [],\n };\n case 'timeout':\n return {\n title: 'Request timed out',\n message: 'The request took too long to complete. Send a message to continue the run.',\n recoveries: [],\n };\n case 'rate_limited':\n return {\n title: 'Provider rate limit reached',\n message: 'Wait a moment, then send a message to continue the run.',\n recoveries: [],\n };\n case 'unavailable':\n return {\n title: 'Model provider temporarily unavailable',\n message: 'The provider could not complete the request. Send a message to continue the run.',\n recoveries: [],\n };\n case 'unknown':\n return describeUnclassifiedError(error);\n }\n}\n\nfunction afterProviderReason(error: RunErrorIdentity, next: string, fallback?: string): string {\n const reason = providerErrorReason(error) ?? fallback;\n return reason ? `${reason.replace(/\\.?$/, '.')} ${next}` : next;\n}\n\nfunction describeUnclassifiedError(error: RunErrorIdentity): Omit<RunErrorDescription, 'model'> {\n const message = providerErrorReason(error) ?? (error.message || UNEXPECTED_ERROR);\n return {\n title: PROVIDER_CALL_ERROR_NAME.test(error.name) ? 'Model request failed' : 'Run stopped',\n message,\n recoveries: [],\n };\n}\n"],"mappings":";;;AAsBA,MAAM,mBAAmB;AACzB,MAAM,sBACJ;AAEF,SAAgB,iBAAiB,OAA8C;CAC7E,OAAO;EAAE,GAAG,gBAAgB,KAAK;EAAG,OAAO,gBAAgB,KAAK;CAAE;AACpE;AAEA,SAAS,gBAAgB,OAA6D;CACpF,IAAI,MAAM,SAAS,8BACjB,OAAO;EAAE,OAAO;EAA6B,SAAS,MAAM;EAAS,YAAY,CAAC,eAAe,OAAO;CAAE;CAE5G,QAAQ,sBAAsB,KAAK,GAAnC;EACE,KAAK,SACH,OAAO;GACL,OAAO;GACP,SAAS,oBACP,OACA,8FACF;GACA,YAAY,CAAC,eAAe,OAAO;EACrC;EACF,KAAK,QACH,OAAO;GACL,OAAO;GACP,SAAS,oBAAoB,OAAO,sEAAsE;GAC1G,YAAY,CAAC,eAAe,OAAO;EACrC;EACF,KAAK,oBACH,OAAO;GACL,OAAO;GACP,SAAS;GACT,YAAY,CAAC,OAAO;EACtB;EACF,KAAK,kBACH,OAAO;GACL,OAAO;GACP,SAAS,oBACP,OACA,0CACA,mCACF;GACA,YAAY,CAAC,OAAO;EACtB;EACF,KAAK,yBACH,OAAO;GACL,OAAO;GACP,SACE;GACF,YAAY,CAAC;EACf;EACF,KAAK,mBACH,OAAO;GACL,OAAO;GACP,SAAS,oBAAoB,KAAK,KAAK;GACvC,YAAY,CAAC;EACf;EACF,KAAK,WACH,OAAO;GACL,OAAO;GACP,SAAS;GACT,YAAY,CAAC;EACf;EACF,KAAK,gBACH,OAAO;GACL,OAAO;GACP,SAAS;GACT,YAAY,CAAC;EACf;EACF,KAAK,eACH,OAAO;GACL,OAAO;GACP,SAAS;GACT,YAAY,CAAC;EACf;EACF,KAAK,WACH,OAAO,0BAA0B,KAAK;CAC1C;AACF;AAEA,SAAS,oBAAoB,OAAyB,MAAc,UAA2B;CAC7F,MAAM,SAAS,oBAAoB,KAAK,KAAK;CAC7C,OAAO,SAAS,GAAG,OAAO,QAAQ,QAAQ,GAAG,EAAE,GAAG,SAAS;AAC7D;AAEA,SAAS,0BAA0B,OAA6D;CAC9F,MAAM,UAAU,oBAAoB,KAAK,MAAM,MAAM,WAAW;CAChE,OAAO;EACL,OAAO,yBAAyB,KAAK,MAAM,IAAI,IAAI,yBAAyB;EAC5E;EACA,YAAY,CAAC;CACf;AACF"}
|
|
@@ -5,7 +5,7 @@ import type { FactoryModelPolicyDependencies } from './credential-policy.js';
|
|
|
5
5
|
interface FactoryThinkingDefaultDependencies extends FactoryModelPolicyDependencies {
|
|
6
6
|
modelDefaults: Pick<ModelDefaultsStorage, 'ensureReady' | 'get'>;
|
|
7
7
|
}
|
|
8
|
-
/** A session's unpicked thinking level:
|
|
8
|
+
/** A session's unpicked thinking level: the level of the defaults set it routes to; unsigned requests get the Factory's. */
|
|
9
9
|
export declare function resolveFactoryThinkingLevel(requestContext: RequestContext | undefined, deps: FactoryThinkingDefaultDependencies): Promise<ThinkingLevelSetting>;
|
|
10
10
|
export {};
|
|
11
11
|
//# sourceMappingURL=thinking-default.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-default.d.ts","sourceRoot":"","sources":["../../src/session/thinking-default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"thinking-default.d.ts","sourceRoot":"","sources":["../../src/session/thinking-default.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEtF,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AAI7E,UAAU,kCAAmC,SAAQ,8BAA8B;IACjF,aAAa,EAAE,IAAI,CAAC,oBAAoB,EAAE,aAAa,GAAG,KAAK,CAAC,CAAC;CAClE;AAkBD,4HAA4H;AAC5H,wBAAsB,2BAA2B,CAC/C,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,IAAI,EAAE,kCAAkC,GACvC,OAAO,CAAC,oBAAoB,CAAC,CAuB/B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DEFAULT_THINKING_LEVEL } from "../default-thinking-level.js";
|
|
2
2
|
import { UUID_PATTERN } from "../routes/contracts.js";
|
|
3
3
|
import { resolveFactoryModelPolicy, routedPersonalDefaults } from "./credential-policy.js";
|
|
4
4
|
// src/session/thinking-default.ts
|
|
@@ -12,23 +12,22 @@ function sessionlessFactoryProjectId(controller) {
|
|
|
12
12
|
async function unsignedFactoryProjectId(controller, deps) {
|
|
13
13
|
return (controller.sessionId ? await deps.sourceControl.getBySessionId(controller.sessionId) : null)?.factoryProjectId ?? sessionlessFactoryProjectId(controller);
|
|
14
14
|
}
|
|
15
|
-
/** A session's unpicked thinking level:
|
|
15
|
+
/** A session's unpicked thinking level: the level of the defaults set it routes to; unsigned requests get the Factory's. */
|
|
16
16
|
async function resolveFactoryThinkingLevel(requestContext, deps) {
|
|
17
|
-
if (!requestContext) return
|
|
17
|
+
if (!requestContext) return DEFAULT_THINKING_LEVEL;
|
|
18
18
|
const controller = requestContext.get("controller");
|
|
19
|
-
if (!controller) return
|
|
19
|
+
if (!controller) return DEFAULT_THINKING_LEVEL;
|
|
20
20
|
try {
|
|
21
21
|
const policy = await resolveFactoryModelPolicy(requestContext, deps);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (!policy) return factoryLevel;
|
|
25
|
-
return (await routedPersonalDefaults(policy.tenant, deps.modelDefaults).catch((error) => {
|
|
26
|
-
console.warn("[factory] Could not read the personal thinking default; using the Factory level.", error);
|
|
22
|
+
if (policy && policy.tenant.credentialScope !== "factory") return (await routedPersonalDefaults(policy.tenant, deps.modelDefaults).catch((error) => {
|
|
23
|
+
console.warn("[factory] Could not read the personal thinking default; using medium.", error);
|
|
27
24
|
return null;
|
|
28
|
-
}))?.thinkingLevel ??
|
|
25
|
+
}))?.thinkingLevel ?? "medium";
|
|
26
|
+
const factoryProjectId = policy ? policy.factoryProjectId ?? sessionlessFactoryProjectId(controller) : await unsignedFactoryProjectId(controller, deps);
|
|
27
|
+
return (factoryProjectId ? await deps.projects.getById({ id: factoryProjectId }) : null)?.defaultThinkingLevel ?? "medium";
|
|
29
28
|
} catch (error) {
|
|
30
29
|
console.warn("[factory] Could not read the thinking default; using medium.", error);
|
|
31
|
-
return
|
|
30
|
+
return DEFAULT_THINKING_LEVEL;
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
export { resolveFactoryThinkingLevel };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thinking-default.js","names":[],"sources":["../../src/session/thinking-default.ts"],"sourcesContent":["import type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport {
|
|
1
|
+
{"version":3,"file":"thinking-default.js","names":[],"sources":["../../src/session/thinking-default.ts"],"sourcesContent":["import type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { DEFAULT_THINKING_LEVEL } from '../default-thinking-level.js';\nimport { UUID_PATTERN } from '../routes/contracts.js';\nimport type { ModelDefaultsStorage } from '../storage/domains/model-defaults/base.js';\nimport { resolveFactoryModelPolicy, routedPersonalDefaults } from './credential-policy.js';\nimport type { FactoryModelPolicyDependencies } from './credential-policy.js';\n\nconst FACTORY_PROJECT_ID = new RegExp(UUID_PATTERN);\n\ninterface FactoryThinkingDefaultDependencies extends FactoryModelPolicyDependencies {\n modelDefaults: Pick<ModelDefaultsStorage, 'ensureReady' | 'get'>;\n}\n\nfunction sessionlessFactoryProjectId(controller: AgentControllerRequestContext): string | undefined {\n const stateProjectId = (controller.getState?.() as { factoryProjectId?: unknown } | undefined)?.factoryProjectId;\n if (typeof stateProjectId === 'string' && stateProjectId) return stateProjectId;\n // Session-less Factory pages address the Factory itself: the resourceId is the project id.\n const { resourceId } = controller;\n return resourceId && FACTORY_PROJECT_ID.test(resourceId) ? resourceId : undefined;\n}\n\nasync function unsignedFactoryProjectId(\n controller: AgentControllerRequestContext,\n deps: FactoryThinkingDefaultDependencies,\n): Promise<string | undefined> {\n const sourceSession = controller.sessionId ? await deps.sourceControl.getBySessionId(controller.sessionId) : null;\n return sourceSession?.factoryProjectId ?? sessionlessFactoryProjectId(controller);\n}\n\n/** A session's unpicked thinking level: the level of the defaults set it routes to; unsigned requests get the Factory's. */\nexport async function resolveFactoryThinkingLevel(\n requestContext: RequestContext | undefined,\n deps: FactoryThinkingDefaultDependencies,\n): Promise<ThinkingLevelSetting> {\n if (!requestContext) return DEFAULT_THINKING_LEVEL;\n const controller = requestContext.get('controller') as AgentControllerRequestContext | undefined;\n if (!controller) return DEFAULT_THINKING_LEVEL;\n try {\n const policy = await resolveFactoryModelPolicy(requestContext, deps);\n if (policy && policy.tenant.credentialScope !== 'factory') {\n const personalDefaults = await routedPersonalDefaults(policy.tenant, deps.modelDefaults).catch(error => {\n console.warn('[factory] Could not read the personal thinking default; using medium.', error);\n return null;\n });\n return personalDefaults?.thinkingLevel ?? DEFAULT_THINKING_LEVEL;\n }\n const factoryProjectId = policy\n ? (policy.factoryProjectId ?? sessionlessFactoryProjectId(controller))\n : await unsignedFactoryProjectId(controller, deps);\n const project = factoryProjectId ? await deps.projects.getById({ id: factoryProjectId }) : null;\n return project?.defaultThinkingLevel ?? DEFAULT_THINKING_LEVEL;\n } catch (error) {\n // A missed lookup costs reasoning depth, not the run.\n console.warn('[factory] Could not read the thinking default; using medium.', error);\n return DEFAULT_THINKING_LEVEL;\n }\n}\n"],"mappings":";;;;AAUA,MAAM,qBAAqB,IAAI,OAAO,YAAY;AAMlD,SAAS,4BAA4B,YAA+D;CAClG,MAAM,kBAAkB,WAAW,WAAW,EAAA,EAAkD;CAChG,IAAI,OAAO,mBAAmB,YAAY,gBAAgB,OAAO;CAEjE,MAAM,EAAE,eAAe;CACvB,OAAO,cAAc,mBAAmB,KAAK,UAAU,IAAI,aAAa,KAAA;AAC1E;AAEA,eAAe,yBACb,YACA,MAC6B;CAE7B,QADsB,WAAW,YAAY,MAAM,KAAK,cAAc,eAAe,WAAW,SAAS,IAAI,KAAA,EACvF,oBAAoB,4BAA4B,UAAU;AAClF;;AAGA,eAAsB,4BACpB,gBACA,MAC+B;CAC/B,IAAI,CAAC,gBAAgB,OAAO;CAC5B,MAAM,aAAa,eAAe,IAAI,YAAY;CAClD,IAAI,CAAC,YAAY,OAAO;CACxB,IAAI;EACF,MAAM,SAAS,MAAM,0BAA0B,gBAAgB,IAAI;EACnE,IAAI,UAAU,OAAO,OAAO,oBAAoB,WAK9C,QAAO,MAJwB,uBAAuB,OAAO,QAAQ,KAAK,aAAa,CAAC,CAAC,OAAM,UAAS;GACtG,QAAQ,KAAK,yEAAyE,KAAK;GAC3F,OAAO;EACT,CAAC,EAAA,EACwB,iBAAA;EAE3B,MAAM,mBAAmB,SACpB,OAAO,oBAAoB,4BAA4B,UAAU,IAClE,MAAM,yBAAyB,YAAY,IAAI;EAEnD,QADgB,mBAAmB,MAAM,KAAK,SAAS,QAAQ,EAAE,IAAI,iBAAiB,CAAC,IAAI,KAAA,EAC3E,wBAAA;CAClB,SAAS,OAAO;EAEd,QAAQ,KAAK,gEAAgE,KAAK;EAClF,OAAO;CACT;AACF"}
|
|
@@ -77,6 +77,11 @@ export declare class IntakeStorage extends FactoryStorageDomain {
|
|
|
77
77
|
orgId: string;
|
|
78
78
|
config: IntakeConfig;
|
|
79
79
|
}): Promise<void>;
|
|
80
|
+
/** Overlay `config` onto the stored selections; integrations it omits keep their stored value. */
|
|
81
|
+
mergeConfig({ orgId, config }: {
|
|
82
|
+
orgId: string;
|
|
83
|
+
config: IntakeConfig;
|
|
84
|
+
}): Promise<IntakeConfig>;
|
|
80
85
|
/** Every source binding in the org, optionally narrowed to one integration. */
|
|
81
86
|
listBindings({ orgId, integrationId, }: {
|
|
82
87
|
orgId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/intake/base.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3B,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpD;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE3D,eAAO,MAAM,qBAAqB,EAAE,YAAiB,CAAC;AAEtD,eAAO,MAAM,0BAA0B,EAAE,gBAUxC,CAAC;AAEF,6HAA6H;AAC7H,eAAO,MAAM,kCAAkC,EAAE,gBAWhD,CAAC;AAEF,2IAA2I;AAC3I,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,YAAY,CAcpF;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,6BAA6B,EAAE,gBAqB3C,CAAC;AAEF,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kFAAkF;IAClF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,gBAqBxC,CAAC;AAEF,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAiBD,yEAAyE;AACzE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,gBAAgB,EAAE,EACnC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GACpC,gBAAgB,GAAG,SAAS,CAI9B;AAiBD,qBAAa,aAAc,SAAQ,oBAAoB;;;IAK/C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAWpC,SAAS,CAAC,EACd,KAAK,EACL,cAAc,GACf,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd;;;WAGG;QACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,GAAG,OAAO,CAAC,YAAY,CAAC;IASnB,UAAU,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/intake/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -219,6 +219,15 @@ var IntakeStorage = class extends FactoryStorageDomain {
|
|
|
219
219
|
updated_at: now
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
|
+
/** Overlay `config` onto the stored selections; integrations it omits keep their stored value. */
|
|
223
|
+
async mergeConfig({ orgId, config }) {
|
|
224
|
+
const merged = Object.assign(Object.create(null), await this.getConfig({ orgId }), config);
|
|
225
|
+
await this.saveConfig({
|
|
226
|
+
orgId,
|
|
227
|
+
config: merged
|
|
228
|
+
});
|
|
229
|
+
return merged;
|
|
230
|
+
}
|
|
222
231
|
async #insertConfigIfAbsent(orgId, config) {
|
|
223
232
|
const now = /* @__PURE__ */ new Date();
|
|
224
233
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/intake/base.ts"],"sourcesContent":["/**\n * Org-wide intake selections for every configured intake integration.\n *\n * Integration ids are dynamic. Each integration contributes provider-neutral\n * sources through `FactoryIntegration.intake`; this domain only persists which\n * source ids the organization selected. The selection feeds shared boards, so\n * every member reads the same one.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface IntakeSelection {\n enabled: boolean;\n /** Provider-owned source ids; `null` means nothing is selected. */\n sourceIds: string[] | null;\n repositoryByLinearProject?: Record<string, string>;\n}\n\nexport type IntakeConfig = Record<string, IntakeSelection>;\n\nexport const DEFAULT_INTAKE_CONFIG: IntakeConfig = {};\n\nexport const INTAKE_ORG_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'intake_org_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'intake_org_settings_org_unique', columns: ['org_id'] }],\n};\n\n/** Collection evolution is additive, so the per-member unique index cannot be narrowed: rows stay and are folded at boot. */\nexport const LEGACY_INTAKE_USER_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'intake_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'intake_settings_org_user_unique', columns: ['org_id', 'user_id'] }],\n};\n\n/** What the org effectively saw while selections were personal: a member's sources fed the shared board only while their switch was on. */\nexport function mergeIntakeSelections(configs: readonly IntakeConfig[]): IntakeConfig {\n const merged: IntakeConfig = Object.create(null);\n for (const config of configs) {\n for (const [integrationId, selection] of Object.entries(config)) {\n const current = merged[integrationId] ?? { enabled: false, sourceIds: null };\n const syncing = selection.enabled ? (selection.sourceIds ?? []) : [];\n const sourceIds = [...new Set([...(current.sourceIds ?? []), ...syncing])];\n merged[integrationId] = {\n enabled: current.enabled || selection.enabled,\n sourceIds: sourceIds.length ? sourceIds : null,\n };\n }\n }\n return merged;\n}\n\n/**\n * Binds an intake source to the Factory project its items belong to.\n *\n * Intake selections are org-wide, so they cannot say *where* an\n * ingested item should land. GitHub items are naturally scoped by their linked\n * repository; providers without that link (Linear) need this explicit binding\n * so viewing one project's board cannot materialize another project's items.\n */\nexport const INTAKE_SOURCE_BINDINGS_SCHEMA: CollectionSchema = {\n name: 'intake_source_bindings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n integration_id: { type: 'text' },\n source_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n /** Installed board the source feeds; `null` keeps the legacy source-type routing (issues → work, PRs → review). */\n board: { type: 'text', nullable: true },\n created_by_user_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'intake_source_bindings_project_idx', columns: ['org_id', 'factory_project_id'] }],\n uniqueIndexes: [\n {\n name: 'intake_source_bindings_org_integration_source_unique',\n columns: ['org_id', 'integration_id', 'source_id'],\n },\n ],\n};\n\nexport interface IntakeSourceBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n /** Installed board the source feeds, or `null` for legacy source-type routing. */\n board: string | null;\n}\n\n/**\n * Routes items carrying a label to an installed board, per Factory project.\n *\n * Repository-scoped providers (GitHub) already know which project an item\n * belongs to, but one repository can feed several boards: an issue labelled\n * `release` belongs on a release board while the rest stay on Work. Labels\n * without a route fall back to the provider's built-in board.\n */\nexport const INTAKE_LABEL_ROUTES_SCHEMA: CollectionSchema = {\n name: 'intake_label_routes',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n integration_id: { type: 'text' },\n /** Label as the provider reports it; matched case-insensitively. */\n label: { type: 'text' },\n board: { type: 'text' },\n created_by_user_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'intake_label_routes_project_idx', columns: ['org_id', 'factory_project_id'] }],\n uniqueIndexes: [\n {\n name: 'intake_label_routes_org_project_integration_label_unique',\n columns: ['org_id', 'factory_project_id', 'integration_id', 'label'],\n },\n ],\n};\n\nexport interface IntakeLabelRoute {\n factoryProjectId: string;\n integrationId: string;\n label: string;\n board: string;\n}\n\ntype IntakeLabelRouteRow = {\n factory_project_id: string;\n integration_id: string;\n label: string;\n board: string;\n};\nfunction toIntakeLabelRoute(row: IntakeLabelRouteRow): IntakeLabelRoute {\n return {\n factoryProjectId: row.factory_project_id,\n integrationId: row.integration_id,\n label: row.label,\n board: row.board,\n };\n}\n\n/** Labels are matched the way GitHub treats them: case-insensitively. */\nexport function normalizeIntakeLabel(label: string): string {\n return label.trim().toLowerCase();\n}\n\n/**\n * The board a labelled item should land on under `routes`, or `undefined` when\n * none of its labels is routed. Ties resolve to the first route in `routes`\n * order, so callers should pass a deterministically ordered list.\n */\nexport function resolveIntakeLabelRoute(\n routes: readonly IntakeLabelRoute[],\n labels: readonly string[] | undefined,\n): IntakeLabelRoute | undefined {\n if (!labels?.length) return undefined;\n const normalized = new Set(labels.map(normalizeIntakeLabel));\n return routes.find(route => normalized.has(normalizeIntakeLabel(route.label)));\n}\n\ntype IntakeSourceBindingRow = {\n integration_id: string;\n source_id: string;\n factory_project_id: string;\n board?: string | null;\n};\nfunction toIntakeSourceBinding(row: IntakeSourceBindingRow): IntakeSourceBinding {\n return {\n integrationId: row.integration_id,\n sourceId: row.source_id,\n factoryProjectId: row.factory_project_id,\n board: row.board ?? null,\n };\n}\n\nexport class IntakeStorage extends FactoryStorageDomain {\n constructor() {\n super('intake');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([\n INTAKE_ORG_SETTINGS_SCHEMA,\n LEGACY_INTAKE_USER_SETTINGS_SCHEMA,\n INTAKE_SOURCE_BINDINGS_SCHEMA,\n INTAKE_LABEL_ROUTES_SCHEMA,\n ]);\n await this.#foldLegacyUserSelections();\n }\n\n async #foldLegacyUserSelections(): Promise<void> {\n const legacyRows = await this.ops.findMany<{ org_id: string; config: IntakeConfig }>('intake_settings', {});\n const byOrg = new Map<string, IntakeConfig[]>();\n for (const row of legacyRows) {\n byOrg.set(row.org_id, [...(byOrg.get(row.org_id) ?? []), row.config]);\n }\n for (const [orgId, configs] of byOrg) {\n const folded = await this.ops.findOne('intake_org_settings', { org_id: orgId });\n if (folded) continue;\n await this.#insertConfigIfAbsent(orgId, mergeIntakeSelections(configs));\n }\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('intake_org_settings', {});\n await this.ops.deleteMany('intake_settings', {});\n await this.ops.deleteMany('intake_source_bindings', {});\n await this.ops.deleteMany('intake_label_routes', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async getConfig({\n orgId,\n integrationIds,\n }: {\n orgId: string;\n /**\n * When provided, the result contains exactly these integration ids, with\n * unset integrations defaulting to `{ enabled: true, sourceIds: null }`.\n */\n integrationIds?: string[];\n }): Promise<IntakeConfig> {\n const row = await this.#db.findOne<{ config: IntakeConfig }>('intake_org_settings', { org_id: orgId });\n const saved = structuredClone(row?.config ?? DEFAULT_INTAKE_CONFIG);\n if (!integrationIds) return saved;\n return Object.fromEntries(\n integrationIds.map(integrationId => [integrationId, saved[integrationId] ?? { enabled: true, sourceIds: null }]),\n );\n }\n\n async saveConfig({ orgId, config }: { orgId: string; config: IntakeConfig }): Promise<void> {\n const now = new Date();\n const where = { org_id: orgId };\n const updated = await this.#db.updateMany('intake_org_settings', where, { config, updated_at: now });\n if (updated > 0) return;\n if (await this.#insertConfigIfAbsent(orgId, config)) return;\n await this.#db.updateMany('intake_org_settings', where, { config, updated_at: now });\n }\n\n async #insertConfigIfAbsent(orgId: string, config: IntakeConfig): Promise<boolean> {\n const now = new Date();\n try {\n await this.#db.insertOne('intake_org_settings', { org_id: orgId, config, created_at: now, updated_at: now });\n return true;\n } catch (error) {\n if (error instanceof UniqueViolationError) return false;\n throw error;\n }\n }\n\n /** Every source binding in the org, optionally narrowed to one integration. */\n async listBindings({\n orgId,\n integrationId,\n }: {\n orgId: string;\n integrationId?: string;\n }): Promise<IntakeSourceBinding[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n ...(integrationId ? { integration_id: integrationId } : {}),\n });\n return rows.map(toIntakeSourceBinding);\n }\n\n /** Source ids bound to one Factory project. Empty means \"nothing bound\". */\n async listBoundSourceIds({\n orgId,\n integrationId,\n factoryProjectId,\n }: {\n orgId: string;\n integrationId: string;\n factoryProjectId: string;\n }): Promise<string[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n factory_project_id: factoryProjectId,\n });\n return rows.map(row => row.source_id);\n }\n\n /** The binding for one source, or `null` when it is unbound. */\n async getBinding({\n orgId,\n integrationId,\n sourceId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n }): Promise<IntakeSourceBinding | null> {\n const row = await this.#db.findOne<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n source_id: sourceId,\n });\n return row ? toIntakeSourceBinding(row) : null;\n }\n\n async clearBinding({\n orgId,\n integrationId,\n sourceId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n }): Promise<IntakeSourceBinding | null> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n return this.storage.withTransaction(\n async ops => {\n const row = await ops.findOne<IntakeSourceBindingRow>('intake_source_bindings', where);\n if (!row) return null;\n await ops.deleteMany('intake_source_bindings', where);\n return toIntakeSourceBinding(row);\n },\n { isolationLevel: 'serializable' },\n );\n }\n\n async setBinding({\n orgId,\n integrationId,\n sourceId,\n factoryProjectId,\n board = null,\n userId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n board?: string | null;\n userId?: string;\n }): Promise<void> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n const now = new Date();\n const patch = { factory_project_id: factoryProjectId, board, updated_at: now };\n const updated = await this.#db.updateMany('intake_source_bindings', where, patch);\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_source_bindings', {\n ...where,\n factory_project_id: factoryProjectId,\n board,\n created_by_user_id: userId ?? null,\n created_at: now,\n updated_at: now,\n });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_source_bindings', where, patch);\n }\n }\n /** Atomically replace legacy provider source ids in both selection and routing state. */\n async migrateSourceIds({\n orgId,\n integrationId,\n migrations,\n }: {\n orgId: string;\n integrationId: string;\n migrations: Array<{ from: string; to: string }>;\n }): Promise<{ migrations: Array<{ from: string; to: string }>; conflicts: Array<{ from: string; to: string }> }> {\n if (migrations.length === 0) return { migrations: [], conflicts: [] };\n return this.storage.withTransaction(\n async ops => {\n const bindings = await ops.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n });\n const bindingBySource = new Map(bindings.map(binding => [binding.source_id, binding] as const));\n const grouped = new Map<string, Array<{ from: string; to: string }>>();\n for (const migration of migrations) {\n grouped.set(migration.to, [...(grouped.get(migration.to) ?? []), migration]);\n }\n const conflicts: Array<{ from: string; to: string }> = [];\n for (const [to, group] of grouped) {\n const candidates = [bindingBySource.get(to), ...group.map(({ from }) => bindingBySource.get(from))].filter(\n (binding): binding is IntakeSourceBindingRow => binding !== undefined,\n );\n const destinations = new Set(\n candidates.map(binding => `${binding.factory_project_id}\\0${binding.board ?? ''}`),\n );\n if (destinations.size > 1) conflicts.push(...group);\n }\n if (conflicts.length > 0) return { migrations: [], conflicts };\n\n const now = new Date();\n for (const [to, group] of grouped) {\n const canonical = bindingBySource.get(to);\n const legacy = group.flatMap(({ from }) => {\n const binding = bindingBySource.get(from);\n return binding ? [{ from, binding }] : [];\n });\n if (!canonical && legacy.length > 0) {\n const [first, ...duplicates] = legacy;\n await ops.updateMany(\n 'intake_source_bindings',\n { org_id: orgId, integration_id: integrationId, source_id: first!.from },\n { source_id: to, updated_at: now },\n );\n for (const duplicate of duplicates) {\n await ops.deleteMany('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n source_id: duplicate.from,\n });\n }\n } else if (canonical) {\n for (const entry of legacy) {\n await ops.deleteMany('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n source_id: entry.from,\n });\n }\n }\n }\n\n const configRow = await ops.findOne<{ config: IntakeConfig }>('intake_org_settings', { org_id: orgId });\n if (configRow) {\n const replacements = new Map(migrations.map(({ from, to }) => [from, to] as const));\n const selected = configRow.config[integrationId]?.sourceIds ?? [];\n const nextSelected = [...new Set(selected.map(sourceId => replacements.get(sourceId) ?? sourceId))];\n if (\n nextSelected.length !== selected.length ||\n nextSelected.some((sourceId, index) => sourceId !== selected[index])\n ) {\n const config = structuredClone(configRow.config);\n config[integrationId] = {\n ...(config[integrationId] ?? { enabled: true }),\n sourceIds: nextSelected.length > 0 ? nextSelected : null,\n };\n await ops.updateMany('intake_org_settings', { org_id: orgId }, { config, updated_at: now });\n }\n }\n return { migrations, conflicts: [] };\n },\n { isolationLevel: 'serializable' },\n );\n }\n\n /** Label routes in the org, optionally narrowed to one project and/or integration. Sorted by label. */\n async listLabelRoutes({\n orgId,\n factoryProjectId,\n integrationId,\n }: {\n orgId: string;\n factoryProjectId?: string;\n integrationId?: string;\n }): Promise<IntakeLabelRoute[]> {\n const rows = await this.#db.findMany<IntakeLabelRouteRow>('intake_label_routes', {\n org_id: orgId,\n ...(factoryProjectId ? { factory_project_id: factoryProjectId } : {}),\n ...(integrationId ? { integration_id: integrationId } : {}),\n });\n return rows.map(toIntakeLabelRoute).sort((a, b) => a.label.localeCompare(b.label));\n }\n\n async setLabelRoute({\n orgId,\n factoryProjectId,\n integrationId,\n label,\n board,\n userId,\n }: {\n orgId: string;\n factoryProjectId: string;\n integrationId: string;\n label: string;\n board: string;\n userId?: string;\n }): Promise<void> {\n const where = {\n org_id: orgId,\n factory_project_id: factoryProjectId,\n integration_id: integrationId,\n label: normalizeIntakeLabel(label),\n };\n const now = new Date();\n const patch = { board, updated_at: now };\n const updated = await this.#db.updateMany('intake_label_routes', where, patch);\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_label_routes', {\n ...where,\n board,\n created_by_user_id: userId ?? null,\n created_at: now,\n updated_at: now,\n });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_label_routes', where, patch);\n }\n }\n\n /** Remove a label route; returns the route that was removed, or `null` when none existed. */\n async clearLabelRoute({\n orgId,\n factoryProjectId,\n integrationId,\n label,\n }: {\n orgId: string;\n factoryProjectId: string;\n integrationId: string;\n label: string;\n }): Promise<IntakeLabelRoute | null> {\n const where = {\n org_id: orgId,\n factory_project_id: factoryProjectId,\n integration_id: integrationId,\n label: normalizeIntakeLabel(label),\n };\n return this.storage.withTransaction(\n async ops => {\n const row = await ops.findOne<IntakeLabelRouteRow>('intake_label_routes', where);\n if (!row) return null;\n await ops.deleteMany('intake_label_routes', where);\n return toIntakeLabelRoute(row);\n },\n { isolationLevel: 'serializable' },\n );\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/intake/base.ts"],"sourcesContent":["/**\n * Org-wide intake selections for every configured intake integration.\n *\n * Integration ids are dynamic. Each integration contributes provider-neutral\n * sources through `FactoryIntegration.intake`; this domain only persists which\n * source ids the organization selected. The selection feeds shared boards, so\n * every member reads the same one.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface IntakeSelection {\n enabled: boolean;\n /** Provider-owned source ids; `null` means nothing is selected. */\n sourceIds: string[] | null;\n repositoryByLinearProject?: Record<string, string>;\n}\n\nexport type IntakeConfig = Record<string, IntakeSelection>;\n\nexport const DEFAULT_INTAKE_CONFIG: IntakeConfig = {};\n\nexport const INTAKE_ORG_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'intake_org_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'intake_org_settings_org_unique', columns: ['org_id'] }],\n};\n\n/** Collection evolution is additive, so the per-member unique index cannot be narrowed: rows stay and are folded at boot. */\nexport const LEGACY_INTAKE_USER_SETTINGS_SCHEMA: CollectionSchema = {\n name: 'intake_settings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n config: { type: 'json' },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'intake_settings_org_user_unique', columns: ['org_id', 'user_id'] }],\n};\n\n/** What the org effectively saw while selections were personal: a member's sources fed the shared board only while their switch was on. */\nexport function mergeIntakeSelections(configs: readonly IntakeConfig[]): IntakeConfig {\n const merged: IntakeConfig = Object.create(null);\n for (const config of configs) {\n for (const [integrationId, selection] of Object.entries(config)) {\n const current = merged[integrationId] ?? { enabled: false, sourceIds: null };\n const syncing = selection.enabled ? (selection.sourceIds ?? []) : [];\n const sourceIds = [...new Set([...(current.sourceIds ?? []), ...syncing])];\n merged[integrationId] = {\n enabled: current.enabled || selection.enabled,\n sourceIds: sourceIds.length ? sourceIds : null,\n };\n }\n }\n return merged;\n}\n\n/**\n * Binds an intake source to the Factory project its items belong to.\n *\n * Intake selections are org-wide, so they cannot say *where* an\n * ingested item should land. GitHub items are naturally scoped by their linked\n * repository; providers without that link (Linear) need this explicit binding\n * so viewing one project's board cannot materialize another project's items.\n */\nexport const INTAKE_SOURCE_BINDINGS_SCHEMA: CollectionSchema = {\n name: 'intake_source_bindings',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n integration_id: { type: 'text' },\n source_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n /** Installed board the source feeds; `null` keeps the legacy source-type routing (issues → work, PRs → review). */\n board: { type: 'text', nullable: true },\n created_by_user_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'intake_source_bindings_project_idx', columns: ['org_id', 'factory_project_id'] }],\n uniqueIndexes: [\n {\n name: 'intake_source_bindings_org_integration_source_unique',\n columns: ['org_id', 'integration_id', 'source_id'],\n },\n ],\n};\n\nexport interface IntakeSourceBinding {\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n /** Installed board the source feeds, or `null` for legacy source-type routing. */\n board: string | null;\n}\n\n/**\n * Routes items carrying a label to an installed board, per Factory project.\n *\n * Repository-scoped providers (GitHub) already know which project an item\n * belongs to, but one repository can feed several boards: an issue labelled\n * `release` belongs on a release board while the rest stay on Work. Labels\n * without a route fall back to the provider's built-in board.\n */\nexport const INTAKE_LABEL_ROUTES_SCHEMA: CollectionSchema = {\n name: 'intake_label_routes',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n factory_project_id: { type: 'text' },\n integration_id: { type: 'text' },\n /** Label as the provider reports it; matched case-insensitively. */\n label: { type: 'text' },\n board: { type: 'text' },\n created_by_user_id: { type: 'text', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'intake_label_routes_project_idx', columns: ['org_id', 'factory_project_id'] }],\n uniqueIndexes: [\n {\n name: 'intake_label_routes_org_project_integration_label_unique',\n columns: ['org_id', 'factory_project_id', 'integration_id', 'label'],\n },\n ],\n};\n\nexport interface IntakeLabelRoute {\n factoryProjectId: string;\n integrationId: string;\n label: string;\n board: string;\n}\n\ntype IntakeLabelRouteRow = {\n factory_project_id: string;\n integration_id: string;\n label: string;\n board: string;\n};\nfunction toIntakeLabelRoute(row: IntakeLabelRouteRow): IntakeLabelRoute {\n return {\n factoryProjectId: row.factory_project_id,\n integrationId: row.integration_id,\n label: row.label,\n board: row.board,\n };\n}\n\n/** Labels are matched the way GitHub treats them: case-insensitively. */\nexport function normalizeIntakeLabel(label: string): string {\n return label.trim().toLowerCase();\n}\n\n/**\n * The board a labelled item should land on under `routes`, or `undefined` when\n * none of its labels is routed. Ties resolve to the first route in `routes`\n * order, so callers should pass a deterministically ordered list.\n */\nexport function resolveIntakeLabelRoute(\n routes: readonly IntakeLabelRoute[],\n labels: readonly string[] | undefined,\n): IntakeLabelRoute | undefined {\n if (!labels?.length) return undefined;\n const normalized = new Set(labels.map(normalizeIntakeLabel));\n return routes.find(route => normalized.has(normalizeIntakeLabel(route.label)));\n}\n\ntype IntakeSourceBindingRow = {\n integration_id: string;\n source_id: string;\n factory_project_id: string;\n board?: string | null;\n};\nfunction toIntakeSourceBinding(row: IntakeSourceBindingRow): IntakeSourceBinding {\n return {\n integrationId: row.integration_id,\n sourceId: row.source_id,\n factoryProjectId: row.factory_project_id,\n board: row.board ?? null,\n };\n}\n\nexport class IntakeStorage extends FactoryStorageDomain {\n constructor() {\n super('intake');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([\n INTAKE_ORG_SETTINGS_SCHEMA,\n LEGACY_INTAKE_USER_SETTINGS_SCHEMA,\n INTAKE_SOURCE_BINDINGS_SCHEMA,\n INTAKE_LABEL_ROUTES_SCHEMA,\n ]);\n await this.#foldLegacyUserSelections();\n }\n\n async #foldLegacyUserSelections(): Promise<void> {\n const legacyRows = await this.ops.findMany<{ org_id: string; config: IntakeConfig }>('intake_settings', {});\n const byOrg = new Map<string, IntakeConfig[]>();\n for (const row of legacyRows) {\n byOrg.set(row.org_id, [...(byOrg.get(row.org_id) ?? []), row.config]);\n }\n for (const [orgId, configs] of byOrg) {\n const folded = await this.ops.findOne('intake_org_settings', { org_id: orgId });\n if (folded) continue;\n await this.#insertConfigIfAbsent(orgId, mergeIntakeSelections(configs));\n }\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('intake_org_settings', {});\n await this.ops.deleteMany('intake_settings', {});\n await this.ops.deleteMany('intake_source_bindings', {});\n await this.ops.deleteMany('intake_label_routes', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async getConfig({\n orgId,\n integrationIds,\n }: {\n orgId: string;\n /**\n * When provided, the result contains exactly these integration ids, with\n * unset integrations defaulting to `{ enabled: true, sourceIds: null }`.\n */\n integrationIds?: string[];\n }): Promise<IntakeConfig> {\n const row = await this.#db.findOne<{ config: IntakeConfig }>('intake_org_settings', { org_id: orgId });\n const saved = structuredClone(row?.config ?? DEFAULT_INTAKE_CONFIG);\n if (!integrationIds) return saved;\n return Object.fromEntries(\n integrationIds.map(integrationId => [integrationId, saved[integrationId] ?? { enabled: true, sourceIds: null }]),\n );\n }\n\n async saveConfig({ orgId, config }: { orgId: string; config: IntakeConfig }): Promise<void> {\n const now = new Date();\n const where = { org_id: orgId };\n const updated = await this.#db.updateMany('intake_org_settings', where, { config, updated_at: now });\n if (updated > 0) return;\n if (await this.#insertConfigIfAbsent(orgId, config)) return;\n await this.#db.updateMany('intake_org_settings', where, { config, updated_at: now });\n }\n\n /** Overlay `config` onto the stored selections; integrations it omits keep their stored value. */\n async mergeConfig({ orgId, config }: { orgId: string; config: IntakeConfig }): Promise<IntakeConfig> {\n const merged: IntakeConfig = Object.assign(Object.create(null), await this.getConfig({ orgId }), config);\n await this.saveConfig({ orgId, config: merged });\n return merged;\n }\n\n async #insertConfigIfAbsent(orgId: string, config: IntakeConfig): Promise<boolean> {\n const now = new Date();\n try {\n await this.#db.insertOne('intake_org_settings', { org_id: orgId, config, created_at: now, updated_at: now });\n return true;\n } catch (error) {\n if (error instanceof UniqueViolationError) return false;\n throw error;\n }\n }\n\n /** Every source binding in the org, optionally narrowed to one integration. */\n async listBindings({\n orgId,\n integrationId,\n }: {\n orgId: string;\n integrationId?: string;\n }): Promise<IntakeSourceBinding[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n ...(integrationId ? { integration_id: integrationId } : {}),\n });\n return rows.map(toIntakeSourceBinding);\n }\n\n /** Source ids bound to one Factory project. Empty means \"nothing bound\". */\n async listBoundSourceIds({\n orgId,\n integrationId,\n factoryProjectId,\n }: {\n orgId: string;\n integrationId: string;\n factoryProjectId: string;\n }): Promise<string[]> {\n const rows = await this.#db.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n factory_project_id: factoryProjectId,\n });\n return rows.map(row => row.source_id);\n }\n\n /** The binding for one source, or `null` when it is unbound. */\n async getBinding({\n orgId,\n integrationId,\n sourceId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n }): Promise<IntakeSourceBinding | null> {\n const row = await this.#db.findOne<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n source_id: sourceId,\n });\n return row ? toIntakeSourceBinding(row) : null;\n }\n\n async clearBinding({\n orgId,\n integrationId,\n sourceId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n }): Promise<IntakeSourceBinding | null> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n return this.storage.withTransaction(\n async ops => {\n const row = await ops.findOne<IntakeSourceBindingRow>('intake_source_bindings', where);\n if (!row) return null;\n await ops.deleteMany('intake_source_bindings', where);\n return toIntakeSourceBinding(row);\n },\n { isolationLevel: 'serializable' },\n );\n }\n\n async setBinding({\n orgId,\n integrationId,\n sourceId,\n factoryProjectId,\n board = null,\n userId,\n }: {\n orgId: string;\n integrationId: string;\n sourceId: string;\n factoryProjectId: string;\n board?: string | null;\n userId?: string;\n }): Promise<void> {\n const where = { org_id: orgId, integration_id: integrationId, source_id: sourceId };\n const now = new Date();\n const patch = { factory_project_id: factoryProjectId, board, updated_at: now };\n const updated = await this.#db.updateMany('intake_source_bindings', where, patch);\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_source_bindings', {\n ...where,\n factory_project_id: factoryProjectId,\n board,\n created_by_user_id: userId ?? null,\n created_at: now,\n updated_at: now,\n });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_source_bindings', where, patch);\n }\n }\n /** Atomically replace legacy provider source ids in both selection and routing state. */\n async migrateSourceIds({\n orgId,\n integrationId,\n migrations,\n }: {\n orgId: string;\n integrationId: string;\n migrations: Array<{ from: string; to: string }>;\n }): Promise<{ migrations: Array<{ from: string; to: string }>; conflicts: Array<{ from: string; to: string }> }> {\n if (migrations.length === 0) return { migrations: [], conflicts: [] };\n return this.storage.withTransaction(\n async ops => {\n const bindings = await ops.findMany<IntakeSourceBindingRow>('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n });\n const bindingBySource = new Map(bindings.map(binding => [binding.source_id, binding] as const));\n const grouped = new Map<string, Array<{ from: string; to: string }>>();\n for (const migration of migrations) {\n grouped.set(migration.to, [...(grouped.get(migration.to) ?? []), migration]);\n }\n const conflicts: Array<{ from: string; to: string }> = [];\n for (const [to, group] of grouped) {\n const candidates = [bindingBySource.get(to), ...group.map(({ from }) => bindingBySource.get(from))].filter(\n (binding): binding is IntakeSourceBindingRow => binding !== undefined,\n );\n const destinations = new Set(\n candidates.map(binding => `${binding.factory_project_id}\\0${binding.board ?? ''}`),\n );\n if (destinations.size > 1) conflicts.push(...group);\n }\n if (conflicts.length > 0) return { migrations: [], conflicts };\n\n const now = new Date();\n for (const [to, group] of grouped) {\n const canonical = bindingBySource.get(to);\n const legacy = group.flatMap(({ from }) => {\n const binding = bindingBySource.get(from);\n return binding ? [{ from, binding }] : [];\n });\n if (!canonical && legacy.length > 0) {\n const [first, ...duplicates] = legacy;\n await ops.updateMany(\n 'intake_source_bindings',\n { org_id: orgId, integration_id: integrationId, source_id: first!.from },\n { source_id: to, updated_at: now },\n );\n for (const duplicate of duplicates) {\n await ops.deleteMany('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n source_id: duplicate.from,\n });\n }\n } else if (canonical) {\n for (const entry of legacy) {\n await ops.deleteMany('intake_source_bindings', {\n org_id: orgId,\n integration_id: integrationId,\n source_id: entry.from,\n });\n }\n }\n }\n\n const configRow = await ops.findOne<{ config: IntakeConfig }>('intake_org_settings', { org_id: orgId });\n if (configRow) {\n const replacements = new Map(migrations.map(({ from, to }) => [from, to] as const));\n const selected = configRow.config[integrationId]?.sourceIds ?? [];\n const nextSelected = [...new Set(selected.map(sourceId => replacements.get(sourceId) ?? sourceId))];\n if (\n nextSelected.length !== selected.length ||\n nextSelected.some((sourceId, index) => sourceId !== selected[index])\n ) {\n const config = structuredClone(configRow.config);\n config[integrationId] = {\n ...(config[integrationId] ?? { enabled: true }),\n sourceIds: nextSelected.length > 0 ? nextSelected : null,\n };\n await ops.updateMany('intake_org_settings', { org_id: orgId }, { config, updated_at: now });\n }\n }\n return { migrations, conflicts: [] };\n },\n { isolationLevel: 'serializable' },\n );\n }\n\n /** Label routes in the org, optionally narrowed to one project and/or integration. Sorted by label. */\n async listLabelRoutes({\n orgId,\n factoryProjectId,\n integrationId,\n }: {\n orgId: string;\n factoryProjectId?: string;\n integrationId?: string;\n }): Promise<IntakeLabelRoute[]> {\n const rows = await this.#db.findMany<IntakeLabelRouteRow>('intake_label_routes', {\n org_id: orgId,\n ...(factoryProjectId ? { factory_project_id: factoryProjectId } : {}),\n ...(integrationId ? { integration_id: integrationId } : {}),\n });\n return rows.map(toIntakeLabelRoute).sort((a, b) => a.label.localeCompare(b.label));\n }\n\n async setLabelRoute({\n orgId,\n factoryProjectId,\n integrationId,\n label,\n board,\n userId,\n }: {\n orgId: string;\n factoryProjectId: string;\n integrationId: string;\n label: string;\n board: string;\n userId?: string;\n }): Promise<void> {\n const where = {\n org_id: orgId,\n factory_project_id: factoryProjectId,\n integration_id: integrationId,\n label: normalizeIntakeLabel(label),\n };\n const now = new Date();\n const patch = { board, updated_at: now };\n const updated = await this.#db.updateMany('intake_label_routes', where, patch);\n if (updated > 0) return;\n try {\n await this.#db.insertOne('intake_label_routes', {\n ...where,\n board,\n created_by_user_id: userId ?? null,\n created_at: now,\n updated_at: now,\n });\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n await this.#db.updateMany('intake_label_routes', where, patch);\n }\n }\n\n /** Remove a label route; returns the route that was removed, or `null` when none existed. */\n async clearLabelRoute({\n orgId,\n factoryProjectId,\n integrationId,\n label,\n }: {\n orgId: string;\n factoryProjectId: string;\n integrationId: string;\n label: string;\n }): Promise<IntakeLabelRoute | null> {\n const where = {\n org_id: orgId,\n factory_project_id: factoryProjectId,\n integration_id: integrationId,\n label: normalizeIntakeLabel(label),\n };\n return this.storage.withTransaction(\n async ops => {\n const row = await ops.findOne<IntakeLabelRouteRow>('intake_label_routes', where);\n if (!row) return null;\n await ops.deleteMany('intake_label_routes', where);\n return toIntakeLabelRoute(row);\n },\n { isolationLevel: 'serializable' },\n );\n }\n}\n"],"mappings":"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"}
|
|
@@ -2,7 +2,6 @@ import type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';
|
|
|
2
2
|
import { FactoryStorageDomain } from '@mastra/core/storage';
|
|
3
3
|
import type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';
|
|
4
4
|
export declare const DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES = 10;
|
|
5
|
-
export declare const DEFAULT_FACTORY_THINKING_LEVEL: ThinkingLevelSetting;
|
|
6
5
|
export declare const FACTORY_MODEL_SCOPES: readonly ["factory", "user", "hybrid"];
|
|
7
6
|
export type FactoryModelScope = (typeof FACTORY_MODEL_SCOPES)[number];
|
|
8
7
|
/** The single credential scope one session or run resolves from; `hybrid` is never effective. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/projects/base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/projects/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { DEFAULT_THINKING_LEVEL } from "../../../default-thinking-level.js";
|
|
1
2
|
import { FactoryStorageDomain } from "@mastra/core/storage";
|
|
2
3
|
import { isThinkingLevelSetting } from "@mastra/code-sdk/thinking";
|
|
3
4
|
// src/storage/domains/projects/base.ts
|
|
4
5
|
const DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES = 10;
|
|
5
|
-
const DEFAULT_FACTORY_THINKING_LEVEL = "medium";
|
|
6
6
|
const FACTORY_MODEL_SCOPES = [
|
|
7
7
|
"factory",
|
|
8
8
|
"user",
|
|
@@ -37,7 +37,7 @@ const FACTORY_PROJECTS_SCHEMA = {
|
|
|
37
37
|
},
|
|
38
38
|
default_thinking_level: {
|
|
39
39
|
type: "text",
|
|
40
|
-
default:
|
|
40
|
+
default: DEFAULT_THINKING_LEVEL
|
|
41
41
|
},
|
|
42
42
|
slack_work_items_enabled: {
|
|
43
43
|
type: "boolean",
|
|
@@ -141,7 +141,7 @@ function toFactoryProject(row) {
|
|
|
141
141
|
description: row.description,
|
|
142
142
|
defaultModelId: row.default_model_id,
|
|
143
143
|
modelScope: row.model_scope === "factory" || row.model_scope === "user" ? row.model_scope : "hybrid",
|
|
144
|
-
defaultThinkingLevel: isThinkingLevelSetting(row.default_thinking_level) ? row.default_thinking_level :
|
|
144
|
+
defaultThinkingLevel: isThinkingLevelSetting(row.default_thinking_level) ? row.default_thinking_level : DEFAULT_THINKING_LEVEL,
|
|
145
145
|
autoRunEnabled: row.auto_run_enabled,
|
|
146
146
|
autoApprovePlans: row.auto_approve_plans ?? false,
|
|
147
147
|
sandboxWorkingDirectory: row.sandbox_working_directory ?? null,
|
|
@@ -366,6 +366,6 @@ var FactoryProjectsStorage = class extends FactoryStorageDomain {
|
|
|
366
366
|
}) > 0 ? project : null;
|
|
367
367
|
}
|
|
368
368
|
};
|
|
369
|
-
export { DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES,
|
|
369
|
+
export { DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES, FACTORY_MODEL_SCOPES, FACTORY_PROJECTS_SCHEMA, FactoryProjectsStorage, effectiveModelScope };
|
|
370
370
|
|
|
371
371
|
//# sourceMappingURL=base.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/projects/base.ts"],"sourcesContent":["import { isThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport { FactoryStorageDomain } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport const DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES = 10;\nexport const DEFAULT_FACTORY_THINKING_LEVEL: ThinkingLevelSetting = 'medium';\nexport const FACTORY_MODEL_SCOPES = ['factory', 'user', 'hybrid'] as const;\nexport type FactoryModelScope = (typeof FACTORY_MODEL_SCOPES)[number];\n/** The single credential scope one session or run resolves from; `hybrid` is never effective. */\nexport type EffectiveModelScope = Exclude<FactoryModelScope, 'hybrid'>;\n\n/**\n * Resolve the scope one context runs under. `hybrid` splits by context: work-item runs use the\n * Factory's model and credentials, interactive sessions use the session owner's.\n */\nexport function effectiveModelScope(scope: FactoryModelScope, context: 'run' | 'session'): EffectiveModelScope {\n if (scope === 'hybrid') return context === 'run' ? 'factory' : 'user';\n return scope;\n}\n\nexport interface FactoryProject {\n id: string;\n orgId: string;\n createdBy: string;\n name: string;\n description: string | null;\n /** Default model for sessions/runs started under this Factory (null = harness default). */\n defaultModelId: string | null;\n /**\n * Whose model and credentials sessions and runs use: the Factory's, the session owner's, or\n * `hybrid` (owner's for interactive sessions, the Factory's for work-item runs).\n */\n modelScope: FactoryModelScope;\n /** Thinking level for this Factory's sessions and runs that have not picked their own. */\n defaultThinkingLevel: ThinkingLevelSetting;\n /** Whether rules may start agent runs on their own; off, a run waits for approval on its card. */\n autoRunEnabled: boolean;\n /** Whether the Factory answers a run's plan itself instead of waiting for a person. */\n autoApprovePlans: boolean;\n /** Workspace root in the sandbox; linked repositories are checked out beneath it. */\n sandboxWorkingDirectory: string | null;\n /** Provider settings document validated by the configured FactorySandbox. Only keys the user set. */\n sandboxSettings: Record<string, unknown> | null;\n /** Command run once in the workspace root after every repository's own setup. */\n workspaceSetupCommand: string | null;\n /** Template id of the environment's current build, written by the build path. */\n activeTemplateId: string | null;\n /** Repository slug → commit the active template was built at. */\n activeTemplateHeads: Record<string, string> | null;\n /** Provider build id of the most recent build attempt; its status is read live from the provider. */\n lastBuildId: string | null;\n /** Repository slug → commit the most recent build attempt pinned, so its status can be read back. */\n lastBuildHeads: Record<string, string> | null;\n /** Fingerprint of the settings, repositories and setup commands the most recent build attempt used. */\n lastBuildInputs: string | null;\n /** When the most recent build attempt started, whatever triggered it. */\n lastBuildAttemptedAt: Date | null;\n /** When a push last started a build: the leading edge of the push debounce window. */\n lastPushBuildAt: Date | null;\n /**\n * A run's build-start lease lapsed, so a build it may have started is not\n * on record. Cleared once a run reconciles it with a provider that lists builds.\n */\n buildStartUncertain: boolean;\n /** Whether a push to an environment repository's default branch starts a build. */\n buildOnPushEnabled: boolean;\n /** Minutes after a push-triggered attempt during which further pushes do not build. */\n buildPushDebounceMinutes: number;\n /** Whether Review agents publish their test results as a commit check. Defaults to off. */\n reviewTestChecksEnabled: boolean;\n /** Whether Work items get an independent self-review before the change request opens. Defaults to off. */\n selfReviewEnabled: boolean;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface CreateFactoryProjectInput {\n name: string;\n description?: string | null;\n defaultModelId?: string | null;\n modelScope?: FactoryModelScope;\n}\n\nexport interface UpdateFactoryProjectInput {\n name?: string;\n description?: string | null;\n defaultModelId?: string | null;\n modelScope?: FactoryModelScope;\n defaultThinkingLevel?: ThinkingLevelSetting;\n autoRunEnabled?: boolean;\n autoApprovePlans?: boolean;\n sandboxWorkingDirectory?: string | null;\n sandboxSettings?: Record<string, unknown> | null;\n workspaceSetupCommand?: string | null;\n activeTemplateId?: string | null;\n activeTemplateHeads?: Record<string, string> | null;\n lastBuildId?: string | null;\n lastBuildHeads?: Record<string, string> | null;\n lastBuildInputs?: string | null;\n lastBuildAttemptedAt?: Date | null;\n buildOnPushEnabled?: boolean;\n buildPushDebounceMinutes?: number;\n reviewTestChecksEnabled?: boolean;\n selfReviewEnabled?: boolean;\n}\n\nexport const FACTORY_PROJECTS_SCHEMA: CollectionSchema = {\n name: 'factory_projects',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n name: { type: 'text' },\n description: { type: 'text', nullable: true },\n default_model_id: { type: 'text', nullable: true },\n model_scope: { type: 'text', default: 'hybrid' },\n default_thinking_level: { type: 'text', default: DEFAULT_FACTORY_THINKING_LEVEL },\n // Deprecated: no longer read or written. Kept so existing databases need no\n // migration. Old rows may still hold `true`, so never read it to bring Slack\n // work items back. Drop it in a later migration.\n slack_work_items_enabled: { type: 'boolean', default: false },\n auto_run_enabled: { type: 'boolean', default: false },\n auto_approve_plans: { type: 'boolean', default: false },\n sandbox_working_directory: { type: 'text', nullable: true },\n sandbox_settings: { type: 'json', nullable: true },\n workspace_setup_command: { type: 'text', nullable: true },\n active_template_id: { type: 'text', nullable: true },\n active_template_heads: { type: 'json', nullable: true },\n last_build_id: { type: 'text', nullable: true },\n last_build_heads: { type: 'json', nullable: true },\n last_build_inputs: { type: 'text', nullable: true },\n last_build_attempted_at: { type: 'timestamp', nullable: true },\n last_push_build_at: { type: 'timestamp', nullable: true },\n build_start_uncertain: { type: 'boolean', default: false },\n build_start_claim: { type: 'text', nullable: true },\n build_start_claim_expires_at: { type: 'timestamp', nullable: true },\n build_on_push_enabled: { type: 'boolean', default: false },\n build_push_debounce_minutes: { type: 'integer', default: DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES },\n review_test_checks_enabled: { type: 'boolean', default: false },\n self_review_enabled: { type: 'boolean', default: false },\n /** Set once the source-control domain has backfilled repository positions and environment membership. */\n environment_backfilled_at: { type: 'timestamp', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'factory_projects_org_updated_at_idx', columns: ['org_id', 'updated_at'] }],\n};\n\ninterface FactoryProjectDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n name: string;\n description: string | null;\n default_model_id: string | null;\n model_scope: string;\n default_thinking_level: string | null;\n auto_run_enabled: boolean;\n auto_approve_plans: boolean;\n sandbox_working_directory: string | null;\n sandbox_settings: Record<string, unknown> | null;\n workspace_setup_command: string | null;\n active_template_id: string | null;\n active_template_heads: Record<string, string> | null;\n last_build_id: string | null;\n last_build_heads: Record<string, string> | null;\n last_build_inputs: string | null;\n last_build_attempted_at: Date | null;\n last_push_build_at: Date | null;\n build_start_uncertain: boolean | null;\n build_start_claim: string | null;\n build_start_claim_expires_at: Date | null;\n build_on_push_enabled: boolean | null;\n build_push_debounce_minutes: number | null;\n review_test_checks_enabled: boolean | null;\n self_review_enabled: boolean | null;\n environment_backfilled_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toFactoryProject(row: FactoryProjectDbRow): FactoryProject {\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n name: row.name,\n description: row.description,\n defaultModelId: row.default_model_id,\n modelScope: row.model_scope === 'factory' || row.model_scope === 'user' ? row.model_scope : 'hybrid',\n defaultThinkingLevel: isThinkingLevelSetting(row.default_thinking_level)\n ? row.default_thinking_level\n : DEFAULT_FACTORY_THINKING_LEVEL,\n autoRunEnabled: row.auto_run_enabled,\n autoApprovePlans: row.auto_approve_plans ?? false,\n sandboxWorkingDirectory: row.sandbox_working_directory ?? null,\n sandboxSettings: row.sandbox_settings ?? null,\n workspaceSetupCommand: row.workspace_setup_command ?? null,\n activeTemplateId: row.active_template_id ?? null,\n activeTemplateHeads: row.active_template_heads ?? null,\n lastBuildId: row.last_build_id ?? null,\n lastBuildHeads: row.last_build_heads ?? null,\n lastBuildInputs: row.last_build_inputs ?? null,\n lastBuildAttemptedAt: row.last_build_attempted_at ?? null,\n lastPushBuildAt: row.last_push_build_at ?? null,\n buildStartUncertain: row.build_start_uncertain ?? false,\n buildOnPushEnabled: row.build_on_push_enabled ?? false,\n buildPushDebounceMinutes: row.build_push_debounce_minutes ?? DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES,\n reviewTestChecksEnabled: row.review_test_checks_enabled ?? false,\n selfReviewEnabled: row.self_review_enabled ?? false,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport class FactoryProjectsStorage extends FactoryStorageDomain {\n constructor() {\n super('projects');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('factory_projects', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async create({\n orgId,\n userId,\n input,\n }: {\n orgId: string;\n userId: string;\n input: CreateFactoryProjectInput;\n }): Promise<FactoryProject> {\n const now = new Date();\n const row = await this.#db.insertOne<FactoryProjectDbRow>('factory_projects', {\n org_id: orgId,\n created_by: userId,\n name: input.name,\n description: input.description ?? null,\n default_model_id: input.defaultModelId ?? null,\n model_scope: input.modelScope ?? 'hybrid',\n auto_run_enabled: false,\n auto_approve_plans: false,\n created_at: now,\n updated_at: now,\n });\n return toFactoryProject(row);\n }\n\n async list({ orgId }: { orgId: string }): Promise<FactoryProject[]> {\n const rows = await this.#db.findMany<FactoryProjectDbRow>(\n 'factory_projects',\n { org_id: orgId },\n { orderBy: [['updated_at', 'desc']] },\n );\n return rows.map(toFactoryProject);\n }\n\n async listAll(): Promise<FactoryProject[]> {\n const rows = await this.#db.findMany<FactoryProjectDbRow>(\n 'factory_projects',\n {},\n { orderBy: [['updated_at', 'desc']] },\n );\n return rows.map(toFactoryProject);\n }\n\n async get({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id });\n return row ? toFactoryProject(row) : null;\n }\n\n async getById({ id }: { id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { id });\n return row ? toFactoryProject(row) : null;\n }\n\n async update({\n orgId,\n id,\n input,\n }: {\n orgId: string;\n id: string;\n /** A function input runs in a transaction; writes through its `ops` commit or roll back with the project. */\n input:\n | UpdateFactoryProjectInput\n | ((current: FactoryProject, ops: FactoryStorageOps) => Promise<UpdateFactoryProjectInput | null>);\n }): Promise<FactoryProject | null> {\n const update = (ops: FactoryStorageOps) =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id }, async current => {\n const changes = typeof input === 'function' ? await input(toFactoryProject(current), ops) : input;\n if (changes === null) return null;\n return {\n ...(changes.name !== undefined ? { name: changes.name } : {}),\n ...(changes.description !== undefined ? { description: changes.description } : {}),\n ...(changes.defaultModelId !== undefined ? { default_model_id: changes.defaultModelId } : {}),\n ...(changes.modelScope !== undefined ? { model_scope: changes.modelScope } : {}),\n ...(changes.defaultThinkingLevel !== undefined\n ? { default_thinking_level: changes.defaultThinkingLevel }\n : {}),\n ...(changes.autoRunEnabled !== undefined ? { auto_run_enabled: changes.autoRunEnabled } : {}),\n ...(changes.autoApprovePlans !== undefined ? { auto_approve_plans: changes.autoApprovePlans } : {}),\n ...(changes.sandboxWorkingDirectory !== undefined\n ? { sandbox_working_directory: changes.sandboxWorkingDirectory }\n : {}),\n ...(changes.sandboxSettings !== undefined ? { sandbox_settings: changes.sandboxSettings } : {}),\n ...(changes.workspaceSetupCommand !== undefined\n ? { workspace_setup_command: changes.workspaceSetupCommand }\n : {}),\n ...(changes.activeTemplateId !== undefined ? { active_template_id: changes.activeTemplateId } : {}),\n ...(changes.activeTemplateHeads !== undefined ? { active_template_heads: changes.activeTemplateHeads } : {}),\n ...(changes.lastBuildId !== undefined ? { last_build_id: changes.lastBuildId } : {}),\n ...(changes.lastBuildHeads !== undefined ? { last_build_heads: changes.lastBuildHeads } : {}),\n ...(changes.lastBuildInputs !== undefined ? { last_build_inputs: changes.lastBuildInputs } : {}),\n ...(changes.lastBuildAttemptedAt !== undefined\n ? { last_build_attempted_at: changes.lastBuildAttemptedAt }\n : {}),\n ...(changes.buildOnPushEnabled !== undefined ? { build_on_push_enabled: changes.buildOnPushEnabled } : {}),\n ...(changes.buildPushDebounceMinutes !== undefined\n ? { build_push_debounce_minutes: changes.buildPushDebounceMinutes }\n : {}),\n ...(changes.reviewTestChecksEnabled !== undefined\n ? { review_test_checks_enabled: changes.reviewTestChecksEnabled }\n : {}),\n ...(changes.selfReviewEnabled !== undefined ? { self_review_enabled: changes.selfReviewEnabled } : {}),\n updated_at: new Date(),\n };\n });\n // LibSQL's standalone updateAtomic only serializes one client; a transaction\n // also protects a read-dependent update against other database connections.\n const row = typeof input === 'function' ? await this.storage.withTransaction(update) : await update(this.#db);\n return row ? toFactoryProject(row) : null;\n }\n\n /**\n * Leading-edge debounce for push-triggered builds: stamps\n * `last_push_build_at = now` and resolves true when no push started a build\n * within the last `debounceMinutes`, else writes nothing and resolves\n * false. Taken right before the provider start, so a push that skips\n * (another run starting, a build in progress) never closes the window. The read and the write run in one transaction, so two concurrent\n * webhooks never both win, even on different database connections.\n */\n async claimBuildAttempt({\n id,\n debounceMinutes,\n now = new Date(),\n }: {\n id: string;\n debounceMinutes: number;\n now?: Date;\n }): Promise<boolean> {\n let claimed = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n const last = current.last_push_build_at;\n if (last && now.getTime() - last.getTime() < debounceMinutes * 60_000) return null;\n claimed = true;\n return { last_push_build_at: now, updated_at: now };\n }),\n );\n return claimed;\n }\n\n /**\n * Takes the project's build-start lease for `token`. Every trigger (manual,\n * schedule, push) takes it before reading build state and holds it through\n * `builds.start` and recording the result, so two runs never both start a\n * build. `claimed` is false while another run holds an unexpired lease. The\n * holder renews the lease while it works (`renewBuildStart`), so a lease\n * only expires when its run died or stalled. Taking over an expired lease\n * marks the start uncertain (that run's build may have reached the provider\n * unrecorded), and the mark stays until a release resolves it; `uncertain`\n * reports it. Only the claim is transactional; provider calls run outside it.\n */\n async claimBuildStart({\n id,\n token,\n leaseMs,\n now = new Date(),\n }: {\n id: string;\n token: string;\n leaseMs: number;\n now?: Date;\n }): Promise<{ claimed: boolean; uncertain: boolean }> {\n const result = { claimed: false, uncertain: false };\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n const expiresAt = current.build_start_claim_expires_at;\n if (current.build_start_claim && expiresAt && expiresAt.getTime() > now.getTime()) return null;\n result.claimed = true;\n result.uncertain = Boolean(current.build_start_claim) || current.build_start_uncertain === true;\n return {\n build_start_claim: token,\n build_start_claim_expires_at: new Date(now.getTime() + leaseMs),\n ...(result.uncertain ? { build_start_uncertain: true } : {}),\n updated_at: now,\n };\n }),\n );\n return result;\n }\n\n /**\n * Extends the build-start lease held by `token` to `now + leaseMs`. Resolves\n * false, writing nothing, when `token` no longer holds it.\n */\n async renewBuildStart({\n id,\n token,\n leaseMs,\n now = new Date(),\n }: {\n id: string;\n token: string;\n leaseMs: number;\n now?: Date;\n }): Promise<boolean> {\n let renewed = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n if (current.build_start_claim !== token) return null;\n renewed = true;\n return { build_start_claim_expires_at: new Date(now.getTime() + leaseMs), updated_at: now };\n }),\n );\n return renewed;\n }\n\n /**\n * Releases the build-start lease taken with `token` and writes `changes` in\n * the same transaction, but only while `token` still holds the lease. A run\n * whose lease expired and was taken by a newer run writes nothing, so it\n * never clears or overwrites that run's build. `resolved` clears an\n * uncertain start. Resolves false when skipped.\n */\n async releaseBuildStart({\n id,\n token,\n changes = {},\n resolved = false,\n now = new Date(),\n }: {\n id: string;\n token: string;\n resolved?: boolean;\n changes?: Pick<\n UpdateFactoryProjectInput,\n 'lastBuildId' | 'lastBuildHeads' | 'lastBuildInputs' | 'lastBuildAttemptedAt'\n >;\n now?: Date;\n }): Promise<boolean> {\n let released = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n if (current.build_start_claim !== token) return null;\n released = true;\n return {\n ...(changes.lastBuildId !== undefined ? { last_build_id: changes.lastBuildId } : {}),\n ...(changes.lastBuildHeads !== undefined ? { last_build_heads: changes.lastBuildHeads } : {}),\n ...(changes.lastBuildInputs !== undefined ? { last_build_inputs: changes.lastBuildInputs } : {}),\n ...(changes.lastBuildAttemptedAt !== undefined\n ? { last_build_attempted_at: changes.lastBuildAttemptedAt }\n : {}),\n ...(resolved ? { build_start_uncertain: false } : {}),\n build_start_claim: null,\n build_start_claim_expires_at: null,\n updated_at: now,\n };\n }),\n );\n return released;\n }\n\n /**\n * Pins the active template to a finished build, but only while that build is\n * still the project's last one. Overlapping runs (Build now during a push\n * build) can finish out of order; the write and the `last_build_id` check\n * run in one transaction so a stale run never overwrites a newer build's\n * pin. Resolves false when the pin was skipped.\n */\n async pinActiveTemplate({\n id,\n buildId,\n templateId,\n heads,\n now = new Date(),\n }: {\n id: string;\n buildId: string;\n templateId: string;\n heads: Record<string, string>;\n now?: Date;\n }): Promise<boolean> {\n let pinned = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n if (current.last_build_id !== buildId) return null;\n pinned = true;\n return { active_template_id: templateId, active_template_heads: heads, updated_at: now };\n }),\n );\n return pinned;\n }\n\n async delete({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const project = await this.get({ orgId, id });\n if (!project) return null;\n const deleted = await this.#db.deleteMany('factory_projects', { org_id: orgId, id });\n return deleted > 0 ? project : null;\n }\n}\n"],"mappings":";;;AAKA,MAAa,sCAAsC;AACnD,MAAa,iCAAuD;AACpE,MAAa,uBAAuB;CAAC;CAAW;CAAQ;AAAQ;;;;;AAShE,SAAgB,oBAAoB,OAA0B,SAAiD;CAC7G,IAAI,UAAU,UAAU,OAAO,YAAY,QAAQ,YAAY;CAC/D,OAAO;AACT;AAwFA,MAAa,0BAA4C;CACvD,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,YAAY,EAAE,MAAM,OAAO;EAC3B,MAAM,EAAE,MAAM,OAAO;EACrB,aAAa;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC5C,kBAAkB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACjD,aAAa;GAAE,MAAM;GAAQ,SAAS;EAAS;EAC/C,wBAAwB;GAAE,MAAM;GAAQ,SAAS;EAA+B;EAIhF,0BAA0B;GAAE,MAAM;GAAW,SAAS;EAAM;EAC5D,kBAAkB;GAAE,MAAM;GAAW,SAAS;EAAM;EACpD,oBAAoB;GAAE,MAAM;GAAW,SAAS;EAAM;EACtD,2BAA2B;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC1D,kBAAkB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACjD,yBAAyB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACxD,oBAAoB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACnD,uBAAuB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACtD,eAAe;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC9C,kBAAkB;GAAE,MAAM;GAAQ,UAAU;EAAK;EACjD,mBAAmB;GAAE,MAAM;GAAQ,UAAU;EAAK;EAClD,yBAAyB;GAAE,MAAM;GAAa,UAAU;EAAK;EAC7D,oBAAoB;GAAE,MAAM;GAAa,UAAU;EAAK;EACxD,uBAAuB;GAAE,MAAM;GAAW,SAAS;EAAM;EACzD,mBAAmB;GAAE,MAAM;GAAQ,UAAU;EAAK;EAClD,8BAA8B;GAAE,MAAM;GAAa,UAAU;EAAK;EAClE,uBAAuB;GAAE,MAAM;GAAW,SAAS;EAAM;EACzD,6BAA6B;GAAE,MAAM;GAAW,SAAA;EAA6C;EAC7F,4BAA4B;GAAE,MAAM;GAAW,SAAS;EAAM;EAC9D,qBAAqB;GAAE,MAAM;GAAW,SAAS;EAAM;;EAEvD,2BAA2B;GAAE,MAAM;GAAa,UAAU;EAAK;EAC/D,YAAY,EAAE,MAAM,YAAY;EAChC,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,SAAS,CAAC;EAAE,MAAM;EAAuC,SAAS,CAAC,UAAU,YAAY;CAAE,CAAC;AAC9F;AAmCA,SAAS,iBAAiB,KAA0C;CAClE,OAAO;EACL,IAAI,IAAI;EACR,OAAO,IAAI;EACX,WAAW,IAAI;EACf,MAAM,IAAI;EACV,aAAa,IAAI;EACjB,gBAAgB,IAAI;EACpB,YAAY,IAAI,gBAAgB,aAAa,IAAI,gBAAgB,SAAS,IAAI,cAAc;EAC5F,sBAAsB,uBAAuB,IAAI,sBAAsB,IACnE,IAAI,yBACJ;EACJ,gBAAgB,IAAI;EACpB,kBAAkB,IAAI,sBAAsB;EAC5C,yBAAyB,IAAI,6BAA6B;EAC1D,iBAAiB,IAAI,oBAAoB;EACzC,uBAAuB,IAAI,2BAA2B;EACtD,kBAAkB,IAAI,sBAAsB;EAC5C,qBAAqB,IAAI,yBAAyB;EAClD,aAAa,IAAI,iBAAiB;EAClC,gBAAgB,IAAI,oBAAoB;EACxC,iBAAiB,IAAI,qBAAqB;EAC1C,sBAAsB,IAAI,2BAA2B;EACrD,iBAAiB,IAAI,sBAAsB;EAC3C,qBAAqB,IAAI,yBAAyB;EAClD,oBAAoB,IAAI,yBAAyB;EACjD,0BAA0B,IAAI,+BAAA;EAC9B,yBAAyB,IAAI,8BAA8B;EAC3D,mBAAmB,IAAI,uBAAuB;EAC9C,WAAW,IAAI;EACf,WAAW,IAAI;CACjB;AACF;AAEA,IAAa,yBAAb,cAA4C,qBAAqB;CAC/D,cAAc;EACZ,MAAM,UAAU;CAClB;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,uBAAuB,CAAC;CACxD;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,oBAAoB,CAAC,CAAC;CAClD;CAEA,IAAI,MAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,MAAM,OAAO,EACX,OACA,QACA,SAK0B;EAC1B,MAAM,sBAAM,IAAI,KAAK;EAarB,OAAO,iBAAiB,MAZN,KAAK,IAAI,UAA+B,oBAAoB;GAC5E,QAAQ;GACR,YAAY;GACZ,MAAM,MAAM;GACZ,aAAa,MAAM,eAAe;GAClC,kBAAkB,MAAM,kBAAkB;GAC1C,aAAa,MAAM,cAAc;GACjC,kBAAkB;GAClB,oBAAoB;GACpB,YAAY;GACZ,YAAY;EACd,CAAC,CAC0B;CAC7B;CAEA,MAAM,KAAK,EAAE,SAAuD;EAMlE,QAAO,MALY,KAAK,IAAI,SAC1B,oBACA,EAAE,QAAQ,MAAM,GAChB,EAAE,SAAS,CAAC,CAAC,cAAc,MAAM,CAAC,EAAE,CACtC,EAAA,CACY,IAAI,gBAAgB;CAClC;CAEA,MAAM,UAAqC;EAMzC,QAAO,MALY,KAAK,IAAI,SAC1B,oBACA,CAAC,GACD,EAAE,SAAS,CAAC,CAAC,cAAc,MAAM,CAAC,EAAE,CACtC,EAAA,CACY,IAAI,gBAAgB;CAClC;CAEA,MAAM,IAAI,EAAE,OAAO,MAAqE;EACtF,MAAM,MAAM,MAAM,KAAK,IAAI,QAA6B,oBAAoB;GAAE,QAAQ;GAAO;EAAG,CAAC;EACjG,OAAO,MAAM,iBAAiB,GAAG,IAAI;CACvC;CAEA,MAAM,QAAQ,EAAE,MAAsD;EACpE,MAAM,MAAM,MAAM,KAAK,IAAI,QAA6B,oBAAoB,EAAE,GAAG,CAAC;EAClF,OAAO,MAAM,iBAAiB,GAAG,IAAI;CACvC;CAEA,MAAM,OAAO,EACX,OACA,IACA,SAQiC;EACjC,MAAM,UAAU,QACd,IAAI,aAAkC,oBAAoB;GAAE,QAAQ;GAAO;EAAG,GAAG,OAAM,YAAW;GAChG,MAAM,UAAU,OAAO,UAAU,aAAa,MAAM,MAAM,iBAAiB,OAAO,GAAG,GAAG,IAAI;GAC5F,IAAI,YAAY,MAAM,OAAO;GAC7B,OAAO;IACL,GAAI,QAAQ,SAAS,KAAA,IAAY,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;IAC3D,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,aAAa,QAAQ,YAAY,IAAI,CAAC;IAChF,GAAI,QAAQ,mBAAmB,KAAA,IAAY,EAAE,kBAAkB,QAAQ,eAAe,IAAI,CAAC;IAC3F,GAAI,QAAQ,eAAe,KAAA,IAAY,EAAE,aAAa,QAAQ,WAAW,IAAI,CAAC;IAC9E,GAAI,QAAQ,yBAAyB,KAAA,IACjC,EAAE,wBAAwB,QAAQ,qBAAqB,IACvD,CAAC;IACL,GAAI,QAAQ,mBAAmB,KAAA,IAAY,EAAE,kBAAkB,QAAQ,eAAe,IAAI,CAAC;IAC3F,GAAI,QAAQ,qBAAqB,KAAA,IAAY,EAAE,oBAAoB,QAAQ,iBAAiB,IAAI,CAAC;IACjG,GAAI,QAAQ,4BAA4B,KAAA,IACpC,EAAE,2BAA2B,QAAQ,wBAAwB,IAC7D,CAAC;IACL,GAAI,QAAQ,oBAAoB,KAAA,IAAY,EAAE,kBAAkB,QAAQ,gBAAgB,IAAI,CAAC;IAC7F,GAAI,QAAQ,0BAA0B,KAAA,IAClC,EAAE,yBAAyB,QAAQ,sBAAsB,IACzD,CAAC;IACL,GAAI,QAAQ,qBAAqB,KAAA,IAAY,EAAE,oBAAoB,QAAQ,iBAAiB,IAAI,CAAC;IACjG,GAAI,QAAQ,wBAAwB,KAAA,IAAY,EAAE,uBAAuB,QAAQ,oBAAoB,IAAI,CAAC;IAC1G,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,eAAe,QAAQ,YAAY,IAAI,CAAC;IAClF,GAAI,QAAQ,mBAAmB,KAAA,IAAY,EAAE,kBAAkB,QAAQ,eAAe,IAAI,CAAC;IAC3F,GAAI,QAAQ,oBAAoB,KAAA,IAAY,EAAE,mBAAmB,QAAQ,gBAAgB,IAAI,CAAC;IAC9F,GAAI,QAAQ,yBAAyB,KAAA,IACjC,EAAE,yBAAyB,QAAQ,qBAAqB,IACxD,CAAC;IACL,GAAI,QAAQ,uBAAuB,KAAA,IAAY,EAAE,uBAAuB,QAAQ,mBAAmB,IAAI,CAAC;IACxG,GAAI,QAAQ,6BAA6B,KAAA,IACrC,EAAE,6BAA6B,QAAQ,yBAAyB,IAChE,CAAC;IACL,GAAI,QAAQ,4BAA4B,KAAA,IACpC,EAAE,4BAA4B,QAAQ,wBAAwB,IAC9D,CAAC;IACL,GAAI,QAAQ,sBAAsB,KAAA,IAAY,EAAE,qBAAqB,QAAQ,kBAAkB,IAAI,CAAC;IACpG,4BAAY,IAAI,KAAK;GACvB;EACF,CAAC;EAGH,MAAM,MAAM,OAAO,UAAU,aAAa,MAAM,KAAK,QAAQ,gBAAgB,MAAM,IAAI,MAAM,OAAO,KAAK,GAAG;EAC5G,OAAO,MAAM,iBAAiB,GAAG,IAAI;CACvC;;;;;;;;;CAUA,MAAM,kBAAkB,EACtB,IACA,iBACA,sBAAM,IAAI,KAAK,KAKI;EACnB,IAAI,UAAU;EACd,MAAM,KAAK,QAAQ,iBAAgB,QACjC,IAAI,aAAkC,oBAAoB,EAAE,GAAG,IAAG,YAAW;GAC3E,MAAM,OAAO,QAAQ;GACrB,IAAI,QAAQ,IAAI,QAAQ,IAAI,KAAK,QAAQ,IAAI,kBAAkB,KAAQ,OAAO;GAC9E,UAAU;GACV,OAAO;IAAE,oBAAoB;IAAK,YAAY;GAAI;EACpD,CAAC,CACH;EACA,OAAO;CACT;;;;;;;;;;;;CAaA,MAAM,gBAAgB,EACpB,IACA,OACA,SACA,sBAAM,IAAI,KAAK,KAMqC;EACpD,MAAM,SAAS;GAAE,SAAS;GAAO,WAAW;EAAM;EAClD,MAAM,KAAK,QAAQ,iBAAgB,QACjC,IAAI,aAAkC,oBAAoB,EAAE,GAAG,IAAG,YAAW;GAC3E,MAAM,YAAY,QAAQ;GAC1B,IAAI,QAAQ,qBAAqB,aAAa,UAAU,QAAQ,IAAI,IAAI,QAAQ,GAAG,OAAO;GAC1F,OAAO,UAAU;GACjB,OAAO,YAAY,QAAQ,QAAQ,iBAAiB,KAAK,QAAQ,0BAA0B;GAC3F,OAAO;IACL,mBAAmB;IACnB,8BAA8B,IAAI,KAAK,IAAI,QAAQ,IAAI,OAAO;IAC9D,GAAI,OAAO,YAAY,EAAE,uBAAuB,KAAK,IAAI,CAAC;IAC1D,YAAY;GACd;EACF,CAAC,CACH;EACA,OAAO;CACT;;;;;CAMA,MAAM,gBAAgB,EACpB,IACA,OACA,SACA,sBAAM,IAAI,KAAK,KAMI;EACnB,IAAI,UAAU;EACd,MAAM,KAAK,QAAQ,iBAAgB,QACjC,IAAI,aAAkC,oBAAoB,EAAE,GAAG,IAAG,YAAW;GAC3E,IAAI,QAAQ,sBAAsB,OAAO,OAAO;GAChD,UAAU;GACV,OAAO;IAAE,8BAA8B,IAAI,KAAK,IAAI,QAAQ,IAAI,OAAO;IAAG,YAAY;GAAI;EAC5F,CAAC,CACH;EACA,OAAO;CACT;;;;;;;;CASA,MAAM,kBAAkB,EACtB,IACA,OACA,UAAU,CAAC,GACX,WAAW,OACX,sBAAM,IAAI,KAAK,KAUI;EACnB,IAAI,WAAW;EACf,MAAM,KAAK,QAAQ,iBAAgB,QACjC,IAAI,aAAkC,oBAAoB,EAAE,GAAG,IAAG,YAAW;GAC3E,IAAI,QAAQ,sBAAsB,OAAO,OAAO;GAChD,WAAW;GACX,OAAO;IACL,GAAI,QAAQ,gBAAgB,KAAA,IAAY,EAAE,eAAe,QAAQ,YAAY,IAAI,CAAC;IAClF,GAAI,QAAQ,mBAAmB,KAAA,IAAY,EAAE,kBAAkB,QAAQ,eAAe,IAAI,CAAC;IAC3F,GAAI,QAAQ,oBAAoB,KAAA,IAAY,EAAE,mBAAmB,QAAQ,gBAAgB,IAAI,CAAC;IAC9F,GAAI,QAAQ,yBAAyB,KAAA,IACjC,EAAE,yBAAyB,QAAQ,qBAAqB,IACxD,CAAC;IACL,GAAI,WAAW,EAAE,uBAAuB,MAAM,IAAI,CAAC;IACnD,mBAAmB;IACnB,8BAA8B;IAC9B,YAAY;GACd;EACF,CAAC,CACH;EACA,OAAO;CACT;;;;;;;;CASA,MAAM,kBAAkB,EACtB,IACA,SACA,YACA,OACA,sBAAM,IAAI,KAAK,KAOI;EACnB,IAAI,SAAS;EACb,MAAM,KAAK,QAAQ,iBAAgB,QACjC,IAAI,aAAkC,oBAAoB,EAAE,GAAG,IAAG,YAAW;GAC3E,IAAI,QAAQ,kBAAkB,SAAS,OAAO;GAC9C,SAAS;GACT,OAAO;IAAE,oBAAoB;IAAY,uBAAuB;IAAO,YAAY;GAAI;EACzF,CAAC,CACH;EACA,OAAO;CACT;CAEA,MAAM,OAAO,EAAE,OAAO,MAAqE;EACzF,MAAM,UAAU,MAAM,KAAK,IAAI;GAAE;GAAO;EAAG,CAAC;EAC5C,IAAI,CAAC,SAAS,OAAO;EAErB,OAAO,MADe,KAAK,IAAI,WAAW,oBAAoB;GAAE,QAAQ;GAAO;EAAG,CAAC,IAClE,IAAI,UAAU;CACjC;AACF"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/projects/base.ts"],"sourcesContent":["import { isThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport { FactoryStorageDomain } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nimport { DEFAULT_THINKING_LEVEL } from '../../../default-thinking-level.js';\n\nexport const DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES = 10;\nexport const FACTORY_MODEL_SCOPES = ['factory', 'user', 'hybrid'] as const;\nexport type FactoryModelScope = (typeof FACTORY_MODEL_SCOPES)[number];\n/** The single credential scope one session or run resolves from; `hybrid` is never effective. */\nexport type EffectiveModelScope = Exclude<FactoryModelScope, 'hybrid'>;\n\n/**\n * Resolve the scope one context runs under. `hybrid` splits by context: work-item runs use the\n * Factory's model and credentials, interactive sessions use the session owner's.\n */\nexport function effectiveModelScope(scope: FactoryModelScope, context: 'run' | 'session'): EffectiveModelScope {\n if (scope === 'hybrid') return context === 'run' ? 'factory' : 'user';\n return scope;\n}\n\nexport interface FactoryProject {\n id: string;\n orgId: string;\n createdBy: string;\n name: string;\n description: string | null;\n /** Default model for sessions/runs started under this Factory (null = harness default). */\n defaultModelId: string | null;\n /**\n * Whose model and credentials sessions and runs use: the Factory's, the session owner's, or\n * `hybrid` (owner's for interactive sessions, the Factory's for work-item runs).\n */\n modelScope: FactoryModelScope;\n /** Thinking level for this Factory's sessions and runs that have not picked their own. */\n defaultThinkingLevel: ThinkingLevelSetting;\n /** Whether rules may start agent runs on their own; off, a run waits for approval on its card. */\n autoRunEnabled: boolean;\n /** Whether the Factory answers a run's plan itself instead of waiting for a person. */\n autoApprovePlans: boolean;\n /** Workspace root in the sandbox; linked repositories are checked out beneath it. */\n sandboxWorkingDirectory: string | null;\n /** Provider settings document validated by the configured FactorySandbox. Only keys the user set. */\n sandboxSettings: Record<string, unknown> | null;\n /** Command run once in the workspace root after every repository's own setup. */\n workspaceSetupCommand: string | null;\n /** Template id of the environment's current build, written by the build path. */\n activeTemplateId: string | null;\n /** Repository slug → commit the active template was built at. */\n activeTemplateHeads: Record<string, string> | null;\n /** Provider build id of the most recent build attempt; its status is read live from the provider. */\n lastBuildId: string | null;\n /** Repository slug → commit the most recent build attempt pinned, so its status can be read back. */\n lastBuildHeads: Record<string, string> | null;\n /** Fingerprint of the settings, repositories and setup commands the most recent build attempt used. */\n lastBuildInputs: string | null;\n /** When the most recent build attempt started, whatever triggered it. */\n lastBuildAttemptedAt: Date | null;\n /** When a push last started a build: the leading edge of the push debounce window. */\n lastPushBuildAt: Date | null;\n /**\n * A run's build-start lease lapsed, so a build it may have started is not\n * on record. Cleared once a run reconciles it with a provider that lists builds.\n */\n buildStartUncertain: boolean;\n /** Whether a push to an environment repository's default branch starts a build. */\n buildOnPushEnabled: boolean;\n /** Minutes after a push-triggered attempt during which further pushes do not build. */\n buildPushDebounceMinutes: number;\n /** Whether Review agents publish their test results as a commit check. Defaults to off. */\n reviewTestChecksEnabled: boolean;\n /** Whether Work items get an independent self-review before the change request opens. Defaults to off. */\n selfReviewEnabled: boolean;\n createdAt: Date;\n updatedAt: Date;\n}\n\nexport interface CreateFactoryProjectInput {\n name: string;\n description?: string | null;\n defaultModelId?: string | null;\n modelScope?: FactoryModelScope;\n}\n\nexport interface UpdateFactoryProjectInput {\n name?: string;\n description?: string | null;\n defaultModelId?: string | null;\n modelScope?: FactoryModelScope;\n defaultThinkingLevel?: ThinkingLevelSetting;\n autoRunEnabled?: boolean;\n autoApprovePlans?: boolean;\n sandboxWorkingDirectory?: string | null;\n sandboxSettings?: Record<string, unknown> | null;\n workspaceSetupCommand?: string | null;\n activeTemplateId?: string | null;\n activeTemplateHeads?: Record<string, string> | null;\n lastBuildId?: string | null;\n lastBuildHeads?: Record<string, string> | null;\n lastBuildInputs?: string | null;\n lastBuildAttemptedAt?: Date | null;\n buildOnPushEnabled?: boolean;\n buildPushDebounceMinutes?: number;\n reviewTestChecksEnabled?: boolean;\n selfReviewEnabled?: boolean;\n}\n\nexport const FACTORY_PROJECTS_SCHEMA: CollectionSchema = {\n name: 'factory_projects',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n created_by: { type: 'text' },\n name: { type: 'text' },\n description: { type: 'text', nullable: true },\n default_model_id: { type: 'text', nullable: true },\n model_scope: { type: 'text', default: 'hybrid' },\n default_thinking_level: { type: 'text', default: DEFAULT_THINKING_LEVEL },\n // Deprecated: no longer read or written. Kept so existing databases need no\n // migration. Old rows may still hold `true`, so never read it to bring Slack\n // work items back. Drop it in a later migration.\n slack_work_items_enabled: { type: 'boolean', default: false },\n auto_run_enabled: { type: 'boolean', default: false },\n auto_approve_plans: { type: 'boolean', default: false },\n sandbox_working_directory: { type: 'text', nullable: true },\n sandbox_settings: { type: 'json', nullable: true },\n workspace_setup_command: { type: 'text', nullable: true },\n active_template_id: { type: 'text', nullable: true },\n active_template_heads: { type: 'json', nullable: true },\n last_build_id: { type: 'text', nullable: true },\n last_build_heads: { type: 'json', nullable: true },\n last_build_inputs: { type: 'text', nullable: true },\n last_build_attempted_at: { type: 'timestamp', nullable: true },\n last_push_build_at: { type: 'timestamp', nullable: true },\n build_start_uncertain: { type: 'boolean', default: false },\n build_start_claim: { type: 'text', nullable: true },\n build_start_claim_expires_at: { type: 'timestamp', nullable: true },\n build_on_push_enabled: { type: 'boolean', default: false },\n build_push_debounce_minutes: { type: 'integer', default: DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES },\n review_test_checks_enabled: { type: 'boolean', default: false },\n self_review_enabled: { type: 'boolean', default: false },\n /** Set once the source-control domain has backfilled repository positions and environment membership. */\n environment_backfilled_at: { type: 'timestamp', nullable: true },\n created_at: { type: 'timestamp' },\n updated_at: { type: 'timestamp' },\n },\n indexes: [{ name: 'factory_projects_org_updated_at_idx', columns: ['org_id', 'updated_at'] }],\n};\n\ninterface FactoryProjectDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n created_by: string;\n name: string;\n description: string | null;\n default_model_id: string | null;\n model_scope: string;\n default_thinking_level: string | null;\n auto_run_enabled: boolean;\n auto_approve_plans: boolean;\n sandbox_working_directory: string | null;\n sandbox_settings: Record<string, unknown> | null;\n workspace_setup_command: string | null;\n active_template_id: string | null;\n active_template_heads: Record<string, string> | null;\n last_build_id: string | null;\n last_build_heads: Record<string, string> | null;\n last_build_inputs: string | null;\n last_build_attempted_at: Date | null;\n last_push_build_at: Date | null;\n build_start_uncertain: boolean | null;\n build_start_claim: string | null;\n build_start_claim_expires_at: Date | null;\n build_on_push_enabled: boolean | null;\n build_push_debounce_minutes: number | null;\n review_test_checks_enabled: boolean | null;\n self_review_enabled: boolean | null;\n environment_backfilled_at: Date | null;\n created_at: Date;\n updated_at: Date;\n}\n\nfunction toFactoryProject(row: FactoryProjectDbRow): FactoryProject {\n return {\n id: row.id,\n orgId: row.org_id,\n createdBy: row.created_by,\n name: row.name,\n description: row.description,\n defaultModelId: row.default_model_id,\n modelScope: row.model_scope === 'factory' || row.model_scope === 'user' ? row.model_scope : 'hybrid',\n defaultThinkingLevel: isThinkingLevelSetting(row.default_thinking_level)\n ? row.default_thinking_level\n : DEFAULT_THINKING_LEVEL,\n autoRunEnabled: row.auto_run_enabled,\n autoApprovePlans: row.auto_approve_plans ?? false,\n sandboxWorkingDirectory: row.sandbox_working_directory ?? null,\n sandboxSettings: row.sandbox_settings ?? null,\n workspaceSetupCommand: row.workspace_setup_command ?? null,\n activeTemplateId: row.active_template_id ?? null,\n activeTemplateHeads: row.active_template_heads ?? null,\n lastBuildId: row.last_build_id ?? null,\n lastBuildHeads: row.last_build_heads ?? null,\n lastBuildInputs: row.last_build_inputs ?? null,\n lastBuildAttemptedAt: row.last_build_attempted_at ?? null,\n lastPushBuildAt: row.last_push_build_at ?? null,\n buildStartUncertain: row.build_start_uncertain ?? false,\n buildOnPushEnabled: row.build_on_push_enabled ?? false,\n buildPushDebounceMinutes: row.build_push_debounce_minutes ?? DEFAULT_BUILD_PUSH_DEBOUNCE_MINUTES,\n reviewTestChecksEnabled: row.review_test_checks_enabled ?? false,\n selfReviewEnabled: row.self_review_enabled ?? false,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n };\n}\n\nexport class FactoryProjectsStorage extends FactoryStorageDomain {\n constructor() {\n super('projects');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('factory_projects', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async create({\n orgId,\n userId,\n input,\n }: {\n orgId: string;\n userId: string;\n input: CreateFactoryProjectInput;\n }): Promise<FactoryProject> {\n const now = new Date();\n const row = await this.#db.insertOne<FactoryProjectDbRow>('factory_projects', {\n org_id: orgId,\n created_by: userId,\n name: input.name,\n description: input.description ?? null,\n default_model_id: input.defaultModelId ?? null,\n model_scope: input.modelScope ?? 'hybrid',\n auto_run_enabled: false,\n auto_approve_plans: false,\n created_at: now,\n updated_at: now,\n });\n return toFactoryProject(row);\n }\n\n async list({ orgId }: { orgId: string }): Promise<FactoryProject[]> {\n const rows = await this.#db.findMany<FactoryProjectDbRow>(\n 'factory_projects',\n { org_id: orgId },\n { orderBy: [['updated_at', 'desc']] },\n );\n return rows.map(toFactoryProject);\n }\n\n async listAll(): Promise<FactoryProject[]> {\n const rows = await this.#db.findMany<FactoryProjectDbRow>(\n 'factory_projects',\n {},\n { orderBy: [['updated_at', 'desc']] },\n );\n return rows.map(toFactoryProject);\n }\n\n async get({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id });\n return row ? toFactoryProject(row) : null;\n }\n\n async getById({ id }: { id: string }): Promise<FactoryProject | null> {\n const row = await this.#db.findOne<FactoryProjectDbRow>('factory_projects', { id });\n return row ? toFactoryProject(row) : null;\n }\n\n async update({\n orgId,\n id,\n input,\n }: {\n orgId: string;\n id: string;\n /** A function input runs in a transaction; writes through its `ops` commit or roll back with the project. */\n input:\n | UpdateFactoryProjectInput\n | ((current: FactoryProject, ops: FactoryStorageOps) => Promise<UpdateFactoryProjectInput | null>);\n }): Promise<FactoryProject | null> {\n const update = (ops: FactoryStorageOps) =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { org_id: orgId, id }, async current => {\n const changes = typeof input === 'function' ? await input(toFactoryProject(current), ops) : input;\n if (changes === null) return null;\n return {\n ...(changes.name !== undefined ? { name: changes.name } : {}),\n ...(changes.description !== undefined ? { description: changes.description } : {}),\n ...(changes.defaultModelId !== undefined ? { default_model_id: changes.defaultModelId } : {}),\n ...(changes.modelScope !== undefined ? { model_scope: changes.modelScope } : {}),\n ...(changes.defaultThinkingLevel !== undefined\n ? { default_thinking_level: changes.defaultThinkingLevel }\n : {}),\n ...(changes.autoRunEnabled !== undefined ? { auto_run_enabled: changes.autoRunEnabled } : {}),\n ...(changes.autoApprovePlans !== undefined ? { auto_approve_plans: changes.autoApprovePlans } : {}),\n ...(changes.sandboxWorkingDirectory !== undefined\n ? { sandbox_working_directory: changes.sandboxWorkingDirectory }\n : {}),\n ...(changes.sandboxSettings !== undefined ? { sandbox_settings: changes.sandboxSettings } : {}),\n ...(changes.workspaceSetupCommand !== undefined\n ? { workspace_setup_command: changes.workspaceSetupCommand }\n : {}),\n ...(changes.activeTemplateId !== undefined ? { active_template_id: changes.activeTemplateId } : {}),\n ...(changes.activeTemplateHeads !== undefined ? { active_template_heads: changes.activeTemplateHeads } : {}),\n ...(changes.lastBuildId !== undefined ? { last_build_id: changes.lastBuildId } : {}),\n ...(changes.lastBuildHeads !== undefined ? { last_build_heads: changes.lastBuildHeads } : {}),\n ...(changes.lastBuildInputs !== undefined ? { last_build_inputs: changes.lastBuildInputs } : {}),\n ...(changes.lastBuildAttemptedAt !== undefined\n ? { last_build_attempted_at: changes.lastBuildAttemptedAt }\n : {}),\n ...(changes.buildOnPushEnabled !== undefined ? { build_on_push_enabled: changes.buildOnPushEnabled } : {}),\n ...(changes.buildPushDebounceMinutes !== undefined\n ? { build_push_debounce_minutes: changes.buildPushDebounceMinutes }\n : {}),\n ...(changes.reviewTestChecksEnabled !== undefined\n ? { review_test_checks_enabled: changes.reviewTestChecksEnabled }\n : {}),\n ...(changes.selfReviewEnabled !== undefined ? { self_review_enabled: changes.selfReviewEnabled } : {}),\n updated_at: new Date(),\n };\n });\n // LibSQL's standalone updateAtomic only serializes one client; a transaction\n // also protects a read-dependent update against other database connections.\n const row = typeof input === 'function' ? await this.storage.withTransaction(update) : await update(this.#db);\n return row ? toFactoryProject(row) : null;\n }\n\n /**\n * Leading-edge debounce for push-triggered builds: stamps\n * `last_push_build_at = now` and resolves true when no push started a build\n * within the last `debounceMinutes`, else writes nothing and resolves\n * false. Taken right before the provider start, so a push that skips\n * (another run starting, a build in progress) never closes the window. The read and the write run in one transaction, so two concurrent\n * webhooks never both win, even on different database connections.\n */\n async claimBuildAttempt({\n id,\n debounceMinutes,\n now = new Date(),\n }: {\n id: string;\n debounceMinutes: number;\n now?: Date;\n }): Promise<boolean> {\n let claimed = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n const last = current.last_push_build_at;\n if (last && now.getTime() - last.getTime() < debounceMinutes * 60_000) return null;\n claimed = true;\n return { last_push_build_at: now, updated_at: now };\n }),\n );\n return claimed;\n }\n\n /**\n * Takes the project's build-start lease for `token`. Every trigger (manual,\n * schedule, push) takes it before reading build state and holds it through\n * `builds.start` and recording the result, so two runs never both start a\n * build. `claimed` is false while another run holds an unexpired lease. The\n * holder renews the lease while it works (`renewBuildStart`), so a lease\n * only expires when its run died or stalled. Taking over an expired lease\n * marks the start uncertain (that run's build may have reached the provider\n * unrecorded), and the mark stays until a release resolves it; `uncertain`\n * reports it. Only the claim is transactional; provider calls run outside it.\n */\n async claimBuildStart({\n id,\n token,\n leaseMs,\n now = new Date(),\n }: {\n id: string;\n token: string;\n leaseMs: number;\n now?: Date;\n }): Promise<{ claimed: boolean; uncertain: boolean }> {\n const result = { claimed: false, uncertain: false };\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n const expiresAt = current.build_start_claim_expires_at;\n if (current.build_start_claim && expiresAt && expiresAt.getTime() > now.getTime()) return null;\n result.claimed = true;\n result.uncertain = Boolean(current.build_start_claim) || current.build_start_uncertain === true;\n return {\n build_start_claim: token,\n build_start_claim_expires_at: new Date(now.getTime() + leaseMs),\n ...(result.uncertain ? { build_start_uncertain: true } : {}),\n updated_at: now,\n };\n }),\n );\n return result;\n }\n\n /**\n * Extends the build-start lease held by `token` to `now + leaseMs`. Resolves\n * false, writing nothing, when `token` no longer holds it.\n */\n async renewBuildStart({\n id,\n token,\n leaseMs,\n now = new Date(),\n }: {\n id: string;\n token: string;\n leaseMs: number;\n now?: Date;\n }): Promise<boolean> {\n let renewed = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n if (current.build_start_claim !== token) return null;\n renewed = true;\n return { build_start_claim_expires_at: new Date(now.getTime() + leaseMs), updated_at: now };\n }),\n );\n return renewed;\n }\n\n /**\n * Releases the build-start lease taken with `token` and writes `changes` in\n * the same transaction, but only while `token` still holds the lease. A run\n * whose lease expired and was taken by a newer run writes nothing, so it\n * never clears or overwrites that run's build. `resolved` clears an\n * uncertain start. Resolves false when skipped.\n */\n async releaseBuildStart({\n id,\n token,\n changes = {},\n resolved = false,\n now = new Date(),\n }: {\n id: string;\n token: string;\n resolved?: boolean;\n changes?: Pick<\n UpdateFactoryProjectInput,\n 'lastBuildId' | 'lastBuildHeads' | 'lastBuildInputs' | 'lastBuildAttemptedAt'\n >;\n now?: Date;\n }): Promise<boolean> {\n let released = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n if (current.build_start_claim !== token) return null;\n released = true;\n return {\n ...(changes.lastBuildId !== undefined ? { last_build_id: changes.lastBuildId } : {}),\n ...(changes.lastBuildHeads !== undefined ? { last_build_heads: changes.lastBuildHeads } : {}),\n ...(changes.lastBuildInputs !== undefined ? { last_build_inputs: changes.lastBuildInputs } : {}),\n ...(changes.lastBuildAttemptedAt !== undefined\n ? { last_build_attempted_at: changes.lastBuildAttemptedAt }\n : {}),\n ...(resolved ? { build_start_uncertain: false } : {}),\n build_start_claim: null,\n build_start_claim_expires_at: null,\n updated_at: now,\n };\n }),\n );\n return released;\n }\n\n /**\n * Pins the active template to a finished build, but only while that build is\n * still the project's last one. Overlapping runs (Build now during a push\n * build) can finish out of order; the write and the `last_build_id` check\n * run in one transaction so a stale run never overwrites a newer build's\n * pin. Resolves false when the pin was skipped.\n */\n async pinActiveTemplate({\n id,\n buildId,\n templateId,\n heads,\n now = new Date(),\n }: {\n id: string;\n buildId: string;\n templateId: string;\n heads: Record<string, string>;\n now?: Date;\n }): Promise<boolean> {\n let pinned = false;\n await this.storage.withTransaction(ops =>\n ops.updateAtomic<FactoryProjectDbRow>('factory_projects', { id }, current => {\n if (current.last_build_id !== buildId) return null;\n pinned = true;\n return { active_template_id: templateId, active_template_heads: heads, updated_at: now };\n }),\n );\n return pinned;\n }\n\n async delete({ orgId, id }: { orgId: string; id: string }): Promise<FactoryProject | null> {\n const project = await this.get({ orgId, id });\n if (!project) return null;\n const deleted = await this.#db.deleteMany('factory_projects', { org_id: orgId, id });\n return deleted > 0 ? project : null;\n }\n}\n"],"mappings":"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"}
|