@mastra/factory 0.24.0 → 0.25.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/README.md +36 -9
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +18 -4
- package/dist/auth.js.map +1 -1
- package/dist/boards/review.d.ts.map +1 -1
- package/dist/boards/review.js +3 -2
- package/dist/boards/review.js.map +1 -1
- package/dist/factory.d.ts +3 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +61 -69
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +14 -1
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/{session → integrations/slack}/channel-reply-processor.d.ts +4 -3
- package/dist/integrations/slack/channel-reply-processor.d.ts.map +1 -0
- package/dist/integrations/slack/channel-reply-processor.js +61 -0
- package/dist/integrations/slack/channel-reply-processor.js.map +1 -0
- package/dist/integrations/slack/channel-session-link-processor.d.ts.map +1 -0
- package/dist/{session → integrations/slack}/channel-session-link-processor.js +13 -4
- package/dist/integrations/slack/channel-session-link-processor.js.map +1 -0
- package/dist/integrations/slack/integration.d.ts +25 -1
- package/dist/integrations/slack/integration.d.ts.map +1 -1
- package/dist/integrations/slack/integration.js +55 -5
- package/dist/integrations/slack/integration.js.map +1 -1
- package/dist/integrations/slack/reply-reminder-processor.d.ts +23 -0
- package/dist/integrations/slack/reply-reminder-processor.d.ts.map +1 -0
- package/dist/integrations/slack/reply-reminder-processor.js +58 -0
- package/dist/integrations/slack/reply-reminder-processor.js.map +1 -0
- package/dist/integrations/slack/reply-tool.d.ts +31 -0
- package/dist/integrations/slack/reply-tool.d.ts.map +1 -0
- package/dist/integrations/slack/reply-tool.js +203 -0
- package/dist/integrations/slack/reply-tool.js.map +1 -0
- package/dist/integrations/slack/share-file-tool.d.ts +45 -0
- package/dist/integrations/slack/share-file-tool.d.ts.map +1 -0
- package/dist/integrations/slack/share-file-tool.js +106 -0
- package/dist/integrations/slack/share-file-tool.js.map +1 -0
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +54 -6
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/mcp/events.d.ts +73 -0
- package/dist/mcp/events.d.ts.map +1 -0
- package/dist/mcp/events.js +158 -0
- package/dist/mcp/events.js.map +1 -0
- package/dist/mcp/routes.d.ts +0 -1
- package/dist/mcp/routes.d.ts.map +1 -1
- package/dist/mcp/routes.js +30 -3
- package/dist/mcp/routes.js.map +1 -1
- package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
- package/dist/platform-auth-host.d.ts.map +1 -1
- package/dist/platform-auth-host.js +1 -1
- package/dist/platform-auth-host.js.map +1 -1
- package/dist/routes/config.d.ts +2 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +50 -9
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/contracts.d.ts +2 -0
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/contracts.js +1 -1
- package/dist/routes/contracts.js.map +1 -1
- package/dist/routes/session-access.d.ts +7 -2
- package/dist/routes/session-access.d.ts.map +1 -1
- package/dist/routes/session-access.js +41 -4
- package/dist/routes/session-access.js.map +1 -1
- package/dist/routes/skills.d.ts.map +1 -1
- package/dist/routes/skills.js +4 -3
- package/dist/routes/skills.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts +2 -0
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +161 -24
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/surface.d.ts +2 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +10 -4
- package/dist/routes/surface.js.map +1 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +11 -3
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts +2 -2
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +3 -3
- package/dist/rules/processor.js.map +1 -1
- package/dist/sandbox/session-retirement.d.ts +11 -0
- package/dist/sandbox/session-retirement.d.ts.map +1 -1
- package/dist/sandbox/session-retirement.js +38 -1
- package/dist/sandbox/session-retirement.js.map +1 -1
- package/dist/sandbox/session-sandbox.d.ts +2 -0
- package/dist/sandbox/session-sandbox.d.ts.map +1 -1
- package/dist/sandbox/session-sandbox.js +5 -1
- package/dist/sandbox/session-sandbox.js.map +1 -1
- package/dist/session/credential-policy.d.ts +4 -2
- package/dist/session/credential-policy.d.ts.map +1 -1
- package/dist/session/credential-policy.js +11 -1
- package/dist/session/credential-policy.js.map +1 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +14 -1
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/run-audit.d.ts +1 -0
- package/dist/session/run-audit.d.ts.map +1 -1
- package/dist/session/run-audit.js +7 -2
- package/dist/session/run-audit.js.map +1 -1
- package/dist/session/session-threads.d.ts +5 -0
- package/dist/session/session-threads.d.ts.map +1 -1
- package/dist/session/session-threads.js +8 -1
- package/dist/session/session-threads.js.map +1 -1
- package/dist/session/thinking-default.d.ts +6 -13
- package/dist/session/thinking-default.d.ts.map +1 -1
- package/dist/session/thinking-default.js +18 -22
- package/dist/session/thinking-default.js.map +1 -1
- package/dist/session-owner-signal.d.ts +2 -0
- package/dist/session-owner-signal.d.ts.map +1 -0
- package/dist/session-owner-signal.js +6 -0
- package/dist/session-owner-signal.js.map +1 -0
- package/dist/skills/service.d.ts +6 -7
- package/dist/skills/service.d.ts.map +1 -1
- package/dist/skills/service.js +9 -19
- package/dist/skills/service.js.map +1 -1
- package/dist/storage/domains/audit/actions.d.ts +1 -0
- package/dist/storage/domains/audit/actions.d.ts.map +1 -1
- package/dist/storage/domains/audit/actions.js +2 -1
- package/dist/storage/domains/audit/actions.js.map +1 -1
- package/dist/storage/domains/audit/base.d.ts +18 -2
- package/dist/storage/domains/audit/base.d.ts.map +1 -1
- package/dist/storage/domains/audit/base.js +22 -4
- package/dist/storage/domains/audit/base.js.map +1 -1
- package/dist/storage/domains/model-defaults/base.d.ts +8 -0
- package/dist/storage/domains/model-defaults/base.d.ts.map +1 -1
- package/dist/storage/domains/model-defaults/base.js +18 -0
- package/dist/storage/domains/model-defaults/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +26 -1
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +69 -5
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +19 -1
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +30 -2
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/tools/permissions.d.ts.map +1 -1
- package/dist/tools/permissions.js +2 -1
- package/dist/tools/permissions.js.map +1 -1
- package/dist/tools/sandbox-tools.d.ts +12 -0
- package/dist/tools/sandbox-tools.d.ts.map +1 -0
- package/dist/tools/sandbox-tools.js +40 -0
- package/dist/tools/sandbox-tools.js.map +1 -0
- package/dist/tools/scope.d.ts.map +1 -1
- package/dist/tools/scope.js +2 -5
- package/dist/tools/scope.js.map +1 -1
- package/dist/tools/session-tools.d.ts.map +1 -1
- package/dist/tools/session-tools.js +6 -2
- package/dist/tools/session-tools.js.map +1 -1
- package/dist/work-item-branch.d.ts +11 -3
- package/dist/work-item-branch.d.ts.map +1 -1
- package/dist/work-item-branch.js +34 -9
- package/dist/work-item-branch.js.map +1 -1
- package/dist/workspace.d.ts +2 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +4 -2
- package/dist/workspace.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +2 -2
- package/package.json +14 -13
- package/dist/session/channel-reply-processor.d.ts.map +0 -1
- package/dist/session/channel-reply-processor.js +0 -46
- package/dist/session/channel-reply-processor.js.map +0 -1
- package/dist/session/channel-session-link-processor.d.ts.map +0 -1
- package/dist/session/channel-session-link-processor.js.map +0 -1
- /package/dist/{session → integrations/slack}/channel-session-link-processor.d.ts +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-owner-signal.d.ts","sourceRoot":"","sources":["../src/session-owner-signal.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,0BAA0B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-owner-signal.js","names":[],"sources":["../src/session-owner-signal.ts"],"sourcesContent":["export const SESSION_OWNER_SIGNAL_TAG = 'factory-session-owner';\n"],"mappings":";AAAA,MAAa,2BAA2B"}
|
package/dist/skills/service.d.ts
CHANGED
|
@@ -36,18 +36,21 @@ export declare class SkillInvocationError extends Error {
|
|
|
36
36
|
readonly code: 'session_not_found' | 'skill_not_found';
|
|
37
37
|
constructor(code: SkillInvocationError['code'], message: string);
|
|
38
38
|
}
|
|
39
|
+
/** Wraps skill contents in the `<skill>` envelope a human-invoked skill sends as its own message. */
|
|
40
|
+
export declare function formatSkillMessage(name: string, contents: string): string;
|
|
39
41
|
/** Kicks a run off from a plain prompt, for runs that activate no skill. */
|
|
40
42
|
export declare function resolvePromptInvocation(controller: Pick<AgentController<MastraCodeState>, 'getSession'>, input: {
|
|
41
43
|
sessionId: string;
|
|
42
44
|
prompt: string;
|
|
43
45
|
}): Promise<{
|
|
44
46
|
session: SkillSession;
|
|
45
|
-
|
|
47
|
+
skillName?: undefined;
|
|
48
|
+
contents: string;
|
|
46
49
|
}>;
|
|
47
50
|
export declare function resolveSkillInvocation(controller: Pick<AgentController<MastraCodeState>, 'getSession'>, input: SkillInvocationInput): Promise<{
|
|
48
51
|
session: SkillSession;
|
|
49
52
|
skillName: string;
|
|
50
|
-
|
|
53
|
+
contents: string;
|
|
51
54
|
}>;
|
|
52
55
|
/**
|
|
53
56
|
* Same-stage re-entry: the skill is already active in the card's live session.
|
|
@@ -59,10 +62,6 @@ export declare function resolveSkillInvocation(controller: Pick<AgentController<
|
|
|
59
62
|
export declare function resolveSkillResumeInvocation(controller: Pick<AgentController<MastraCodeState>, 'getSession'>, input: SkillInvocationInput): Promise<{
|
|
60
63
|
session: SkillSession;
|
|
61
64
|
skillName: string;
|
|
62
|
-
|
|
63
|
-
}>;
|
|
64
|
-
export declare function dispatchSkillInvocation(controller: Pick<AgentController<MastraCodeState>, 'getSession'>, input: SkillInvocationInput, requestContext?: RequestContext): Promise<{
|
|
65
|
-
skillName: string;
|
|
66
|
-
message: string;
|
|
65
|
+
contents: string;
|
|
67
66
|
}>;
|
|
68
67
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/skills/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIxD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,IAAI,SAAS,CAAC;IAC1B,WAAW,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,sBAAsB,CACpB,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;QAChD,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,EACD,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;SAAE,CAAC;QAChD,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;SAAE,CAAC;QAC3C,cAAc,CAAC,EAAE,cAAc,CAAC;KACjC,GACA,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC;CACnE;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,mBAAmB,GAAG,iBAAiB,CAAC;gBAE3C,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAKhE;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/skills/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAEnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAIxD,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,IAAI,SAAS,CAAC;IAC1B,WAAW,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,sBAAsB,CACpB,KAAK,EAAE;QACL,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;QAChD,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,EACD,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;SAAE,CAAC;QAChD,MAAM,CAAC,EAAE;YAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;SAAE,CAAC;QAC3C,cAAc,CAAC,EAAE,cAAc,CAAC;KACjC,GACA,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC;CACnE;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,mBAAmB,GAAG,iBAAiB,CAAC;gBAE3C,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM;CAKhE;AAiBD,qGAAqG;AACrG,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED,4EAA4E;AAC5E,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,YAAY,CAAC,EAChE,KAAK,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,SAAS,CAAC,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAI7E;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,YAAY,CAAC,EAChE,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAYzE;AAED;;;;;;GAMG;AACH,wBAAsB,4BAA4B,CAChD,UAAU,EAAE,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,EAAE,YAAY,CAAC,EAChE,KAAK,EAAE,oBAAoB,GAC1B,OAAO,CAAC;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBzE"}
|
package/dist/skills/service.js
CHANGED
|
@@ -22,8 +22,9 @@ async function findSkill(session, name) {
|
|
|
22
22
|
}
|
|
23
23
|
return skills.get(name);
|
|
24
24
|
}
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/** Wraps skill contents in the `<skill>` envelope a human-invoked skill sends as its own message. */
|
|
26
|
+
function formatSkillMessage(name, contents) {
|
|
27
|
+
return `<skill name="${name}">\n${contents.replaceAll("</skill>", "</skill>")}\n</skill>`;
|
|
27
28
|
}
|
|
28
29
|
/** Kicks a run off from a plain prompt, for runs that activate no skill. */
|
|
29
30
|
async function resolvePromptInvocation(controller, input) {
|
|
@@ -31,7 +32,7 @@ async function resolvePromptInvocation(controller, input) {
|
|
|
31
32
|
if (!session) throw new SkillInvocationError("session_not_found", "Agent controller session not found.");
|
|
32
33
|
return {
|
|
33
34
|
session,
|
|
34
|
-
|
|
35
|
+
contents: input.prompt
|
|
35
36
|
};
|
|
36
37
|
}
|
|
37
38
|
async function resolveSkillInvocation(controller, input) {
|
|
@@ -40,11 +41,11 @@ async function resolveSkillInvocation(controller, input) {
|
|
|
40
41
|
const skill = await findSkill(session, input.name);
|
|
41
42
|
if (!skill || skill["user-invocable"] === false) throw new SkillInvocationError("skill_not_found", `Skill not found: ${input.name}.`);
|
|
42
43
|
const args = input.arguments?.trim();
|
|
43
|
-
const
|
|
44
|
+
const contents = `${formatSkillActivation(skill)}${args ? `\n\nARGUMENTS: ${args}` : ""}`.trim();
|
|
44
45
|
return {
|
|
45
46
|
session,
|
|
46
47
|
skillName: skill.name,
|
|
47
|
-
|
|
48
|
+
contents
|
|
48
49
|
};
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
@@ -60,7 +61,7 @@ async function resolveSkillResumeInvocation(controller, input) {
|
|
|
60
61
|
const skill = await findSkill(session, input.name);
|
|
61
62
|
if (!skill || skill["user-invocable"] === false) throw new SkillInvocationError("skill_not_found", `Skill not found: ${input.name}.`);
|
|
62
63
|
const args = input.arguments?.trim();
|
|
63
|
-
const
|
|
64
|
+
const contents = [
|
|
64
65
|
`Resume the active ${skill.name} session — it is already open in this thread; continue it rather than restarting.`,
|
|
65
66
|
`Re-open the ${skill.name} skill with the skill tool if its instructions are no longer in context.`,
|
|
66
67
|
...args ? [`ARGUMENTS: ${args}`] : []
|
|
@@ -68,21 +69,10 @@ async function resolveSkillResumeInvocation(controller, input) {
|
|
|
68
69
|
return {
|
|
69
70
|
session,
|
|
70
71
|
skillName: skill.name,
|
|
71
|
-
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
async function dispatchSkillInvocation(controller, input, requestContext) {
|
|
75
|
-
const resolved = await resolveSkillInvocation(controller, input);
|
|
76
|
-
await resolved.session.sendMessage({
|
|
77
|
-
content: resolved.message,
|
|
78
|
-
...requestContext ? { requestContext } : {}
|
|
79
|
-
});
|
|
80
|
-
return {
|
|
81
|
-
skillName: resolved.skillName,
|
|
82
|
-
message: resolved.message
|
|
72
|
+
contents
|
|
83
73
|
};
|
|
84
74
|
}
|
|
85
75
|
//#endregion
|
|
86
|
-
export { SkillInvocationError,
|
|
76
|
+
export { SkillInvocationError, formatSkillMessage, resolvePromptInvocation, resolveSkillInvocation, resolveSkillResumeInvocation };
|
|
87
77
|
|
|
88
78
|
//# sourceMappingURL=service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../../src/skills/service.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { SendAgentSignalResult } from '@mastra/core/agent';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { formatSkillActivation } from '@mastra/core/workspace';\nimport type { Workspace } from '@mastra/core/workspace';\n\nimport { REVIEW_ONLY_FACTORY_SKILLS, rescanFactorySkills } from '../workspace.js';\n\nexport interface SkillInvocationInput {\n sessionId: string;\n name: string;\n arguments?: string;\n}\n\nexport interface SkillSession {\n getWorkspace(): Workspace;\n sendMessage(input: { content: string; requestContext?: RequestContext }): Promise<unknown>;\n sendNotificationSignal(\n input: {\n source: string;\n kind: string;\n summary: string;\n priority?: 'low' | 'medium' | 'high' | 'urgent';\n payload?: unknown;\n dedupeKey?: string;\n sourceId?: string;\n },\n options?: {\n ifActive?: { behavior?: 'deliver' | 'persist' };\n ifIdle?: { behavior?: 'persist' | 'wake' };\n requestContext?: RequestContext;\n },\n ): Promise<Pick<SendAgentSignalResult, 'persisted' | 'accepted'>>;\n}\n\nexport class SkillInvocationError extends Error {\n readonly code: 'session_not_found' | 'skill_not_found';\n\n constructor(code: SkillInvocationError['code'], message: string) {\n super(message);\n this.name = 'SkillInvocationError';\n this.code = code;\n }\n}\n\nasync function findSkill(session: SkillSession, name: string) {\n const skills = session.getWorkspace().skills;\n if (!skills) return undefined;\n await skills.maybeRefresh();\n const skill = await skills.get(name);\n if (skill || !REVIEW_ONLY_FACTORY_SKILLS.has(name)) return skill;\n // The cache may predate the review binding this run was just given.\n try {\n await rescanFactorySkills(skills);\n } catch {\n return undefined;\n }\n return skills.get(name);\n}\n\
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../src/skills/service.ts"],"sourcesContent":["import type { MastraCodeState } from '@mastra/code-sdk/schema';\nimport type { SendAgentSignalResult } from '@mastra/core/agent';\nimport type { AgentController } from '@mastra/core/agent-controller';\nimport type { RequestContext } from '@mastra/core/request-context';\nimport { formatSkillActivation } from '@mastra/core/workspace';\nimport type { Workspace } from '@mastra/core/workspace';\n\nimport { REVIEW_ONLY_FACTORY_SKILLS, rescanFactorySkills } from '../workspace.js';\n\nexport interface SkillInvocationInput {\n sessionId: string;\n name: string;\n arguments?: string;\n}\n\nexport interface SkillSession {\n getWorkspace(): Workspace;\n sendMessage(input: { content: string; requestContext?: RequestContext }): Promise<unknown>;\n sendNotificationSignal(\n input: {\n source: string;\n kind: string;\n summary: string;\n priority?: 'low' | 'medium' | 'high' | 'urgent';\n payload?: unknown;\n dedupeKey?: string;\n sourceId?: string;\n },\n options?: {\n ifActive?: { behavior?: 'deliver' | 'persist' };\n ifIdle?: { behavior?: 'persist' | 'wake' };\n requestContext?: RequestContext;\n },\n ): Promise<Pick<SendAgentSignalResult, 'persisted' | 'accepted'>>;\n}\n\nexport class SkillInvocationError extends Error {\n readonly code: 'session_not_found' | 'skill_not_found';\n\n constructor(code: SkillInvocationError['code'], message: string) {\n super(message);\n this.name = 'SkillInvocationError';\n this.code = code;\n }\n}\n\nasync function findSkill(session: SkillSession, name: string) {\n const skills = session.getWorkspace().skills;\n if (!skills) return undefined;\n await skills.maybeRefresh();\n const skill = await skills.get(name);\n if (skill || !REVIEW_ONLY_FACTORY_SKILLS.has(name)) return skill;\n // The cache may predate the review binding this run was just given.\n try {\n await rescanFactorySkills(skills);\n } catch {\n return undefined;\n }\n return skills.get(name);\n}\n\n/** Wraps skill contents in the `<skill>` envelope a human-invoked skill sends as its own message. */\nexport function formatSkillMessage(name: string, contents: string): string {\n return `<skill name=\"${name}\">\\n${contents.replaceAll('</skill>', '</skill>')}\\n</skill>`;\n}\n\n/** Kicks a run off from a plain prompt, for runs that activate no skill. */\nexport async function resolvePromptInvocation(\n controller: Pick<AgentController<MastraCodeState>, 'getSession'>,\n input: { sessionId: string; prompt: string },\n): Promise<{ session: SkillSession; skillName?: undefined; contents: string }> {\n const session = (await controller.getSession(input.sessionId)) as SkillSession | undefined;\n if (!session) throw new SkillInvocationError('session_not_found', 'Agent controller session not found.');\n return { session, contents: input.prompt };\n}\n\nexport async function resolveSkillInvocation(\n controller: Pick<AgentController<MastraCodeState>, 'getSession'>,\n input: SkillInvocationInput,\n): Promise<{ session: SkillSession; skillName: string; contents: string }> {\n const session = (await controller.getSession(input.sessionId)) as SkillSession | undefined;\n if (!session) throw new SkillInvocationError('session_not_found', 'Agent controller session not found.');\n\n const skill = await findSkill(session, input.name);\n if (!skill || skill['user-invocable'] === false) {\n throw new SkillInvocationError('skill_not_found', `Skill not found: ${input.name}.`);\n }\n\n const args = input.arguments?.trim();\n const contents = `${formatSkillActivation(skill)}${args ? `\\n\\nARGUMENTS: ${args}` : ''}`.trim();\n return { session, skillName: skill.name, contents };\n}\n\n/**\n * Same-stage re-entry: the skill is already active in the card's live session.\n * Resolve a compact continuation that references the skill by name and carries\n * only the fresh arguments, instead of re-pasting the whole skill document. If\n * the skill body was compacted out of context, the agent can re-open it with the\n * skill tool.\n */\nexport async function resolveSkillResumeInvocation(\n controller: Pick<AgentController<MastraCodeState>, 'getSession'>,\n input: SkillInvocationInput,\n): Promise<{ session: SkillSession; skillName: string; contents: string }> {\n const session = (await controller.getSession(input.sessionId)) as SkillSession | undefined;\n if (!session) throw new SkillInvocationError('session_not_found', 'Agent controller session not found.');\n\n const skill = await findSkill(session, input.name);\n if (!skill || skill['user-invocable'] === false) {\n throw new SkillInvocationError('skill_not_found', `Skill not found: ${input.name}.`);\n }\n\n const args = input.arguments?.trim();\n const contents = [\n `Resume the active ${skill.name} session — it is already open in this thread; continue it rather than restarting.`,\n `Re-open the ${skill.name} skill with the skill tool if its instructions are no longer in context.`,\n ...(args ? [`ARGUMENTS: ${args}`] : []),\n ].join('\\n');\n return { session, skillName: skill.name, contents };\n}\n"],"mappings":";;;AAoCA,IAAa,uBAAb,cAA0C,MAAM;CAC9C;CAEA,YAAY,MAAoC,SAAiB;EAC/D,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;CACd;AACF;AAEA,eAAe,UAAU,SAAuB,MAAc;CAC5D,MAAM,SAAS,QAAQ,aAAa,CAAC,CAAC;CACtC,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,MAAM,OAAO,aAAa;CAC1B,MAAM,QAAQ,MAAM,OAAO,IAAI,IAAI;CACnC,IAAI,SAAS,CAAC,2BAA2B,IAAI,IAAI,GAAG,OAAO;CAE3D,IAAI;EACF,MAAM,oBAAoB,MAAM;CAClC,QAAQ;EACN;CACF;CACA,OAAO,OAAO,IAAI,IAAI;AACxB;;AAGA,SAAgB,mBAAmB,MAAc,UAA0B;CACzE,OAAO,gBAAgB,KAAK,MAAM,SAAS,WAAW,YAAY,gBAAgB,EAAE;AACtF;;AAGA,eAAsB,wBACpB,YACA,OAC6E;CAC7E,MAAM,UAAW,MAAM,WAAW,WAAW,MAAM,SAAS;CAC5D,IAAI,CAAC,SAAS,MAAM,IAAI,qBAAqB,qBAAqB,qCAAqC;CACvG,OAAO;EAAE;EAAS,UAAU,MAAM;CAAO;AAC3C;AAEA,eAAsB,uBACpB,YACA,OACyE;CACzE,MAAM,UAAW,MAAM,WAAW,WAAW,MAAM,SAAS;CAC5D,IAAI,CAAC,SAAS,MAAM,IAAI,qBAAqB,qBAAqB,qCAAqC;CAEvG,MAAM,QAAQ,MAAM,UAAU,SAAS,MAAM,IAAI;CACjD,IAAI,CAAC,SAAS,MAAM,sBAAsB,OACxC,MAAM,IAAI,qBAAqB,mBAAmB,oBAAoB,MAAM,KAAK,EAAE;CAGrF,MAAM,OAAO,MAAM,WAAW,KAAK;CACnC,MAAM,WAAW,GAAG,sBAAsB,KAAK,IAAI,OAAO,kBAAkB,SAAS,KAAK,KAAK;CAC/F,OAAO;EAAE;EAAS,WAAW,MAAM;EAAM;CAAS;AACpD;;;;;;;;AASA,eAAsB,6BACpB,YACA,OACyE;CACzE,MAAM,UAAW,MAAM,WAAW,WAAW,MAAM,SAAS;CAC5D,IAAI,CAAC,SAAS,MAAM,IAAI,qBAAqB,qBAAqB,qCAAqC;CAEvG,MAAM,QAAQ,MAAM,UAAU,SAAS,MAAM,IAAI;CACjD,IAAI,CAAC,SAAS,MAAM,sBAAsB,OACxC,MAAM,IAAI,qBAAqB,mBAAmB,oBAAoB,MAAM,KAAK,EAAE;CAGrF,MAAM,OAAO,MAAM,WAAW,KAAK;CACnC,MAAM,WAAW;EACf,qBAAqB,MAAM,KAAK;EAChC,eAAe,MAAM,KAAK;EAC1B,GAAI,OAAO,CAAC,cAAc,MAAM,IAAI,CAAC;CACvC,CAAC,CAAC,KAAK,IAAI;CACX,OAAO;EAAE;EAAS,WAAW,MAAM;EAAM;CAAS;AACpD"}
|
|
@@ -8,6 +8,7 @@ export declare const AUDIT_ACTIONS: {
|
|
|
8
8
|
readonly git: readonly ["commit", "push", "pr_opened"];
|
|
9
9
|
readonly agent: readonly ["commit", "push", "pr_opened", "signaled"];
|
|
10
10
|
readonly intake: readonly ["config_updated", "binding_updated", "label_route_updated"];
|
|
11
|
+
readonly session: readonly ["owner_transferred"];
|
|
11
12
|
};
|
|
12
13
|
export type AuditNamespace = keyof typeof AUDIT_ACTIONS;
|
|
13
14
|
export type AuditAction = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/actions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,aAAa
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/actions.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;;;CAkBhB,CAAC;AAEX,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,aAAa,CAAC;AAExD,MAAM,MAAM,WAAW,GAAG;KACvB,SAAS,IAAI,cAAc,GAAG,WAAW,SAAS,IAAI,CAAC,OAAO,aAAa,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,EAAE;CACnG,CAAC,cAAc,CAAC,CAAC;AAElB,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,cAAc,CAEvE;AAED,wBAAgB,eAAe,IAAI,cAAc,EAAE,CAElD;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAIxG;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,WAAW,CAGjE;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,SAAS,cAAc,EAAE,GAAG,MAAM,EAAE,CAExF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.js","names":[],"sources":["../../../../src/storage/domains/audit/actions.ts"],"sourcesContent":["/**\n * Every action the trail can hold, by namespace. Register new ones in the\n * WorkOS dashboard under Audit Logs → Events before the mirror accepts them.\n */\nexport const AUDIT_ACTIONS = {\n work_item: [\n 'created',\n 'updated',\n 'deleted',\n 'stage_moved',\n 'transition_rejected',\n 'comment_created',\n 'comment_edited',\n 'comment_deleted',\n 'comment_mentioned',\n 'labels_reconciled',\n ],\n run: ['started', 'ended', 'approved', 'dismissed', 'retry', 'queued', 'rejected'],\n git: ['commit', 'push', 'pr_opened'],\n agent: ['commit', 'push', 'pr_opened', 'signaled'],\n intake: ['config_updated', 'binding_updated', 'label_route_updated'],\n} as const;\n\nexport type AuditNamespace = keyof typeof AUDIT_ACTIONS;\n\nexport type AuditAction = {\n [Namespace in AuditNamespace]: `factory.${Namespace}.${(typeof AUDIT_ACTIONS)[Namespace][number]}`;\n}[AuditNamespace];\n\nexport function isAuditNamespace(value: string): value is AuditNamespace {\n return Object.hasOwn(AUDIT_ACTIONS, value);\n}\n\nexport function auditNamespaces(): AuditNamespace[] {\n return Object.keys(AUDIT_ACTIONS).filter(isAuditNamespace);\n}\n\nexport function parseAuditAction(action: string): { namespace: AuditNamespace; leaf: string } | undefined {\n const [prefix, namespace, leaf, ...rest] = action.split('.');\n if (prefix !== 'factory' || namespace === undefined || leaf === undefined || rest.length > 0) return undefined;\n return isAuditNamespace(namespace) ? { namespace, leaf } : undefined;\n}\n\nexport function isAuditAction(value: string): value is AuditAction {\n const parsed = parseAuditAction(value);\n return parsed !== undefined && AUDIT_ACTIONS[parsed.namespace].some(leaf => leaf === parsed.leaf);\n}\n\nexport function auditActionsInNamespaces(namespaces: readonly AuditNamespace[]): string[] {\n return namespaces.flatMap(namespace => AUDIT_ACTIONS[namespace].map(leaf => `factory.${namespace}.${leaf}`));\n}\n"],"mappings":";;;;;AAIA,MAAa,gBAAgB;CAC3B,WAAW;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,KAAK;EAAC;EAAW;EAAS;EAAY;EAAa;EAAS;EAAU;CAAU;CAChF,KAAK;EAAC;EAAU;EAAQ;CAAW;CACnC,OAAO;EAAC;EAAU;EAAQ;EAAa;CAAU;CACjD,QAAQ;EAAC;EAAkB;EAAmB;CAAqB;
|
|
1
|
+
{"version":3,"file":"actions.js","names":[],"sources":["../../../../src/storage/domains/audit/actions.ts"],"sourcesContent":["/**\n * Every action the trail can hold, by namespace. Register new ones in the\n * WorkOS dashboard under Audit Logs → Events before the mirror accepts them.\n */\nexport const AUDIT_ACTIONS = {\n work_item: [\n 'created',\n 'updated',\n 'deleted',\n 'stage_moved',\n 'transition_rejected',\n 'comment_created',\n 'comment_edited',\n 'comment_deleted',\n 'comment_mentioned',\n 'labels_reconciled',\n ],\n run: ['started', 'ended', 'approved', 'dismissed', 'retry', 'queued', 'rejected'],\n git: ['commit', 'push', 'pr_opened'],\n agent: ['commit', 'push', 'pr_opened', 'signaled'],\n intake: ['config_updated', 'binding_updated', 'label_route_updated'],\n session: ['owner_transferred'],\n} as const;\n\nexport type AuditNamespace = keyof typeof AUDIT_ACTIONS;\n\nexport type AuditAction = {\n [Namespace in AuditNamespace]: `factory.${Namespace}.${(typeof AUDIT_ACTIONS)[Namespace][number]}`;\n}[AuditNamespace];\n\nexport function isAuditNamespace(value: string): value is AuditNamespace {\n return Object.hasOwn(AUDIT_ACTIONS, value);\n}\n\nexport function auditNamespaces(): AuditNamespace[] {\n return Object.keys(AUDIT_ACTIONS).filter(isAuditNamespace);\n}\n\nexport function parseAuditAction(action: string): { namespace: AuditNamespace; leaf: string } | undefined {\n const [prefix, namespace, leaf, ...rest] = action.split('.');\n if (prefix !== 'factory' || namespace === undefined || leaf === undefined || rest.length > 0) return undefined;\n return isAuditNamespace(namespace) ? { namespace, leaf } : undefined;\n}\n\nexport function isAuditAction(value: string): value is AuditAction {\n const parsed = parseAuditAction(value);\n return parsed !== undefined && AUDIT_ACTIONS[parsed.namespace].some(leaf => leaf === parsed.leaf);\n}\n\nexport function auditActionsInNamespaces(namespaces: readonly AuditNamespace[]): string[] {\n return namespaces.flatMap(namespace => AUDIT_ACTIONS[namespace].map(leaf => `factory.${namespace}.${leaf}`));\n}\n"],"mappings":";;;;;AAIA,MAAa,gBAAgB;CAC3B,WAAW;EACT;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,KAAK;EAAC;EAAW;EAAS;EAAY;EAAa;EAAS;EAAU;CAAU;CAChF,KAAK;EAAC;EAAU;EAAQ;CAAW;CACnC,OAAO;EAAC;EAAU;EAAQ;EAAa;CAAU;CACjD,QAAQ;EAAC;EAAkB;EAAmB;CAAqB;CACnE,SAAS,CAAC,mBAAmB;AAC/B;AAQA,SAAgB,iBAAiB,OAAwC;CACvE,OAAO,OAAO,OAAO,eAAe,KAAK;AAC3C;AAEA,SAAgB,kBAAoC;CAClD,OAAO,OAAO,KAAK,aAAa,CAAC,CAAC,OAAO,gBAAgB;AAC3D;AAEA,SAAgB,iBAAiB,QAAyE;CACxG,MAAM,CAAC,QAAQ,WAAW,MAAM,GAAG,QAAQ,OAAO,MAAM,GAAG;CAC3D,IAAI,WAAW,aAAa,cAAc,KAAA,KAAa,SAAS,KAAA,KAAa,KAAK,SAAS,GAAG,OAAO,KAAA;CACrG,OAAO,iBAAiB,SAAS,IAAI;EAAE;EAAW;CAAK,IAAI,KAAA;AAC7D;AAEA,SAAgB,cAAc,OAAqC;CACjE,MAAM,SAAS,iBAAiB,KAAK;CACrC,OAAO,WAAW,KAAA,KAAa,cAAc,OAAO,UAAU,CAAC,MAAK,SAAQ,SAAS,OAAO,IAAI;AAClG;AAEA,SAAgB,yBAAyB,YAAiD;CACxF,OAAO,WAAW,SAAQ,cAAa,cAAc,UAAU,CAAC,KAAI,SAAQ,WAAW,UAAU,GAAG,MAAM,CAAC;AAC7G"}
|
|
@@ -92,11 +92,16 @@ export interface ListAuditEventsInput {
|
|
|
92
92
|
actorId?: string;
|
|
93
93
|
/** Opaque cursor from a previous page (`nextCursor`). */
|
|
94
94
|
before?: string;
|
|
95
|
+
/**
|
|
96
|
+
* Page oldest-first, strictly after this cursor: `nextCursor` of a previous
|
|
97
|
+
* `after` page, or `encodeAuditCursor` of a row. Can't be combined with `before`.
|
|
98
|
+
*/
|
|
99
|
+
after?: string;
|
|
95
100
|
limit?: number;
|
|
96
101
|
}
|
|
97
102
|
export interface AuditEventPage {
|
|
98
103
|
events: AuditEventRow[];
|
|
99
|
-
/** Pass back as `before` to fetch the next
|
|
104
|
+
/** Pass back as `before` (or `after`, when paging forward) to fetch the next page; absent at the end. */
|
|
100
105
|
nextCursor?: string;
|
|
101
106
|
}
|
|
102
107
|
/** Truncate oversized metadata rather than dropping the whole event. */
|
|
@@ -110,6 +115,17 @@ export declare function decodeAuditCursor(cursor: string): {
|
|
|
110
115
|
occurredAt: Date;
|
|
111
116
|
id: string;
|
|
112
117
|
} | undefined;
|
|
118
|
+
/** An `after` cursor at an instant: paging from it skips every row recorded at or before then. */
|
|
119
|
+
export declare function auditCursorAt(instant: Date): string;
|
|
120
|
+
/**
|
|
121
|
+
* Decode an `after` cursor, or `undefined` when it can't be paged from. Its id
|
|
122
|
+
* must be a UUID like every audit row id: Postgres rejects any other id in the
|
|
123
|
+
* keyset comparison.
|
|
124
|
+
*/
|
|
125
|
+
export declare function decodeAuditAfterCursor(cursor: string): {
|
|
126
|
+
occurredAt: Date;
|
|
127
|
+
id: string;
|
|
128
|
+
} | undefined;
|
|
113
129
|
export declare const AUDIT_EVENTS_SCHEMA: CollectionSchema;
|
|
114
130
|
/**
|
|
115
131
|
* Audit event storage, written once against the generic `FactoryStorageOps`
|
|
@@ -127,7 +143,7 @@ export declare class AuditStorage extends FactoryStorageDomain {
|
|
|
127
143
|
event: AuditEventRow;
|
|
128
144
|
created: boolean;
|
|
129
145
|
}>;
|
|
130
|
-
/** List an org's audit events newest-first with keyset pagination. */
|
|
146
|
+
/** List an org's audit events newest-first (or oldest-first with `after`) with keyset pagination. */
|
|
131
147
|
list(input: ListAuditEventsInput): Promise<AuditEventPage>;
|
|
132
148
|
}
|
|
133
149
|
//# sourceMappingURL=base.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/base.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/audit/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -59,6 +59,20 @@ function decodeAuditCursor(cursor) {
|
|
|
59
59
|
id
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
|
+
/** An `after` cursor at an instant: paging from it skips every row recorded at or before then. */
|
|
63
|
+
function auditCursorAt(instant) {
|
|
64
|
+
return `${instant.toISOString()}_ffffffff-ffff-ffff-ffff-ffffffffffff`;
|
|
65
|
+
}
|
|
66
|
+
const AUDIT_EVENT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
67
|
+
/**
|
|
68
|
+
* Decode an `after` cursor, or `undefined` when it can't be paged from. Its id
|
|
69
|
+
* must be a UUID like every audit row id: Postgres rejects any other id in the
|
|
70
|
+
* keyset comparison.
|
|
71
|
+
*/
|
|
72
|
+
function decodeAuditAfterCursor(cursor) {
|
|
73
|
+
const decoded = decodeAuditCursor(cursor);
|
|
74
|
+
return decoded && AUDIT_EVENT_ID.test(decoded.id) ? decoded : void 0;
|
|
75
|
+
}
|
|
62
76
|
const AUDIT_EVENTS_SCHEMA = {
|
|
63
77
|
name: "audit_events",
|
|
64
78
|
columns: {
|
|
@@ -178,16 +192,20 @@ var AuditStorage = class extends FactoryStorageDomain {
|
|
|
178
192
|
occurred_at: input.occurredAt ?? /* @__PURE__ */ new Date()
|
|
179
193
|
}));
|
|
180
194
|
}
|
|
181
|
-
/** List an org's audit events newest-first with keyset pagination. */
|
|
195
|
+
/** List an org's audit events newest-first (or oldest-first with `after`) with keyset pagination. */
|
|
182
196
|
async list(input) {
|
|
197
|
+
if (input.before && input.after) throw new Error("Audit list takes `before` or `after`, not both");
|
|
183
198
|
const limit = clampAuditLimit(input.limit);
|
|
184
199
|
const where = { org_id: input.orgId };
|
|
185
200
|
if (input.factoryProjectId) where.factory_project_id = input.factoryProjectId;
|
|
186
201
|
if (input.actions && input.actions.length > 0) where.action = { in: input.actions };
|
|
187
202
|
if (input.actorId) where.actor_id = input.actorId;
|
|
188
|
-
const
|
|
203
|
+
const forward = input.after !== void 0;
|
|
204
|
+
const cursor = input.after !== void 0 ? decodeAuditAfterCursor(input.after) : input.before ? decodeAuditCursor(input.before) : void 0;
|
|
205
|
+
if (forward && !cursor) throw new Error(`Invalid audit cursor '${input.after}'`);
|
|
206
|
+
const direction = forward ? "asc" : "desc";
|
|
189
207
|
const rows = await this.#db.findMany("audit_events", where, {
|
|
190
|
-
orderBy: [["occurred_at",
|
|
208
|
+
orderBy: [["occurred_at", direction], ["id", direction]],
|
|
191
209
|
limit: limit + 1,
|
|
192
210
|
...cursor ? { cursor: { values: [cursor.occurredAt, cursor.id] } } : {}
|
|
193
211
|
});
|
|
@@ -201,6 +219,6 @@ var AuditStorage = class extends FactoryStorageDomain {
|
|
|
201
219
|
}
|
|
202
220
|
};
|
|
203
221
|
//#endregion
|
|
204
|
-
export { ACTOR_PROFILE_METADATA_KEY, AUDIT_EVENTS_SCHEMA, AuditStorage, auditActorProfile, boundAuditMetadata, clampAuditLimit, decodeAuditCursor, encodeAuditCursor };
|
|
222
|
+
export { ACTOR_PROFILE_METADATA_KEY, AUDIT_EVENTS_SCHEMA, AuditStorage, auditActorProfile, auditCursorAt, boundAuditMetadata, clampAuditLimit, decodeAuditAfterCursor, decodeAuditCursor, encodeAuditCursor };
|
|
205
223
|
|
|
206
224
|
//# sourceMappingURL=base.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/audit/base.ts"],"sourcesContent":["/**\n * Factory audit events domain — the append-only \"who did what, when\" trail\n * behind the software factory.\n *\n * Rows are append-only: there is no update/delete API, and the table is the\n * local source of truth even when the WorkOS Audit Logs mirror is unavailable.\n * Tenancy is org-first, like `work_items`: `actor_id` records who acted but\n * never scopes reads.\n *\n * The actions the trail holds are listed in `./actions.ts`.\n *\n * Agent events carry `actor_type = 'agent'` with `actor_id = 'agent:<threadId>'`\n * and `metadata.startedBy = <userId>` chaining accountability back to the human\n * whose message drove the run. Rule-driven events carry `actor_type = 'system'`.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\n\nimport type { AuditActorType } from './actors.js';\n\n/** What an audit event acted on (WorkOS Audit Logs target shape). */\nexport interface AuditTarget {\n /** Target kind, e.g. 'work_item', 'worktree', 'issue', 'pull_request'. */\n type: string;\n /** Stable identifier of the target (row id, branch name, issue number...). */\n id: string;\n /** Human-readable label (work-item title, branch name...). */\n name?: string;\n}\n\nexport type { AuditActorType } from './actors.js';\n\n/** Display name and avatar of a human actor, stamped at record time because MastraAuthStudio cannot resolve users by id. */\nexport interface AuditActorProfileInput {\n name?: string;\n avatarUrl?: string;\n}\n\nexport const ACTOR_PROFILE_METADATA_KEY = '__actorProfile';\n\nexport function auditActorProfile(\n user: { name?: string; email?: string; avatarUrl?: string } | undefined,\n): AuditActorProfileInput | undefined {\n const name = user?.name?.trim() || user?.email?.trim();\n const avatarUrl = user?.avatarUrl?.trim();\n if (!name && !avatarUrl) return undefined;\n return { ...(name ? { name } : {}), ...(avatarUrl ? { avatarUrl } : {}) };\n}\n\n/** Request context captured alongside the event. */\nexport interface AuditContext {\n /** Client IP (first hop of `x-forwarded-for`) when available. */\n location?: string;\n /** Request `user-agent` header when available. */\n userAgent?: string;\n}\n\n/** One persisted audit event. */\nexport interface AuditEventRow {\n id: string;\n /** Owning WorkOS organization id — the trail is org-wide. */\n orgId: string;\n /** WorkOS user id of whoever performed the action, or `agent:<threadId>`. */\n actorId: string;\n /** Whether a human or an agent (inside a run) performed the action. */\n actorType: AuditActorType;\n /** Dot-namespaced action, e.g. 'factory.work_item.stage_moved'. */\n action: string;\n /** What was acted on. */\n targets: AuditTarget[];\n /** Bounded event summary — never full payloads, never secrets. */\n metadata: Record<string, unknown>;\n /** Factory project the event is scoped to; null for org-level events. */\n factoryProjectId: string | null;\n /** Project-repository link affected by a repository-specific action. */\n projectRepositoryId: string | null;\n /** Request context (`x-forwarded-for` / `user-agent`). */\n context: AuditContext;\n occurredAt: Date;\n}\n\nexport interface RecordAuditEventInput<Action extends string = string> {\n idempotencyKey?: string;\n orgId: string;\n actorId: string;\n /** Who performed the action; defaults to 'human'. */\n actorType?: AuditActorType;\n actorProfile?: AuditActorProfileInput;\n action: Action;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n factoryProjectId?: string;\n projectRepositoryId?: string;\n context?: AuditContext;\n occurredAt?: Date;\n}\n\n/** A fully-normalized event ready to persist (id assigned on insert). */\nexport type AuditEventInsert = Omit<AuditEventRow, 'id'>;\n\nexport interface ListAuditEventsInput {\n orgId: string;\n factoryProjectId?: string;\n /** Restrict to these actions (exact match). */\n actions?: string[];\n actorId?: string;\n /** Opaque cursor from a previous page (`nextCursor`). */\n before?: string;\n limit?: number;\n}\n\nexport interface AuditEventPage {\n events: AuditEventRow[];\n /** Pass back as `before` to fetch the next (older) page; absent at the end. */\n nextCursor?: string;\n}\n\n/** Metadata is a bounded summary — never full payloads, never secrets. */\nconst MAX_METADATA_JSON_LENGTH = 4096;\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 200;\n\n/** Truncate oversized metadata rather than dropping the whole event. */\nexport function boundAuditMetadata(metadata: Record<string, unknown> | undefined): Record<string, unknown> {\n if (!metadata) return {};\n try {\n if (JSON.stringify(metadata).length <= MAX_METADATA_JSON_LENGTH) return metadata;\n } catch {\n return { truncated: true };\n }\n return { truncated: true };\n}\n\n/** Normalize a requested page size to a finite integer in `[1, MAX_PAGE_SIZE]`. */\nexport function clampAuditLimit(limit: number | undefined): number {\n const normalized = typeof limit === 'number' && Number.isFinite(limit) ? Math.trunc(limit) : DEFAULT_PAGE_SIZE;\n return Math.min(Math.max(normalized, 1), MAX_PAGE_SIZE);\n}\n\n/** Encode the `(occurredAt, id)` keyset cursor of a row. */\nexport function encodeAuditCursor(row: AuditEventRow): string {\n return `${row.occurredAt.toISOString()}_${row.id}`;\n}\n\n/** Decode a cursor back into its `(occurredAt, id)` parts, or `undefined`. */\nexport function decodeAuditCursor(cursor: string): { occurredAt: Date; id: string } | undefined {\n const sep = cursor.lastIndexOf('_');\n if (sep <= 0) return undefined;\n const occurredAt = new Date(cursor.slice(0, sep));\n const id = cursor.slice(sep + 1);\n if (Number.isNaN(occurredAt.getTime()) || !id) return undefined;\n return { occurredAt, id };\n}\n\nexport const AUDIT_EVENTS_SCHEMA: CollectionSchema = {\n name: 'audit_events',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n actor_id: { type: 'text' },\n actor_type: { type: 'text', default: 'human' },\n action: { type: 'text' },\n targets: { type: 'json' },\n metadata: { type: 'json' },\n factory_project_id: { type: 'text', nullable: true },\n project_repository_id: { type: 'text', nullable: true },\n context: { type: 'json' },\n occurred_at: { type: 'timestamp' },\n },\n indexes: [\n { name: 'audit_events_org_occurred_idx', columns: ['org_id', 'occurred_at'] },\n { name: 'audit_events_org_project_occurred_idx', columns: ['org_id', 'factory_project_id', 'occurred_at'] },\n ],\n};\n\n/** Column shape of one `audit_events` row as returned by ops. */\ninterface AuditEventDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n actor_id: string;\n actor_type: AuditActorType;\n action: string;\n targets: AuditTarget[];\n metadata: Record<string, unknown>;\n factory_project_id: string | null;\n project_repository_id: string | null;\n context: AuditContext;\n occurred_at: Date;\n}\n\nfunction toRow(db: AuditEventDbRow): AuditEventRow {\n return {\n id: db.id,\n orgId: db.org_id,\n actorId: db.actor_id,\n actorType: db.actor_type,\n action: db.action,\n targets: db.targets,\n metadata: db.metadata,\n factoryProjectId: db.factory_project_id,\n projectRepositoryId: db.project_repository_id,\n context: db.context,\n occurredAt: db.occurred_at,\n };\n}\n\n/**\n * Audit event storage, written once against the generic `FactoryStorageOps`\n * surface. `record()` normalizes inputs (defaults, metadata bounding);\n * `list()` is keyset-paginated on `(occurred_at, id)` newest-first.\n */\nexport class AuditStorage extends FactoryStorageDomain {\n constructor() {\n super('audit');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([AUDIT_EVENTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('audit_events', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** Append one audit event. Throws on failure — swallow-on-failure lives in the caller. */\n async record(input: RecordAuditEventInput): Promise<AuditEventRow> {\n if (input.idempotencyKey) return (await this.recordOnce(input)).event;\n return this.#insert(input);\n }\n\n async recordOnce(input: RecordAuditEventInput): Promise<{ event: AuditEventRow; created: boolean }> {\n if (!input.idempotencyKey) throw new Error('An audit idempotency key is required');\n const hash = Buffer.from(\n await globalThis.crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(\n JSON.stringify([input.orgId, input.factoryProjectId ?? null, input.action, input.idempotencyKey]),\n ),\n ),\n ).toString('hex');\n const id = `${hash.slice(0, 8)}-${hash.slice(8, 12)}-8${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`;\n try {\n return { event: await this.#insert(input, id), created: true };\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n const existing = await this.#db.findOne<AuditEventDbRow>('audit_events', { id, org_id: input.orgId });\n if (!existing) throw error;\n return { event: toRow(existing), created: false };\n }\n }\n\n async #insert(input: RecordAuditEventInput, id?: string): Promise<AuditEventRow> {\n const inserted = await this.#db.insertOne<AuditEventDbRow>('audit_events', {\n ...(id ? { id } : {}),\n org_id: input.orgId,\n actor_id: input.actorId,\n actor_type: input.actorType ?? 'human',\n action: input.action,\n targets: input.targets,\n metadata: boundAuditMetadata(\n input.actorProfile ? { ...input.metadata, [ACTOR_PROFILE_METADATA_KEY]: input.actorProfile } : input.metadata,\n ),\n factory_project_id: input.factoryProjectId ?? null,\n project_repository_id: input.projectRepositoryId ?? null,\n context: input.context ?? {},\n occurred_at: input.occurredAt ?? new Date(),\n });\n return toRow(inserted);\n }\n\n /** List an org's audit events newest-first with keyset pagination. */\n async list(input: ListAuditEventsInput): Promise<AuditEventPage> {\n const limit = clampAuditLimit(input.limit);\n\n const where: CollectionWhere = { org_id: input.orgId };\n if (input.factoryProjectId) where.factory_project_id = input.factoryProjectId;\n if (input.actions && input.actions.length > 0) where.action = { in: input.actions };\n if (input.actorId) where.actor_id = input.actorId;\n\n const cursor = input.before ? decodeAuditCursor(input.before) : undefined;\n\n const rows = await this.#db.findMany<AuditEventDbRow>('audit_events', where, {\n orderBy: [\n ['occurred_at', 'desc'],\n ['id', 'desc'],\n ],\n limit: limit + 1,\n ...(cursor ? { cursor: { values: [cursor.occurredAt, cursor.id] } } : {}),\n });\n\n const events = rows.slice(0, limit).map(toRow);\n const hasMore = rows.length > limit;\n const last = events[events.length - 1];\n return {\n events,\n ...(hasMore && last ? { nextCursor: encodeAuditCursor(last) } : {}),\n };\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/audit/base.ts"],"sourcesContent":["/**\n * Factory audit events domain — the append-only \"who did what, when\" trail\n * behind the software factory.\n *\n * Rows are append-only: there is no update/delete API, and the table is the\n * local source of truth even when the WorkOS Audit Logs mirror is unavailable.\n * Tenancy is org-first, like `work_items`: `actor_id` records who acted but\n * never scopes reads.\n *\n * The actions the trail holds are listed in `./actions.ts`.\n *\n * Agent events carry `actor_type = 'agent'` with `actor_id = 'agent:<threadId>'`\n * and `metadata.startedBy = <userId>` chaining accountability back to the human\n * whose message drove the run. Rule-driven events carry `actor_type = 'system'`.\n */\n\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, CollectionWhere, FactoryStorageOps } from '@mastra/core/storage';\n\nimport type { AuditActorType } from './actors.js';\n\n/** What an audit event acted on (WorkOS Audit Logs target shape). */\nexport interface AuditTarget {\n /** Target kind, e.g. 'work_item', 'worktree', 'issue', 'pull_request'. */\n type: string;\n /** Stable identifier of the target (row id, branch name, issue number...). */\n id: string;\n /** Human-readable label (work-item title, branch name...). */\n name?: string;\n}\n\nexport type { AuditActorType } from './actors.js';\n\n/** Display name and avatar of a human actor, stamped at record time because MastraAuthStudio cannot resolve users by id. */\nexport interface AuditActorProfileInput {\n name?: string;\n avatarUrl?: string;\n}\n\nexport const ACTOR_PROFILE_METADATA_KEY = '__actorProfile';\n\nexport function auditActorProfile(\n user: { name?: string; email?: string; avatarUrl?: string } | undefined,\n): AuditActorProfileInput | undefined {\n const name = user?.name?.trim() || user?.email?.trim();\n const avatarUrl = user?.avatarUrl?.trim();\n if (!name && !avatarUrl) return undefined;\n return { ...(name ? { name } : {}), ...(avatarUrl ? { avatarUrl } : {}) };\n}\n\n/** Request context captured alongside the event. */\nexport interface AuditContext {\n /** Client IP (first hop of `x-forwarded-for`) when available. */\n location?: string;\n /** Request `user-agent` header when available. */\n userAgent?: string;\n}\n\n/** One persisted audit event. */\nexport interface AuditEventRow {\n id: string;\n /** Owning WorkOS organization id — the trail is org-wide. */\n orgId: string;\n /** WorkOS user id of whoever performed the action, or `agent:<threadId>`. */\n actorId: string;\n /** Whether a human or an agent (inside a run) performed the action. */\n actorType: AuditActorType;\n /** Dot-namespaced action, e.g. 'factory.work_item.stage_moved'. */\n action: string;\n /** What was acted on. */\n targets: AuditTarget[];\n /** Bounded event summary — never full payloads, never secrets. */\n metadata: Record<string, unknown>;\n /** Factory project the event is scoped to; null for org-level events. */\n factoryProjectId: string | null;\n /** Project-repository link affected by a repository-specific action. */\n projectRepositoryId: string | null;\n /** Request context (`x-forwarded-for` / `user-agent`). */\n context: AuditContext;\n occurredAt: Date;\n}\n\nexport interface RecordAuditEventInput<Action extends string = string> {\n idempotencyKey?: string;\n orgId: string;\n actorId: string;\n /** Who performed the action; defaults to 'human'. */\n actorType?: AuditActorType;\n actorProfile?: AuditActorProfileInput;\n action: Action;\n targets: AuditTarget[];\n metadata?: Record<string, unknown>;\n factoryProjectId?: string;\n projectRepositoryId?: string;\n context?: AuditContext;\n occurredAt?: Date;\n}\n\n/** A fully-normalized event ready to persist (id assigned on insert). */\nexport type AuditEventInsert = Omit<AuditEventRow, 'id'>;\n\nexport interface ListAuditEventsInput {\n orgId: string;\n factoryProjectId?: string;\n /** Restrict to these actions (exact match). */\n actions?: string[];\n actorId?: string;\n /** Opaque cursor from a previous page (`nextCursor`). */\n before?: string;\n /**\n * Page oldest-first, strictly after this cursor: `nextCursor` of a previous\n * `after` page, or `encodeAuditCursor` of a row. Can't be combined with `before`.\n */\n after?: string;\n limit?: number;\n}\n\nexport interface AuditEventPage {\n events: AuditEventRow[];\n /** Pass back as `before` (or `after`, when paging forward) to fetch the next page; absent at the end. */\n nextCursor?: string;\n}\n\n/** Metadata is a bounded summary — never full payloads, never secrets. */\nconst MAX_METADATA_JSON_LENGTH = 4096;\n\nconst DEFAULT_PAGE_SIZE = 50;\nconst MAX_PAGE_SIZE = 200;\n\n/** Truncate oversized metadata rather than dropping the whole event. */\nexport function boundAuditMetadata(metadata: Record<string, unknown> | undefined): Record<string, unknown> {\n if (!metadata) return {};\n try {\n if (JSON.stringify(metadata).length <= MAX_METADATA_JSON_LENGTH) return metadata;\n } catch {\n return { truncated: true };\n }\n return { truncated: true };\n}\n\n/** Normalize a requested page size to a finite integer in `[1, MAX_PAGE_SIZE]`. */\nexport function clampAuditLimit(limit: number | undefined): number {\n const normalized = typeof limit === 'number' && Number.isFinite(limit) ? Math.trunc(limit) : DEFAULT_PAGE_SIZE;\n return Math.min(Math.max(normalized, 1), MAX_PAGE_SIZE);\n}\n\n/** Encode the `(occurredAt, id)` keyset cursor of a row. */\nexport function encodeAuditCursor(row: AuditEventRow): string {\n return `${row.occurredAt.toISOString()}_${row.id}`;\n}\n\n/** Decode a cursor back into its `(occurredAt, id)` parts, or `undefined`. */\nexport function decodeAuditCursor(cursor: string): { occurredAt: Date; id: string } | undefined {\n const sep = cursor.lastIndexOf('_');\n if (sep <= 0) return undefined;\n const occurredAt = new Date(cursor.slice(0, sep));\n const id = cursor.slice(sep + 1);\n if (Number.isNaN(occurredAt.getTime()) || !id) return undefined;\n return { occurredAt, id };\n}\n\n/** An `after` cursor at an instant: paging from it skips every row recorded at or before then. */\nexport function auditCursorAt(instant: Date): string {\n return `${instant.toISOString()}_ffffffff-ffff-ffff-ffff-ffffffffffff`;\n}\n\nconst AUDIT_EVENT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;\n\n/**\n * Decode an `after` cursor, or `undefined` when it can't be paged from. Its id\n * must be a UUID like every audit row id: Postgres rejects any other id in the\n * keyset comparison.\n */\nexport function decodeAuditAfterCursor(cursor: string): { occurredAt: Date; id: string } | undefined {\n const decoded = decodeAuditCursor(cursor);\n return decoded && AUDIT_EVENT_ID.test(decoded.id) ? decoded : undefined;\n}\n\nexport const AUDIT_EVENTS_SCHEMA: CollectionSchema = {\n name: 'audit_events',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n actor_id: { type: 'text' },\n actor_type: { type: 'text', default: 'human' },\n action: { type: 'text' },\n targets: { type: 'json' },\n metadata: { type: 'json' },\n factory_project_id: { type: 'text', nullable: true },\n project_repository_id: { type: 'text', nullable: true },\n context: { type: 'json' },\n occurred_at: { type: 'timestamp' },\n },\n indexes: [\n { name: 'audit_events_org_occurred_idx', columns: ['org_id', 'occurred_at'] },\n { name: 'audit_events_org_project_occurred_idx', columns: ['org_id', 'factory_project_id', 'occurred_at'] },\n ],\n};\n\n/** Column shape of one `audit_events` row as returned by ops. */\ninterface AuditEventDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n actor_id: string;\n actor_type: AuditActorType;\n action: string;\n targets: AuditTarget[];\n metadata: Record<string, unknown>;\n factory_project_id: string | null;\n project_repository_id: string | null;\n context: AuditContext;\n occurred_at: Date;\n}\n\nfunction toRow(db: AuditEventDbRow): AuditEventRow {\n return {\n id: db.id,\n orgId: db.org_id,\n actorId: db.actor_id,\n actorType: db.actor_type,\n action: db.action,\n targets: db.targets,\n metadata: db.metadata,\n factoryProjectId: db.factory_project_id,\n projectRepositoryId: db.project_repository_id,\n context: db.context,\n occurredAt: db.occurred_at,\n };\n}\n\n/**\n * Audit event storage, written once against the generic `FactoryStorageOps`\n * surface. `record()` normalizes inputs (defaults, metadata bounding);\n * `list()` is keyset-paginated on `(occurred_at, id)` newest-first.\n */\nexport class AuditStorage extends FactoryStorageDomain {\n constructor() {\n super('audit');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([AUDIT_EVENTS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('audit_events', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n /** Append one audit event. Throws on failure — swallow-on-failure lives in the caller. */\n async record(input: RecordAuditEventInput): Promise<AuditEventRow> {\n if (input.idempotencyKey) return (await this.recordOnce(input)).event;\n return this.#insert(input);\n }\n\n async recordOnce(input: RecordAuditEventInput): Promise<{ event: AuditEventRow; created: boolean }> {\n if (!input.idempotencyKey) throw new Error('An audit idempotency key is required');\n const hash = Buffer.from(\n await globalThis.crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(\n JSON.stringify([input.orgId, input.factoryProjectId ?? null, input.action, input.idempotencyKey]),\n ),\n ),\n ).toString('hex');\n const id = `${hash.slice(0, 8)}-${hash.slice(8, 12)}-8${hash.slice(13, 16)}-a${hash.slice(17, 20)}-${hash.slice(20, 32)}`;\n try {\n return { event: await this.#insert(input, id), created: true };\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n const existing = await this.#db.findOne<AuditEventDbRow>('audit_events', { id, org_id: input.orgId });\n if (!existing) throw error;\n return { event: toRow(existing), created: false };\n }\n }\n\n async #insert(input: RecordAuditEventInput, id?: string): Promise<AuditEventRow> {\n const inserted = await this.#db.insertOne<AuditEventDbRow>('audit_events', {\n ...(id ? { id } : {}),\n org_id: input.orgId,\n actor_id: input.actorId,\n actor_type: input.actorType ?? 'human',\n action: input.action,\n targets: input.targets,\n metadata: boundAuditMetadata(\n input.actorProfile ? { ...input.metadata, [ACTOR_PROFILE_METADATA_KEY]: input.actorProfile } : input.metadata,\n ),\n factory_project_id: input.factoryProjectId ?? null,\n project_repository_id: input.projectRepositoryId ?? null,\n context: input.context ?? {},\n occurred_at: input.occurredAt ?? new Date(),\n });\n return toRow(inserted);\n }\n\n /** List an org's audit events newest-first (or oldest-first with `after`) with keyset pagination. */\n async list(input: ListAuditEventsInput): Promise<AuditEventPage> {\n if (input.before && input.after) throw new Error('Audit list takes `before` or `after`, not both');\n const limit = clampAuditLimit(input.limit);\n\n const where: CollectionWhere = { org_id: input.orgId };\n if (input.factoryProjectId) where.factory_project_id = input.factoryProjectId;\n if (input.actions && input.actions.length > 0) where.action = { in: input.actions };\n if (input.actorId) where.actor_id = input.actorId;\n\n const forward = input.after !== undefined;\n const cursor =\n input.after !== undefined\n ? decodeAuditAfterCursor(input.after)\n : input.before\n ? decodeAuditCursor(input.before)\n : undefined;\n // Paging forward from an unreadable cursor would replay the whole trail.\n if (forward && !cursor) throw new Error(`Invalid audit cursor '${input.after}'`);\n const direction = forward ? 'asc' : 'desc';\n\n const rows = await this.#db.findMany<AuditEventDbRow>('audit_events', where, {\n orderBy: [\n ['occurred_at', direction],\n ['id', direction],\n ],\n limit: limit + 1,\n ...(cursor ? { cursor: { values: [cursor.occurredAt, cursor.id] } } : {}),\n });\n\n const events = rows.slice(0, limit).map(toRow);\n const hasMore = rows.length > limit;\n const last = events[events.length - 1];\n return {\n events,\n ...(hasMore && last ? { nextCursor: encodeAuditCursor(last) } : {}),\n };\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';
|
|
1
2
|
import { FactoryStorageDomain } from '@mastra/core/storage';
|
|
2
3
|
import type { CollectionSchema } from '@mastra/core/storage';
|
|
3
4
|
export interface ModelDefaultRecord {
|
|
4
5
|
orgId: string;
|
|
5
6
|
userId: string;
|
|
6
7
|
modelId: string;
|
|
8
|
+
thinkingLevel: ThinkingLevelSetting | null;
|
|
7
9
|
updatedAt: Date;
|
|
8
10
|
}
|
|
9
11
|
export declare const USER_DEFAULT_MODELS_SCHEMA: CollectionSchema;
|
|
@@ -21,6 +23,12 @@ export declare class ModelDefaultsStorage extends FactoryStorageDomain {
|
|
|
21
23
|
userId: string;
|
|
22
24
|
modelId: string;
|
|
23
25
|
}): Promise<ModelDefaultRecord>;
|
|
26
|
+
/** Null when the user has no personal model: a personal thinking level never exists without one. */
|
|
27
|
+
setThinkingLevel({ orgId, userId, thinkingLevel, }: {
|
|
28
|
+
orgId: string;
|
|
29
|
+
userId: string;
|
|
30
|
+
thinkingLevel: ThinkingLevelSetting | null;
|
|
31
|
+
}): Promise<ModelDefaultRecord | null>;
|
|
24
32
|
clear({ orgId, userId }: {
|
|
25
33
|
orgId: string;
|
|
26
34
|
userId: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/model-defaults/base.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/storage/domains/model-defaults/base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAwB,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAqB,MAAM,sBAAsB,CAAC;AAEhF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,eAAO,MAAM,0BAA0B,EAAE,gBAWxC,CAAC;AAqBF,qBAAa,oBAAqB,SAAQ,oBAAoB;;;IAKtD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQpC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAQ7F,GAAG,CAAC,EACR,KAAK,EACL,MAAM,EACN,OAAO,GACR,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0B/B,oGAAoG;IAC9F,gBAAgB,CAAC,EACrB,KAAK,EACL,MAAM,EACN,aAAa,GACd,EAAE;QACD,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,oBAAoB,GAAG,IAAI,CAAC;KAC5C,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC;IAShC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAGpF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
2
|
+
import { isThinkingLevelSetting } from "@mastra/code-sdk/thinking";
|
|
2
3
|
//#region src/storage/domains/model-defaults/base.ts
|
|
3
4
|
const USER_DEFAULT_MODELS_SCHEMA = {
|
|
4
5
|
name: "user_default_models",
|
|
@@ -7,6 +8,10 @@ const USER_DEFAULT_MODELS_SCHEMA = {
|
|
|
7
8
|
org_id: { type: "text" },
|
|
8
9
|
user_id: { type: "text" },
|
|
9
10
|
model_id: { type: "text" },
|
|
11
|
+
thinking_level: {
|
|
12
|
+
type: "text",
|
|
13
|
+
nullable: true
|
|
14
|
+
},
|
|
10
15
|
updated_at: { type: "timestamp" }
|
|
11
16
|
},
|
|
12
17
|
uniqueIndexes: [{
|
|
@@ -19,6 +24,7 @@ function toRecord(row) {
|
|
|
19
24
|
orgId: row.org_id,
|
|
20
25
|
userId: row.user_id,
|
|
21
26
|
modelId: row.model_id,
|
|
27
|
+
thinkingLevel: isThinkingLevelSetting(row.thinking_level) ? row.thinking_level : null,
|
|
22
28
|
updatedAt: row.updated_at
|
|
23
29
|
};
|
|
24
30
|
}
|
|
@@ -57,6 +63,7 @@ var ModelDefaultsStorage = class extends FactoryStorageDomain {
|
|
|
57
63
|
return toRecord(await this.#db.insertOne("user_default_models", {
|
|
58
64
|
org_id: orgId,
|
|
59
65
|
user_id: userId,
|
|
66
|
+
thinking_level: null,
|
|
60
67
|
...values
|
|
61
68
|
}));
|
|
62
69
|
} catch (error) {
|
|
@@ -66,6 +73,17 @@ var ModelDefaultsStorage = class extends FactoryStorageDomain {
|
|
|
66
73
|
return toRecord(row);
|
|
67
74
|
}
|
|
68
75
|
}
|
|
76
|
+
/** Null when the user has no personal model: a personal thinking level never exists without one. */
|
|
77
|
+
async setThinkingLevel({ orgId, userId, thinkingLevel }) {
|
|
78
|
+
const updated = await this.#db.updateAtomic("user_default_models", {
|
|
79
|
+
org_id: orgId,
|
|
80
|
+
user_id: userId
|
|
81
|
+
}, () => ({
|
|
82
|
+
thinking_level: thinkingLevel,
|
|
83
|
+
updated_at: /* @__PURE__ */ new Date()
|
|
84
|
+
}));
|
|
85
|
+
return updated ? toRecord(updated) : null;
|
|
86
|
+
}
|
|
69
87
|
async clear({ orgId, userId }) {
|
|
70
88
|
return await this.#db.deleteMany("user_default_models", {
|
|
71
89
|
org_id: orgId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/model-defaults/base.ts"],"sourcesContent":["import { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface ModelDefaultRecord {\n orgId: string;\n userId: string;\n modelId: string;\n updatedAt: Date;\n}\n\nexport const USER_DEFAULT_MODELS_SCHEMA: CollectionSchema = {\n name: 'user_default_models',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n model_id: { type: 'text' },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'user_default_models_org_user_key', columns: ['org_id', 'user_id'] }],\n};\n\ninterface ModelDefaultDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n user_id: string;\n model_id: string;\n updated_at: Date;\n}\n\nfunction toRecord(row: ModelDefaultDbRow): ModelDefaultRecord {\n return {\n orgId: row.org_id,\n userId: row.user_id,\n modelId: row.model_id,\n updatedAt: row.updated_at,\n };\n}\n\nexport class ModelDefaultsStorage extends FactoryStorageDomain {\n constructor() {\n super('model-defaults');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([USER_DEFAULT_MODELS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('user_default_models', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async get({ orgId, userId }: { orgId: string; userId: string }): Promise<ModelDefaultRecord | null> {\n const row = await this.#db.findOne<ModelDefaultDbRow>('user_default_models', {\n org_id: orgId,\n user_id: userId,\n });\n return row ? toRecord(row) : null;\n }\n\n async set({\n orgId,\n userId,\n modelId,\n }: {\n orgId: string;\n userId: string;\n modelId: string;\n }): Promise<ModelDefaultRecord> {\n const now = new Date();\n const values = { model_id: modelId, updated_at: now };\n const updateExisting = () =>\n this.#db.updateAtomic<ModelDefaultDbRow>('user_default_models', { org_id: orgId, user_id: userId }, () => values);\n\n const updated = await updateExisting();\n if (updated) return toRecord(updated);\n\n try {\n return toRecord(\n await this.#db.insertOne<ModelDefaultDbRow>('user_default_models', {\n org_id: orgId,\n user_id: userId,\n ...values,\n }),\n );\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n const row = await updateExisting();\n if (!row) throw error;\n return toRecord(row);\n }\n }\n\n async clear({ orgId, userId }: { orgId: string; userId: string }): Promise<boolean> {\n return (await this.#db.deleteMany('user_default_models', { org_id: orgId, user_id: userId })) > 0;\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"base.js","names":[],"sources":["../../../../src/storage/domains/model-defaults/base.ts"],"sourcesContent":["import { isThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport type { ThinkingLevelSetting } from '@mastra/code-sdk/thinking';\nimport { FactoryStorageDomain, UniqueViolationError } from '@mastra/core/storage';\nimport type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';\n\nexport interface ModelDefaultRecord {\n orgId: string;\n userId: string;\n modelId: string;\n thinkingLevel: ThinkingLevelSetting | null;\n updatedAt: Date;\n}\n\nexport const USER_DEFAULT_MODELS_SCHEMA: CollectionSchema = {\n name: 'user_default_models',\n columns: {\n id: { type: 'uuid-pk' },\n org_id: { type: 'text' },\n user_id: { type: 'text' },\n model_id: { type: 'text' },\n thinking_level: { type: 'text', nullable: true },\n updated_at: { type: 'timestamp' },\n },\n uniqueIndexes: [{ name: 'user_default_models_org_user_key', columns: ['org_id', 'user_id'] }],\n};\n\ninterface ModelDefaultDbRow extends Record<string, unknown> {\n id: string;\n org_id: string;\n user_id: string;\n model_id: string;\n thinking_level: string | null;\n updated_at: Date;\n}\n\nfunction toRecord(row: ModelDefaultDbRow): ModelDefaultRecord {\n return {\n orgId: row.org_id,\n userId: row.user_id,\n modelId: row.model_id,\n thinkingLevel: isThinkingLevelSetting(row.thinking_level) ? row.thinking_level : null,\n updatedAt: row.updated_at,\n };\n}\n\nexport class ModelDefaultsStorage extends FactoryStorageDomain {\n constructor() {\n super('model-defaults');\n }\n\n async init(): Promise<void> {\n await this.ensureCollections([USER_DEFAULT_MODELS_SCHEMA]);\n }\n\n async dangerouslyClearAll(): Promise<void> {\n await this.ops.deleteMany('user_default_models', {});\n }\n\n get #db(): FactoryStorageOps {\n return this.ops;\n }\n\n async get({ orgId, userId }: { orgId: string; userId: string }): Promise<ModelDefaultRecord | null> {\n const row = await this.#db.findOne<ModelDefaultDbRow>('user_default_models', {\n org_id: orgId,\n user_id: userId,\n });\n return row ? toRecord(row) : null;\n }\n\n async set({\n orgId,\n userId,\n modelId,\n }: {\n orgId: string;\n userId: string;\n modelId: string;\n }): Promise<ModelDefaultRecord> {\n const now = new Date();\n const values = { model_id: modelId, updated_at: now };\n const updateExisting = () =>\n this.#db.updateAtomic<ModelDefaultDbRow>('user_default_models', { org_id: orgId, user_id: userId }, () => values);\n\n const updated = await updateExisting();\n if (updated) return toRecord(updated);\n\n try {\n return toRecord(\n await this.#db.insertOne<ModelDefaultDbRow>('user_default_models', {\n org_id: orgId,\n user_id: userId,\n thinking_level: null,\n ...values,\n }),\n );\n } catch (error) {\n if (!(error instanceof UniqueViolationError)) throw error;\n const row = await updateExisting();\n if (!row) throw error;\n return toRecord(row);\n }\n }\n\n /** Null when the user has no personal model: a personal thinking level never exists without one. */\n async setThinkingLevel({\n orgId,\n userId,\n thinkingLevel,\n }: {\n orgId: string;\n userId: string;\n thinkingLevel: ThinkingLevelSetting | null;\n }): Promise<ModelDefaultRecord | null> {\n const updated = await this.#db.updateAtomic<ModelDefaultDbRow>(\n 'user_default_models',\n { org_id: orgId, user_id: userId },\n () => ({ thinking_level: thinkingLevel, updated_at: new Date() }),\n );\n return updated ? toRecord(updated) : null;\n }\n\n async clear({ orgId, userId }: { orgId: string; userId: string }): Promise<boolean> {\n return (await this.#db.deleteMany('user_default_models', { org_id: orgId, user_id: userId })) > 0;\n }\n}\n"],"mappings":";;;AAaA,MAAa,6BAA+C;CAC1D,MAAM;CACN,SAAS;EACP,IAAI,EAAE,MAAM,UAAU;EACtB,QAAQ,EAAE,MAAM,OAAO;EACvB,SAAS,EAAE,MAAM,OAAO;EACxB,UAAU,EAAE,MAAM,OAAO;EACzB,gBAAgB;GAAE,MAAM;GAAQ,UAAU;EAAK;EAC/C,YAAY,EAAE,MAAM,YAAY;CAClC;CACA,eAAe,CAAC;EAAE,MAAM;EAAoC,SAAS,CAAC,UAAU,SAAS;CAAE,CAAC;AAC9F;AAWA,SAAS,SAAS,KAA4C;CAC5D,OAAO;EACL,OAAO,IAAI;EACX,QAAQ,IAAI;EACZ,SAAS,IAAI;EACb,eAAe,uBAAuB,IAAI,cAAc,IAAI,IAAI,iBAAiB;EACjF,WAAW,IAAI;CACjB;AACF;AAEA,IAAa,uBAAb,cAA0C,qBAAqB;CAC7D,cAAc;EACZ,MAAM,gBAAgB;CACxB;CAEA,MAAM,OAAsB;EAC1B,MAAM,KAAK,kBAAkB,CAAC,0BAA0B,CAAC;CAC3D;CAEA,MAAM,sBAAqC;EACzC,MAAM,KAAK,IAAI,WAAW,uBAAuB,CAAC,CAAC;CACrD;CAEA,IAAI,MAAyB;EAC3B,OAAO,KAAK;CACd;CAEA,MAAM,IAAI,EAAE,OAAO,UAAiF;EAClG,MAAM,MAAM,MAAM,KAAK,IAAI,QAA2B,uBAAuB;GAC3E,QAAQ;GACR,SAAS;EACX,CAAC;EACD,OAAO,MAAM,SAAS,GAAG,IAAI;CAC/B;CAEA,MAAM,IAAI,EACR,OACA,QACA,WAK8B;EAE9B,MAAM,SAAS;GAAE,UAAU;GAAS,4BAAY,IADhC,KACkC;EAAE;EACpD,MAAM,uBACJ,KAAK,IAAI,aAAgC,uBAAuB;GAAE,QAAQ;GAAO,SAAS;EAAO,SAAS,MAAM;EAElH,MAAM,UAAU,MAAM,eAAe;EACrC,IAAI,SAAS,OAAO,SAAS,OAAO;EAEpC,IAAI;GACF,OAAO,SACL,MAAM,KAAK,IAAI,UAA6B,uBAAuB;IACjE,QAAQ;IACR,SAAS;IACT,gBAAgB;IAChB,GAAG;GACL,CAAC,CACH;EACF,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,uBAAuB,MAAM;GACpD,MAAM,MAAM,MAAM,eAAe;GACjC,IAAI,CAAC,KAAK,MAAM;GAChB,OAAO,SAAS,GAAG;EACrB;CACF;;CAGA,MAAM,iBAAiB,EACrB,OACA,QACA,iBAKqC;EACrC,MAAM,UAAU,MAAM,KAAK,IAAI,aAC7B,uBACA;GAAE,QAAQ;GAAO,SAAS;EAAO,UAC1B;GAAE,gBAAgB;GAAe,4BAAY,IAAI,KAAK;EAAE,EACjE;EACA,OAAO,UAAU,SAAS,OAAO,IAAI;CACvC;CAEA,MAAM,MAAM,EAAE,OAAO,UAA+D;EAClF,OAAQ,MAAM,KAAK,IAAI,WAAW,uBAAuB;GAAE,QAAQ;GAAO,SAAS;EAAO,CAAC,IAAK;CAClG;AACF"}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { FactoryStorageDomain } from '@mastra/core/storage';
|
|
2
2
|
import type { CollectionSchema, FactoryStorageOps } from '@mastra/core/storage';
|
|
3
|
+
/**
|
|
4
|
+
* The user already holds this branch in a session of the same factory on
|
|
5
|
+
* another repository link, so the insert hit the factory-wide unique index.
|
|
6
|
+
* Reuse is keyed by the link, so that session is not this one to reuse.
|
|
7
|
+
*/
|
|
8
|
+
export declare class SessionBranchHeldError extends Error {
|
|
9
|
+
readonly branch: string;
|
|
10
|
+
readonly sessionId: string;
|
|
11
|
+
constructor(branch: string, sessionId: string);
|
|
12
|
+
}
|
|
3
13
|
/**
|
|
4
14
|
* The user already holds the branch on this link in a session without this
|
|
5
15
|
* factory's ownership, left by the factory migration. It is never reused and
|
|
@@ -224,6 +234,14 @@ export type SessionBranchKey = {
|
|
|
224
234
|
userId: string;
|
|
225
235
|
branch: string;
|
|
226
236
|
};
|
|
237
|
+
export type TransferSourceControlSessionOwnerResult = {
|
|
238
|
+
status: 'updated';
|
|
239
|
+
session: SourceControlSession;
|
|
240
|
+
} | {
|
|
241
|
+
status: 'conflict';
|
|
242
|
+
reason: 'stale_owner' | 'branch_owned';
|
|
243
|
+
session: SourceControlSession | null;
|
|
244
|
+
};
|
|
227
245
|
export interface SourceControlStorageHandle {
|
|
228
246
|
readonly integrationId: string;
|
|
229
247
|
readonly installations: {
|
|
@@ -374,14 +392,21 @@ export interface SourceControlStorageHandle {
|
|
|
374
392
|
sessionId: string;
|
|
375
393
|
muted: boolean;
|
|
376
394
|
}): Promise<void>;
|
|
377
|
-
/** Change who can see the session
|
|
395
|
+
/** Change who can see the session while `expectedUserId` still owns it. Null when it does not exist or changed owner. */
|
|
378
396
|
setVisibility(args: {
|
|
379
397
|
sessionId: string;
|
|
380
398
|
visibility: SourceControlSessionVisibility;
|
|
399
|
+
expectedUserId: string;
|
|
381
400
|
}): Promise<SourceControlSession | null>;
|
|
382
401
|
/** Keyed by the link (today's callers) or by the factory. */
|
|
383
402
|
getForBranch(args: SessionBranchKey): Promise<SourceControlSession | null>;
|
|
384
403
|
create(input: CreateSourceControlSessionInput): Promise<SourceControlSession>;
|
|
404
|
+
/** Move a shared session to `toUserId` while `expectedUserId` still owns it; a private one is a `stale_owner` conflict. */
|
|
405
|
+
transferOwner(args: {
|
|
406
|
+
sessionId: string;
|
|
407
|
+
expectedUserId: string;
|
|
408
|
+
toUserId: string;
|
|
409
|
+
}): Promise<TransferSourceControlSessionOwnerResult>;
|
|
385
410
|
setSandbox(args: {
|
|
386
411
|
id: string;
|
|
387
412
|
sandboxId: string | null;
|