@mastra/factory 0.24.0-alpha.0 → 0.24.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -4
- package/dist/auth.d.ts +8 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +15 -1
- package/dist/auth.js.map +1 -1
- package/dist/boards/work-transition-policy.d.ts.map +1 -1
- package/dist/boards/work-transition-policy.js +10 -0
- package/dist/boards/work-transition-policy.js.map +1 -1
- package/dist/boards/work.d.ts +14 -0
- package/dist/boards/work.d.ts.map +1 -1
- package/dist/boards/work.js +70 -7
- package/dist/boards/work.js.map +1 -1
- package/dist/capabilities/version-control.d.ts +26 -0
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +173 -11
- package/dist/factory.js.map +1 -1
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +37 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/gitlab/api.d.ts +15 -0
- package/dist/integrations/gitlab/api.d.ts.map +1 -1
- package/dist/integrations/gitlab/api.js +9 -0
- package/dist/integrations/gitlab/api.js.map +1 -1
- package/dist/integrations/gitlab/version-control.d.ts.map +1 -1
- package/dist/integrations/gitlab/version-control.js +37 -3
- package/dist/integrations/gitlab/version-control.js.map +1 -1
- package/dist/integrations/incidentio/api.d.ts +9 -0
- package/dist/integrations/incidentio/api.d.ts.map +1 -1
- package/dist/integrations/incidentio/api.js +3 -0
- package/dist/integrations/incidentio/api.js.map +1 -1
- package/dist/integrations/incidentio/integration.d.ts.map +1 -1
- package/dist/integrations/incidentio/integration.js +1 -0
- package/dist/integrations/incidentio/integration.js.map +1 -1
- package/dist/integrations/jira/api.d.ts +7 -0
- package/dist/integrations/jira/api.d.ts.map +1 -1
- package/dist/integrations/jira/api.js +4 -0
- package/dist/integrations/jira/api.js.map +1 -1
- package/dist/integrations/platform/api-client.d.ts +6 -0
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +6 -1
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +27 -1
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
- package/dist/integrations/platform/incidentio/integration.js +1 -0
- package/dist/integrations/platform/incidentio/integration.js.map +1 -1
- package/dist/integrations/platform/jira/integration.d.ts.map +1 -1
- package/dist/integrations/platform/jira/integration.js +1 -0
- package/dist/integrations/platform/jira/integration.js.map +1 -1
- package/dist/integrations/subscription-session.d.ts +15 -0
- package/dist/integrations/subscription-session.d.ts.map +1 -1
- package/dist/integrations/subscription-session.js +30 -13
- package/dist/integrations/subscription-session.js.map +1 -1
- package/dist/provider-display-name.d.ts +2 -0
- package/dist/provider-display-name.d.ts.map +1 -0
- package/dist/provider-display-name.js +16 -0
- package/dist/provider-display-name.js.map +1 -0
- package/dist/routes/config.js +2 -2
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/contracts.d.ts +4 -0
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/contracts.js +3 -1
- package/dist/routes/contracts.js.map +1 -1
- package/dist/routes/oauth.js +1 -1
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/provider-credentials.d.ts +6 -2
- package/dist/routes/provider-credentials.d.ts.map +1 -1
- package/dist/routes/provider-credentials.js +20 -7
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts +1 -0
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +1 -1
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/surface.d.ts +1 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +1 -0
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +57 -117
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +7 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +60 -3
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +2 -1
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +82 -3
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +17 -0
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +25 -0
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +2 -0
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -1
- package/dist/sandbox/local-factory-sandbox.js +1 -0
- package/dist/sandbox/local-factory-sandbox.js.map +1 -1
- package/dist/session/factory-session.d.ts +15 -1
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +4 -2
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/reply-credential-processor.d.ts +15 -0
- package/dist/session/reply-credential-processor.d.ts.map +1 -0
- package/dist/session/reply-credential-processor.js +40 -0
- package/dist/session/reply-credential-processor.js.map +1 -0
- package/dist/session/source-control-tools.d.ts +19 -1
- package/dist/session/source-control-tools.d.ts.map +1 -1
- package/dist/session/source-control-tools.js +70 -3
- package/dist/session/source-control-tools.js.map +1 -1
- package/dist/storage/domains/projects/base.d.ts +6 -0
- package/dist/storage/domains/projects/base.d.ts.map +1 -1
- package/dist/storage/domains/projects/base.js +12 -0
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +17 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +23 -0
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +5 -1
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +56 -0
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +139 -25
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/tools/approval-tools.d.ts +15 -0
- package/dist/tools/approval-tools.d.ts.map +1 -0
- package/dist/tools/approval-tools.js +77 -0
- package/dist/tools/approval-tools.js.map +1 -0
- package/dist/tools/deliver.d.ts +54 -0
- package/dist/tools/deliver.d.ts.map +1 -0
- package/dist/tools/deliver.js +105 -0
- package/dist/tools/deliver.js.map +1 -0
- package/dist/tools/index.d.ts +29 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +39 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/messaging-tools.d.ts +28 -0
- package/dist/tools/messaging-tools.d.ts.map +1 -0
- package/dist/tools/messaging-tools.js +312 -0
- package/dist/tools/messaging-tools.js.map +1 -0
- package/dist/tools/permissions.d.ts +31 -0
- package/dist/tools/permissions.d.ts.map +1 -0
- package/dist/tools/permissions.js +44 -0
- package/dist/tools/permissions.js.map +1 -0
- package/dist/tools/read-tools.d.ts +27 -0
- package/dist/tools/read-tools.d.ts.map +1 -0
- package/dist/tools/read-tools.js +285 -0
- package/dist/tools/read-tools.js.map +1 -0
- package/dist/tools/reports.d.ts +163 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +352 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/run-watch.d.ts +46 -0
- package/dist/tools/run-watch.d.ts.map +1 -0
- package/dist/tools/run-watch.js +102 -0
- package/dist/tools/run-watch.js.map +1 -0
- package/dist/tools/scope.d.ts +45 -0
- package/dist/tools/scope.d.ts.map +1 -0
- package/dist/tools/scope.js +73 -0
- package/dist/tools/scope.js.map +1 -0
- package/dist/tools/session-tools.d.ts +31 -0
- package/dist/tools/session-tools.d.ts.map +1 -0
- package/dist/tools/session-tools.js +312 -0
- package/dist/tools/session-tools.js.map +1 -0
- package/dist/tools/sessions-state-processor.d.ts +37 -0
- package/dist/tools/sessions-state-processor.d.ts.map +1 -0
- package/dist/tools/sessions-state-processor.js +206 -0
- package/dist/tools/sessions-state-processor.js.map +1 -0
- package/dist/tools/work-item-tools.d.ts +28 -0
- package/dist/tools/work-item-tools.d.ts.map +1 -0
- package/dist/tools/work-item-tools.js +322 -0
- package/dist/tools/work-item-tools.js.map +1 -0
- package/dist/work-items/create-work-item.d.ts +55 -0
- package/dist/work-items/create-work-item.d.ts.map +1 -0
- package/dist/work-items/create-work-item.js +101 -0
- package/dist/work-items/create-work-item.js.map +1 -0
- package/dist/work-items/decision-actions.d.ts +42 -0
- package/dist/work-items/decision-actions.d.ts.map +1 -0
- package/dist/work-items/decision-actions.js +70 -0
- package/dist/work-items/decision-actions.js.map +1 -0
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +3 -1
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-gitlab-rereview/SKILL.md +1 -1
- package/factory-skills/factory-gitlab-review/SKILL.md +1 -1
- package/factory-skills/factory-plan/SKILL.md +4 -4
- package/factory-skills/factory-rereview/SKILL.md +3 -1
- package/factory-skills/factory-review/SKILL.md +3 -1
- package/factory-skills/factory-self-review/SKILL.md +46 -0
- package/package.json +7 -7
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IntegrationTools } from '../integrations/base.js';
|
|
2
|
+
import type { SourceControlSessionLookup } from '../session/factory-session.js';
|
|
3
|
+
import type { AuditRecorder } from '../storage/domains/audit/domain.js';
|
|
4
|
+
import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
5
|
+
import type { FactoryMessaging } from './messaging-tools.js';
|
|
6
|
+
import type { FactoryToolScope } from './scope.js';
|
|
7
|
+
export interface FactoryApprovalToolDependencies {
|
|
8
|
+
sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;
|
|
9
|
+
workItems: Pick<WorkItemsStorage, 'findActiveRunBindingForSession' | 'listSessionSubscriptions'>;
|
|
10
|
+
auditRecorder: AuditRecorder;
|
|
11
|
+
messaging: () => Promise<FactoryMessaging | null>;
|
|
12
|
+
}
|
|
13
|
+
/** Answers a tool call another session holds for approval. Interactive user turns only. */
|
|
14
|
+
export declare function createFactoryApprovalTools(scope: FactoryToolScope, deps: FactoryApprovalToolDependencies): IntegrationTools;
|
|
15
|
+
//# sourceMappingURL=approval-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-tools.d.ts","sourceRoot":"","sources":["../../src/tools/approval-tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AACxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAC9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,CAAC,CAAC;IAC7D,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,gCAAgC,GAAG,0BAA0B,CAAC,CAAC;IACjG,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CACnD;AAYD,2FAA2F;AAC3F,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,+BAA+B,GACpC,gBAAgB,CAmFlB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { createTool } from "@mastra/core/tools";
|
|
3
|
+
//#region src/tools/approval-tools.ts
|
|
4
|
+
/** Answers a tool call another session holds for approval. Interactive user turns only. */
|
|
5
|
+
function createFactoryApprovalTools(scope, deps) {
|
|
6
|
+
return { factory_answer_approval: createTool({
|
|
7
|
+
id: "factory_answer_approval",
|
|
8
|
+
description: [
|
|
9
|
+
"Approve or decline a tool call a session you follow is waiting on, as you.",
|
|
10
|
+
"A `factory-session` notification of kind `approval` names the session, tool, arguments and toolCallId.",
|
|
11
|
+
"Answer only what the person in this conversation agreed to; decline with a reason the session will see."
|
|
12
|
+
].join(" "),
|
|
13
|
+
inputSchema: z.object({
|
|
14
|
+
sessionId: z.string().uuid().describe("The session holding the call, from the approval notice."),
|
|
15
|
+
toolCallId: z.string().trim().min(1).max(200),
|
|
16
|
+
decision: z.enum(["approve", "decline"]),
|
|
17
|
+
reason: z.string().trim().min(1).max(1e3).optional().describe("Why, when declining.")
|
|
18
|
+
}).strict(),
|
|
19
|
+
execute: async ({ sessionId, toolCallId, decision, reason }) => {
|
|
20
|
+
const row = await deps.sessions.getBySessionId(sessionId);
|
|
21
|
+
if (!row || row.orgId !== scope.orgId || row.factoryProjectId !== scope.factoryProjectId) throw new Error(`Session ${sessionId} was not found in this Factory.`);
|
|
22
|
+
if (row.visibility === "private" && row.userId !== scope.userId) throw new Error(`Session ${sessionId} is private to its owner.`);
|
|
23
|
+
const binding = await deps.workItems.findActiveRunBindingForSession({
|
|
24
|
+
orgId: scope.orgId,
|
|
25
|
+
factoryProjectId: scope.factoryProjectId,
|
|
26
|
+
sessionId
|
|
27
|
+
});
|
|
28
|
+
const isOrigin = row.originSessionId === scope.sessionId;
|
|
29
|
+
const follows = binding !== null && (await deps.workItems.listSessionSubscriptions({
|
|
30
|
+
orgId: scope.orgId,
|
|
31
|
+
factoryProjectId: scope.factoryProjectId,
|
|
32
|
+
sessionId: scope.sessionId
|
|
33
|
+
})).some((subscription) => subscription.workItemId === binding.workItemId);
|
|
34
|
+
if (!isOrigin && !follows) throw new Error(`This session doesn't follow session ${sessionId}: follow its work item first, or answer from that session.`);
|
|
35
|
+
const messaging = await deps.messaging();
|
|
36
|
+
if (!messaging) throw new Error("Answering approvals is not available on this server.");
|
|
37
|
+
const live = await messaging.controller.getSessionByResource(sessionId);
|
|
38
|
+
if (!live?.approval.isArmed({ toolCallId })) throw new Error("Nothing is waiting on that tool call any more: the run that asked has ended or it was already answered. Message the session with factory_signal_session if it should try again.");
|
|
39
|
+
const result = live.respondToToolApproval({
|
|
40
|
+
decision,
|
|
41
|
+
toolCallId,
|
|
42
|
+
...decision === "decline" ? { declineContext: { reason: reason ?? "Declined by a session following it." } } : {}
|
|
43
|
+
});
|
|
44
|
+
if (!result.accepted) throw new Error(`The answer was not accepted (${result.reason}).`);
|
|
45
|
+
deps.auditRecorder.record({
|
|
46
|
+
orgId: scope.orgId,
|
|
47
|
+
actorId: scope.userId,
|
|
48
|
+
actorType: "human",
|
|
49
|
+
...scope.actorProfile ? { actorProfile: scope.actorProfile } : {},
|
|
50
|
+
action: decision === "approve" ? "factory.run.approved" : "factory.run.dismissed",
|
|
51
|
+
factoryProjectId: scope.factoryProjectId,
|
|
52
|
+
targets: [{
|
|
53
|
+
type: "session",
|
|
54
|
+
id: sessionId
|
|
55
|
+
}, ...binding ? [{
|
|
56
|
+
type: "work_item",
|
|
57
|
+
id: binding.workItemId
|
|
58
|
+
}] : []],
|
|
59
|
+
metadata: {
|
|
60
|
+
toolCallId,
|
|
61
|
+
sessionId,
|
|
62
|
+
decision,
|
|
63
|
+
cause: "factory_tools"
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
return {
|
|
67
|
+
answered: decision,
|
|
68
|
+
sessionId,
|
|
69
|
+
toolCallId
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}) };
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
export { createFactoryApprovalTools };
|
|
76
|
+
|
|
77
|
+
//# sourceMappingURL=approval-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approval-tools.js","names":[],"sources":["../../src/tools/approval-tools.ts"],"sourcesContent":["import { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../integrations/base.js';\nimport type { SourceControlSessionLookup } from '../session/factory-session.js';\nimport type { AuditRecorder } from '../storage/domains/audit/domain.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { FactoryMessaging } from './messaging-tools.js';\nimport type { FactoryToolScope } from './scope.js';\n\nexport interface FactoryApprovalToolDependencies {\n sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n workItems: Pick<WorkItemsStorage, 'findActiveRunBindingForSession' | 'listSessionSubscriptions'>;\n auditRecorder: AuditRecorder;\n messaging: () => Promise<FactoryMessaging | null>;\n}\n\n/** The parts of a live session an answer needs: its open approval gates. */\ninterface ApprovalGateSession {\n approval: { isArmed(filter: { toolCallId: string }): boolean };\n respondToToolApproval(input: {\n decision: 'approve' | 'decline';\n toolCallId: string;\n declineContext?: { reason?: string };\n }): { accepted: true } | { accepted: false; reason: string };\n}\n\n/** Answers a tool call another session holds for approval. Interactive user turns only. */\nexport function createFactoryApprovalTools(\n scope: FactoryToolScope,\n deps: FactoryApprovalToolDependencies,\n): IntegrationTools {\n const answer = createTool({\n id: 'factory_answer_approval',\n description: [\n 'Approve or decline a tool call a session you follow is waiting on, as you.',\n 'A `factory-session` notification of kind `approval` names the session, tool, arguments and toolCallId.',\n 'Answer only what the person in this conversation agreed to; decline with a reason the session will see.',\n ].join(' '),\n inputSchema: z\n .object({\n sessionId: z.string().uuid().describe('The session holding the call, from the approval notice.'),\n toolCallId: z.string().trim().min(1).max(200),\n decision: z.enum(['approve', 'decline']),\n reason: z.string().trim().min(1).max(1000).optional().describe('Why, when declining.'),\n })\n .strict(),\n execute: async ({ sessionId, toolCallId, decision, reason }) => {\n const row = await deps.sessions.getBySessionId(sessionId);\n if (!row || row.orgId !== scope.orgId || row.factoryProjectId !== scope.factoryProjectId) {\n throw new Error(`Session ${sessionId} was not found in this Factory.`);\n }\n if (row.visibility === 'private' && row.userId !== scope.userId) {\n throw new Error(`Session ${sessionId} is private to its owner.`);\n }\n\n // Only the sessions it reports to may answer it: its origin, or followers of its item.\n const binding = await deps.workItems.findActiveRunBindingForSession({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n sessionId,\n });\n const isOrigin = row.originSessionId === scope.sessionId;\n const follows =\n binding !== null &&\n (\n await deps.workItems.listSessionSubscriptions({\n orgId: scope.orgId,\n factoryProjectId: scope.factoryProjectId,\n sessionId: scope.sessionId,\n })\n ).some(subscription => subscription.workItemId === binding.workItemId);\n if (!isOrigin && !follows) {\n throw new Error(\n `This session doesn't follow session ${sessionId}: follow its work item first, or answer from that session.`,\n );\n }\n\n const messaging = await deps.messaging();\n if (!messaging) throw new Error('Answering approvals is not available on this server.');\n const live = (await messaging.controller.getSessionByResource(sessionId)) as ApprovalGateSession | null;\n if (!live?.approval.isArmed({ toolCallId })) {\n throw new Error(\n 'Nothing is waiting on that tool call any more: the run that asked has ended or it was already answered. ' +\n 'Message the session with factory_signal_session if it should try again.',\n );\n }\n const result = live.respondToToolApproval({\n decision,\n toolCallId,\n ...(decision === 'decline'\n ? { declineContext: { reason: reason ?? 'Declined by a session following it.' } }\n : {}),\n });\n if (!result.accepted) throw new Error(`The answer was not accepted (${result.reason}).`);\n\n deps.auditRecorder.record({\n orgId: scope.orgId,\n actorId: scope.userId,\n actorType: 'human',\n ...(scope.actorProfile ? { actorProfile: scope.actorProfile } : {}),\n action: decision === 'approve' ? 'factory.run.approved' : 'factory.run.dismissed',\n factoryProjectId: scope.factoryProjectId,\n targets: [\n { type: 'session', id: sessionId },\n ...(binding ? [{ type: 'work_item' as const, id: binding.workItemId }] : []),\n ],\n metadata: { toolCallId, sessionId, decision, cause: 'factory_tools' },\n });\n return { answered: decision, sessionId, toolCallId };\n },\n });\n\n return { factory_answer_approval: answer };\n}\n"],"mappings":";;;;AA4BA,SAAgB,2BACd,OACA,MACkB;CAkFlB,OAAO,EAAE,yBAjFM,WAAW;EACxB,IAAI;EACJ,aAAa;GACX;GACA;GACA;EACF,CAAC,CAAC,KAAK,GAAG;EACV,aAAa,EACV,OAAO;GACN,WAAW,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,SAAS,yDAAyD;GAC/F,YAAY,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG;GAC5C,UAAU,EAAE,KAAK,CAAC,WAAW,SAAS,CAAC;GACvC,QAAQ,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAI,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,sBAAsB;EACvF,CAAC,CAAC,CACD,OAAO;EACV,SAAS,OAAO,EAAE,WAAW,YAAY,UAAU,aAAa;GAC9D,MAAM,MAAM,MAAM,KAAK,SAAS,eAAe,SAAS;GACxD,IAAI,CAAC,OAAO,IAAI,UAAU,MAAM,SAAS,IAAI,qBAAqB,MAAM,kBACtE,MAAM,IAAI,MAAM,WAAW,UAAU,gCAAgC;GAEvE,IAAI,IAAI,eAAe,aAAa,IAAI,WAAW,MAAM,QACvD,MAAM,IAAI,MAAM,WAAW,UAAU,0BAA0B;GAIjE,MAAM,UAAU,MAAM,KAAK,UAAU,+BAA+B;IAClE,OAAO,MAAM;IACb,kBAAkB,MAAM;IACxB;GACF,CAAC;GACD,MAAM,WAAW,IAAI,oBAAoB,MAAM;GAC/C,MAAM,UACJ,YAAY,SAEV,MAAM,KAAK,UAAU,yBAAyB;IAC5C,OAAO,MAAM;IACb,kBAAkB,MAAM;IACxB,WAAW,MAAM;GACnB,CAAC,EAAA,CACD,MAAK,iBAAgB,aAAa,eAAe,QAAQ,UAAU;GACvE,IAAI,CAAC,YAAY,CAAC,SAChB,MAAM,IAAI,MACR,uCAAuC,UAAU,2DACnD;GAGF,MAAM,YAAY,MAAM,KAAK,UAAU;GACvC,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,sDAAsD;GACtF,MAAM,OAAQ,MAAM,UAAU,WAAW,qBAAqB,SAAS;GACvE,IAAI,CAAC,MAAM,SAAS,QAAQ,EAAE,WAAW,CAAC,GACxC,MAAM,IAAI,MACR,iLAEF;GAEF,MAAM,SAAS,KAAK,sBAAsB;IACxC;IACA;IACA,GAAI,aAAa,YACb,EAAE,gBAAgB,EAAE,QAAQ,UAAU,sCAAsC,EAAE,IAC9E,CAAC;GACP,CAAC;GACD,IAAI,CAAC,OAAO,UAAU,MAAM,IAAI,MAAM,gCAAgC,OAAO,OAAO,GAAG;GAEvF,KAAK,cAAc,OAAO;IACxB,OAAO,MAAM;IACb,SAAS,MAAM;IACf,WAAW;IACX,GAAI,MAAM,eAAe,EAAE,cAAc,MAAM,aAAa,IAAI,CAAC;IACjE,QAAQ,aAAa,YAAY,yBAAyB;IAC1D,kBAAkB,MAAM;IACxB,SAAS,CACP;KAAE,MAAM;KAAW,IAAI;IAAU,GACjC,GAAI,UAAU,CAAC;KAAE,MAAM;KAAsB,IAAI,QAAQ;IAAW,CAAC,IAAI,CAAC,CAC5E;IACA,UAAU;KAAE;KAAY;KAAW;KAAU,OAAO;IAAgB;GACtE,CAAC;GACD,OAAO;IAAE,UAAU;IAAU;IAAW;GAAW;EACrD;CACF,CAEuC,EAAE;AAC3C"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { MountedMastraCode } from '@mastra/code-sdk';
|
|
2
|
+
import type { SendNotificationSignalInput } from '@mastra/core/notifications';
|
|
3
|
+
import type { SourceControlSessionLookup } from '../session/factory-session.js';
|
|
4
|
+
import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
5
|
+
/** Notifications between Factory sessions all carry this source. */
|
|
6
|
+
export declare const FACTORY_SESSION_NOTIFICATION_SOURCE = "factory-session";
|
|
7
|
+
export type FactoryDeliveryController = Pick<MountedMastraCode['controller'], 'queryThreadById' | 'queryThreads' | 'getSessionByResource' | 'createSession' | 'getCurrentAgent'>;
|
|
8
|
+
export interface FactoryDeliveryTarget {
|
|
9
|
+
/** The target's source-control session id. */
|
|
10
|
+
sessionId: string;
|
|
11
|
+
/** The stored thread the notification belongs to. Never the session's current thread by default. */
|
|
12
|
+
threadId: string;
|
|
13
|
+
}
|
|
14
|
+
export type FactoryDeliveryResult = {
|
|
15
|
+
delivered: 'woken' | 'persisted' | 'summarized';
|
|
16
|
+
notificationId: string;
|
|
17
|
+
reason?: string;
|
|
18
|
+
} | {
|
|
19
|
+
delivered: 'skipped';
|
|
20
|
+
notificationId?: string;
|
|
21
|
+
reason: 'thread-not-found' | 'thread-not-owned' | 'session-not-found' | 'offline' | 'discarded';
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Deliver a notification to a Factory session's stored thread.
|
|
25
|
+
*
|
|
26
|
+
* Sent through the target's agent with an explicit `{ resourceId, threadId }`,
|
|
27
|
+
* never `Session.sendNotificationSignal`: that one builds the wake from the
|
|
28
|
+
* sender's request context and aims at the target's current thread. On this
|
|
29
|
+
* path the SDK builds the wake from the session that owns the target resource,
|
|
30
|
+
* so the woken run is the target row's owner, with that owner's credentials.
|
|
31
|
+
*
|
|
32
|
+
* A target that isn't live is created first, as its row's owner and bound to
|
|
33
|
+
* the stored thread: waking a resource no session owns starts a run with no
|
|
34
|
+
* model. A live session's thread is never switched.
|
|
35
|
+
*/
|
|
36
|
+
export declare function deliverFactorySessionNotification({ controller, sessions, target, notification, onlyIfLive, }: {
|
|
37
|
+
controller: FactoryDeliveryController;
|
|
38
|
+
sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;
|
|
39
|
+
target: FactoryDeliveryTarget;
|
|
40
|
+
notification: Omit<SendNotificationSignalInput, 'source'>;
|
|
41
|
+
/** Skip, without creating anything, when no live session owns the target (stage notices). */
|
|
42
|
+
onlyIfLive?: boolean;
|
|
43
|
+
}): Promise<FactoryDeliveryResult>;
|
|
44
|
+
/**
|
|
45
|
+
* The thread a message to a session should land on: the thread its role runs
|
|
46
|
+
* on, else the thread it has open, else its most recently updated thread.
|
|
47
|
+
*/
|
|
48
|
+
export declare function resolveSessionThread({ controller, sessions, workItems, sessionId, }: {
|
|
49
|
+
controller: Pick<FactoryDeliveryController, 'getSessionByResource' | 'queryThreads'>;
|
|
50
|
+
sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;
|
|
51
|
+
workItems: Pick<WorkItemsStorage, 'findActiveRunBindingForSession'>;
|
|
52
|
+
sessionId: string;
|
|
53
|
+
}): Promise<string | null>;
|
|
54
|
+
//# sourceMappingURL=deliver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../../src/tools/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAG9E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAE9E,oEAAoE;AACpE,eAAO,MAAM,mCAAmC,oBAAoB,CAAC;AAErE,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,iBAAiB,CAAC,YAAY,CAAC,EAC/B,iBAAiB,GAAG,cAAc,GAAG,sBAAsB,GAAG,eAAe,GAAG,iBAAiB,CAClG,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,oGAAoG;IACpG,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,SAAS,EAAE,OAAO,GAAG,WAAW,GAAG,YAAY,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5F;IACE,SAAS,EAAE,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,kBAAkB,GAAG,kBAAkB,GAAG,mBAAmB,GAAG,SAAS,GAAG,WAAW,CAAC;CACjG,CAAC;AAEN;;;;;;;;;;;;GAYG;AACH,wBAAsB,iCAAiC,CAAC,EACtD,UAAU,EACV,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,UAAkB,GACnB,EAAE;IACD,UAAU,EAAE,yBAAyB,CAAC;IACtC,QAAQ,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,CAAC,CAAC;IAC7D,MAAM,EAAE,qBAAqB,CAAC;IAC9B,YAAY,EAAE,IAAI,CAAC,2BAA2B,EAAE,QAAQ,CAAC,CAAC;IAC1D,6FAA6F;IAC7F,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAiDjC;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,EACzC,UAAU,EACV,QAAQ,EACR,SAAS,EACT,SAAS,GACV,EAAE;IACD,UAAU,EAAE,IAAI,CAAC,yBAAyB,EAAE,sBAAsB,GAAG,cAAc,CAAC,CAAC;IACrF,QAAQ,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,CAAC,CAAC;IAC7D,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,gCAAgC,CAAC,CAAC;IACpE,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBzB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { createSessionAsRowOwner } from "../integrations/subscription-session.js";
|
|
2
|
+
//#region src/tools/deliver.ts
|
|
3
|
+
/** Notifications between Factory sessions all carry this source. */
|
|
4
|
+
const FACTORY_SESSION_NOTIFICATION_SOURCE = "factory-session";
|
|
5
|
+
/**
|
|
6
|
+
* Deliver a notification to a Factory session's stored thread.
|
|
7
|
+
*
|
|
8
|
+
* Sent through the target's agent with an explicit `{ resourceId, threadId }`,
|
|
9
|
+
* never `Session.sendNotificationSignal`: that one builds the wake from the
|
|
10
|
+
* sender's request context and aims at the target's current thread. On this
|
|
11
|
+
* path the SDK builds the wake from the session that owns the target resource,
|
|
12
|
+
* so the woken run is the target row's owner, with that owner's credentials.
|
|
13
|
+
*
|
|
14
|
+
* A target that isn't live is created first, as its row's owner and bound to
|
|
15
|
+
* the stored thread: waking a resource no session owns starts a run with no
|
|
16
|
+
* model. A live session's thread is never switched.
|
|
17
|
+
*/
|
|
18
|
+
async function deliverFactorySessionNotification({ controller, sessions, target, notification, onlyIfLive = false }) {
|
|
19
|
+
const thread = await controller.queryThreadById({ threadId: target.threadId });
|
|
20
|
+
if (!thread) return {
|
|
21
|
+
delivered: "skipped",
|
|
22
|
+
reason: "thread-not-found"
|
|
23
|
+
};
|
|
24
|
+
if (thread.resourceId && thread.resourceId !== target.sessionId) return {
|
|
25
|
+
delivered: "skipped",
|
|
26
|
+
reason: "thread-not-owned"
|
|
27
|
+
};
|
|
28
|
+
const ownerResourceId = target.sessionId;
|
|
29
|
+
let session = await controller.getSessionByResource(ownerResourceId);
|
|
30
|
+
if (!session) {
|
|
31
|
+
if (onlyIfLive) return {
|
|
32
|
+
delivered: "skipped",
|
|
33
|
+
reason: "offline"
|
|
34
|
+
};
|
|
35
|
+
const row = await sessions.getBySessionId(target.sessionId);
|
|
36
|
+
if (!row?.factoryProjectId) return {
|
|
37
|
+
delivered: "skipped",
|
|
38
|
+
reason: "session-not-found"
|
|
39
|
+
};
|
|
40
|
+
session = await createSessionAsRowOwner(controller, {
|
|
41
|
+
sessionId: target.sessionId,
|
|
42
|
+
ownerResourceId,
|
|
43
|
+
row,
|
|
44
|
+
tags: {
|
|
45
|
+
factoryProjectId: row.factoryProjectId,
|
|
46
|
+
...row.projectRepositoryId ? { projectRepositoryId: row.projectRepositoryId } : {}
|
|
47
|
+
},
|
|
48
|
+
threadId: target.threadId
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const result = await controller.getCurrentAgent(session).sendNotificationSignal({
|
|
52
|
+
...notification,
|
|
53
|
+
source: FACTORY_SESSION_NOTIFICATION_SOURCE
|
|
54
|
+
}, {
|
|
55
|
+
resourceId: ownerResourceId,
|
|
56
|
+
threadId: target.threadId
|
|
57
|
+
});
|
|
58
|
+
await Promise.all([result.persisted, result.accepted].filter(Boolean));
|
|
59
|
+
const notificationId = result.record.id;
|
|
60
|
+
const reason = result.decision.reason ? { reason: result.decision.reason } : {};
|
|
61
|
+
switch (result.decision.action) {
|
|
62
|
+
case "deliver": return {
|
|
63
|
+
delivered: "woken",
|
|
64
|
+
notificationId,
|
|
65
|
+
...reason
|
|
66
|
+
};
|
|
67
|
+
case "summarize": return {
|
|
68
|
+
delivered: "summarized",
|
|
69
|
+
notificationId,
|
|
70
|
+
...reason
|
|
71
|
+
};
|
|
72
|
+
case "discard": return {
|
|
73
|
+
delivered: "skipped",
|
|
74
|
+
notificationId,
|
|
75
|
+
reason: "discarded"
|
|
76
|
+
};
|
|
77
|
+
default: return {
|
|
78
|
+
delivered: "persisted",
|
|
79
|
+
notificationId,
|
|
80
|
+
...reason
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* The thread a message to a session should land on: the thread its role runs
|
|
86
|
+
* on, else the thread it has open, else its most recently updated thread.
|
|
87
|
+
*/
|
|
88
|
+
async function resolveSessionThread({ controller, sessions, workItems, sessionId }) {
|
|
89
|
+
const row = await sessions.getBySessionId(sessionId);
|
|
90
|
+
if (row?.factoryProjectId) {
|
|
91
|
+
const binding = await workItems.findActiveRunBindingForSession({
|
|
92
|
+
orgId: row.orgId,
|
|
93
|
+
factoryProjectId: row.factoryProjectId,
|
|
94
|
+
sessionId
|
|
95
|
+
});
|
|
96
|
+
if (binding) return binding.threadId;
|
|
97
|
+
}
|
|
98
|
+
const current = (await controller.getSessionByResource(sessionId))?.thread.getId();
|
|
99
|
+
if (current) return current;
|
|
100
|
+
return [...await controller.queryThreads({ resourceId: sessionId })].sort((left, right) => right.updatedAt.getTime() - left.updatedAt.getTime())[0]?.id ?? null;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { FACTORY_SESSION_NOTIFICATION_SOURCE, deliverFactorySessionNotification, resolveSessionThread };
|
|
104
|
+
|
|
105
|
+
//# sourceMappingURL=deliver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deliver.js","names":[],"sources":["../../src/tools/deliver.ts"],"sourcesContent":["import type { MountedMastraCode } from '@mastra/code-sdk';\nimport type { SendNotificationSignalInput } from '@mastra/core/notifications';\n\nimport { createSessionAsRowOwner } from '../integrations/subscription-session.js';\nimport type { SourceControlSessionLookup } from '../session/factory-session.js';\nimport type { WorkItemsStorage } from '../storage/domains/work-items/base.js';\n\n/** Notifications between Factory sessions all carry this source. */\nexport const FACTORY_SESSION_NOTIFICATION_SOURCE = 'factory-session';\n\nexport type FactoryDeliveryController = Pick<\n MountedMastraCode['controller'],\n 'queryThreadById' | 'queryThreads' | 'getSessionByResource' | 'createSession' | 'getCurrentAgent'\n>;\n\nexport interface FactoryDeliveryTarget {\n /** The target's source-control session id. */\n sessionId: string;\n /** The stored thread the notification belongs to. Never the session's current thread by default. */\n threadId: string;\n}\n\nexport type FactoryDeliveryResult =\n | { delivered: 'woken' | 'persisted' | 'summarized'; notificationId: string; reason?: string }\n | {\n delivered: 'skipped';\n notificationId?: string;\n reason: 'thread-not-found' | 'thread-not-owned' | 'session-not-found' | 'offline' | 'discarded';\n };\n\n/**\n * Deliver a notification to a Factory session's stored thread.\n *\n * Sent through the target's agent with an explicit `{ resourceId, threadId }`,\n * never `Session.sendNotificationSignal`: that one builds the wake from the\n * sender's request context and aims at the target's current thread. On this\n * path the SDK builds the wake from the session that owns the target resource,\n * so the woken run is the target row's owner, with that owner's credentials.\n *\n * A target that isn't live is created first, as its row's owner and bound to\n * the stored thread: waking a resource no session owns starts a run with no\n * model. A live session's thread is never switched.\n */\nexport async function deliverFactorySessionNotification({\n controller,\n sessions,\n target,\n notification,\n onlyIfLive = false,\n}: {\n controller: FactoryDeliveryController;\n sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n target: FactoryDeliveryTarget;\n notification: Omit<SendNotificationSignalInput, 'source'>;\n /** Skip, without creating anything, when no live session owns the target (stage notices). */\n onlyIfLive?: boolean;\n}): Promise<FactoryDeliveryResult> {\n const thread = await controller.queryThreadById({ threadId: target.threadId });\n if (!thread) return { delivered: 'skipped', reason: 'thread-not-found' };\n // A Factory session's threads live under its session id. A thread owned by\n // anything else is not this session's, whatever the caller paired it with.\n if (thread.resourceId && thread.resourceId !== target.sessionId) {\n return { delivered: 'skipped', reason: 'thread-not-owned' };\n }\n const ownerResourceId = target.sessionId;\n\n // No request context: without a user the controller's access check is a no-op,\n // and this is a server-side lookup on the target's behalf, not the sender's.\n let session = await controller.getSessionByResource(ownerResourceId);\n if (!session) {\n if (onlyIfLive) return { delivered: 'skipped', reason: 'offline' };\n const row = await sessions.getBySessionId(target.sessionId);\n if (!row?.factoryProjectId) return { delivered: 'skipped', reason: 'session-not-found' };\n session = await createSessionAsRowOwner(controller, {\n sessionId: target.sessionId,\n ownerResourceId,\n row,\n tags: {\n factoryProjectId: row.factoryProjectId,\n ...(row.projectRepositoryId ? { projectRepositoryId: row.projectRepositoryId } : {}),\n },\n threadId: target.threadId,\n });\n }\n\n const result = await controller\n .getCurrentAgent(session)\n .sendNotificationSignal(\n { ...notification, source: FACTORY_SESSION_NOTIFICATION_SOURCE },\n { resourceId: ownerResourceId, threadId: target.threadId },\n );\n await Promise.all([result.persisted, result.accepted].filter(Boolean));\n\n const notificationId = result.record.id;\n const reason = result.decision.reason ? { reason: result.decision.reason } : {};\n switch (result.decision.action) {\n case 'deliver':\n return { delivered: 'woken', notificationId, ...reason };\n case 'summarize':\n return { delivered: 'summarized', notificationId, ...reason };\n case 'discard':\n return { delivered: 'skipped', notificationId, reason: 'discarded' };\n default:\n return { delivered: 'persisted', notificationId, ...reason };\n }\n}\n\n/**\n * The thread a message to a session should land on: the thread its role runs\n * on, else the thread it has open, else its most recently updated thread.\n */\nexport async function resolveSessionThread({\n controller,\n sessions,\n workItems,\n sessionId,\n}: {\n controller: Pick<FactoryDeliveryController, 'getSessionByResource' | 'queryThreads'>;\n sessions: Pick<SourceControlSessionLookup, 'getBySessionId'>;\n workItems: Pick<WorkItemsStorage, 'findActiveRunBindingForSession'>;\n sessionId: string;\n}): Promise<string | null> {\n const row = await sessions.getBySessionId(sessionId);\n if (row?.factoryProjectId) {\n const binding = await workItems.findActiveRunBindingForSession({\n orgId: row.orgId,\n factoryProjectId: row.factoryProjectId,\n sessionId,\n });\n if (binding) return binding.threadId;\n }\n const live = await controller.getSessionByResource(sessionId);\n const current = live?.thread.getId();\n if (current) return current;\n const threads = await controller.queryThreads({ resourceId: sessionId });\n const newest = [...threads].sort((left, right) => right.updatedAt.getTime() - left.updatedAt.getTime())[0];\n return newest?.id ?? null;\n}\n"],"mappings":";;;AAQA,MAAa,sCAAsC;;;;;;;;;;;;;;AAmCnD,eAAsB,kCAAkC,EACtD,YACA,UACA,QACA,cACA,aAAa,SAQoB;CACjC,MAAM,SAAS,MAAM,WAAW,gBAAgB,EAAE,UAAU,OAAO,SAAS,CAAC;CAC7E,IAAI,CAAC,QAAQ,OAAO;EAAE,WAAW;EAAW,QAAQ;CAAmB;CAGvE,IAAI,OAAO,cAAc,OAAO,eAAe,OAAO,WACpD,OAAO;EAAE,WAAW;EAAW,QAAQ;CAAmB;CAE5D,MAAM,kBAAkB,OAAO;CAI/B,IAAI,UAAU,MAAM,WAAW,qBAAqB,eAAe;CACnE,IAAI,CAAC,SAAS;EACZ,IAAI,YAAY,OAAO;GAAE,WAAW;GAAW,QAAQ;EAAU;EACjE,MAAM,MAAM,MAAM,SAAS,eAAe,OAAO,SAAS;EAC1D,IAAI,CAAC,KAAK,kBAAkB,OAAO;GAAE,WAAW;GAAW,QAAQ;EAAoB;EACvF,UAAU,MAAM,wBAAwB,YAAY;GAClD,WAAW,OAAO;GAClB;GACA;GACA,MAAM;IACJ,kBAAkB,IAAI;IACtB,GAAI,IAAI,sBAAsB,EAAE,qBAAqB,IAAI,oBAAoB,IAAI,CAAC;GACpF;GACA,UAAU,OAAO;EACnB,CAAC;CACH;CAEA,MAAM,SAAS,MAAM,WAClB,gBAAgB,OAAO,CAAC,CACxB,uBACC;EAAE,GAAG;EAAc,QAAQ;CAAoC,GAC/D;EAAE,YAAY;EAAiB,UAAU,OAAO;CAAS,CAC3D;CACF,MAAM,QAAQ,IAAI,CAAC,OAAO,WAAW,OAAO,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC;CAErE,MAAM,iBAAiB,OAAO,OAAO;CACrC,MAAM,SAAS,OAAO,SAAS,SAAS,EAAE,QAAQ,OAAO,SAAS,OAAO,IAAI,CAAC;CAC9E,QAAQ,OAAO,SAAS,QAAxB;EACE,KAAK,WACH,OAAO;GAAE,WAAW;GAAS;GAAgB,GAAG;EAAO;EACzD,KAAK,aACH,OAAO;GAAE,WAAW;GAAc;GAAgB,GAAG;EAAO;EAC9D,KAAK,WACH,OAAO;GAAE,WAAW;GAAW;GAAgB,QAAQ;EAAY;EACrE,SACE,OAAO;GAAE,WAAW;GAAa;GAAgB,GAAG;EAAO;CAC/D;AACF;;;;;AAMA,eAAsB,qBAAqB,EACzC,YACA,UACA,WACA,aAMyB;CACzB,MAAM,MAAM,MAAM,SAAS,eAAe,SAAS;CACnD,IAAI,KAAK,kBAAkB;EACzB,MAAM,UAAU,MAAM,UAAU,+BAA+B;GAC7D,OAAO,IAAI;GACX,kBAAkB,IAAI;GACtB;EACF,CAAC;EACD,IAAI,SAAS,OAAO,QAAQ;CAC9B;CAEA,MAAM,WAAU,MADG,WAAW,qBAAqB,SAAS,EAAA,EACtC,OAAO,MAAM;CACnC,IAAI,SAAS,OAAO;CAGpB,OADe,CAAC,GAAG,MADG,WAAW,aAAa,EAAE,YAAY,UAAU,CAAC,CAC7C,CAAC,CAAC,MAAM,MAAM,UAAU,MAAM,UAAU,QAAQ,IAAI,KAAK,UAAU,QAAQ,CAAC,CAAC,CAAC,EAC3F,EAAE,MAAM;AACvB"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IntegrationTools } from '../integrations/base.js';
|
|
2
|
+
import type { FactoryApprovalToolDependencies } from './approval-tools.js';
|
|
3
|
+
import type { FactoryMessagingToolDependencies } from './messaging-tools.js';
|
|
4
|
+
import type { FactoryReadToolDependencies } from './read-tools.js';
|
|
5
|
+
import type { FactoryToolScope } from './scope.js';
|
|
6
|
+
import type { FactorySessionToolDependencies } from './session-tools.js';
|
|
7
|
+
import type { FactoryWorkItemToolDependencies } from './work-item-tools.js';
|
|
8
|
+
export { FACTORY_TOOLS_WITHOUT_APPROVAL, applyFactoryToolPermissions } from './permissions.js';
|
|
9
|
+
export { FACTORY_INTERACTIVE_KEY, resolveFactoryToolScope } from './scope.js';
|
|
10
|
+
export type { FactoryToolScope } from './scope.js';
|
|
11
|
+
/** Storage and services the Factory tools act through. */
|
|
12
|
+
export type FactoryToolDependencies = FactoryReadToolDependencies & FactoryWorkItemToolDependencies & FactoryMessagingToolDependencies & FactorySessionToolDependencies & FactoryApprovalToolDependencies;
|
|
13
|
+
export interface CreateFactoryToolsOptions {
|
|
14
|
+
scope: FactoryToolScope;
|
|
15
|
+
deps: FactoryToolDependencies;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* The Factory toolset for a resolved scope. Tools take scope and storage by
|
|
19
|
+
* closure and never read the request context, so a non-session transport
|
|
20
|
+
* (an MCP server) can mount the same set with a scope it resolved itself.
|
|
21
|
+
*
|
|
22
|
+
* Worker and side sessions only answer messages. User sessions get everything; turns
|
|
23
|
+
* nobody signed in started (notification wakes, webhooks, dispatcher notices)
|
|
24
|
+
* get the read tools and messaging limited to sessions they follow: text from
|
|
25
|
+
* other people reaches those turns, and the management tools act as the
|
|
26
|
+
* session owner.
|
|
27
|
+
*/
|
|
28
|
+
export declare function createFactoryTools({ scope, deps }: CreateFactoryToolsOptions): IntegrationTools;
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,qBAAqB,CAAC;AAE3E,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,sBAAsB,CAAC;AAE7E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,oBAAoB,CAAC;AAEzE,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,EAAE,8BAA8B,EAAE,2BAA2B,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAC9E,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,0DAA0D;AAC1D,MAAM,MAAM,uBAAuB,GAAG,2BAA2B,GAC/D,+BAA+B,GAC/B,gCAAgC,GAChC,8BAA8B,GAC9B,+BAA+B,CAAC;AAElC,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,gBAAgB,CAAC;IACxB,IAAI,EAAE,uBAAuB,CAAC;CAC/B;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,yBAAyB,GAAG,gBAAgB,CAW/F"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FACTORY_INTERACTIVE_KEY } from "../auth.js";
|
|
2
|
+
import { createFactoryApprovalTools } from "./approval-tools.js";
|
|
3
|
+
import { createFactoryReadTools } from "./read-tools.js";
|
|
4
|
+
import { createFactoryMessagingTools } from "./messaging-tools.js";
|
|
5
|
+
import { createFactorySessionTools } from "./session-tools.js";
|
|
6
|
+
import { createFactoryWorkItemTools } from "./work-item-tools.js";
|
|
7
|
+
import { FACTORY_TOOLS_WITHOUT_APPROVAL, applyFactoryToolPermissions } from "./permissions.js";
|
|
8
|
+
import { resolveFactoryToolScope } from "./scope.js";
|
|
9
|
+
//#region src/tools/index.ts
|
|
10
|
+
/**
|
|
11
|
+
* The Factory toolset for a resolved scope. Tools take scope and storage by
|
|
12
|
+
* closure and never read the request context, so a non-session transport
|
|
13
|
+
* (an MCP server) can mount the same set with a scope it resolved itself.
|
|
14
|
+
*
|
|
15
|
+
* Worker and side sessions only answer messages. User sessions get everything; turns
|
|
16
|
+
* nobody signed in started (notification wakes, webhooks, dispatcher notices)
|
|
17
|
+
* get the read tools and messaging limited to sessions they follow: text from
|
|
18
|
+
* other people reaches those turns, and the management tools act as the
|
|
19
|
+
* session owner.
|
|
20
|
+
*/
|
|
21
|
+
function createFactoryTools({ scope, deps }) {
|
|
22
|
+
const messagingTools = createFactoryMessagingTools(scope, deps);
|
|
23
|
+
if (scope.kind !== "user") return messagingTools;
|
|
24
|
+
const readTools = {
|
|
25
|
+
...createFactoryReadTools(scope, deps),
|
|
26
|
+
...messagingTools
|
|
27
|
+
};
|
|
28
|
+
if (!scope.interactive) return readTools;
|
|
29
|
+
return {
|
|
30
|
+
...readTools,
|
|
31
|
+
...createFactoryWorkItemTools(scope, deps),
|
|
32
|
+
...createFactorySessionTools(scope, deps),
|
|
33
|
+
...createFactoryApprovalTools(scope, deps)
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { FACTORY_INTERACTIVE_KEY, FACTORY_TOOLS_WITHOUT_APPROVAL, applyFactoryToolPermissions, createFactoryTools, resolveFactoryToolScope };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/tools/index.ts"],"sourcesContent":["import type { IntegrationTools } from '../integrations/base.js';\nimport { createFactoryApprovalTools } from './approval-tools.js';\nimport type { FactoryApprovalToolDependencies } from './approval-tools.js';\nimport { createFactoryMessagingTools } from './messaging-tools.js';\nimport type { FactoryMessagingToolDependencies } from './messaging-tools.js';\nimport { createFactoryReadTools } from './read-tools.js';\nimport type { FactoryReadToolDependencies } from './read-tools.js';\nimport type { FactoryToolScope } from './scope.js';\nimport { createFactorySessionTools } from './session-tools.js';\nimport type { FactorySessionToolDependencies } from './session-tools.js';\nimport { createFactoryWorkItemTools } from './work-item-tools.js';\nimport type { FactoryWorkItemToolDependencies } from './work-item-tools.js';\n\nexport { FACTORY_TOOLS_WITHOUT_APPROVAL, applyFactoryToolPermissions } from './permissions.js';\nexport { FACTORY_INTERACTIVE_KEY, resolveFactoryToolScope } from './scope.js';\nexport type { FactoryToolScope } from './scope.js';\n\n/** Storage and services the Factory tools act through. */\nexport type FactoryToolDependencies = FactoryReadToolDependencies &\n FactoryWorkItemToolDependencies &\n FactoryMessagingToolDependencies &\n FactorySessionToolDependencies &\n FactoryApprovalToolDependencies;\n\nexport interface CreateFactoryToolsOptions {\n scope: FactoryToolScope;\n deps: FactoryToolDependencies;\n}\n\n/**\n * The Factory toolset for a resolved scope. Tools take scope and storage by\n * closure and never read the request context, so a non-session transport\n * (an MCP server) can mount the same set with a scope it resolved itself.\n *\n * Worker and side sessions only answer messages. User sessions get everything; turns\n * nobody signed in started (notification wakes, webhooks, dispatcher notices)\n * get the read tools and messaging limited to sessions they follow: text from\n * other people reaches those turns, and the management tools act as the\n * session owner.\n */\nexport function createFactoryTools({ scope, deps }: CreateFactoryToolsOptions): IntegrationTools {\n const messagingTools = createFactoryMessagingTools(scope, deps);\n if (scope.kind !== 'user') return messagingTools;\n const readTools = { ...createFactoryReadTools(scope, deps), ...messagingTools };\n if (!scope.interactive) return readTools;\n return {\n ...readTools,\n ...createFactoryWorkItemTools(scope, deps),\n ...createFactorySessionTools(scope, deps),\n ...createFactoryApprovalTools(scope, deps),\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwCA,SAAgB,mBAAmB,EAAE,OAAO,QAAqD;CAC/F,MAAM,iBAAiB,4BAA4B,OAAO,IAAI;CAC9D,IAAI,MAAM,SAAS,QAAQ,OAAO;CAClC,MAAM,YAAY;EAAE,GAAG,uBAAuB,OAAO,IAAI;EAAG,GAAG;CAAe;CAC9E,IAAI,CAAC,MAAM,aAAa,OAAO;CAC/B,OAAO;EACL,GAAG;EACH,GAAG,2BAA2B,OAAO,IAAI;EACzC,GAAG,0BAA0B,OAAO,IAAI;EACxC,GAAG,2BAA2B,OAAO,IAAI;CAC3C;AACF"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Messages between Factory sessions. A user session can message any session in
|
|
3
|
+
* its Factory it can see; the receiver answers that exact message. Both
|
|
4
|
+
* directions are `factory-session` notifications on the receiver's stored thread.
|
|
5
|
+
*/
|
|
6
|
+
import type { NotificationsStorage } from '@mastra/core/notifications';
|
|
7
|
+
import type { IntegrationTools } from '../integrations/base.js';
|
|
8
|
+
import type { SourceControlSessionLookup } from '../session/factory-session.js';
|
|
9
|
+
import type { AuditRecorder } from '../storage/domains/audit/domain.js';
|
|
10
|
+
import type { WorkItemsStorage } from '../storage/domains/work-items/base.js';
|
|
11
|
+
import type { FactoryDeliveryController } from './deliver.js';
|
|
12
|
+
import type { FactoryToolScope } from './scope.js';
|
|
13
|
+
/** Messages plus replies, per sender session, per target thread, per window. */
|
|
14
|
+
export declare const SIGNAL_RATE_LIMIT = 10;
|
|
15
|
+
export declare const SIGNAL_RATE_WINDOW_MS: number;
|
|
16
|
+
export interface FactoryMessaging {
|
|
17
|
+
controller: FactoryDeliveryController;
|
|
18
|
+
notifications: Pick<NotificationsStorage, 'listNotifications' | 'getNotification'>;
|
|
19
|
+
}
|
|
20
|
+
export interface FactoryMessagingToolDependencies {
|
|
21
|
+
sessions: Pick<SourceControlSessionLookup, 'getBySessionId' | 'listByOrigin'>;
|
|
22
|
+
workItems: Pick<WorkItemsStorage, 'findActiveRunBindingForSession' | 'getForProject' | 'listSessionSubscriptions' | 'listRunBindings' | 'listWorkItemSubscribers'>;
|
|
23
|
+
auditRecorder: AuditRecorder;
|
|
24
|
+
/** The live controller and notifications store; resolved per call because they exist only once the mount is up. */
|
|
25
|
+
messaging: () => Promise<FactoryMessaging | null>;
|
|
26
|
+
}
|
|
27
|
+
export declare function createFactoryMessagingTools(scope: FactoryToolScope, deps: FactoryMessagingToolDependencies): IntegrationTools;
|
|
28
|
+
//# sourceMappingURL=messaging-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging-tools.d.ts","sourceRoot":"","sources":["../../src/tools/messaging-tools.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAA4C,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAIjH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAExE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AAM9E,OAAO,KAAK,EAAE,yBAAyB,EAAyB,MAAM,cAAc,CAAC;AAGrF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEnD,gFAAgF;AAChF,eAAO,MAAM,iBAAiB,KAAK,CAAC;AACpC,eAAO,MAAM,qBAAqB,QAAiB,CAAC;AAKpD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,yBAAyB,CAAC;IACtC,aAAa,EAAE,IAAI,CAAC,oBAAoB,EAAE,mBAAmB,GAAG,iBAAiB,CAAC,CAAC;CACpF;AAED,MAAM,WAAW,gCAAgC;IAC/C,QAAQ,EAAE,IAAI,CAAC,0BAA0B,EAAE,gBAAgB,GAAG,cAAc,CAAC,CAAC;IAC9E,SAAS,EAAE,IAAI,CACb,gBAAgB,EACd,gCAAgC,GAChC,eAAe,GACf,0BAA0B,GAC1B,iBAAiB,GACjB,yBAAyB,CAC5B,CAAC;IACF,aAAa,EAAE,aAAa,CAAC;IAC7B,mHAAmH;IACnH,SAAS,EAAE,MAAM,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;CACnD;AAgBD,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,gBAAgB,EACvB,IAAI,EAAE,gCAAgC,GACrC,gBAAgB,CAyRlB"}
|