@mastra/factory 0.25.0-alpha.8 → 0.26.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/factory.d.ts +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +27 -3
- package/dist/factory.js.map +1 -1
- package/dist/integrations/slack/integration.d.ts +7 -8
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +11 -3
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/run-error-card.d.ts.map +1 -1
- package/dist/integrations/slack/run-error-card.js +25 -8
- package/dist/integrations/slack/run-error-card.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +3 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/config.d.ts +4 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +153 -56
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/provider-credentials.d.ts +6 -1
- package/dist/routes/provider-credentials.d.ts.map +1 -1
- package/dist/routes/provider-credentials.js +2 -2
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/run-error.d.ts +1 -1
- package/dist/run-error.d.ts.map +1 -1
- package/dist/run-error.js +16 -11
- package/dist/run-error.js.map +1 -1
- package/dist/session/reply-credential-processor.d.ts +8 -0
- package/dist/session/reply-credential-processor.d.ts.map +1 -1
- package/dist/session/reply-credential-processor.js +20 -11
- package/dist/session/reply-credential-processor.js.map +1 -1
- package/dist/storage/domains/audit/domain.d.ts.map +1 -1
- package/dist/storage/domains/audit/domain.js +10 -3
- package/dist/storage/domains/audit/domain.js.map +1 -1
- package/package.json +15 -15
package/dist/run-error.js
CHANGED
|
@@ -29,7 +29,7 @@ function explainRunError(error) {
|
|
|
29
29
|
case "context_overflow": return {
|
|
30
30
|
title: "Conversation exceeds the model’s limit",
|
|
31
31
|
message: "Choose a model with a larger context window or start a new session with a shorter prompt.",
|
|
32
|
-
recoveries: ["model"]
|
|
32
|
+
recoveries: ["model", "new_session"]
|
|
33
33
|
};
|
|
34
34
|
case "model_rejected": return {
|
|
35
35
|
title: "Model not available",
|
|
@@ -38,28 +38,32 @@ function explainRunError(error) {
|
|
|
38
38
|
};
|
|
39
39
|
case "tool_history_rejected": return {
|
|
40
40
|
title: "Conversation history rejected",
|
|
41
|
-
message: "The provider could not match a tool call with its result.
|
|
42
|
-
recoveries: []
|
|
41
|
+
message: "The provider could not match a tool call with its result. Start a new session to keep working, and copy the error details so this session can be investigated.",
|
|
42
|
+
recoveries: ["new_session"]
|
|
43
43
|
};
|
|
44
44
|
case "invalid_request": return {
|
|
45
45
|
title: "Model request rejected",
|
|
46
46
|
message: providerErrorReason(error) ?? NO_REASON_AVAILABLE,
|
|
47
|
-
recoveries: []
|
|
47
|
+
recoveries: ["model"]
|
|
48
48
|
};
|
|
49
49
|
case "timeout": return {
|
|
50
50
|
title: "Request timed out",
|
|
51
51
|
message: "The request took too long to complete. Send a message to continue the run.",
|
|
52
|
-
recoveries: []
|
|
52
|
+
recoveries: ["continue"]
|
|
53
53
|
};
|
|
54
54
|
case "rate_limited": return {
|
|
55
55
|
title: "Provider rate limit reached",
|
|
56
|
-
message: "
|
|
57
|
-
recoveries: [
|
|
56
|
+
message: "The provider account behind this session’s credential hit a rate or usage limit. Continue once it resets, or use another credential or model.",
|
|
57
|
+
recoveries: [
|
|
58
|
+
"continue",
|
|
59
|
+
"credentials",
|
|
60
|
+
"model"
|
|
61
|
+
]
|
|
58
62
|
};
|
|
59
63
|
case "unavailable": return {
|
|
60
64
|
title: "Model provider temporarily unavailable",
|
|
61
|
-
message: "The provider could not complete the request. Send a message to continue the run.",
|
|
62
|
-
recoveries: []
|
|
65
|
+
message: "The provider could not complete the request. Send a message to continue the run, or choose another model.",
|
|
66
|
+
recoveries: ["continue", "model"]
|
|
63
67
|
};
|
|
64
68
|
case "unknown": return describeUnclassifiedError(error);
|
|
65
69
|
}
|
|
@@ -70,10 +74,11 @@ function afterProviderReason(error, next, fallback) {
|
|
|
70
74
|
}
|
|
71
75
|
function describeUnclassifiedError(error) {
|
|
72
76
|
const message = providerErrorReason(error) ?? (error.message || UNEXPECTED_ERROR);
|
|
77
|
+
const providerCall = PROVIDER_CALL_ERROR_NAME.test(error.name);
|
|
73
78
|
return {
|
|
74
|
-
title:
|
|
79
|
+
title: providerCall ? "Model request failed" : "Run stopped",
|
|
75
80
|
message,
|
|
76
|
-
recoveries: []
|
|
81
|
+
recoveries: providerCall ? ["model"] : []
|
|
77
82
|
};
|
|
78
83
|
}
|
|
79
84
|
export { describeRunError };
|
package/dist/run-error.js.map
CHANGED
|
@@ -1 +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.
|
|
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 = 'continue' | 'credentials' | 'model' | 'new_session';\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', 'new_session'],\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. Start a new session to keep working, and copy the error details so this session can be investigated.',\n recoveries: ['new_session'],\n };\n case 'invalid_request':\n return {\n title: 'Model request rejected',\n message: providerErrorReason(error) ?? NO_REASON_AVAILABLE,\n recoveries: ['model'],\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: ['continue'],\n };\n case 'rate_limited':\n return {\n title: 'Provider rate limit reached',\n message:\n 'The provider account behind this session’s credential hit a rate or usage limit. Continue once it resets, or use another credential or model.',\n recoveries: ['continue', 'credentials', 'model'],\n };\n case 'unavailable':\n return {\n title: 'Model provider temporarily unavailable',\n message:\n 'The provider could not complete the request. Send a message to continue the run, or choose another model.',\n recoveries: ['continue', 'model'],\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 const providerCall = PROVIDER_CALL_ERROR_NAME.test(error.name);\n return {\n title: providerCall ? 'Model request failed' : 'Run stopped',\n message,\n recoveries: providerCall ? ['model'] : [],\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,SAAS,aAAa;EACrC;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,aAAa;EAC5B;EACF,KAAK,mBACH,OAAO;GACL,OAAO;GACP,SAAS,oBAAoB,KAAK,KAAK;GACvC,YAAY,CAAC,OAAO;EACtB;EACF,KAAK,WACH,OAAO;GACL,OAAO;GACP,SAAS;GACT,YAAY,CAAC,UAAU;EACzB;EACF,KAAK,gBACH,OAAO;GACL,OAAO;GACP,SACE;GACF,YAAY;IAAC;IAAY;IAAe;GAAO;EACjD;EACF,KAAK,eACH,OAAO;GACL,OAAO;GACP,SACE;GACF,YAAY,CAAC,YAAY,OAAO;EAClC;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,MAAM,eAAe,yBAAyB,KAAK,MAAM,IAAI;CAC7D,OAAO;EACL,OAAO,eAAe,yBAAyB;EAC/C;EACA,YAAY,eAAe,CAAC,OAAO,IAAI,CAAC;CAC1C;AACF"}
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';
|
|
2
|
+
import type { RequestContext } from '@mastra/core/request-context';
|
|
3
|
+
/** A personal credential carries its owner's id; an organization credential has none. */
|
|
4
|
+
export interface RunCredential {
|
|
5
|
+
label: string;
|
|
6
|
+
ownerId?: string;
|
|
7
|
+
}
|
|
8
|
+
/** The credential this request's calls to `provider` (`anthropic`, `openai.responses`) run on. */
|
|
9
|
+
export declare function runCredential(requestContext: RequestContext | undefined, provider: string): RunCredential | undefined;
|
|
2
10
|
/**
|
|
3
11
|
* Stamps each assistant reply with the credential that answered it, as
|
|
4
12
|
* `content.metadata.credentialLabel` ("Claude Pro/Max", "Anthropic API key")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reply-credential-processor.d.ts","sourceRoot":"","sources":["../../src/session/reply-credential-processor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"reply-credential-processor.d.ts","sourceRoot":"","sources":["../../src/session/reply-credential-processor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAInE,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,cAAc,EAAE,cAAc,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAUrH;AAED;;;;;;;GAOG;AACH,qBAAa,+BAAgC,YAAW,SAAS;IAC/D,QAAQ,CAAC,EAAE,8BAA8B;IACzC,QAAQ,CAAC,IAAI,8BAA8B;IAE3C,iBAAiB,CAAC,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE,qBAAqB;CAYzE"}
|
|
@@ -2,6 +2,21 @@ import { credentialLabel, getAuthProviderId } from "../routes/provider-credentia
|
|
|
2
2
|
import { resolveCredentialStore, resolveTenantFromRequestContext } from "@mastra/code-sdk/agents/credential-resolver";
|
|
3
3
|
import { normalizeLegacyProviderId } from "@mastra/code-sdk/providers/model-ids";
|
|
4
4
|
// src/session/reply-credential-processor.ts
|
|
5
|
+
/** The credential this request's calls to `provider` (`anthropic`, `openai.responses`) run on. */
|
|
6
|
+
function runCredential(requestContext, provider) {
|
|
7
|
+
const tenant = resolveTenantFromRequestContext(requestContext);
|
|
8
|
+
const store = resolveCredentialStore(requestContext);
|
|
9
|
+
if (!tenant || !store) return void 0;
|
|
10
|
+
const [sdkProviderRoot = provider] = provider.split(".");
|
|
11
|
+
const authProvider = normalizeLegacyProviderId(sdkProviderRoot);
|
|
12
|
+
const credential = store.get(getAuthProviderId(authProvider));
|
|
13
|
+
const label = credential && credentialLabel(authProvider, credential.type);
|
|
14
|
+
if (!label) return void 0;
|
|
15
|
+
return tenant.credentialScope === "user" ? {
|
|
16
|
+
label,
|
|
17
|
+
ownerId: tenant.userId
|
|
18
|
+
} : { label };
|
|
19
|
+
}
|
|
5
20
|
/**
|
|
6
21
|
* Stamps each assistant reply with the credential that answered it, as
|
|
7
22
|
* `content.metadata.credentialLabel` ("Claude Pro/Max", "Anthropic API key")
|
|
@@ -14,24 +29,18 @@ var FactoryReplyCredentialProcessor = class {
|
|
|
14
29
|
id = "factory-reply-credential";
|
|
15
30
|
name = "Factory reply credential";
|
|
16
31
|
processOutputStep({ messageList, requestContext }) {
|
|
17
|
-
const tenant = resolveTenantFromRequestContext(requestContext);
|
|
18
|
-
const store = resolveCredentialStore(requestContext);
|
|
19
|
-
if (!tenant || !store) return messageList;
|
|
20
32
|
for (const message of messageList.get.response.db()) {
|
|
21
33
|
const metadata = message.content.metadata;
|
|
22
34
|
if (message.role !== "assistant" || !metadata || typeof metadata.provider !== "string") continue;
|
|
23
35
|
if (metadata.credentialLabel !== void 0) continue;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (!label) continue;
|
|
29
|
-
metadata.credentialLabel = label;
|
|
30
|
-
if (tenant.credentialScope === "user") metadata.credentialOwnerId = tenant.userId;
|
|
36
|
+
const credential = runCredential(requestContext, metadata.provider);
|
|
37
|
+
if (!credential) continue;
|
|
38
|
+
metadata.credentialLabel = credential.label;
|
|
39
|
+
if (credential.ownerId) metadata.credentialOwnerId = credential.ownerId;
|
|
31
40
|
}
|
|
32
41
|
return messageList;
|
|
33
42
|
}
|
|
34
43
|
};
|
|
35
|
-
export { FactoryReplyCredentialProcessor };
|
|
44
|
+
export { FactoryReplyCredentialProcessor, runCredential };
|
|
36
45
|
|
|
37
46
|
//# sourceMappingURL=reply-credential-processor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reply-credential-processor.js","names":[],"sources":["../../src/session/reply-credential-processor.ts"],"sourcesContent":["import { resolveCredentialStore, resolveTenantFromRequestContext } from '@mastra/code-sdk/agents/credential-resolver';\nimport { normalizeLegacyProviderId } from '@mastra/code-sdk/providers/model-ids';\nimport type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';\n\nimport { credentialLabel, getAuthProviderId } from '../routes/provider-credentials.js';\n\n/**\n * Stamps each assistant reply with the credential that answered it, as\n * `content.metadata.credentialLabel` (\"Claude Pro/Max\", \"Anthropic API key\")\n * and, for a personal credential, `content.metadata.credentialOwnerId`. A\n * reply without an owner id was answered by the organization's credential.\n * Stamped at send time so a reply keeps its label after the thread's\n * credential scope changes.\n */\nexport class FactoryReplyCredentialProcessor implements Processor {\n readonly id = 'factory-reply-credential';\n readonly name = 'Factory reply credential';\n\n processOutputStep({ messageList, requestContext }: ProcessOutputStepArgs) {\n
|
|
1
|
+
{"version":3,"file":"reply-credential-processor.js","names":[],"sources":["../../src/session/reply-credential-processor.ts"],"sourcesContent":["import { resolveCredentialStore, resolveTenantFromRequestContext } from '@mastra/code-sdk/agents/credential-resolver';\nimport { normalizeLegacyProviderId } from '@mastra/code-sdk/providers/model-ids';\nimport type { Processor, ProcessOutputStepArgs } from '@mastra/core/processors';\nimport type { RequestContext } from '@mastra/core/request-context';\n\nimport { credentialLabel, getAuthProviderId } from '../routes/provider-credentials.js';\n\n/** A personal credential carries its owner's id; an organization credential has none. */\nexport interface RunCredential {\n label: string;\n ownerId?: string;\n}\n\n/** The credential this request's calls to `provider` (`anthropic`, `openai.responses`) run on. */\nexport function runCredential(requestContext: RequestContext | undefined, provider: string): RunCredential | undefined {\n const tenant = resolveTenantFromRequestContext(requestContext);\n const store = resolveCredentialStore(requestContext);\n if (!tenant || !store) return undefined;\n const [sdkProviderRoot = provider] = provider.split('.');\n const authProvider = normalizeLegacyProviderId(sdkProviderRoot);\n const credential = store.get(getAuthProviderId(authProvider));\n const label = credential && credentialLabel(authProvider, credential.type);\n if (!label) return undefined;\n return tenant.credentialScope === 'user' ? { label, ownerId: tenant.userId } : { label };\n}\n\n/**\n * Stamps each assistant reply with the credential that answered it, as\n * `content.metadata.credentialLabel` (\"Claude Pro/Max\", \"Anthropic API key\")\n * and, for a personal credential, `content.metadata.credentialOwnerId`. A\n * reply without an owner id was answered by the organization's credential.\n * Stamped at send time so a reply keeps its label after the thread's\n * credential scope changes.\n */\nexport class FactoryReplyCredentialProcessor implements Processor {\n readonly id = 'factory-reply-credential';\n readonly name = 'Factory reply credential';\n\n processOutputStep({ messageList, requestContext }: ProcessOutputStepArgs) {\n for (const message of messageList.get.response.db()) {\n const metadata = message.content.metadata;\n if (message.role !== 'assistant' || !metadata || typeof metadata.provider !== 'string') continue;\n if (metadata.credentialLabel !== undefined) continue;\n const credential = runCredential(requestContext, metadata.provider);\n if (!credential) continue;\n metadata.credentialLabel = credential.label;\n if (credential.ownerId) metadata.credentialOwnerId = credential.ownerId;\n }\n return messageList;\n }\n}\n"],"mappings":";;;;;AAcA,SAAgB,cAAc,gBAA4C,UAA6C;CACrH,MAAM,SAAS,gCAAgC,cAAc;CAC7D,MAAM,QAAQ,uBAAuB,cAAc;CACnD,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO,KAAA;CAC9B,MAAM,CAAC,kBAAkB,YAAY,SAAS,MAAM,GAAG;CACvD,MAAM,eAAe,0BAA0B,eAAe;CAC9D,MAAM,aAAa,MAAM,IAAI,kBAAkB,YAAY,CAAC;CAC5D,MAAM,QAAQ,cAAc,gBAAgB,cAAc,WAAW,IAAI;CACzE,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,OAAO,OAAO,oBAAoB,SAAS;EAAE;EAAO,SAAS,OAAO;CAAO,IAAI,EAAE,MAAM;AACzF;;;;;;;;;AAUA,IAAa,kCAAb,MAAkE;CAChE,KAAc;CACd,OAAgB;CAEhB,kBAAkB,EAAE,aAAa,kBAAyC;EACxE,KAAK,MAAM,WAAW,YAAY,IAAI,SAAS,GAAG,GAAG;GACnD,MAAM,WAAW,QAAQ,QAAQ;GACjC,IAAI,QAAQ,SAAS,eAAe,CAAC,YAAY,OAAO,SAAS,aAAa,UAAU;GACxF,IAAI,SAAS,oBAAoB,KAAA,GAAW;GAC5C,MAAM,aAAa,cAAc,gBAAgB,SAAS,QAAQ;GAClE,IAAI,CAAC,YAAY;GACjB,SAAS,kBAAkB,WAAW;GACtC,IAAI,WAAW,SAAS,SAAS,oBAAoB,WAAW;EAClE;EACA,OAAO;CACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/domain.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAGpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAElE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,OAAO,KAAK,EACV,sBAAsB,EACtB,YAAY,EACZ,cAAc,EACd,aAAa,EACb,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,WAAW,CAAC;AAInB,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,mBAAmB,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChG;AAED,kHAAkH;AAClH,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC;AAExD,oGAAoG;AACpG,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,aAAa,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAmBD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,mCAAmC;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,mEAAmE;IACnE,QAAQ,EAAE,sBAAsB,CAAC;IACjC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,UAAU,CAAC,CAAC;IACpD,2EAA2E;IAC3E,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;IACpB,mFAAmF;IACnF,WAAW,CAAC,EAAE,CAAC,cAAc,EAAE,cAAc,KAAK;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACnG;
|
|
1
|
+
{"version":3,"file":"domain.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/domain.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getFactoryAuthUser } from "../../../auth.js";
|
|
2
2
|
import { ACTOR_PROFILE_METADATA_KEY, auditActorProfile } from "./base.js";
|
|
3
|
-
import { auditActionsInNamespaces, isAuditNamespace } from "./actions.js";
|
|
3
|
+
import { auditActionsInNamespaces, isAuditAction, isAuditNamespace } from "./actions.js";
|
|
4
4
|
import { isHumanActorId } from "./actors.js";
|
|
5
5
|
import { toWireAuditEvent } from "./wire.js";
|
|
6
6
|
import { registerApiRoute } from "@mastra/core/server";
|
|
@@ -28,6 +28,10 @@ function actionsInRequestedNamespaces(raw) {
|
|
|
28
28
|
if (!raw) return void 0;
|
|
29
29
|
return auditActionsInNamespaces(raw.split(",").map((namespace) => namespace.trim()).filter(isAuditNamespace));
|
|
30
30
|
}
|
|
31
|
+
function requestedActions(raw) {
|
|
32
|
+
if (!raw) return void 0;
|
|
33
|
+
return raw.split(",").map((action) => action.trim()).filter(isAuditAction);
|
|
34
|
+
}
|
|
31
35
|
function parseActorIdsParam(raw) {
|
|
32
36
|
if (!raw) return [];
|
|
33
37
|
return [...new Set(raw.split(",").map((actorId) => actorId.trim()).filter(isHumanActorId))].slice(0, MAX_ACTOR_PROFILES);
|
|
@@ -208,8 +212,11 @@ var AuditDomain = class {
|
|
|
208
212
|
orgId: tenant.orgId,
|
|
209
213
|
id: projectId
|
|
210
214
|
})) return c.json({ error: "Project not found" }, 404);
|
|
211
|
-
const
|
|
212
|
-
if (
|
|
215
|
+
const inNamespaces = actionsInRequestedNamespaces(c.req.query("namespaces"));
|
|
216
|
+
if (inNamespaces?.length === 0) return c.json({ error: "unknown_namespaces" }, 400);
|
|
217
|
+
const named = requestedActions(c.req.query("actions"));
|
|
218
|
+
const actions = named && inNamespaces ? named.filter((action) => inNamespaces.includes(action)) : named ?? inNamespaces;
|
|
219
|
+
if (actions?.length === 0) return c.json({ error: "unknown_actions" }, 400);
|
|
213
220
|
const page = await this.list({
|
|
214
221
|
orgId: tenant.orgId,
|
|
215
222
|
factoryProjectId: projectId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.js","names":[],"sources":["../../../../src/storage/domains/audit/domain.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute, IUserProvider } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport { getFactoryAuthUser } from '../../../auth.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../projects/base.js';\nimport { auditActionsInNamespaces, isAuditNamespace } from './actions.js';\nimport type { AuditAction } from './actions.js';\nimport { isHumanActorId } from './actors.js';\nimport { ACTOR_PROFILE_METADATA_KEY, auditActorProfile } from './base.js';\nimport type {\n AuditActorProfileInput,\n AuditContext,\n AuditEventPage,\n AuditEventRow,\n AuditStorage,\n AuditTarget,\n ListAuditEventsInput,\n RecordAuditEventInput,\n} from './base.js';\nimport { toWireAuditEvent } from './wire.js';\nimport type { WireAuditActor, WireAuditPage } from './wire.js';\n\nexport interface EmitAuditInput {\n action: AuditAction;\n factoryProjectId?: string;\n projectRepositoryId?: string;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface EmitAgentAuditInput {\n action: AuditAction;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface AuditEmitter {\n emit(args: { context: Context; input: EmitAuditInput }): Promise<void>;\n}\n\nexport interface AuditAgentEmitter {\n emitAgent(args: { requestContext: RequestContext; input: EmitAgentAuditInput }): Promise<void>;\n}\n\n/** Records with an explicit actor, for the paths that have no request: rule transitions, run starts, run ends. */\nexport type AuditRecorder = Pick<AuditDomain, 'record'>;\n\n/** Best-effort destination for locally persisted audit events (e.g. an integration's audit log). */\nexport interface AuditSink {\n id: string;\n audit?(args: { event: AuditEventRow }): Promise<void>;\n}\n\ninterface FactorySessionState {\n factoryProjectId?: string;\n projectRepositoryId?: string;\n}\n\nfunction readStoredActorProfile(metadata: Record<string, unknown> | undefined): AuditActorProfileInput | undefined {\n if (!metadata) return undefined;\n const raw = metadata[ACTOR_PROFILE_METADATA_KEY];\n if (!raw || typeof raw !== 'object') return undefined;\n const profile = raw as Record<string, unknown>;\n const name = typeof profile.name === 'string' && profile.name.trim() ? profile.name.trim() : undefined;\n const avatarUrl =\n typeof profile.avatarUrl === 'string' && profile.avatarUrl.trim() ? profile.avatarUrl.trim() : undefined;\n if (!name && !avatarUrl) return undefined;\n return { ...(name ? { name } : {}), ...(avatarUrl ? { avatarUrl } : {}) };\n}\n\nexport interface AuditDomainOptions {\n auth: RouteAuth;\n /** Audit storage domain handle. */\n audit: AuditStorage;\n /** Projects domain handle, used to scope the audit trail route. */\n projects: FactoryProjectsStorage;\n /** Resolve persisted human actor ids to display names and profile images. */\n users?: Pick<IUserProvider, 'getUser' | 'getUsers'>;\n /** Best-effort fan-out destinations notified after each recorded event. */\n sinks?: AuditSink[];\n /** Resolve the acting tenant for agent-emitted events from the request context. */\n agentTenant?: (requestContext: RequestContext) => { orgId?: string; userId?: string } | undefined;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_ACTOR_PROFILES = 100;\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\n/** The actions a `namespaces=` query names: none when it names only unknown ones, `undefined` when it names nothing. */\nfunction actionsInRequestedNamespaces(raw: string | undefined): string[] | undefined {\n if (!raw) return undefined;\n return auditActionsInNamespaces(\n raw\n .split(',')\n .map(namespace => namespace.trim())\n .filter(isAuditNamespace),\n );\n}\n\nfunction parseActorIdsParam(raw: string | undefined): string[] {\n if (!raw) return [];\n return [\n ...new Set(\n raw\n .split(',')\n .map(actorId => actorId.trim())\n .filter(isHumanActorId),\n ),\n ].slice(0, MAX_ACTOR_PROFILES);\n}\n\nfunction parseLimitParam(raw: string | undefined): number | undefined {\n if (!raw) return undefined;\n const limit = Number.parseInt(raw, 10);\n return Number.isFinite(limit) ? limit : undefined;\n}\n\nexport function auditRequestContext(c: Context): AuditContext {\n const location = c.req.header('x-forwarded-for')?.split(',')[0]?.trim();\n const userAgent = c.req.header('user-agent');\n return {\n ...(location ? { location } : {}),\n ...(userAgent ? { userAgent } : {}),\n };\n}\n\n/** Who a browser request is and where it came from: the pair every funnel row carries. */\nexport function auditRequestOrigin(c: Context): {\n actorProfile: AuditActorProfileInput | undefined;\n context: AuditContext;\n} {\n return { actorProfile: auditActorProfile(getFactoryAuthUser(c)), context: auditRequestContext(c) };\n}\n\n/** Factory-owned audit behavior backed by the audit storage domain. */\nexport class AuditDomain implements AuditEmitter, AuditAgentEmitter {\n readonly #auth: RouteAuth;\n readonly #audit: AuditStorage;\n readonly #projects: FactoryProjectsStorage;\n readonly #users: AuditDomainOptions['users'];\n readonly #sinks: AuditSink[];\n readonly #agentTenant: AuditDomainOptions['agentTenant'];\n\n constructor({ auth, audit, projects, users, sinks = [], agentTenant }: AuditDomainOptions) {\n this.#auth = auth;\n this.#audit = audit;\n this.#projects = projects;\n this.#users = users;\n this.#sinks = sinks;\n this.#agentTenant = agentTenant;\n\n const ids = new Set<string>();\n for (const sink of this.#sinks) {\n if (!sink.id) throw new Error('Audit integration id must not be empty');\n if (ids.has(sink.id)) throw new Error(`Duplicate audit integration id '${sink.id}'`);\n ids.add(sink.id);\n }\n }\n\n async record(input: RecordAuditEventInput<AuditAction>): Promise<AuditEventRow | null> {\n try {\n await this.#audit.ensureReady();\n const { event: row, created } = input.idempotencyKey\n ? await this.#audit.recordOnce(input)\n : { event: await this.#audit.record(input), created: true };\n if (!created) return row;\n for (const sink of this.#sinks) {\n if (!sink.audit) continue;\n void Promise.resolve()\n .then(() => sink.audit?.({ event: row }))\n .catch(err => {\n console.warn('[Audit] Audit integration failed', {\n integration: sink.id,\n action: row.action,\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n return row;\n } catch (err) {\n console.warn('[Audit] Failed to record audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n return null;\n }\n }\n\n async list(input: ListAuditEventsInput): Promise<AuditEventPage> {\n await this.#audit.ensureReady();\n return this.#audit.list(input);\n }\n\n async emit({ context, input }: { context: Context; input: EmitAuditInput }): Promise<void> {\n try {\n const tenant = this.#auth.tenant(context);\n if (!tenant?.orgId) return;\n await this.record({\n orgId: tenant.orgId,\n actorId: tenant.userId,\n actorProfile: auditActorProfile(getFactoryAuthUser(context)),\n action: input.action,\n targets: input.targets,\n metadata: input.metadata,\n factoryProjectId: input.factoryProjectId,\n projectRepositoryId: input.projectRepositoryId,\n context: auditRequestContext(context),\n });\n } catch (err) {\n console.warn('[Audit] Failed to emit audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n async emitAgent({\n requestContext,\n input,\n }: {\n requestContext: RequestContext;\n input: EmitAgentAuditInput;\n }): Promise<void> {\n try {\n const context = requestContext.get('controller') as\n | AgentControllerRequestContext<FactorySessionState>\n | undefined;\n const tenant = this.#agentTenant?.(requestContext);\n const orgId = tenant?.orgId;\n const userId = tenant?.userId;\n const threadId = context?.threadId;\n const state = context?.getState();\n if (!orgId || !userId || !threadId || !state?.factoryProjectId) return;\n\n const modelId = context.session.modelId?.trim();\n await this.record({\n orgId,\n actorId: `agent:${threadId}`,\n actorType: 'agent',\n action: input.action,\n targets: input.targets,\n metadata: {\n ...input.metadata,\n startedBy: userId,\n ...(modelId ? { modelId } : {}),\n },\n factoryProjectId: state.factoryProjectId,\n projectRepositoryId: state.projectRepositoryId,\n context: {},\n });\n } catch (err) {\n console.warn('[Audit] Failed to emit agent audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n async #resolveActorProfiles(\n events: AuditEventRow[],\n requestedActorIds: string[] = [],\n ): Promise<Record<string, WireAuditActor>> {\n const humanActorIds = [\n ...new Set(\n [\n ...requestedActorIds,\n ...events.filter(event => event.actorType === 'human').map(event => event.actorId),\n ].filter(isHumanActorId),\n ),\n ].slice(0, MAX_ACTOR_PROFILES);\n if (humanActorIds.length === 0) return {};\n\n // Prefer the profile stamped into event metadata at record time: it works\n // regardless of the auth provider's ability to resolve a user by id\n // (MastraAuthStudio's getUser() always returns null, WorkOS looks up by\n // id but every provider stamps here uniformly).\n const profiles: Record<string, WireAuditActor> = {};\n for (const event of events) {\n if (event.actorType !== 'human') continue;\n if (!isHumanActorId(event.actorId)) continue;\n if (profiles[event.actorId]) continue;\n const stored = readStoredActorProfile(event.metadata);\n if (!stored?.name) continue;\n profiles[event.actorId] = {\n id: event.actorId,\n name: stored.name,\n ...(stored.avatarUrl ? { avatarUrl: stored.avatarUrl } : {}),\n };\n }\n\n const unresolved = humanActorIds.filter(actorId => !profiles[actorId]);\n if (unresolved.length === 0 || !this.#users) return profiles;\n\n try {\n const users = this.#users.getUsers\n ? await this.#users.getUsers(unresolved)\n : await Promise.all(unresolved.map(actorId => this.#users?.getUser(actorId) ?? null));\n for (const [index, user] of users.entries()) {\n if (!user) continue;\n const name = user.name?.trim() || user.email?.trim() || user.id;\n const actorId = unresolved[index] ?? user.id;\n profiles[actorId] = {\n id: user.id,\n name,\n ...(user.avatarUrl ? { avatarUrl: user.avatarUrl } : {}),\n };\n }\n } catch (err) {\n console.warn('[Audit] Failed to resolve audit actor profiles', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return profiles;\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects/:id/audit', {\n method: 'GET',\n handler: async cc => {\n const c = loose(cc);\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant.response;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return c.json({ error: 'Project not found' }, 404);\n await this.#projects.ensureReady();\n const project = await this.#projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) return c.json({ error: 'Project not found' }, 404);\n\n const actions = actionsInRequestedNamespaces(c.req.query('namespaces'));\n if (actions?.length === 0) return c.json({ error: 'unknown_namespaces' }, 400);\n\n const page = await this.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n actions,\n actorId: c.req.query('actor') || undefined,\n before: c.req.query('before') || undefined,\n limit: parseLimitParam(c.req.query('limit')),\n });\n const actors = await this.#resolveActorProfiles(page.events, parseActorIdsParam(c.req.query('actorIds')));\n const body: WireAuditPage = {\n events: page.events.map(toWireAuditEvent),\n actors,\n ...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),\n };\n return c.json(body);\n },\n }),\n ];\n }\n\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.#auth.ensureUser(c);\n const tenant = this.#auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json({ error: 'organization_required', message: 'The audit trail requires an organization.' }, 403),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n}\n"],"mappings":";;;;;;;AA8DA,SAAS,uBAAuB,UAAmF;CACjH,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,MAAM,MAAM,SAAS;CACrB,IAAI,CAAC,OAAO,OAAO,QAAQ,UAAU,OAAO,KAAA;CAC5C,MAAM,UAAU;CAChB,MAAM,OAAO,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,KAAK,IAAI,KAAA;CAC7F,MAAM,YACJ,OAAO,QAAQ,cAAc,YAAY,QAAQ,UAAU,KAAK,IAAI,QAAQ,UAAU,KAAK,IAAI,KAAA;CACjG,IAAI,CAAC,QAAQ,CAAC,WAAW,OAAO,KAAA;CAChC,OAAO;EAAE,GAAI,OAAO,EAAE,KAAK,IAAI,CAAC;EAAI,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CAAG;AAC1E;AAgBA,MAAM,UAAU;AAChB,MAAM,qBAAqB;AAE3B,SAAS,MAAM,GAAqB;CAClC,OAAO;AACT;;AAGA,SAAS,6BAA6B,KAA+C;CACnF,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,OAAO,yBACL,IACG,MAAM,GAAG,CAAC,CACV,KAAI,cAAa,UAAU,KAAK,CAAC,CAAC,CAClC,OAAO,gBAAgB,CAC5B;AACF;AAEA,SAAS,mBAAmB,KAAmC;CAC7D,IAAI,CAAC,KAAK,OAAO,CAAC;CAClB,OAAO,CACL,GAAG,IAAI,IACL,IACG,MAAM,GAAG,CAAC,CACV,KAAI,YAAW,QAAQ,KAAK,CAAC,CAAC,CAC9B,OAAO,cAAc,CAC1B,CACF,CAAC,CAAC,MAAM,GAAG,kBAAkB;AAC/B;AAEA,SAAS,gBAAgB,KAA6C;CACpE,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,MAAM,QAAQ,OAAO,SAAS,KAAK,EAAE;CACrC,OAAO,OAAO,SAAS,KAAK,IAAI,QAAQ,KAAA;AAC1C;AAEA,SAAgB,oBAAoB,GAA0B;CAC5D,MAAM,WAAW,EAAE,IAAI,OAAO,iBAAiB,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,EAAE,KAAK;CACtE,MAAM,YAAY,EAAE,IAAI,OAAO,YAAY;CAC3C,OAAO;EACL,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;EAC/B,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;CACnC;AACF;;AAGA,SAAgB,mBAAmB,GAGjC;CACA,OAAO;EAAE,cAAc,kBAAkB,mBAAmB,CAAC,CAAC;EAAG,SAAS,oBAAoB,CAAC;CAAE;AACnG;;AAGA,IAAa,cAAb,MAAoE;CAClE;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,EAAE,MAAM,OAAO,UAAU,OAAO,QAAQ,CAAC,GAAG,eAAmC;EACzF,KAAK,QAAQ;EACb,KAAK,SAAS;EACd,KAAK,YAAY;EACjB,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,eAAe;EAEpB,MAAM,sBAAM,IAAI,IAAY;EAC5B,KAAK,MAAM,QAAQ,KAAK,QAAQ;GAC9B,IAAI,CAAC,KAAK,IAAI,MAAM,IAAI,MAAM,wCAAwC;GACtE,IAAI,IAAI,IAAI,KAAK,EAAE,GAAG,MAAM,IAAI,MAAM,mCAAmC,KAAK,GAAG,EAAE;GACnF,IAAI,IAAI,KAAK,EAAE;EACjB;CACF;CAEA,MAAM,OAAO,OAA0E;EACrF,IAAI;GACF,MAAM,KAAK,OAAO,YAAY;GAC9B,MAAM,EAAE,OAAO,KAAK,YAAY,MAAM,iBAClC,MAAM,KAAK,OAAO,WAAW,KAAK,IAClC;IAAE,OAAO,MAAM,KAAK,OAAO,OAAO,KAAK;IAAG,SAAS;GAAK;GAC5D,IAAI,CAAC,SAAS,OAAO;GACrB,KAAK,MAAM,QAAQ,KAAK,QAAQ;IAC9B,IAAI,CAAC,KAAK,OAAO;IACjB,QAAa,QAAQ,CAAC,CACnB,WAAW,KAAK,QAAQ,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,CACxC,OAAM,QAAO;KACZ,QAAQ,KAAK,oCAAoC;MAC/C,aAAa,KAAK;MAClB,QAAQ,IAAI;MACZ,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;KACxD,CAAC;IACH,CAAC;GACL;GACA,OAAO;EACT,SAAS,KAAK;GACZ,QAAQ,KAAK,wCAAwC;IACnD,QAAQ,MAAM;IACd,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GACxD,CAAC;GACD,OAAO;EACT;CACF;CAEA,MAAM,KAAK,OAAsD;EAC/D,MAAM,KAAK,OAAO,YAAY;EAC9B,OAAO,KAAK,OAAO,KAAK,KAAK;CAC/B;CAEA,MAAM,KAAK,EAAE,SAAS,SAAqE;EACzF,IAAI;GACF,MAAM,SAAS,KAAK,MAAM,OAAO,OAAO;GACxC,IAAI,CAAC,QAAQ,OAAO;GACpB,MAAM,KAAK,OAAO;IAChB,OAAO,OAAO;IACd,SAAS,OAAO;IAChB,cAAc,kBAAkB,mBAAmB,OAAO,CAAC;IAC3D,QAAQ,MAAM;IACd,SAAS,MAAM;IACf,UAAU,MAAM;IAChB,kBAAkB,MAAM;IACxB,qBAAqB,MAAM;IAC3B,SAAS,oBAAoB,OAAO;GACtC,CAAC;EACH,SAAS,KAAK;GACZ,QAAQ,KAAK,sCAAsC;IACjD,QAAQ,MAAM;IACd,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GACxD,CAAC;EACH;CACF;CAEA,MAAM,UAAU,EACd,gBACA,SAIgB;EAChB,IAAI;GACF,MAAM,UAAU,eAAe,IAAI,YAAY;GAG/C,MAAM,SAAS,KAAK,eAAe,cAAc;GACjD,MAAM,QAAQ,QAAQ;GACtB,MAAM,SAAS,QAAQ;GACvB,MAAM,WAAW,SAAS;GAC1B,MAAM,QAAQ,SAAS,SAAS;GAChC,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,kBAAkB;GAEhE,MAAM,UAAU,QAAQ,QAAQ,SAAS,KAAK;GAC9C,MAAM,KAAK,OAAO;IAChB;IACA,SAAS,SAAS;IAClB,WAAW;IACX,QAAQ,MAAM;IACd,SAAS,MAAM;IACf,UAAU;KACR,GAAG,MAAM;KACT,WAAW;KACX,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;IAC/B;IACA,kBAAkB,MAAM;IACxB,qBAAqB,MAAM;IAC3B,SAAS,CAAC;GACZ,CAAC;EACH,SAAS,KAAK;GACZ,QAAQ,KAAK,4CAA4C;IACvD,QAAQ,MAAM;IACd,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;GACxD,CAAC;EACH;CACF;CAEA,MAAM,sBACJ,QACA,oBAA8B,CAAC,GACU;EACzC,MAAM,gBAAgB,CACpB,GAAG,IAAI,IACL,CACE,GAAG,mBACH,GAAG,OAAO,QAAO,UAAS,MAAM,cAAc,OAAO,CAAC,CAAC,KAAI,UAAS,MAAM,OAAO,CACnF,CAAC,CAAC,OAAO,cAAc,CACzB,CACF,CAAC,CAAC,MAAM,GAAG,kBAAkB;EAC7B,IAAI,cAAc,WAAW,GAAG,OAAO,CAAC;EAMxC,MAAM,WAA2C,CAAC;EAClD,KAAK,MAAM,SAAS,QAAQ;GAC1B,IAAI,MAAM,cAAc,SAAS;GACjC,IAAI,CAAC,eAAe,MAAM,OAAO,GAAG;GACpC,IAAI,SAAS,MAAM,UAAU;GAC7B,MAAM,SAAS,uBAAuB,MAAM,QAAQ;GACpD,IAAI,CAAC,QAAQ,MAAM;GACnB,SAAS,MAAM,WAAW;IACxB,IAAI,MAAM;IACV,MAAM,OAAO;IACb,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;GAC5D;EACF;EAEA,MAAM,aAAa,cAAc,QAAO,YAAW,CAAC,SAAS,QAAQ;EACrE,IAAI,WAAW,WAAW,KAAK,CAAC,KAAK,QAAQ,OAAO;EAEpD,IAAI;GACF,MAAM,QAAQ,KAAK,OAAO,WACtB,MAAM,KAAK,OAAO,SAAS,UAAU,IACrC,MAAM,QAAQ,IAAI,WAAW,KAAI,YAAW,KAAK,QAAQ,QAAQ,OAAO,KAAK,IAAI,CAAC;GACtF,KAAK,MAAM,CAAC,OAAO,SAAS,MAAM,QAAQ,GAAG;IAC3C,IAAI,CAAC,MAAM;IACX,MAAM,OAAO,KAAK,MAAM,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,KAAK;IAC7D,MAAM,UAAU,WAAW,UAAU,KAAK;IAC1C,SAAS,WAAW;KAClB,IAAI,KAAK;KACT;KACA,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;IACxD;GACF;EACF,SAAS,KAAK;GACZ,QAAQ,KAAK,kDAAkD,EAC7D,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,EACxD,CAAC;EACH;EACA,OAAO;CACT;CAEA,SAAqB;EACnB,OAAO,CACL,iBAAiB,mCAAmC;GAClD,QAAQ;GACR,SAAS,OAAM,OAAM;IACnB,MAAM,IAAI,MAAM,EAAE;IAClB,MAAM,SAAS,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,cAAc,QAAQ,OAAO,OAAO;IAExC,MAAM,YAAY,EAAE,IAAI,MAAM,IAAI;IAClC,IAAI,CAAC,aAAa,CAAC,QAAQ,KAAK,SAAS,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG;IAC7F,MAAM,KAAK,UAAU,YAAY;IAEjC,IAAI,CAAC,MADiB,KAAK,UAAU,IAAI;KAAE,OAAO,OAAO;KAAO,IAAI;IAAU,CAAC,GACjE,OAAO,EAAE,KAAK,EAAE,OAAO,oBAAoB,GAAG,GAAG;IAE/D,MAAM,UAAU,6BAA6B,EAAE,IAAI,MAAM,YAAY,CAAC;IACtE,IAAI,SAAS,WAAW,GAAG,OAAO,EAAE,KAAK,EAAE,OAAO,qBAAqB,GAAG,GAAG;IAE7E,MAAM,OAAO,MAAM,KAAK,KAAK;KAC3B,OAAO,OAAO;KACd,kBAAkB;KAClB;KACA,SAAS,EAAE,IAAI,MAAM,OAAO,KAAK,KAAA;KACjC,QAAQ,EAAE,IAAI,MAAM,QAAQ,KAAK,KAAA;KACjC,OAAO,gBAAgB,EAAE,IAAI,MAAM,OAAO,CAAC;IAC7C,CAAC;IACD,MAAM,SAAS,MAAM,KAAK,sBAAsB,KAAK,QAAQ,mBAAmB,EAAE,IAAI,MAAM,UAAU,CAAC,CAAC;IACxG,MAAM,OAAsB;KAC1B,QAAQ,KAAK,OAAO,IAAI,gBAAgB;KACxC;KACA,GAAI,KAAK,aAAa,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;IAC3D;IACA,OAAO,EAAE,KAAK,IAAI;GACpB;EACF,CAAC,CACH;CACF;CAEA,MAAM,eAAe,GAAiF;EACpG,MAAM,KAAK,MAAM,WAAW,CAAC;EAC7B,MAAM,SAAS,KAAK,MAAM,OAAO,CAAC;EAClC,IAAI,CAAC,QAAQ,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,eAAe,GAAG,GAAG,EAAE;EACvE,IAAI,CAAC,OAAO,OACV,OAAO,EACL,UAAU,EAAE,KAAK;GAAE,OAAO;GAAyB,SAAS;EAA4C,GAAG,GAAG,EAChH;EAEF,OAAO;GAAE,OAAO,OAAO;GAAO,QAAQ,OAAO;EAAO;CACtD;AACF"}
|
|
1
|
+
{"version":3,"file":"domain.js","names":[],"sources":["../../../../src/storage/domains/audit/domain.ts"],"sourcesContent":["import type { AgentControllerRequestContext } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport type { ApiRoute, IUserProvider } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport { getFactoryAuthUser } from '../../../auth.js';\nimport type { RouteAuth } from '../../../routes/route.js';\nimport type { FactoryProjectsStorage } from '../projects/base.js';\nimport { auditActionsInNamespaces, isAuditAction, isAuditNamespace } from './actions.js';\nimport type { AuditAction } from './actions.js';\nimport { isHumanActorId } from './actors.js';\nimport { ACTOR_PROFILE_METADATA_KEY, auditActorProfile } from './base.js';\nimport type {\n AuditActorProfileInput,\n AuditContext,\n AuditEventPage,\n AuditEventRow,\n AuditStorage,\n AuditTarget,\n ListAuditEventsInput,\n RecordAuditEventInput,\n} from './base.js';\nimport { toWireAuditEvent } from './wire.js';\nimport type { WireAuditActor, WireAuditPage } from './wire.js';\n\nexport interface EmitAuditInput {\n action: AuditAction;\n factoryProjectId?: string;\n projectRepositoryId?: string;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface EmitAgentAuditInput {\n action: AuditAction;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface AuditEmitter {\n emit(args: { context: Context; input: EmitAuditInput }): Promise<void>;\n}\n\nexport interface AuditAgentEmitter {\n emitAgent(args: { requestContext: RequestContext; input: EmitAgentAuditInput }): Promise<void>;\n}\n\n/** Records with an explicit actor, for the paths that have no request: rule transitions, run starts, run ends. */\nexport type AuditRecorder = Pick<AuditDomain, 'record'>;\n\n/** Best-effort destination for locally persisted audit events (e.g. an integration's audit log). */\nexport interface AuditSink {\n id: string;\n audit?(args: { event: AuditEventRow }): Promise<void>;\n}\n\ninterface FactorySessionState {\n factoryProjectId?: string;\n projectRepositoryId?: string;\n}\n\nfunction readStoredActorProfile(metadata: Record<string, unknown> | undefined): AuditActorProfileInput | undefined {\n if (!metadata) return undefined;\n const raw = metadata[ACTOR_PROFILE_METADATA_KEY];\n if (!raw || typeof raw !== 'object') return undefined;\n const profile = raw as Record<string, unknown>;\n const name = typeof profile.name === 'string' && profile.name.trim() ? profile.name.trim() : undefined;\n const avatarUrl =\n typeof profile.avatarUrl === 'string' && profile.avatarUrl.trim() ? profile.avatarUrl.trim() : undefined;\n if (!name && !avatarUrl) return undefined;\n return { ...(name ? { name } : {}), ...(avatarUrl ? { avatarUrl } : {}) };\n}\n\nexport interface AuditDomainOptions {\n auth: RouteAuth;\n /** Audit storage domain handle. */\n audit: AuditStorage;\n /** Projects domain handle, used to scope the audit trail route. */\n projects: FactoryProjectsStorage;\n /** Resolve persisted human actor ids to display names and profile images. */\n users?: Pick<IUserProvider, 'getUser' | 'getUsers'>;\n /** Best-effort fan-out destinations notified after each recorded event. */\n sinks?: AuditSink[];\n /** Resolve the acting tenant for agent-emitted events from the request context. */\n agentTenant?: (requestContext: RequestContext) => { orgId?: string; userId?: string } | undefined;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\nconst MAX_ACTOR_PROFILES = 100;\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\n/** The actions a `namespaces=` query names: none when it names only unknown ones, `undefined` when it names nothing. */\nfunction actionsInRequestedNamespaces(raw: string | undefined): string[] | undefined {\n if (!raw) return undefined;\n return auditActionsInNamespaces(\n raw\n .split(',')\n .map(namespace => namespace.trim())\n .filter(isAuditNamespace),\n );\n}\n\nfunction requestedActions(raw: string | undefined): string[] | undefined {\n if (!raw) return undefined;\n return raw\n .split(',')\n .map(action => action.trim())\n .filter(isAuditAction);\n}\n\nfunction parseActorIdsParam(raw: string | undefined): string[] {\n if (!raw) return [];\n return [\n ...new Set(\n raw\n .split(',')\n .map(actorId => actorId.trim())\n .filter(isHumanActorId),\n ),\n ].slice(0, MAX_ACTOR_PROFILES);\n}\n\nfunction parseLimitParam(raw: string | undefined): number | undefined {\n if (!raw) return undefined;\n const limit = Number.parseInt(raw, 10);\n return Number.isFinite(limit) ? limit : undefined;\n}\n\nexport function auditRequestContext(c: Context): AuditContext {\n const location = c.req.header('x-forwarded-for')?.split(',')[0]?.trim();\n const userAgent = c.req.header('user-agent');\n return {\n ...(location ? { location } : {}),\n ...(userAgent ? { userAgent } : {}),\n };\n}\n\n/** Who a browser request is and where it came from: the pair every funnel row carries. */\nexport function auditRequestOrigin(c: Context): {\n actorProfile: AuditActorProfileInput | undefined;\n context: AuditContext;\n} {\n return { actorProfile: auditActorProfile(getFactoryAuthUser(c)), context: auditRequestContext(c) };\n}\n\n/** Factory-owned audit behavior backed by the audit storage domain. */\nexport class AuditDomain implements AuditEmitter, AuditAgentEmitter {\n readonly #auth: RouteAuth;\n readonly #audit: AuditStorage;\n readonly #projects: FactoryProjectsStorage;\n readonly #users: AuditDomainOptions['users'];\n readonly #sinks: AuditSink[];\n readonly #agentTenant: AuditDomainOptions['agentTenant'];\n\n constructor({ auth, audit, projects, users, sinks = [], agentTenant }: AuditDomainOptions) {\n this.#auth = auth;\n this.#audit = audit;\n this.#projects = projects;\n this.#users = users;\n this.#sinks = sinks;\n this.#agentTenant = agentTenant;\n\n const ids = new Set<string>();\n for (const sink of this.#sinks) {\n if (!sink.id) throw new Error('Audit integration id must not be empty');\n if (ids.has(sink.id)) throw new Error(`Duplicate audit integration id '${sink.id}'`);\n ids.add(sink.id);\n }\n }\n\n async record(input: RecordAuditEventInput<AuditAction>): Promise<AuditEventRow | null> {\n try {\n await this.#audit.ensureReady();\n const { event: row, created } = input.idempotencyKey\n ? await this.#audit.recordOnce(input)\n : { event: await this.#audit.record(input), created: true };\n if (!created) return row;\n for (const sink of this.#sinks) {\n if (!sink.audit) continue;\n void Promise.resolve()\n .then(() => sink.audit?.({ event: row }))\n .catch(err => {\n console.warn('[Audit] Audit integration failed', {\n integration: sink.id,\n action: row.action,\n error: err instanceof Error ? err.message : String(err),\n });\n });\n }\n return row;\n } catch (err) {\n console.warn('[Audit] Failed to record audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n return null;\n }\n }\n\n async list(input: ListAuditEventsInput): Promise<AuditEventPage> {\n await this.#audit.ensureReady();\n return this.#audit.list(input);\n }\n\n async emit({ context, input }: { context: Context; input: EmitAuditInput }): Promise<void> {\n try {\n const tenant = this.#auth.tenant(context);\n if (!tenant?.orgId) return;\n await this.record({\n orgId: tenant.orgId,\n actorId: tenant.userId,\n actorProfile: auditActorProfile(getFactoryAuthUser(context)),\n action: input.action,\n targets: input.targets,\n metadata: input.metadata,\n factoryProjectId: input.factoryProjectId,\n projectRepositoryId: input.projectRepositoryId,\n context: auditRequestContext(context),\n });\n } catch (err) {\n console.warn('[Audit] Failed to emit audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n async emitAgent({\n requestContext,\n input,\n }: {\n requestContext: RequestContext;\n input: EmitAgentAuditInput;\n }): Promise<void> {\n try {\n const context = requestContext.get('controller') as\n | AgentControllerRequestContext<FactorySessionState>\n | undefined;\n const tenant = this.#agentTenant?.(requestContext);\n const orgId = tenant?.orgId;\n const userId = tenant?.userId;\n const threadId = context?.threadId;\n const state = context?.getState();\n if (!orgId || !userId || !threadId || !state?.factoryProjectId) return;\n\n const modelId = context.session.modelId?.trim();\n await this.record({\n orgId,\n actorId: `agent:${threadId}`,\n actorType: 'agent',\n action: input.action,\n targets: input.targets,\n metadata: {\n ...input.metadata,\n startedBy: userId,\n ...(modelId ? { modelId } : {}),\n },\n factoryProjectId: state.factoryProjectId,\n projectRepositoryId: state.projectRepositoryId,\n context: {},\n });\n } catch (err) {\n console.warn('[Audit] Failed to emit agent audit event', {\n action: input.action,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n async #resolveActorProfiles(\n events: AuditEventRow[],\n requestedActorIds: string[] = [],\n ): Promise<Record<string, WireAuditActor>> {\n const humanActorIds = [\n ...new Set(\n [\n ...requestedActorIds,\n ...events.filter(event => event.actorType === 'human').map(event => event.actorId),\n ].filter(isHumanActorId),\n ),\n ].slice(0, MAX_ACTOR_PROFILES);\n if (humanActorIds.length === 0) return {};\n\n // Prefer the profile stamped into event metadata at record time: it works\n // regardless of the auth provider's ability to resolve a user by id\n // (MastraAuthStudio's getUser() always returns null, WorkOS looks up by\n // id but every provider stamps here uniformly).\n const profiles: Record<string, WireAuditActor> = {};\n for (const event of events) {\n if (event.actorType !== 'human') continue;\n if (!isHumanActorId(event.actorId)) continue;\n if (profiles[event.actorId]) continue;\n const stored = readStoredActorProfile(event.metadata);\n if (!stored?.name) continue;\n profiles[event.actorId] = {\n id: event.actorId,\n name: stored.name,\n ...(stored.avatarUrl ? { avatarUrl: stored.avatarUrl } : {}),\n };\n }\n\n const unresolved = humanActorIds.filter(actorId => !profiles[actorId]);\n if (unresolved.length === 0 || !this.#users) return profiles;\n\n try {\n const users = this.#users.getUsers\n ? await this.#users.getUsers(unresolved)\n : await Promise.all(unresolved.map(actorId => this.#users?.getUser(actorId) ?? null));\n for (const [index, user] of users.entries()) {\n if (!user) continue;\n const name = user.name?.trim() || user.email?.trim() || user.id;\n const actorId = unresolved[index] ?? user.id;\n profiles[actorId] = {\n id: user.id,\n name,\n ...(user.avatarUrl ? { avatarUrl: user.avatarUrl } : {}),\n };\n }\n } catch (err) {\n console.warn('[Audit] Failed to resolve audit actor profiles', {\n error: err instanceof Error ? err.message : String(err),\n });\n }\n return profiles;\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects/:id/audit', {\n method: 'GET',\n handler: async cc => {\n const c = loose(cc);\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant.response;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) return c.json({ error: 'Project not found' }, 404);\n await this.#projects.ensureReady();\n const project = await this.#projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) return c.json({ error: 'Project not found' }, 404);\n\n const inNamespaces = actionsInRequestedNamespaces(c.req.query('namespaces'));\n if (inNamespaces?.length === 0) return c.json({ error: 'unknown_namespaces' }, 400);\n const named = requestedActions(c.req.query('actions'));\n const actions =\n named && inNamespaces ? named.filter(action => inNamespaces.includes(action)) : (named ?? inNamespaces);\n if (actions?.length === 0) return c.json({ error: 'unknown_actions' }, 400);\n\n const page = await this.list({\n orgId: tenant.orgId,\n factoryProjectId: projectId,\n actions,\n actorId: c.req.query('actor') || undefined,\n before: c.req.query('before') || undefined,\n limit: parseLimitParam(c.req.query('limit')),\n });\n const actors = await this.#resolveActorProfiles(page.events, parseActorIdsParam(c.req.query('actorIds')));\n const body: WireAuditPage = {\n events: page.events.map(toWireAuditEvent),\n actors,\n ...(page.nextCursor ? { nextCursor: page.nextCursor } : {}),\n };\n return c.json(body);\n },\n }),\n ];\n }\n\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.#auth.ensureUser(c);\n const tenant = this.#auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json({ error: 'organization_required', message: 'The audit trail requires an organization.' }, 403),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/factory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0-alpha.0",
|
|
4
4
|
"description": "Mastra Software Factory module: the server core behind the Mastra Software Factory — storage domains, integrations, and surfaces for agent-powered software delivery",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -58,13 +58,13 @@
|
|
|
58
58
|
"hono": "^4.13.13",
|
|
59
59
|
"posthog-node": "^5.57.1",
|
|
60
60
|
"zod": "^4.6.7",
|
|
61
|
-
"@mastra/
|
|
62
|
-
"@mastra/
|
|
63
|
-
"@mastra/
|
|
64
|
-
"@mastra/
|
|
65
|
-
"@mastra/platform-workspace": "1.10.0
|
|
66
|
-
"@mastra/
|
|
67
|
-
"@mastra/
|
|
61
|
+
"@mastra/code-sdk": "1.16.0",
|
|
62
|
+
"@mastra/auth-studio": "1.3.11",
|
|
63
|
+
"@mastra/core": "1.80.0",
|
|
64
|
+
"@mastra/mcp": "2.6.0",
|
|
65
|
+
"@mastra/platform-workspace": "1.10.0",
|
|
66
|
+
"@mastra/auth-workos": "1.6.8",
|
|
67
|
+
"@mastra/slack": "1.8.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "22.19.15",
|
|
@@ -72,13 +72,13 @@
|
|
|
72
72
|
"tsdown": "0.23.1",
|
|
73
73
|
"typescript": "^6.0.3",
|
|
74
74
|
"vitest": "4.1.12",
|
|
75
|
-
"@internal/lint": "0.0.
|
|
76
|
-
"@internal/
|
|
77
|
-
"@internal/
|
|
78
|
-
"@mastra/deployer": "1.80.0
|
|
79
|
-
"@mastra/libsql": "1.27.1
|
|
80
|
-
"@mastra/
|
|
81
|
-
"@mastra/
|
|
75
|
+
"@internal/lint": "0.0.146",
|
|
76
|
+
"@internal/workspace": "0.0.18",
|
|
77
|
+
"@internal/types-builder": "0.0.121",
|
|
78
|
+
"@mastra/deployer": "1.80.0",
|
|
79
|
+
"@mastra/libsql": "1.27.1",
|
|
80
|
+
"@mastra/server": "1.80.0",
|
|
81
|
+
"@mastra/pg": "1.33.0"
|
|
82
82
|
},
|
|
83
83
|
"engines": {
|
|
84
84
|
"node": ">=22.19.0"
|