@mastra/factory 0.12.1-alpha.0 → 0.13.0-alpha.10
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 +163 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +35 -3
- package/dist/auth.js.map +1 -1
- package/dist/boards/define-board.d.ts +42 -0
- package/dist/boards/define-board.d.ts.map +1 -0
- package/dist/boards/define-board.js +53 -0
- package/dist/boards/define-board.js.map +1 -0
- package/dist/boards/index.d.ts +12 -0
- package/dist/boards/index.d.ts.map +1 -0
- package/dist/boards/index.js +20 -0
- package/dist/boards/index.js.map +1 -0
- package/dist/boards/registry.d.ts +16 -0
- package/dist/boards/registry.d.ts.map +1 -0
- package/dist/boards/registry.js +27 -0
- package/dist/boards/registry.js.map +1 -0
- package/dist/boards/review.d.ts +4 -0
- package/dist/boards/review.d.ts.map +1 -0
- package/dist/boards/review.js +76 -0
- package/dist/boards/review.js.map +1 -0
- package/dist/boards/transition-policy.d.ts +63 -0
- package/dist/boards/transition-policy.d.ts.map +1 -0
- package/dist/boards/transition-policy.js +36 -0
- package/dist/boards/transition-policy.js.map +1 -0
- package/dist/boards/work-transition-policy.d.ts +3 -0
- package/dist/boards/work-transition-policy.d.ts.map +1 -0
- package/dist/boards/work-transition-policy.js +31 -0
- package/dist/boards/work-transition-policy.js.map +1 -0
- package/dist/boards/work.d.ts +4 -0
- package/dist/boards/work.d.ts.map +1 -0
- package/dist/boards/work.js +154 -0
- package/dist/boards/work.js.map +1 -0
- package/dist/factory.d.ts +6 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +101 -10
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/integrations/github/acceptance-labels.d.ts +0 -1
- package/dist/integrations/github/acceptance-labels.d.ts.map +1 -1
- package/dist/integrations/github/acceptance-labels.js +2 -2
- package/dist/integrations/github/acceptance-labels.js.map +1 -1
- package/dist/integrations/github/default-rules.d.ts +194 -0
- package/dist/integrations/github/default-rules.d.ts.map +1 -0
- package/dist/integrations/github/default-rules.js +230 -0
- package/dist/integrations/github/default-rules.js.map +1 -0
- package/dist/integrations/github/integration.d.ts +4 -0
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +6 -0
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/provenance.d.ts +2 -0
- package/dist/integrations/github/provenance.d.ts.map +1 -1
- package/dist/integrations/github/provenance.js +3 -2
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +15 -41
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +3 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +36 -7
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +35 -3
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/linear/agent-tools.d.ts +3 -3
- package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
- package/dist/integrations/linear/agent-tools.js +5 -3
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/linear/default-rules.d.ts +44 -0
- package/dist/integrations/linear/default-rules.d.ts.map +1 -0
- package/dist/integrations/linear/default-rules.js +65 -0
- package/dist/integrations/linear/default-rules.js.map +1 -0
- package/dist/integrations/linear/integration.d.ts +5 -1
- package/dist/integrations/linear/integration.d.ts.map +1 -1
- package/dist/integrations/linear/integration.js +7 -1
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/issue-reconciler.d.ts +1 -1
- package/dist/integrations/linear/issue-reconciler.d.ts.map +1 -1
- package/dist/integrations/linear/issue-reconciler.js +2 -1
- package/dist/integrations/linear/issue-reconciler.js.map +1 -1
- package/dist/integrations/linear/rules.d.ts +5 -1
- package/dist/integrations/linear/rules.d.ts.map +1 -1
- package/dist/integrations/linear/rules.js +5 -4
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +4 -0
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +6 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.d.ts +5 -1
- package/dist/integrations/platform/linear/integration.d.ts.map +1 -1
- package/dist/integrations/platform/linear/integration.js +9 -3
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/slack/slack.d.ts +3 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +20 -2
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/routes/attention-proposed.d.ts +3 -0
- package/dist/routes/attention-proposed.d.ts.map +1 -0
- package/dist/routes/attention-proposed.js +21 -0
- package/dist/routes/attention-proposed.js.map +1 -0
- package/dist/routes/attention-providers.d.ts +33 -4
- package/dist/routes/attention-providers.d.ts.map +1 -1
- package/dist/routes/attention-providers.js +180 -47
- package/dist/routes/attention-providers.js.map +1 -1
- package/dist/routes/attention.d.ts.map +1 -1
- package/dist/routes/attention.js +31 -30
- package/dist/routes/attention.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 +3 -2
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/projects.d.ts +9 -1
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +26 -5
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/supervisor.d.ts +23 -0
- package/dist/routes/supervisor.d.ts.map +1 -0
- package/dist/routes/supervisor.js +34 -0
- package/dist/routes/supervisor.js.map +1 -0
- package/dist/routes/surface.d.ts +3 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +11 -6
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts +3 -0
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +60 -53
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/defaults.d.ts.map +1 -1
- package/dist/rules/defaults.js +2 -418
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatcher.d.ts +1 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +87 -26
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/index.d.ts +2 -2
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +2 -2
- package/dist/rules/processor.d.ts +2 -0
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +10 -7
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.d.ts +4 -5
- package/dist/rules/resolve.d.ts.map +1 -1
- package/dist/rules/resolve.js +4 -9
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.d.ts +3 -16
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +12 -43
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/terminal-cleanup.d.ts +3 -1
- package/dist/rules/terminal-cleanup.d.ts.map +1 -1
- package/dist/rules/terminal-cleanup.js +5 -0
- package/dist/rules/terminal-cleanup.js.map +1 -1
- package/dist/rules/transition-service.d.ts +3 -0
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +56 -31
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +18 -17
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +10 -2
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.d.ts.map +1 -1
- package/dist/rules/validation.js +2 -38
- package/dist/rules/validation.js.map +1 -1
- package/dist/sandbox/session-retirement.js +1 -1
- package/dist/session/org-seed.d.ts +1 -1
- package/dist/session/org-seed.js +1 -1
- package/dist/session/org-seed.js.map +1 -1
- package/dist/storage/domains/comments/domain.js +1 -1
- package/dist/storage/domains/comments/routes.js +1 -1
- package/dist/storage/domains/work-items/base.d.ts +54 -19
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +215 -38
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/health-worker.d.ts +17 -0
- package/dist/supervisor/health-worker.d.ts.map +1 -0
- package/dist/supervisor/health-worker.js +72 -0
- package/dist/supervisor/health-worker.js.map +1 -0
- package/dist/supervisor/health.d.ts +81 -0
- package/dist/supervisor/health.d.ts.map +1 -0
- package/dist/supervisor/health.js +244 -0
- package/dist/supervisor/health.js.map +1 -0
- package/dist/supervisor/instructions.d.ts +6 -0
- package/dist/supervisor/instructions.d.ts.map +1 -0
- package/dist/supervisor/instructions.js +64 -0
- package/dist/supervisor/instructions.js.map +1 -0
- package/dist/supervisor/read-tools.d.ts +41 -0
- package/dist/supervisor/read-tools.d.ts.map +1 -0
- package/dist/supervisor/read-tools.js +338 -0
- package/dist/supervisor/read-tools.js.map +1 -0
- package/dist/supervisor/session.d.ts +38 -0
- package/dist/supervisor/session.d.ts.map +1 -0
- package/dist/supervisor/session.js +63 -0
- package/dist/supervisor/session.js.map +1 -0
- package/dist/supervisor/write-tools.d.ts +26 -0
- package/dist/supervisor/write-tools.d.ts.map +1 -0
- package/dist/supervisor/write-tools.js +227 -0
- package/dist/supervisor/write-tools.js.map +1 -0
- package/dist/work-item-branch.d.ts +6 -0
- package/dist/work-item-branch.d.ts.map +1 -1
- package/dist/work-item-branch.js +15 -9
- package/dist/work-item-branch.js.map +1 -1
- package/dist/workspace.d.ts +3 -0
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +15 -5
- package/dist/workspace.js.map +1 -1
- package/factory-skills/configure-factory-rules/SKILL.md +42 -11
- package/factory-skills/factory-triage/SKILL.md +1 -1
- package/package.json +13 -7
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { FACTORY_RULE_STAGES, factoryRuleStage } from "../rules/types.js";
|
|
2
|
+
import { createTool } from "@mastra/core/tools";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
//#region src/supervisor/write-tools.ts
|
|
5
|
+
function createFactorySupervisorWriteTools(deps) {
|
|
6
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
7
|
+
const audit = async (action, target, metadata = {}) => deps.audit.record({
|
|
8
|
+
orgId: deps.scope.orgId,
|
|
9
|
+
actorId: deps.userId,
|
|
10
|
+
actorType: "human",
|
|
11
|
+
action,
|
|
12
|
+
targets: [target],
|
|
13
|
+
factoryProjectId: deps.scope.factoryProjectId,
|
|
14
|
+
metadata: {
|
|
15
|
+
...metadata,
|
|
16
|
+
cause: "supervisor"
|
|
17
|
+
},
|
|
18
|
+
occurredAt: now()
|
|
19
|
+
});
|
|
20
|
+
return {
|
|
21
|
+
factory_retry_decision: createTool({
|
|
22
|
+
id: "factory_retry_decision",
|
|
23
|
+
description: "Retry one failed Factory decision after the person confirms the repair.",
|
|
24
|
+
inputSchema: z.object({ decisionId: z.string().min(1) }),
|
|
25
|
+
requireApproval: true,
|
|
26
|
+
execute: async ({ decisionId }) => {
|
|
27
|
+
const decision = await deps.workItems.retryDeferredDecision(deps.scope.orgId, deps.scope.factoryProjectId, decisionId, now());
|
|
28
|
+
if (!decision) throw new Error("The decision is not failed, no longer exists, or belongs to another factory.");
|
|
29
|
+
await audit("factory.run.retry", {
|
|
30
|
+
type: "rule_decision",
|
|
31
|
+
id: decisionId
|
|
32
|
+
}, { workItemId: decision.workItemId });
|
|
33
|
+
return {
|
|
34
|
+
decisionId,
|
|
35
|
+
status: decision.status,
|
|
36
|
+
workItemId: decision.workItemId
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}),
|
|
40
|
+
factory_dismiss_decision: createTool({
|
|
41
|
+
id: "factory_dismiss_decision",
|
|
42
|
+
description: "Dismiss one parked Factory proposal after the person confirms it is no longer wanted.",
|
|
43
|
+
inputSchema: z.object({ decisionId: z.string().min(1) }),
|
|
44
|
+
requireApproval: true,
|
|
45
|
+
execute: async ({ decisionId }) => {
|
|
46
|
+
const decision = await deps.workItems.dismissDeferredDecision(deps.scope.orgId, deps.scope.factoryProjectId, decisionId, now());
|
|
47
|
+
if (!decision) throw new Error("The decision is not proposed, no longer exists, or belongs to another factory.");
|
|
48
|
+
await audit("factory.run.dismissed", {
|
|
49
|
+
type: "rule_decision",
|
|
50
|
+
id: decisionId
|
|
51
|
+
}, { workItemId: decision.workItemId });
|
|
52
|
+
return {
|
|
53
|
+
decisionId,
|
|
54
|
+
status: decision.status,
|
|
55
|
+
workItemId: decision.workItemId
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}),
|
|
59
|
+
factory_resolve_proposal: createTool({
|
|
60
|
+
id: "factory_resolve_proposal",
|
|
61
|
+
description: "Approve or dismiss one proposed Factory decision after the person confirms the choice.",
|
|
62
|
+
inputSchema: z.object({
|
|
63
|
+
decisionId: z.string().min(1),
|
|
64
|
+
resolution: z.enum(["approve", "dismiss"])
|
|
65
|
+
}),
|
|
66
|
+
requireApproval: true,
|
|
67
|
+
execute: async ({ decisionId, resolution }) => {
|
|
68
|
+
const decision = resolution === "approve" ? await deps.workItems.approveDeferredDecision(deps.scope.orgId, deps.scope.factoryProjectId, decisionId, now(), deps.userId) : await deps.workItems.dismissDeferredDecision(deps.scope.orgId, deps.scope.factoryProjectId, decisionId, now());
|
|
69
|
+
if (!decision) throw new Error("The proposal is no longer open or belongs to another factory.");
|
|
70
|
+
await audit(resolution === "approve" ? "factory.run.approved" : "factory.run.dismissed", {
|
|
71
|
+
type: "rule_decision",
|
|
72
|
+
id: decisionId
|
|
73
|
+
}, { workItemId: decision.workItemId });
|
|
74
|
+
return {
|
|
75
|
+
decisionId,
|
|
76
|
+
resolution,
|
|
77
|
+
status: decision.status,
|
|
78
|
+
workItemId: decision.workItemId
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}),
|
|
82
|
+
factory_transition_work_item: createTool({
|
|
83
|
+
id: "factory_transition_work_item",
|
|
84
|
+
description: "Move or accept one Factory work item after the person confirms the destination stage.",
|
|
85
|
+
inputSchema: z.object({
|
|
86
|
+
workItemId: z.string().min(1),
|
|
87
|
+
stage: z.enum(FACTORY_RULE_STAGES)
|
|
88
|
+
}),
|
|
89
|
+
requireApproval: true,
|
|
90
|
+
execute: async ({ workItemId, stage }) => {
|
|
91
|
+
const item = await deps.workItems.get({
|
|
92
|
+
orgId: deps.scope.orgId,
|
|
93
|
+
id: workItemId
|
|
94
|
+
});
|
|
95
|
+
if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error("Work item not found.");
|
|
96
|
+
const from = factoryRuleStage(item.stages);
|
|
97
|
+
if (!from) throw new Error("The work item does not have one valid Factory stage.");
|
|
98
|
+
const result = await deps.transitionService.transition({
|
|
99
|
+
orgId: deps.scope.orgId,
|
|
100
|
+
factoryProjectId: deps.scope.factoryProjectId,
|
|
101
|
+
workItemId,
|
|
102
|
+
board: item.externalSource?.type === "pull-request" ? "review" : "work",
|
|
103
|
+
stage,
|
|
104
|
+
expectedRevision: item.revision,
|
|
105
|
+
actor: {
|
|
106
|
+
type: "human",
|
|
107
|
+
id: deps.userId
|
|
108
|
+
},
|
|
109
|
+
ingress: {
|
|
110
|
+
type: "human",
|
|
111
|
+
identity: `supervisor:${deps.userId}:${workItemId}:${item.revision}:${stage}`
|
|
112
|
+
},
|
|
113
|
+
cause: "supervisor"
|
|
114
|
+
});
|
|
115
|
+
await audit(result.status === "accepted" ? "factory.work_item.stage_moved" : "factory.work_item.transition_rejected", {
|
|
116
|
+
type: "work_item",
|
|
117
|
+
id: workItemId
|
|
118
|
+
}, result.status === "accepted" ? {
|
|
119
|
+
from,
|
|
120
|
+
to: result.stage,
|
|
121
|
+
revision: result.revision,
|
|
122
|
+
transitionId: result.transitionId
|
|
123
|
+
} : {
|
|
124
|
+
from,
|
|
125
|
+
to: stage,
|
|
126
|
+
code: result.code,
|
|
127
|
+
reason: result.reason,
|
|
128
|
+
transitionId: result.transitionId
|
|
129
|
+
});
|
|
130
|
+
if (result.status !== "accepted") throw new Error(`The transition was rejected (${result.code}): ${result.reason}`);
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
}),
|
|
134
|
+
factory_reconcile_labels: createTool({
|
|
135
|
+
id: "factory_reconcile_labels",
|
|
136
|
+
description: "Reconcile stale acceptance labels for one accepted work item after the person confirms the repair.",
|
|
137
|
+
inputSchema: z.object({ workItemId: z.string().min(1) }),
|
|
138
|
+
requireApproval: true,
|
|
139
|
+
execute: async ({ workItemId }) => {
|
|
140
|
+
if (!deps.reconcileAcceptanceLabels) throw new Error("Acceptance-label reconciliation is unavailable.");
|
|
141
|
+
const item = await deps.workItems.get({
|
|
142
|
+
orgId: deps.scope.orgId,
|
|
143
|
+
id: workItemId
|
|
144
|
+
});
|
|
145
|
+
if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error("Work item not found.");
|
|
146
|
+
if (!item.acceptedAt) throw new Error("The work item has not been accepted.");
|
|
147
|
+
await deps.reconcileAcceptanceLabels({
|
|
148
|
+
...deps.scope,
|
|
149
|
+
item
|
|
150
|
+
});
|
|
151
|
+
await audit("factory.work_item.labels_reconciled", {
|
|
152
|
+
type: "work_item",
|
|
153
|
+
id: workItemId
|
|
154
|
+
});
|
|
155
|
+
return {
|
|
156
|
+
workItemId,
|
|
157
|
+
reconciled: true
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
}),
|
|
161
|
+
factory_revoke_binding: createTool({
|
|
162
|
+
id: "factory_revoke_binding",
|
|
163
|
+
description: "Revoke an orphaned or stale run binding after a health finding and person confirmation.",
|
|
164
|
+
inputSchema: z.object({ bindingId: z.string().min(1) }),
|
|
165
|
+
requireApproval: true,
|
|
166
|
+
execute: async ({ bindingId }) => {
|
|
167
|
+
const binding = await deps.workItems.revokeRunBinding({
|
|
168
|
+
orgId: deps.scope.orgId,
|
|
169
|
+
factoryProjectId: deps.scope.factoryProjectId,
|
|
170
|
+
bindingId,
|
|
171
|
+
revokedAt: now()
|
|
172
|
+
});
|
|
173
|
+
if (!binding) throw new Error("The binding is not active, no longer exists, or belongs to another factory.");
|
|
174
|
+
await audit("factory.intake.binding_updated", {
|
|
175
|
+
type: "run_binding",
|
|
176
|
+
id: bindingId
|
|
177
|
+
}, {
|
|
178
|
+
workItemId: binding.workItemId,
|
|
179
|
+
role: binding.role,
|
|
180
|
+
status: "revoked"
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
bindingId,
|
|
184
|
+
status: binding.status,
|
|
185
|
+
workItemId: binding.workItemId,
|
|
186
|
+
role: binding.role
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
}),
|
|
190
|
+
factory_signal_session: createTool({
|
|
191
|
+
id: "factory_signal_session",
|
|
192
|
+
description: "Send bounded guidance to a worker session after the person confirms the exact message.",
|
|
193
|
+
inputSchema: z.object({
|
|
194
|
+
sessionId: z.string().min(1),
|
|
195
|
+
message: z.string().trim().min(1).max(2e3)
|
|
196
|
+
}),
|
|
197
|
+
requireApproval: true,
|
|
198
|
+
execute: async ({ sessionId, message }) => {
|
|
199
|
+
if (!deps.signalSession) throw new Error("Worker session signaling is unavailable.");
|
|
200
|
+
const binding = (await deps.workItems.listRunBindings(deps.scope.orgId, deps.scope.factoryProjectId)).find((row) => row.sessionId === sessionId);
|
|
201
|
+
if (!binding) throw new Error("The session does not belong to this factory.");
|
|
202
|
+
await deps.signalSession({
|
|
203
|
+
sessionId,
|
|
204
|
+
message,
|
|
205
|
+
userId: deps.userId
|
|
206
|
+
});
|
|
207
|
+
await audit("factory.agent.signaled", {
|
|
208
|
+
type: "factory_session",
|
|
209
|
+
id: sessionId
|
|
210
|
+
}, {
|
|
211
|
+
workItemId: binding.workItemId,
|
|
212
|
+
role: binding.role
|
|
213
|
+
});
|
|
214
|
+
return {
|
|
215
|
+
sessionId,
|
|
216
|
+
delivered: true,
|
|
217
|
+
workItemId: binding.workItemId,
|
|
218
|
+
role: binding.role
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//#endregion
|
|
225
|
+
export { createFactorySupervisorWriteTools };
|
|
226
|
+
|
|
227
|
+
//# sourceMappingURL=write-tools.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write-tools.js","names":[],"sources":["../../src/supervisor/write-tools.ts"],"sourcesContent":["import { createTool } from '@mastra/core/tools';\nimport { z } from 'zod';\n\nimport type { IntegrationTools } from '../integrations/base.js';\nimport type { FactoryTransitionService } from '../rules/transition-service.js';\nimport { FACTORY_RULE_STAGES, factoryRuleStage } from '../rules/types.js';\nimport type { AuditStorage } from '../storage/domains/audit/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../storage/domains/work-items/base.js';\nimport type { SupervisorScope } from './read-tools.js';\n\ninterface SupervisorWriteDependencies {\n scope: SupervisorScope;\n userId: string;\n workItems: WorkItemsStorage;\n audit: AuditStorage;\n transitionService: FactoryTransitionService;\n reconcileAcceptanceLabels?: (input: { orgId: string; factoryProjectId: string; item: WorkItemRow }) => Promise<void>;\n signalSession?: (input: { sessionId: string; message: string; userId: string }) => Promise<unknown>;\n now?: () => Date;\n}\n\nexport function createFactorySupervisorWriteTools(deps: SupervisorWriteDependencies): IntegrationTools {\n const now = deps.now ?? (() => new Date());\n const audit = async (action: string, target: { type: string; id: string }, metadata: Record<string, unknown> = {}) =>\n deps.audit.record({\n orgId: deps.scope.orgId,\n actorId: deps.userId,\n actorType: 'human',\n action,\n targets: [target],\n factoryProjectId: deps.scope.factoryProjectId,\n metadata: { ...metadata, cause: 'supervisor' },\n occurredAt: now(),\n });\n\n return {\n factory_retry_decision: createTool({\n id: 'factory_retry_decision',\n description: 'Retry one failed Factory decision after the person confirms the repair.',\n inputSchema: z.object({ decisionId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ decisionId }) => {\n const decision = await deps.workItems.retryDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision) throw new Error('The decision is not failed, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.run.retry',\n { type: 'rule_decision', id: decisionId },\n { workItemId: decision.workItemId },\n );\n return { decisionId, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_dismiss_decision: createTool({\n id: 'factory_dismiss_decision',\n description: 'Dismiss one parked Factory proposal after the person confirms it is no longer wanted.',\n inputSchema: z.object({ decisionId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ decisionId }) => {\n const decision = await deps.workItems.dismissDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision)\n throw new Error('The decision is not proposed, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.run.dismissed',\n { type: 'rule_decision', id: decisionId },\n { workItemId: decision.workItemId },\n );\n return { decisionId, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_resolve_proposal: createTool({\n id: 'factory_resolve_proposal',\n description: 'Approve or dismiss one proposed Factory decision after the person confirms the choice.',\n inputSchema: z.object({ decisionId: z.string().min(1), resolution: z.enum(['approve', 'dismiss']) }),\n requireApproval: true,\n execute: async ({ decisionId, resolution }) => {\n const decision =\n resolution === 'approve'\n ? await deps.workItems.approveDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n deps.userId,\n )\n : await deps.workItems.dismissDeferredDecision(\n deps.scope.orgId,\n deps.scope.factoryProjectId,\n decisionId,\n now(),\n );\n if (!decision) throw new Error('The proposal is no longer open or belongs to another factory.');\n await audit(\n resolution === 'approve' ? 'factory.run.approved' : 'factory.run.dismissed',\n {\n type: 'rule_decision',\n id: decisionId,\n },\n { workItemId: decision.workItemId },\n );\n return { decisionId, resolution, status: decision.status, workItemId: decision.workItemId };\n },\n }),\n factory_transition_work_item: createTool({\n id: 'factory_transition_work_item',\n description: 'Move or accept one Factory work item after the person confirms the destination stage.',\n inputSchema: z.object({ workItemId: z.string().min(1), stage: z.enum(FACTORY_RULE_STAGES) }),\n requireApproval: true,\n execute: async ({ workItemId, stage }) => {\n const item = await deps.workItems.get({ orgId: deps.scope.orgId, id: workItemId });\n if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error('Work item not found.');\n const from = factoryRuleStage(item.stages);\n if (!from) throw new Error('The work item does not have one valid Factory stage.');\n const result = await deps.transitionService.transition({\n orgId: deps.scope.orgId,\n factoryProjectId: deps.scope.factoryProjectId,\n workItemId,\n board: item.externalSource?.type === 'pull-request' ? 'review' : 'work',\n stage,\n expectedRevision: item.revision,\n actor: { type: 'human', id: deps.userId },\n ingress: { type: 'human', identity: `supervisor:${deps.userId}:${workItemId}:${item.revision}:${stage}` },\n cause: 'supervisor',\n });\n await audit(\n result.status === 'accepted' ? 'factory.work_item.stage_moved' : 'factory.work_item.transition_rejected',\n { type: 'work_item', id: workItemId },\n result.status === 'accepted'\n ? { from, to: result.stage, revision: result.revision, transitionId: result.transitionId }\n : { from, to: stage, code: result.code, reason: result.reason, transitionId: result.transitionId },\n );\n if (result.status !== 'accepted') {\n throw new Error(`The transition was rejected (${result.code}): ${result.reason}`);\n }\n return result;\n },\n }),\n factory_reconcile_labels: createTool({\n id: 'factory_reconcile_labels',\n description: 'Reconcile stale acceptance labels for one accepted work item after the person confirms the repair.',\n inputSchema: z.object({ workItemId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ workItemId }) => {\n if (!deps.reconcileAcceptanceLabels) throw new Error('Acceptance-label reconciliation is unavailable.');\n const item = await deps.workItems.get({ orgId: deps.scope.orgId, id: workItemId });\n if (!item || item.factoryProjectId !== deps.scope.factoryProjectId) throw new Error('Work item not found.');\n if (!item.acceptedAt) throw new Error('The work item has not been accepted.');\n await deps.reconcileAcceptanceLabels({ ...deps.scope, item });\n await audit('factory.work_item.labels_reconciled', { type: 'work_item', id: workItemId });\n return { workItemId, reconciled: true };\n },\n }),\n factory_revoke_binding: createTool({\n id: 'factory_revoke_binding',\n description: 'Revoke an orphaned or stale run binding after a health finding and person confirmation.',\n inputSchema: z.object({ bindingId: z.string().min(1) }),\n requireApproval: true,\n execute: async ({ bindingId }) => {\n const binding = await deps.workItems.revokeRunBinding({\n orgId: deps.scope.orgId,\n factoryProjectId: deps.scope.factoryProjectId,\n bindingId,\n revokedAt: now(),\n });\n if (!binding) throw new Error('The binding is not active, no longer exists, or belongs to another factory.');\n await audit(\n 'factory.intake.binding_updated',\n { type: 'run_binding', id: bindingId },\n {\n workItemId: binding.workItemId,\n role: binding.role,\n status: 'revoked',\n },\n );\n return { bindingId, status: binding.status, workItemId: binding.workItemId, role: binding.role };\n },\n }),\n factory_signal_session: createTool({\n id: 'factory_signal_session',\n description: 'Send bounded guidance to a worker session after the person confirms the exact message.',\n inputSchema: z.object({ sessionId: z.string().min(1), message: z.string().trim().min(1).max(2000) }),\n requireApproval: true,\n execute: async ({ sessionId, message }) => {\n if (!deps.signalSession) throw new Error('Worker session signaling is unavailable.');\n const bindings = await deps.workItems.listRunBindings(deps.scope.orgId, deps.scope.factoryProjectId);\n const binding = bindings.find(row => row.sessionId === sessionId);\n if (!binding) throw new Error('The session does not belong to this factory.');\n await deps.signalSession({ sessionId, message, userId: deps.userId });\n await audit(\n 'factory.agent.signaled',\n { type: 'factory_session', id: sessionId },\n {\n workItemId: binding.workItemId,\n role: binding.role,\n },\n );\n return { sessionId, delivered: true, workItemId: binding.workItemId, role: binding.role };\n },\n }),\n };\n}\n"],"mappings":"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"}
|
|
@@ -12,6 +12,12 @@ export interface WorkItemBranchInput {
|
|
|
12
12
|
}
|
|
13
13
|
/** Map a stored item's provenance onto the source vocabulary branch naming reads. */
|
|
14
14
|
export declare function workItemBranchSource(externalSource: ExternalWorkItemSource | null | undefined): WorkItemBranchSource;
|
|
15
|
+
/** The provider number a GitHub card carries — the `#12` its runs and thread titles name it by. */
|
|
16
|
+
export declare function workItemNumber(item: Pick<WorkItemBranchInput, 'source' | 'metadata'>): number | undefined;
|
|
17
|
+
/** How a card names its thread: a Linear title already opens with its identifier, a GitHub card gets its number here. */
|
|
18
|
+
export declare function workItemThreadTitle(item: Pick<WorkItemBranchInput, 'source' | 'metadata'> & {
|
|
19
|
+
title: string;
|
|
20
|
+
}): string;
|
|
15
21
|
/**
|
|
16
22
|
* The git branch an item's runs and sessions share, one grammar for both sides
|
|
17
23
|
* of the wire: the dispatcher names autonomous run branches with it and the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-item-branch.d.ts","sourceRoot":"","sources":["../src/work-item-branch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAE7G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,oBAAoB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAAG,oBAAoB,CAOpH;AAOD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"work-item-branch.d.ts","sourceRoot":"","sources":["../src/work-item-branch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG,cAAc,GAAG,WAAW,GAAG,cAAc,GAAG,cAAc,GAAG,QAAQ,CAAC;AAE7G,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,oBAAoB,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,cAAc,EAAE,sBAAsB,GAAG,IAAI,GAAG,SAAS,GAAG,oBAAoB,CAOpH;AAOD,mGAAmG;AACnG,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG,MAAM,GAAG,SAAS,CAKzG;AAED,yHAAyH;AACzH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,IAAI,CAAC,mBAAmB,EAAE,QAAQ,GAAG,UAAU,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GACzE,MAAM,CAIR;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,mBAAmB,GAAG,MAAM,CAWhE"}
|
package/dist/work-item-branch.js
CHANGED
|
@@ -10,6 +10,18 @@ function branchNumber(metadata, key) {
|
|
|
10
10
|
const value = metadata[key] ?? metadata.number;
|
|
11
11
|
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
12
12
|
}
|
|
13
|
+
/** The provider number a GitHub card carries — the `#12` its runs and thread titles name it by. */
|
|
14
|
+
function workItemNumber(item) {
|
|
15
|
+
const metadata = item.metadata ?? {};
|
|
16
|
+
if (item.source === "github-issue") return branchNumber(metadata, "githubIssueNumber");
|
|
17
|
+
if (item.source === "github-pr") return branchNumber(metadata, "githubPullRequestNumber");
|
|
18
|
+
}
|
|
19
|
+
/** How a card names its thread: a Linear title already opens with its identifier, a GitHub card gets its number here. */
|
|
20
|
+
function workItemThreadTitle(item) {
|
|
21
|
+
const number = workItemNumber(item);
|
|
22
|
+
if (number === void 0) return item.title;
|
|
23
|
+
return `${item.source === "github-pr" ? "PR" : "Issue"} #${number}: ${item.title}`;
|
|
24
|
+
}
|
|
13
25
|
/**
|
|
14
26
|
* The git branch an item's runs and sessions share, one grammar for both sides
|
|
15
27
|
* of the wire: the dispatcher names autonomous run branches with it and the
|
|
@@ -19,14 +31,8 @@ function branchNumber(metadata, key) {
|
|
|
19
31
|
*/
|
|
20
32
|
function workItemBranch(item) {
|
|
21
33
|
const metadata = item.metadata ?? {};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (issueNumber !== void 0) return `factory/issue-${issueNumber}`;
|
|
25
|
-
}
|
|
26
|
-
if (item.source === "github-pr") {
|
|
27
|
-
const pullRequestNumber = branchNumber(metadata, "githubPullRequestNumber");
|
|
28
|
-
if (pullRequestNumber !== void 0) return `factory/pr-${pullRequestNumber}`;
|
|
29
|
-
}
|
|
34
|
+
const githubNumber = workItemNumber(item);
|
|
35
|
+
if (githubNumber !== void 0) return item.source === "github-issue" ? `factory/issue-${githubNumber}` : `factory/pr-${githubNumber}`;
|
|
30
36
|
if (item.source === "linear-issue" && typeof metadata.identifier === "string") {
|
|
31
37
|
const identifier = metadata.identifier.trim();
|
|
32
38
|
if (identifier) return `factory/linear-${identifier.toLowerCase()}`;
|
|
@@ -34,6 +40,6 @@ function workItemBranch(item) {
|
|
|
34
40
|
return `factory/item-${item.id}`;
|
|
35
41
|
}
|
|
36
42
|
//#endregion
|
|
37
|
-
export { workItemBranch, workItemBranchSource };
|
|
43
|
+
export { workItemBranch, workItemBranchSource, workItemNumber, workItemThreadTitle };
|
|
38
44
|
|
|
39
45
|
//# sourceMappingURL=work-item-branch.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"work-item-branch.js","names":[],"sources":["../src/work-item-branch.ts"],"sourcesContent":["import type { ExternalWorkItemSource } from './storage/domains/work-items/base.js';\n\n/**\n * Where a card came from, in the vocabulary branch naming reads. Server rows\n * map their `externalSource` into this with {@link workItemBranchSource}; the\n * board's own `WorkItem['source']` is already this union.\n */\nexport type WorkItemBranchSource = 'github-issue' | 'github-pr' | 'linear-issue' | 'slack-thread' | 'manual';\n\nexport interface WorkItemBranchInput {\n id: string;\n source: WorkItemBranchSource;\n metadata?: Record<string, unknown> | null;\n}\n\n/** Map a stored item's provenance onto the source vocabulary branch naming reads. */\nexport function workItemBranchSource(externalSource: ExternalWorkItemSource | null | undefined): WorkItemBranchSource {\n if (!externalSource) return 'manual';\n if (externalSource.integrationId === 'linear') return 'linear-issue';\n // Only GitHub and Linear carry provider identities; anything else (a Slack\n // thread, say) is a plain work item rather than a mislabeled GitHub issue.\n if (externalSource.integrationId !== 'github') return 'manual';\n return externalSource.type === 'pull-request' ? 'github-pr' : 'github-issue';\n}\n\nfunction branchNumber(metadata: Record<string, unknown>, key: string): number | undefined {\n const value = metadata[key] ?? metadata.number;\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\n/**\n * The git branch an item's runs and sessions share, one grammar for both sides\n * of the wire: the dispatcher names autonomous run branches with it and the\n * board opens card sessions on it, so both converge on one checkout per item.\n * Cards without a provider identity (manual, Slack) and cards whose metadata\n * lost their identifier fall back to the id-derived branch.\n */\nexport function workItemBranch(item: WorkItemBranchInput): string {\n const metadata = item.metadata ?? {};\n
|
|
1
|
+
{"version":3,"file":"work-item-branch.js","names":[],"sources":["../src/work-item-branch.ts"],"sourcesContent":["import type { ExternalWorkItemSource } from './storage/domains/work-items/base.js';\n\n/**\n * Where a card came from, in the vocabulary branch naming reads. Server rows\n * map their `externalSource` into this with {@link workItemBranchSource}; the\n * board's own `WorkItem['source']` is already this union.\n */\nexport type WorkItemBranchSource = 'github-issue' | 'github-pr' | 'linear-issue' | 'slack-thread' | 'manual';\n\nexport interface WorkItemBranchInput {\n id: string;\n source: WorkItemBranchSource;\n metadata?: Record<string, unknown> | null;\n}\n\n/** Map a stored item's provenance onto the source vocabulary branch naming reads. */\nexport function workItemBranchSource(externalSource: ExternalWorkItemSource | null | undefined): WorkItemBranchSource {\n if (!externalSource) return 'manual';\n if (externalSource.integrationId === 'linear') return 'linear-issue';\n // Only GitHub and Linear carry provider identities; anything else (a Slack\n // thread, say) is a plain work item rather than a mislabeled GitHub issue.\n if (externalSource.integrationId !== 'github') return 'manual';\n return externalSource.type === 'pull-request' ? 'github-pr' : 'github-issue';\n}\n\nfunction branchNumber(metadata: Record<string, unknown>, key: string): number | undefined {\n const value = metadata[key] ?? metadata.number;\n return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\n/** The provider number a GitHub card carries — the `#12` its runs and thread titles name it by. */\nexport function workItemNumber(item: Pick<WorkItemBranchInput, 'source' | 'metadata'>): number | undefined {\n const metadata = item.metadata ?? {};\n if (item.source === 'github-issue') return branchNumber(metadata, 'githubIssueNumber');\n if (item.source === 'github-pr') return branchNumber(metadata, 'githubPullRequestNumber');\n return;\n}\n\n/** How a card names its thread: a Linear title already opens with its identifier, a GitHub card gets its number here. */\nexport function workItemThreadTitle(\n item: Pick<WorkItemBranchInput, 'source' | 'metadata'> & { title: string },\n): string {\n const number = workItemNumber(item);\n if (number === undefined) return item.title;\n return `${item.source === 'github-pr' ? 'PR' : 'Issue'} #${number}: ${item.title}`;\n}\n\n/**\n * The git branch an item's runs and sessions share, one grammar for both sides\n * of the wire: the dispatcher names autonomous run branches with it and the\n * board opens card sessions on it, so both converge on one checkout per item.\n * Cards without a provider identity (manual, Slack) and cards whose metadata\n * lost their identifier fall back to the id-derived branch.\n */\nexport function workItemBranch(item: WorkItemBranchInput): string {\n const metadata = item.metadata ?? {};\n const githubNumber = workItemNumber(item);\n if (githubNumber !== undefined) {\n return item.source === 'github-issue' ? `factory/issue-${githubNumber}` : `factory/pr-${githubNumber}`;\n }\n if (item.source === 'linear-issue' && typeof metadata.identifier === 'string') {\n const identifier = metadata.identifier.trim();\n if (identifier) return `factory/linear-${identifier.toLowerCase()}`;\n }\n return `factory/item-${item.id}`;\n}\n"],"mappings":";;AAgBA,SAAgB,qBAAqB,gBAAiF;CACpH,IAAI,CAAC,gBAAgB,OAAO;CAC5B,IAAI,eAAe,kBAAkB,UAAU,OAAO;CAGtD,IAAI,eAAe,kBAAkB,UAAU,OAAO;CACtD,OAAO,eAAe,SAAS,iBAAiB,cAAc;AAChE;AAEA,SAAS,aAAa,UAAmC,KAAiC;CACxF,MAAM,QAAQ,SAAS,QAAQ,SAAS;CACxC,OAAO,OAAO,UAAU,YAAY,OAAO,UAAU,KAAK,KAAK,QAAQ,IAAI,QAAQ,KAAA;AACrF;;AAGA,SAAgB,eAAe,MAA4E;CACzG,MAAM,WAAW,KAAK,YAAY,CAAC;CACnC,IAAI,KAAK,WAAW,gBAAgB,OAAO,aAAa,UAAU,mBAAmB;CACrF,IAAI,KAAK,WAAW,aAAa,OAAO,aAAa,UAAU,yBAAyB;AAE1F;;AAGA,SAAgB,oBACd,MACQ;CACR,MAAM,SAAS,eAAe,IAAI;CAClC,IAAI,WAAW,KAAA,GAAW,OAAO,KAAK;CACtC,OAAO,GAAG,KAAK,WAAW,cAAc,OAAO,QAAQ,IAAI,OAAO,IAAI,KAAK;AAC7E;;;;;;;;AASA,SAAgB,eAAe,MAAmC;CAChE,MAAM,WAAW,KAAK,YAAY,CAAC;CACnC,MAAM,eAAe,eAAe,IAAI;CACxC,IAAI,iBAAiB,KAAA,GACnB,OAAO,KAAK,WAAW,iBAAiB,iBAAiB,iBAAiB,cAAc;CAE1F,IAAI,KAAK,WAAW,kBAAkB,OAAO,SAAS,eAAe,UAAU;EAC7E,MAAM,aAAa,SAAS,WAAW,KAAK;EAC5C,IAAI,YAAY,OAAO,kBAAkB,WAAW,YAAY;CAClE;CACA,OAAO,gBAAgB,KAAK;AAC9B"}
|
package/dist/workspace.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Workspace } from '@mastra/core/workspace';
|
|
|
4
4
|
import type { SkillSource, SkillSourceEntry, SkillSourceStat, WorkspaceSandbox } from '@mastra/core/workspace';
|
|
5
5
|
import type { MastraFactorySandboxConfig } from './factory.js';
|
|
6
6
|
import type { GithubIntegration } from './integrations/github/integration.js';
|
|
7
|
+
import type { FactoryProjectsStorage } from './storage/domains/projects/base.js';
|
|
7
8
|
import type { WorkItemsStorage } from './storage/domains/work-items/base.js';
|
|
8
9
|
export declare const BUNDLED_FACTORY_SKILLS_PATH: string;
|
|
9
10
|
/**
|
|
@@ -43,6 +44,8 @@ export interface CreateWorkspaceFactoryOptions {
|
|
|
43
44
|
* review-board sessions get the reviewer PAT as `GH_TOKEN`. Optional —
|
|
44
45
|
* without it every session uses the default (worker) PAT. */
|
|
45
46
|
workItems?: Pick<WorkItemsStorage, 'findRunBindingBySession'>;
|
|
47
|
+
/** Projects storage used to authorize workspace-free supervisor sessions. */
|
|
48
|
+
projects?: Pick<FactoryProjectsStorage, 'get'>;
|
|
46
49
|
/** Runtime workspace/token registrations invalidated when a session retires. */
|
|
47
50
|
workspaceRegistry?: FactoryWorkspaceRegistry;
|
|
48
51
|
}
|
package/dist/workspace.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAA2B,MAAM,mCAAmC,CAAC;AAItG,OAAO,EAAoB,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAEV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAwB9E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;
|
|
1
|
+
{"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAA2B,MAAM,mCAAmC,CAAC;AAItG,OAAO,EAAoB,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,KAAK,EAEV,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sCAAsC,CAAC;AAwB9E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,oCAAoC,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAO7E,eAAO,MAAM,2BAA2B,QAQQ,CAAC;AAEjD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,GAAG,SAAS,CAS7F;AAED,eAAO,MAAM,mBAAmB,aAO9B,CAAC;AAEH,qBAAa,kBAAmB,YAAW,WAAW;;IAMlD,QAAQ,CAAC,QAAQ,EAAE,WAAW;gBAArB,QAAQ,EAAE,WAAW,EAC9B,kBAAkB,EAAE,MAAM,EAAE,EAC5B,eAAe,GAAE,MAAM,GAAG,SAA2C;IAqBjE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAO3C,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAMjD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAMrD,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAyB7D,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAI7C;AAuDD,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAEzE;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnD,MAAM,WAAW,6BAA6B;IAC5C,iEAAiE;IACjE,OAAO,CAAC,EAAE,0BAA0B,CAAC;IACrC,4BAA4B;IAC5B,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,gFAAgF;IAChF,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;iEAE6D;IAC7D,SAAS,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,yBAAyB,CAAC,CAAC;IAC9D,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,IAAI,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;IAC/C,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;CAC9C;AAED,KAAK,mBAAmB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAEtD,gFAAgF;AAChF,qBAAa,wBAAwB;;IAInC,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAI/B,QAAQ,CACZ,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,mBAAmB,GAC9B,OAAO,CAAC,OAAO,CAAC;IAWb,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAS1D;AAED,wBAAgB,sBAAsB,CAAC,OAAO,GAAE,6BAAkC,IAuBlE,4CAA4C,uBAAuB,2MAidlF"}
|
package/dist/workspace.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { timedPhase } from "./timing.js";
|
|
2
|
-
import { getFactoryAuthUserFromContext, getFactoryAuthUserId } from "./auth.js";
|
|
2
|
+
import { getFactoryAuthOrgId, getFactoryAuthUserFromContext, getFactoryAuthUserId } from "./auth.js";
|
|
3
3
|
import { getGithubPat } from "./integrations/github/pat.js";
|
|
4
4
|
import { registerGithubPatKind, registerGithubTokenInjector } from "./integrations/github/token-refresh.js";
|
|
5
|
+
import { DEFAULT_COMMAND_TIMEOUT_MS, SetupCommandError, checkoutSessionBranch, materializeRepo, runSetupCommand, runTeardownCommand } from "./integrations/github/sandbox.js";
|
|
5
6
|
import { requireExec } from "./sandbox/materialization.js";
|
|
6
7
|
import { createSessionSetupHook, evictSessionSandbox, getSessionSandbox, hasFailedSetupCommand, recordFailedSetupCommand, resolveSessionWorkdir } from "./sandbox/session-sandbox.js";
|
|
7
|
-
import { DEFAULT_COMMAND_TIMEOUT_MS, SetupCommandError, checkoutSessionBranch, materializeRepo, runSetupCommand, runTeardownCommand } from "./integrations/github/sandbox.js";
|
|
8
8
|
import { getFactorySessionAddress } from "./rules/binding-context.js";
|
|
9
|
+
import { parseSupervisorResourceId } from "./supervisor/session.js";
|
|
9
10
|
import path, { dirname, join } from "path";
|
|
10
|
-
import { LocalSkillSource, Workspace } from "@mastra/core/workspace";
|
|
11
11
|
import { DEFAULT_CONFIG_DIR } from "@mastra/code-sdk/constants";
|
|
12
12
|
import { SandboxFilesystem } from "@mastra/code-sdk/agents/sandbox-filesystem";
|
|
13
|
+
import { LocalSkillSource, Workspace } from "@mastra/core/workspace";
|
|
13
14
|
import { existsSync } from "fs";
|
|
14
15
|
import { fileURLToPath } from "url";
|
|
15
16
|
import { MASTRACODE_WORKSPACE_TOOLS } from "@mastra/code-sdk/agents/tool-availability";
|
|
@@ -173,7 +174,7 @@ var FactoryWorkspaceRegistry = class {
|
|
|
173
174
|
}
|
|
174
175
|
};
|
|
175
176
|
function createWorkspaceFactory(options = {}) {
|
|
176
|
-
const { sandbox: sandboxConfig, github, workItems } = options;
|
|
177
|
+
const { sandbox: sandboxConfig, github, projects, workItems } = options;
|
|
177
178
|
const eagerSandboxStart = options.sandboxStart === "eager";
|
|
178
179
|
const workspaceRegistry = options.workspaceRegistry ?? new FactoryWorkspaceRegistry();
|
|
179
180
|
const githubTokenInjectors = /* @__PURE__ */ new Map();
|
|
@@ -182,6 +183,15 @@ function createWorkspaceFactory(options = {}) {
|
|
|
182
183
|
return async ({ requestContext, mastra, skillExtension }) => {
|
|
183
184
|
const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
|
|
184
185
|
const ctx = requestContext.get("controller");
|
|
186
|
+
const supervisorProjectId = parseSupervisorResourceId(ctx?.resourceId);
|
|
187
|
+
if (supervisorProjectId) {
|
|
188
|
+
const orgId = getFactoryAuthOrgId(getFactoryAuthUserFromContext(requestContext));
|
|
189
|
+
if (!(orgId && projects ? await projects.get({
|
|
190
|
+
orgId,
|
|
191
|
+
id: supervisorProjectId
|
|
192
|
+
}) : null)) throw new Error(`Factory supervisor ${supervisorProjectId} is not available to the current user`);
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
185
195
|
const session = ctx?.resourceId && github ? await github.sourceControlStorage.sessions.getBySessionId(ctx.resourceId) : null;
|
|
186
196
|
if (!session) return;
|
|
187
197
|
const user = getFactoryAuthUserFromContext(requestContext);
|
|
@@ -268,7 +278,7 @@ function createWorkspaceFactory(options = {}) {
|
|
|
268
278
|
const fireEagerStart = () => {
|
|
269
279
|
if (!startEagerly) return;
|
|
270
280
|
startEagerly = false;
|
|
271
|
-
sessionEntry.sandbox.start?.().catch((error) => {
|
|
281
|
+
Promise.resolve().then(() => sessionEntry.sandbox.start?.()).catch((error) => {
|
|
272
282
|
console.warn(`[factory] Eager sandbox start for session ${session.id} failed:`, error);
|
|
273
283
|
});
|
|
274
284
|
};
|