@treeseed/agent 0.8.5

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.
Files changed (138) hide show
  1. package/Dockerfile +7 -0
  2. package/README.md +198 -0
  3. package/dist/agent-runtime.d.ts +17 -0
  4. package/dist/agent-runtime.js +117 -0
  5. package/dist/agents/adapters/execution.d.ts +41 -0
  6. package/dist/agents/adapters/execution.js +73 -0
  7. package/dist/agents/adapters/mutations.d.ts +22 -0
  8. package/dist/agents/adapters/mutations.js +30 -0
  9. package/dist/agents/adapters/notification.d.ts +26 -0
  10. package/dist/agents/adapters/notification.js +46 -0
  11. package/dist/agents/adapters/repository.d.ts +28 -0
  12. package/dist/agents/adapters/repository.js +61 -0
  13. package/dist/agents/adapters/research.d.ts +26 -0
  14. package/dist/agents/adapters/research.js +59 -0
  15. package/dist/agents/adapters/verification.d.ts +36 -0
  16. package/dist/agents/adapters/verification.js +62 -0
  17. package/dist/agents/cli-tools.d.ts +1 -0
  18. package/dist/agents/cli-tools.js +5 -0
  19. package/dist/agents/cli.d.ts +15 -0
  20. package/dist/agents/cli.js +109 -0
  21. package/dist/agents/contracts/messages.d.ts +88 -0
  22. package/dist/agents/contracts/messages.js +138 -0
  23. package/dist/agents/contracts/run.d.ts +21 -0
  24. package/dist/agents/contracts/run.js +0 -0
  25. package/dist/agents/index.d.ts +1 -0
  26. package/dist/agents/index.js +5 -0
  27. package/dist/agents/kernel/agent-kernel.d.ts +63 -0
  28. package/dist/agents/kernel/agent-kernel.js +291 -0
  29. package/dist/agents/kernel/trigger-resolver.d.ts +19 -0
  30. package/dist/agents/kernel/trigger-resolver.js +157 -0
  31. package/dist/agents/registry-helper.d.ts +4 -0
  32. package/dist/agents/registry-helper.js +14 -0
  33. package/dist/agents/registry.d.ts +6 -0
  34. package/dist/agents/registry.js +98 -0
  35. package/dist/agents/runtime-types.d.ts +118 -0
  36. package/dist/agents/runtime-types.js +0 -0
  37. package/dist/agents/spec-loader.d.ts +18 -0
  38. package/dist/agents/spec-loader.js +54 -0
  39. package/dist/agents/spec-normalizer.d.ts +2 -0
  40. package/dist/agents/spec-normalizer.js +327 -0
  41. package/dist/agents/spec-types.d.ts +64 -0
  42. package/dist/agents/spec-types.js +0 -0
  43. package/dist/agents/testing/agents-smoke.d.ts +1 -0
  44. package/dist/agents/testing/agents-smoke.js +32 -0
  45. package/dist/agents/testing/e2e-harness.d.ts +44 -0
  46. package/dist/agents/testing/e2e-harness.js +503 -0
  47. package/dist/api/agent-routes.d.ts +13 -0
  48. package/dist/api/agent-routes.js +327 -0
  49. package/dist/api/app.d.ts +8 -0
  50. package/dist/api/app.js +444 -0
  51. package/dist/api/auth/d1-database.d.ts +3 -0
  52. package/dist/api/auth/d1-database.js +20 -0
  53. package/dist/api/auth/d1-provider.d.ts +79 -0
  54. package/dist/api/auth/d1-provider.js +92 -0
  55. package/dist/api/auth/d1-store.d.ts +114 -0
  56. package/dist/api/auth/d1-store.js +895 -0
  57. package/dist/api/auth/memory-provider.d.ts +77 -0
  58. package/dist/api/auth/memory-provider.js +249 -0
  59. package/dist/api/auth/rbac.d.ts +22 -0
  60. package/dist/api/auth/rbac.js +162 -0
  61. package/dist/api/auth/tokens.d.ts +18 -0
  62. package/dist/api/auth/tokens.js +56 -0
  63. package/dist/api/capabilities.d.ts +9 -0
  64. package/dist/api/capabilities.js +33 -0
  65. package/dist/api/config.d.ts +2 -0
  66. package/dist/api/config.js +77 -0
  67. package/dist/api/http.d.ts +28 -0
  68. package/dist/api/http.js +51 -0
  69. package/dist/api/index.d.ts +9 -0
  70. package/dist/api/index.js +20 -0
  71. package/dist/api/operations-routes.d.ts +11 -0
  72. package/dist/api/operations-routes.js +87 -0
  73. package/dist/api/operations.d.ts +3 -0
  74. package/dist/api/operations.js +26 -0
  75. package/dist/api/project-routes.d.ts +8 -0
  76. package/dist/api/project-routes.js +585 -0
  77. package/dist/api/providers.d.ts +2 -0
  78. package/dist/api/providers.js +62 -0
  79. package/dist/api/railway.d.ts +51 -0
  80. package/dist/api/railway.js +71 -0
  81. package/dist/api/sdk-dispatch.d.ts +5 -0
  82. package/dist/api/sdk-dispatch.js +13 -0
  83. package/dist/api/sdk-routes.d.ts +11 -0
  84. package/dist/api/sdk-routes.js +29 -0
  85. package/dist/api/server.d.ts +2 -0
  86. package/dist/api/server.js +10 -0
  87. package/dist/api/templates.d.ts +3 -0
  88. package/dist/api/templates.js +31 -0
  89. package/dist/api/types.d.ts +237 -0
  90. package/dist/api/types.js +0 -0
  91. package/dist/env.yaml +957 -0
  92. package/dist/index.d.ts +14 -0
  93. package/dist/index.js +41 -0
  94. package/dist/scripts/assert-release-tag-version.d.ts +1 -0
  95. package/dist/scripts/assert-release-tag-version.js +20 -0
  96. package/dist/scripts/build-dist.d.ts +1 -0
  97. package/dist/scripts/build-dist.js +106 -0
  98. package/dist/scripts/package-tools.d.ts +1 -0
  99. package/dist/scripts/package-tools.js +7 -0
  100. package/dist/scripts/publish-package.d.ts +1 -0
  101. package/dist/scripts/publish-package.js +24 -0
  102. package/dist/scripts/release-verify.d.ts +1 -0
  103. package/dist/scripts/release-verify.js +152 -0
  104. package/dist/scripts/test-smoke.d.ts +1 -0
  105. package/dist/scripts/test-smoke.js +23 -0
  106. package/dist/scripts/treeseed-agent-api.d.ts +2 -0
  107. package/dist/scripts/treeseed-agent-api.js +25 -0
  108. package/dist/scripts/treeseed-agent-service.d.ts +2 -0
  109. package/dist/scripts/treeseed-agent-service.js +36 -0
  110. package/dist/scripts/treeseed-agents.d.ts +2 -0
  111. package/dist/scripts/treeseed-agents.js +13 -0
  112. package/dist/services/agents.d.ts +17 -0
  113. package/dist/services/agents.js +48 -0
  114. package/dist/services/common.d.ts +66 -0
  115. package/dist/services/common.js +212 -0
  116. package/dist/services/index.d.ts +6 -0
  117. package/dist/services/index.js +19 -0
  118. package/dist/services/manager.d.ts +333 -0
  119. package/dist/services/manager.js +1368 -0
  120. package/dist/services/remote-runner.d.ts +30 -0
  121. package/dist/services/remote-runner.js +230 -0
  122. package/dist/services/workday-content.d.ts +53 -0
  123. package/dist/services/workday-content.js +190 -0
  124. package/dist/services/workday-manager.d.ts +391 -0
  125. package/dist/services/workday-manager.js +163 -0
  126. package/dist/services/workday-report.d.ts +238 -0
  127. package/dist/services/workday-report.js +17 -0
  128. package/dist/services/workday-start.d.ts +238 -0
  129. package/dist/services/workday-start.js +17 -0
  130. package/dist/services/worker-capacity.d.ts +58 -0
  131. package/dist/services/worker-capacity.js +208 -0
  132. package/dist/services/worker-pool-scaler.d.ts +27 -0
  133. package/dist/services/worker-pool-scaler.js +127 -0
  134. package/dist/services/worker.d.ts +19 -0
  135. package/dist/services/worker.js +436 -0
  136. package/dist/templates/github/deploy-processing.workflow.yml +119 -0
  137. package/package.json +136 -0
  138. package/templates/github/deploy-processing.workflow.yml +119 -0
@@ -0,0 +1,291 @@
1
+ import { createExecutionAdapter } from "../adapters/execution.js";
2
+ import { LocalBranchMutationAdapter } from "../adapters/mutations.js";
3
+ import { createNotificationAdapter } from "../adapters/notification.js";
4
+ import { createRepositoryInspectionAdapter } from "../adapters/repository.js";
5
+ import { createResearchAdapter } from "../adapters/research.js";
6
+ import { createVerificationAdapter } from "../adapters/verification.js";
7
+ import { resolveAgentHandler } from "../registry.js";
8
+ import { followCursorKey, resolveTriggerDecision } from "./trigger-resolver.js";
9
+ import { loadActiveAgentSpecs, loadAllAgentSpecs, summarizeAgentSpec } from "../spec-loader.js";
10
+ import { getTreeseedAgentProviderSelections } from "@treeseed/sdk/platform/deploy-runtime";
11
+ import { resolveAgentRuntimeProviders } from "../../agent-runtime.js";
12
+ function nowIso() {
13
+ return (/* @__PURE__ */ new Date()).toISOString();
14
+ }
15
+ class AgentKernel {
16
+ constructor(sdk, repoRoot, options) {
17
+ this.sdk = sdk;
18
+ this.repoRoot = repoRoot;
19
+ const runtimeProviders = resolveAgentRuntimeProviders(repoRoot, getTreeseedAgentProviderSelections());
20
+ this.execution = options?.execution ?? runtimeProviders.execution ?? createExecutionAdapter();
21
+ this.mutations = options?.mutations ?? runtimeProviders.mutations ?? new LocalBranchMutationAdapter(repoRoot);
22
+ this.repository = options?.repository ?? runtimeProviders.repository ?? createRepositoryInspectionAdapter();
23
+ this.verification = options?.verification ?? runtimeProviders.verification ?? createVerificationAdapter();
24
+ this.notifications = options?.notifications ?? runtimeProviders.notifications ?? createNotificationAdapter();
25
+ this.research = options?.research ?? runtimeProviders.research ?? createResearchAdapter();
26
+ }
27
+ sdk;
28
+ repoRoot;
29
+ execution;
30
+ mutations;
31
+ repository;
32
+ verification;
33
+ notifications;
34
+ research;
35
+ activeRuns = /* @__PURE__ */ new Set();
36
+ lastRunAt = /* @__PURE__ */ new Map();
37
+ async doctor() {
38
+ const { specs, diagnostics } = await loadAllAgentSpecs(this.sdk);
39
+ for (const agent of specs.filter((entry) => entry.enabled)) {
40
+ await resolveAgentHandler(agent.handler);
41
+ }
42
+ const errors = diagnostics.filter((entry) => entry.severity === "error");
43
+ if (errors.length) {
44
+ throw new Error(
45
+ `Agent spec validation failed: ${errors.map((entry) => `${entry.slug}:${entry.field}:${entry.message}`).join(" | ")}`
46
+ );
47
+ }
48
+ return {
49
+ agents: specs.map(summarizeAgentSpec),
50
+ diagnostics
51
+ };
52
+ }
53
+ sortAgents(agents) {
54
+ const priority = {
55
+ planner: 10,
56
+ researcher: 20,
57
+ architect: 30,
58
+ engineer: 40,
59
+ reviewer: 50,
60
+ releaser: 60,
61
+ notifier: 70
62
+ };
63
+ return [...agents].sort(
64
+ (left, right) => (priority[left.handler] ?? 100) - (priority[right.handler] ?? 100)
65
+ );
66
+ }
67
+ async resolveTrigger(agent, mode = "auto") {
68
+ const decision = await resolveTriggerDecision({
69
+ agent,
70
+ mode,
71
+ isRunning: this.activeRuns.has(agent.slug),
72
+ lastRunAt: this.lastRunAt.get(agent.slug),
73
+ sdk: this.sdk.scopeForAgent(agent)
74
+ });
75
+ return decision.kind === "ready" ? decision.invocation ?? null : null;
76
+ }
77
+ async recordRunTrace(trace) {
78
+ await this.sdk.recordRun({ run: trace });
79
+ }
80
+ buildTrace(agent, runId, trigger, overrides) {
81
+ return {
82
+ runId,
83
+ agentSlug: agent.slug,
84
+ handlerKind: agent.handler,
85
+ triggerKind: trigger.kind,
86
+ triggerSource: trigger.source,
87
+ claimedMessageId: trigger.message?.id ?? null,
88
+ selectedItemKey: null,
89
+ branchName: null,
90
+ commitSha: null,
91
+ changedPaths: [],
92
+ summary: null,
93
+ error: null,
94
+ errorCategory: null,
95
+ startedAt: nowIso(),
96
+ finishedAt: null,
97
+ status: "running",
98
+ ...overrides
99
+ };
100
+ }
101
+ categorizeError(error) {
102
+ const message = error instanceof Error ? error.message : String(error);
103
+ if (message.includes("not allowed")) {
104
+ return "permission_error";
105
+ }
106
+ if (message.includes("message")) {
107
+ return "message_claim_error";
108
+ }
109
+ if (message.includes("lease")) {
110
+ return "lease_error";
111
+ }
112
+ if (message.includes("commit") || message.includes("worktree") || message.includes("artifact")) {
113
+ return "mutation_error";
114
+ }
115
+ if (message.includes("Copilot") || message.includes("execution")) {
116
+ return "execution_error";
117
+ }
118
+ return "sdk_error";
119
+ }
120
+ async executeAgent(agent, trigger) {
121
+ if (this.activeRuns.has(agent.slug)) {
122
+ return {
123
+ status: "waiting",
124
+ summary: `Agent ${agent.slug} is already running.`
125
+ };
126
+ }
127
+ this.activeRuns.add(agent.slug);
128
+ const runId = crypto.randomUUID();
129
+ const handler = await resolveAgentHandler(agent.handler);
130
+ const scopedSdk = this.sdk.scopeForAgent(agent);
131
+ const context = {
132
+ runId,
133
+ repoRoot: this.repoRoot,
134
+ agent,
135
+ sdk: scopedSdk,
136
+ trigger,
137
+ execution: this.execution,
138
+ mutations: this.mutations,
139
+ repository: this.repository,
140
+ verification: this.verification,
141
+ notifications: this.notifications,
142
+ research: this.research
143
+ };
144
+ await this.recordRunTrace(this.buildTrace(agent, runId, trigger, {}));
145
+ try {
146
+ const inputs = await handler.resolveInputs(context);
147
+ const result = await handler.execute(context, inputs);
148
+ const output = await handler.emitOutputs(context, result);
149
+ if (trigger.message) {
150
+ await scopedSdk.ackMessage({
151
+ id: trigger.message.id,
152
+ status: output.status === "completed" ? "completed" : output.status === "waiting" ? "pending" : "failed"
153
+ });
154
+ }
155
+ await this.recordRunTrace(
156
+ this.buildTrace(agent, runId, trigger, {
157
+ status: output.status,
158
+ branchName: output.metadata?.branchName ?? null,
159
+ commitSha: output.metadata?.commitSha ?? null,
160
+ changedPaths: output.metadata?.changedPaths ?? [],
161
+ summary: output.summary,
162
+ error: output.status === "failed" ? output.stderr ?? output.summary : null,
163
+ errorCategory: output.status === "failed" ? output.errorCategory ?? "execution_error" : null,
164
+ finishedAt: nowIso()
165
+ })
166
+ );
167
+ await this.sdk.upsertCursor({
168
+ agentSlug: agent.slug,
169
+ cursorKey: "last_run_at",
170
+ cursorValue: nowIso()
171
+ });
172
+ if (trigger.kind === "follow") {
173
+ await this.sdk.upsertCursor({
174
+ agentSlug: agent.slug,
175
+ cursorKey: followCursorKey(trigger.followModels),
176
+ cursorValue: nowIso()
177
+ });
178
+ }
179
+ this.lastRunAt.set(agent.slug, Date.now());
180
+ return output;
181
+ } catch (error) {
182
+ if (trigger.message) {
183
+ await scopedSdk.ackMessage({
184
+ id: trigger.message.id,
185
+ status: "failed"
186
+ });
187
+ }
188
+ await this.recordRunTrace(
189
+ this.buildTrace(agent, runId, trigger, {
190
+ status: "failed",
191
+ error: error instanceof Error ? error.message : String(error),
192
+ errorCategory: this.categorizeError(error),
193
+ finishedAt: nowIso()
194
+ })
195
+ );
196
+ throw error;
197
+ } finally {
198
+ this.activeRuns.delete(agent.slug);
199
+ }
200
+ }
201
+ async runAgent(slug, mode = "manual", invocation) {
202
+ const { specs, diagnostics } = await loadActiveAgentSpecs(this.sdk);
203
+ const errors = diagnostics.filter((entry) => entry.severity === "error");
204
+ if (errors.length) {
205
+ throw new Error(
206
+ `Agent spec validation failed: ${errors.map((entry) => `${entry.slug}:${entry.field}:${entry.message}`).join(" | ")}`
207
+ );
208
+ }
209
+ const agents = this.sortAgents(specs);
210
+ const agent = agents.find((entry) => entry.slug === slug);
211
+ if (!agent) {
212
+ throw new Error(`Unknown or disabled agent "${slug}".`);
213
+ }
214
+ const trigger = invocation ?? await this.resolveTrigger(agent, mode);
215
+ if (!trigger) {
216
+ return {
217
+ status: "waiting",
218
+ summary: `No runnable trigger found for ${slug}.`
219
+ };
220
+ }
221
+ return this.executeAgent(agent, trigger);
222
+ }
223
+ async runCycle() {
224
+ const { specs, diagnostics } = await loadActiveAgentSpecs(this.sdk);
225
+ const errors = diagnostics.filter((entry) => entry.severity === "error");
226
+ if (errors.length) {
227
+ throw new Error(
228
+ `Agent spec validation failed: ${errors.map((entry) => `${entry.slug}:${entry.field}:${entry.message}`).join(" | ")}`
229
+ );
230
+ }
231
+ const agents = this.sortAgents(specs);
232
+ const results = [];
233
+ for (const agent of agents) {
234
+ const runsThisCycle = agent.triggerPolicy?.maxRunsPerCycle ?? 1;
235
+ for (let index = 0; index < runsThisCycle; index += 1) {
236
+ const trigger = await this.resolveTrigger(agent, "auto");
237
+ if (!trigger) {
238
+ break;
239
+ }
240
+ results.push({
241
+ slug: agent.slug,
242
+ result: await this.executeAgent(agent, trigger)
243
+ });
244
+ }
245
+ }
246
+ return results;
247
+ }
248
+ async start(intervalMs = Number(process.env.TREESEED_AGENT_SUPERVISOR_INTERVAL_MS ?? 6e4)) {
249
+ await this.runCycle();
250
+ setInterval(() => {
251
+ void this.runCycle();
252
+ }, intervalMs);
253
+ }
254
+ async drainMessages() {
255
+ const { specs, diagnostics } = await loadActiveAgentSpecs(this.sdk);
256
+ const errors = diagnostics.filter((entry) => entry.severity === "error");
257
+ if (errors.length) {
258
+ throw new Error(
259
+ `Agent spec validation failed: ${errors.map((entry) => `${entry.slug}:${entry.field}:${entry.message}`).join(" | ")}`
260
+ );
261
+ }
262
+ const agents = this.sortAgents(specs);
263
+ const messageAgents = agents.filter(
264
+ (agent) => agent.triggers.some((trigger) => trigger.type === "message")
265
+ );
266
+ const results = [];
267
+ for (const agent of messageAgents) {
268
+ results.push({
269
+ slug: agent.slug,
270
+ result: await this.runAgent(agent.slug, "auto")
271
+ });
272
+ }
273
+ return results;
274
+ }
275
+ releaseLeases() {
276
+ return this.sdk.releaseAllLeases();
277
+ }
278
+ async replayMessage(id) {
279
+ await this.sdk.ackMessage({
280
+ id,
281
+ status: "pending"
282
+ });
283
+ return {
284
+ id,
285
+ status: "pending"
286
+ };
287
+ }
288
+ }
289
+ export {
290
+ AgentKernel
291
+ };
@@ -0,0 +1,19 @@
1
+ import type { AgentRuntimeSpec, AgentTriggerConfig } from '@treeseed/sdk/types/agents';
2
+ import type { ScopedAgentSdk } from '@treeseed/sdk/sdk';
3
+ import type { AgentTriggerInvocation } from '../runtime-types.ts';
4
+ export type TriggerDecisionKind = 'ready' | 'skip' | 'blocked_by_cooldown' | 'blocked_by_concurrency' | 'no_message_available' | 'no_follow_activity' | 'no_trigger_available';
5
+ export interface TriggerDecision {
6
+ kind: TriggerDecisionKind;
7
+ invocation?: AgentTriggerInvocation;
8
+ reason?: string;
9
+ selectedTrigger?: AgentTriggerConfig;
10
+ }
11
+ export interface TriggerResolverInput {
12
+ agent: AgentRuntimeSpec;
13
+ mode?: 'auto' | 'manual';
14
+ isRunning: boolean;
15
+ lastRunAt?: number;
16
+ sdk: ScopedAgentSdk;
17
+ }
18
+ export declare function followCursorKey(models: string[] | undefined): string;
19
+ export declare function resolveTriggerDecision(input: TriggerResolverInput): Promise<TriggerDecision>;
@@ -0,0 +1,157 @@
1
+ import crypto from "node:crypto";
2
+ function followCursorKey(models) {
3
+ return `follow:${(models ?? []).join(",") || "all"}`;
4
+ }
5
+ function buildManualInvocation(agent) {
6
+ const scheduleLike = agent.triggers.find((trigger) => trigger.type === "schedule" || trigger.type === "startup");
7
+ if (!scheduleLike) {
8
+ return null;
9
+ }
10
+ return {
11
+ kind: "manual",
12
+ source: "manual",
13
+ trigger: scheduleLike
14
+ };
15
+ }
16
+ function evaluateCooldown(agent, lastRunAt) {
17
+ const cooldownMs = agent.execution.cooldownSeconds * 1e3;
18
+ return (lastRunAt ?? 0) > 0 && Date.now() - (lastRunAt ?? 0) < cooldownMs;
19
+ }
20
+ async function resolveMessageTrigger(agent, sdk, trigger, messageBatchSize) {
21
+ for (let index = 0; index < messageBatchSize; index += 1) {
22
+ const claimed = await sdk.claimMessage({
23
+ workerId: `${agent.slug}-${crypto.randomUUID()}`,
24
+ messageTypes: trigger.messageTypes ?? [],
25
+ leaseSeconds: agent.execution.leaseSeconds
26
+ });
27
+ if (claimed.payload) {
28
+ return {
29
+ kind: "ready",
30
+ selectedTrigger: trigger,
31
+ invocation: {
32
+ kind: "message",
33
+ source: "message",
34
+ trigger,
35
+ message: claimed.payload
36
+ }
37
+ };
38
+ }
39
+ }
40
+ return {
41
+ kind: "no_message_available",
42
+ selectedTrigger: trigger,
43
+ reason: `No matching messages for ${agent.slug}.`
44
+ };
45
+ }
46
+ async function resolveFollowTrigger(agent, sdk, trigger) {
47
+ const models = trigger.models ?? [];
48
+ const since = (await sdk.getCursor({
49
+ agentSlug: agent.slug,
50
+ cursorKey: followCursorKey(models)
51
+ })).payload ?? trigger.sinceField ?? (/* @__PURE__ */ new Date(0)).toISOString();
52
+ for (const model of models) {
53
+ const followed = await sdk.follow({
54
+ model,
55
+ since: String(since)
56
+ });
57
+ if (followed.payload.items.length) {
58
+ return {
59
+ kind: "ready",
60
+ selectedTrigger: trigger,
61
+ invocation: {
62
+ kind: "follow",
63
+ source: "follow",
64
+ trigger,
65
+ followModels: models,
66
+ cursorValue: String(since)
67
+ }
68
+ };
69
+ }
70
+ }
71
+ return {
72
+ kind: "no_follow_activity",
73
+ selectedTrigger: trigger,
74
+ reason: `No followed activity for ${agent.slug}.`
75
+ };
76
+ }
77
+ function resolveScheduleLikeTrigger(agent, trigger, mode, lastRunAt) {
78
+ if (mode !== "manual" && evaluateCooldown(agent, lastRunAt)) {
79
+ return {
80
+ kind: "blocked_by_cooldown",
81
+ selectedTrigger: trigger,
82
+ reason: `Agent ${agent.slug} is cooling down.`
83
+ };
84
+ }
85
+ return {
86
+ kind: "ready",
87
+ selectedTrigger: trigger,
88
+ invocation: {
89
+ kind: mode === "manual" ? "manual" : trigger.type === "startup" || trigger.runOnStart ? "startup" : "schedule",
90
+ source: mode === "manual" ? "manual" : trigger.type === "startup" || trigger.runOnStart ? "startup" : "schedule",
91
+ trigger
92
+ }
93
+ };
94
+ }
95
+ async function resolveTriggerDecision(input) {
96
+ if (input.isRunning) {
97
+ return {
98
+ kind: "blocked_by_concurrency",
99
+ reason: `Agent ${input.agent.slug} is already running.`
100
+ };
101
+ }
102
+ const mode = input.mode ?? "auto";
103
+ const messageBatchSize = input.agent.triggerPolicy?.messageBatchSize ?? 1;
104
+ const triggerGroups = {
105
+ message: input.agent.triggers.filter((trigger) => trigger.type === "message"),
106
+ follow: input.agent.triggers.filter((trigger) => trigger.type === "follow"),
107
+ schedule: input.agent.triggers.filter((trigger) => trigger.type === "startup" || trigger.type === "schedule")
108
+ };
109
+ for (const trigger of triggerGroups.message) {
110
+ const decision = await resolveMessageTrigger(input.agent, input.sdk, trigger, messageBatchSize);
111
+ if (!decision) {
112
+ continue;
113
+ }
114
+ if (decision.kind === "ready") {
115
+ return decision;
116
+ }
117
+ }
118
+ for (const trigger of triggerGroups.follow) {
119
+ const decision = await resolveFollowTrigger(input.agent, input.sdk, trigger);
120
+ if (decision.kind === "ready") {
121
+ return decision;
122
+ }
123
+ }
124
+ for (const trigger of triggerGroups.schedule) {
125
+ const decision = resolveScheduleLikeTrigger(input.agent, trigger, mode, input.lastRunAt);
126
+ if (decision.kind === "ready") {
127
+ return decision;
128
+ }
129
+ if (decision.kind === "blocked_by_cooldown") {
130
+ return decision;
131
+ }
132
+ }
133
+ if (mode === "manual") {
134
+ const invocation = buildManualInvocation(input.agent);
135
+ return invocation ? { kind: "ready", invocation, selectedTrigger: invocation.trigger } : { kind: "no_trigger_available", reason: `No manual-capable trigger found for ${input.agent.slug}.` };
136
+ }
137
+ if (triggerGroups.message.length) {
138
+ return {
139
+ kind: "no_message_available",
140
+ reason: `No matching messages for ${input.agent.slug}.`
141
+ };
142
+ }
143
+ if (triggerGroups.follow.length) {
144
+ return {
145
+ kind: "no_follow_activity",
146
+ reason: `No followed activity for ${input.agent.slug}.`
147
+ };
148
+ }
149
+ return {
150
+ kind: "no_trigger_available",
151
+ reason: `No runnable triggers defined for ${input.agent.slug}.`
152
+ };
153
+ }
154
+ export {
155
+ followCursorKey,
156
+ resolveTriggerDecision
157
+ };
@@ -0,0 +1,4 @@
1
+ import type { AgentHandlerKind } from '@treeseed/sdk/types/agents';
2
+ import type { AgentHandler } from './runtime-types.ts';
3
+ export declare function defineAgentHandlerRegistry(registry: Partial<Record<AgentHandlerKind, AgentHandler>>): Partial<Record<string, AgentHandler<unknown, unknown>>>;
4
+ export declare function resolveAgentHandlerFromRegistry(registry: Partial<Record<AgentHandlerKind, AgentHandler>>, kind: AgentHandlerKind): AgentHandler<unknown, unknown>;
@@ -0,0 +1,14 @@
1
+ function defineAgentHandlerRegistry(registry) {
2
+ return registry;
3
+ }
4
+ function resolveAgentHandlerFromRegistry(registry, kind) {
5
+ const handler = registry[kind];
6
+ if (!handler) {
7
+ throw new Error(`No runtime handler is registered for agent handler "${kind}".`);
8
+ }
9
+ return handler;
10
+ }
11
+ export {
12
+ defineAgentHandlerRegistry,
13
+ resolveAgentHandlerFromRegistry
14
+ };
@@ -0,0 +1,6 @@
1
+ import type { AgentHandlerKind } from '@treeseed/sdk/types/agents';
2
+ import type { AgentHandler } from './runtime-types.ts';
3
+ export declare function getTenantAgentHandlerModulePaths(kind: AgentHandlerKind, tenantRoot?: string): string[];
4
+ export declare function loadTenantAgentHandlerRegistry(tenantRoot?: string): Promise<Record<string, AgentHandler>>;
5
+ export declare function listRegisteredAgentHandlers(): Promise<string[]>;
6
+ export declare function resolveAgentHandler(kind: AgentHandlerKind): Promise<AgentHandler<unknown, unknown>>;
@@ -0,0 +1,98 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ import { getTreeseedAgentProviderSelections } from "@treeseed/sdk/platform/deploy-runtime";
5
+ import { resolveTreeseedTenantRoot } from "@treeseed/sdk/platform/tenant-config";
6
+ import { resolveAgentRuntimeProviders } from "../agent-runtime.js";
7
+ const BUILTIN_HANDLER_KINDS = [
8
+ "planner",
9
+ "architect",
10
+ "engineer",
11
+ "notifier",
12
+ "researcher",
13
+ "reviewer",
14
+ "releaser"
15
+ ];
16
+ const HANDLER_EXPORT_NAMES = {
17
+ planner: "plannerHandler",
18
+ architect: "architectHandler",
19
+ engineer: "engineerHandler",
20
+ notifier: "notifierHandler",
21
+ researcher: "researcherHandler",
22
+ reviewer: "reviewerHandler",
23
+ releaser: "releaserHandler"
24
+ };
25
+ function getTenantAgentHandlerModulePaths(kind, tenantRoot = resolveTreeseedTenantRoot()) {
26
+ return [
27
+ resolve(tenantRoot, "src/agents", `${kind}.js`),
28
+ resolve(tenantRoot, "src/agents", `${kind}.ts`)
29
+ ];
30
+ }
31
+ async function loadTenantAgentHandlerRegistry(tenantRoot = resolveTreeseedTenantRoot()) {
32
+ const registry = {};
33
+ for (const kind of BUILTIN_HANDLER_KINDS) {
34
+ const modulePath = getTenantAgentHandlerModulePaths(kind, tenantRoot).find((candidate) => existsSync(candidate));
35
+ if (!modulePath) {
36
+ continue;
37
+ }
38
+ let moduleExports;
39
+ try {
40
+ moduleExports = await import(
41
+ /* @vite-ignore */
42
+ pathToFileURL(modulePath).href
43
+ );
44
+ } catch (error) {
45
+ const reason = error instanceof Error ? error.message : String(error);
46
+ throw new Error(`Failed to import tenant agent handler "${kind}" from ${modulePath}: ${reason}`);
47
+ }
48
+ const exportName = HANDLER_EXPORT_NAMES[kind];
49
+ const handler = moduleExports[exportName];
50
+ if (!handler) {
51
+ throw new Error(
52
+ `Tenant agent handler module "${modulePath}" must export "${exportName}" for handler kind "${kind}".`
53
+ );
54
+ }
55
+ const normalizedHandler = handler;
56
+ if (normalizedHandler.kind !== kind) {
57
+ throw new Error(
58
+ `Tenant agent handler "${exportName}" from "${modulePath}" declares kind "${normalizedHandler.kind}", but "${kind}" was expected.`
59
+ );
60
+ }
61
+ registry[kind] = normalizedHandler;
62
+ }
63
+ return registry;
64
+ }
65
+ let agentHandlerRegistryPromise = null;
66
+ async function getAgentHandlerRegistry() {
67
+ if (!agentHandlerRegistryPromise) {
68
+ agentHandlerRegistryPromise = loadTenantAgentHandlerRegistry();
69
+ }
70
+ return agentHandlerRegistryPromise;
71
+ }
72
+ async function listRegisteredAgentHandlers() {
73
+ const registry = await getAgentHandlerRegistry();
74
+ const runtimeProviders = resolveAgentRuntimeProviders(resolveTreeseedTenantRoot(), getTreeseedAgentProviderSelections());
75
+ return [.../* @__PURE__ */ new Set([...Object.keys(registry), ...runtimeProviders.handlers.keys()])];
76
+ }
77
+ async function resolveAgentHandler(kind) {
78
+ const registry = await getAgentHandlerRegistry();
79
+ const runtimeProviders = resolveAgentRuntimeProviders(resolveTreeseedTenantRoot(), getTreeseedAgentProviderSelections());
80
+ const handler = registry[kind] ?? runtimeProviders.handlers.get(kind);
81
+ if (!handler) {
82
+ if (BUILTIN_HANDLER_KINDS.includes(kind)) {
83
+ const expectedPath = getTenantAgentHandlerModulePaths(kind).join('" or "');
84
+ const expectedExport = HANDLER_EXPORT_NAMES[kind];
85
+ throw new Error(
86
+ `No runtime handler is registered for agent handler "${kind}". Expected tenant file "${expectedPath}" exporting "${expectedExport}" or a plugin contribution.`
87
+ );
88
+ }
89
+ throw new Error(`No runtime handler is registered for agent handler "${kind}".`);
90
+ }
91
+ return handler;
92
+ }
93
+ export {
94
+ getTenantAgentHandlerModulePaths,
95
+ listRegisteredAgentHandlers,
96
+ loadTenantAgentHandlerRegistry,
97
+ resolveAgentHandler
98
+ };