@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,585 @@
1
+ import {
2
+ buildKnowledgeCoopKnowledgePackPackage,
3
+ buildKnowledgeCoopTemplatePackage,
4
+ TreeseedWorkflowSdk
5
+ } from "@treeseed/sdk";
6
+ import {
7
+ getTreeseedMachineConfigPaths,
8
+ loadCliDeployConfig,
9
+ loadTreeseedMachineConfig,
10
+ resolveTreeseedRemoteSession
11
+ } from "@treeseed/sdk/workflow-support";
12
+ import { requireTeamCapability } from "./capabilities.js";
13
+ import { jsonError } from "./http.js";
14
+ function withPrefix(prefix, path) {
15
+ if (!prefix) return path;
16
+ return `${prefix}${path}`.replace(/\/{2,}/g, "/");
17
+ }
18
+ function slugify(value) {
19
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "change";
20
+ }
21
+ function nowIso() {
22
+ return (/* @__PURE__ */ new Date()).toISOString();
23
+ }
24
+ function asRecords(value) {
25
+ return Array.isArray(value) ? value : [];
26
+ }
27
+ function readString(record, ...keys) {
28
+ for (const key of keys) {
29
+ const value = record[key];
30
+ if (typeof value === "string" && value.trim()) return value.trim();
31
+ }
32
+ return "";
33
+ }
34
+ function readOptionalString(record, ...keys) {
35
+ const value = readString(record, ...keys);
36
+ return value || null;
37
+ }
38
+ function inferMessageKind(type, status) {
39
+ if (status === "failed" || type.includes("failed")) return "warning";
40
+ if (type.includes("waiting") || type.includes("review") || type.includes("release")) return "action_requested";
41
+ if (type.includes("release_completed") || type.includes("task_verified")) return "release_readiness";
42
+ return "informational";
43
+ }
44
+ async function summarizeDirect(sdk, projectId) {
45
+ const [objectives, questions, notes, proposals, decisions, workstreams, releases] = await Promise.all([
46
+ sdk.search({ model: "objective", sort: [{ field: "updated_at", direction: "desc" }], limit: 50 }),
47
+ sdk.search({ model: "question", sort: [{ field: "updated_at", direction: "desc" }], limit: 50 }),
48
+ sdk.search({ model: "note", sort: [{ field: "updated_at", direction: "desc" }], limit: 50 }),
49
+ sdk.search({ model: "proposal", sort: [{ field: "updated_at", direction: "desc" }], limit: 50 }),
50
+ sdk.search({ model: "decision", sort: [{ field: "updated_at", direction: "desc" }], limit: 50 }),
51
+ sdk.listWorkstreams(projectId),
52
+ sdk.listReleases(projectId)
53
+ ]);
54
+ const workstreamPayload = workstreams.payload ?? [];
55
+ const releasePayload = releases.payload ?? [];
56
+ const linkIndex = /* @__PURE__ */ new Map();
57
+ for (const workstream of workstreamPayload) {
58
+ for (const ref of workstream.linkedItems) {
59
+ const key = `${ref.model}:${ref.id}`;
60
+ const current = linkIndex.get(key) ?? { workstreamIds: [], releaseIds: [] };
61
+ current.workstreamIds.push(workstream.id);
62
+ linkIndex.set(key, current);
63
+ }
64
+ }
65
+ for (const release of releasePayload) {
66
+ for (const workstreamId of release.workstreamIds) {
67
+ const workstream = workstreamPayload.find((entry) => entry.id === workstreamId);
68
+ for (const ref of workstream?.linkedItems ?? []) {
69
+ const key = `${ref.model}:${ref.id}`;
70
+ const current = linkIndex.get(key) ?? { workstreamIds: [], releaseIds: [] };
71
+ current.releaseIds.push(release.id);
72
+ linkIndex.set(key, current);
73
+ }
74
+ }
75
+ }
76
+ const mapItems = (model, entries) => entries.slice(0, 15).map((entry) => {
77
+ const id = readString(entry, "id", "slug");
78
+ const links = linkIndex.get(`${model}:${id}`) ?? { workstreamIds: [], releaseIds: [] };
79
+ return {
80
+ model,
81
+ id,
82
+ title: readString(entry, "title", "name") || id,
83
+ status: readOptionalString(entry, "status"),
84
+ updatedAt: readOptionalString(entry, "updated_at", "updatedAt", "updated"),
85
+ linkedWorkstreamIds: [...new Set(links.workstreamIds)],
86
+ linkedReleaseIds: [...new Set(links.releaseIds)]
87
+ };
88
+ });
89
+ return {
90
+ projectId,
91
+ objectiveCount: objectives.payload.length,
92
+ questionCount: questions.payload.length,
93
+ noteCount: notes.payload.length,
94
+ proposalCount: proposals.payload.length,
95
+ decisionCount: decisions.payload.length,
96
+ savedViews: ["Now", "Blocked", "Ready for research", "Ready for build", "Release-linked"],
97
+ items: [
98
+ ...mapItems("objective", objectives.payload),
99
+ ...mapItems("question", questions.payload),
100
+ ...mapItems("note", notes.payload),
101
+ ...mapItems("proposal", proposals.payload),
102
+ ...mapItems("decision", decisions.payload)
103
+ ].slice(0, 15)
104
+ };
105
+ }
106
+ async function summarizeAgents(sdk, projectId) {
107
+ const [specs, runs, messages] = await Promise.all([
108
+ sdk.listAgentSpecs({ enabled: true }),
109
+ sdk.search({ model: "agent_run", sort: [{ field: "startedAt", direction: "desc" }], limit: 100 }),
110
+ sdk.search({ model: "message", sort: [{ field: "updated_at", direction: "desc" }], limit: 100 })
111
+ ]);
112
+ const workstreams = await sdk.listWorkstreams(projectId);
113
+ const workstreamIds = new Set((workstreams.payload ?? []).map((entry) => entry.id));
114
+ const agentStatuses = specs.map((spec) => {
115
+ const latestRun = runs.payload.find((entry) => readString(entry, "agentSlug", "agent_slug") === spec.slug);
116
+ const latestMessage = messages.payload.find((entry) => readString(entry, "type") && readString(entry, "payloadJson", "payload_json"));
117
+ return {
118
+ agentSlug: spec.slug,
119
+ handler: spec.handler,
120
+ status: readString(latestRun ?? {}, "status") === "failed" ? "failed" : readString(latestRun ?? {}, "status") === "running" ? "active" : latestRun ? "idle" : "waiting",
121
+ currentTask: readOptionalString(latestRun ?? {}, "summary"),
122
+ workstreamId: readOptionalString(latestRun ?? {}, "selectedItemKey"),
123
+ lastMessage: readOptionalString(latestMessage ?? {}, "type"),
124
+ lastRunAt: readOptionalString(latestRun ?? {}, "startedAt", "started_at", "finishedAt", "finished_at")
125
+ };
126
+ });
127
+ const messageRecords = messages.payload.map((entry) => {
128
+ const payloadJson = readString(entry, "payloadJson", "payload_json");
129
+ let parsed = {};
130
+ try {
131
+ parsed = payloadJson ? JSON.parse(payloadJson) : {};
132
+ } catch {
133
+ parsed = {};
134
+ }
135
+ const workstreamId = typeof parsed.workstreamId === "string" && workstreamIds.has(parsed.workstreamId) ? parsed.workstreamId : null;
136
+ return {
137
+ id: String(entry.id ?? ""),
138
+ agentSlug: readString(parsed, "agentSlug") || readString(entry, "relatedId", "related_id") || "agent",
139
+ kind: inferMessageKind(readString(entry, "type"), readString(entry, "status")),
140
+ type: readString(entry, "type"),
141
+ status: readString(entry, "status") || "pending",
142
+ summary: readString(parsed, "summary", "message", "failureSummary", "blockingReason") || readString(entry, "type"),
143
+ workstreamId,
144
+ releaseId: typeof parsed.releaseId === "string" ? parsed.releaseId : null,
145
+ createdAt: readString(entry, "updated_at", "updatedAt", "created_at") || nowIso(),
146
+ metadata: parsed
147
+ };
148
+ });
149
+ return {
150
+ projectId,
151
+ agents: agentStatuses,
152
+ messages: messageRecords
153
+ };
154
+ }
155
+ async function summarizeProject(sdk, config, principal) {
156
+ const [direct, workstreams, agents, releases, packages] = await Promise.all([
157
+ summarizeDirect(sdk, config.projectId),
158
+ sdk.listWorkstreams(config.projectId),
159
+ summarizeAgents(sdk, config.projectId),
160
+ sdk.listReleases(config.projectId),
161
+ sdk.listSharePackages(config.projectId)
162
+ ]);
163
+ const workstreamPayload = workstreams.payload ?? [];
164
+ const releasePayload = releases.payload ?? [];
165
+ const packagePayload = packages.payload ?? [];
166
+ const failedWorkstream = workstreamPayload.find((entry) => entry.verificationStatus === "failed");
167
+ const releaseReady = releasePayload.find((entry) => entry.state === "ready_to_publish");
168
+ const publishingDraft = packagePayload.find((entry) => entry.state === "ready_to_publish" || entry.state === "published");
169
+ let projectConnection = {
170
+ projectId: config.projectId,
171
+ connection: null,
172
+ connected: true,
173
+ hubMode: null,
174
+ runtimeMode: null,
175
+ runtimeRegistration: null,
176
+ runtimeAttached: false,
177
+ runtimeReady: true,
178
+ runnerReady: true,
179
+ projectApiReady: true,
180
+ mode: "disconnected"
181
+ };
182
+ try {
183
+ const deployConfig = loadCliDeployConfig(config.repoRoot);
184
+ const runtimeMode = deployConfig.runtime?.mode ?? "none";
185
+ const runtimeRegistration = deployConfig.runtime?.registration ?? "none";
186
+ const registrationEnabled = runtimeRegistration === "optional" || runtimeRegistration === "required";
187
+ const { configPath } = getTreeseedMachineConfigPaths(config.repoRoot);
188
+ const machineConfig = configPath ? loadTreeseedMachineConfig(config.repoRoot) : null;
189
+ const marketSettings = machineConfig?.settings?.market && typeof machineConfig.settings.market === "object" ? machineConfig.settings.market : null;
190
+ const runnerHostId = typeof marketSettings?.runnerHostId === "string" && marketSettings.runnerHostId.trim() ? marketSettings.runnerHostId.trim() : typeof marketSettings?.projectId === "string" && marketSettings.projectId.trim() ? `market-runner:${marketSettings.projectId.trim()}` : null;
191
+ const runnerSession = runnerHostId ? resolveTreeseedRemoteSession(config.repoRoot, runnerHostId) : null;
192
+ const runtimeReady = runtimeMode === "none" || !registrationEnabled || Boolean(
193
+ marketSettings?.runnerReady === true || typeof runnerSession?.accessToken === "string" && runnerSession.accessToken.length > 0
194
+ );
195
+ projectConnection = {
196
+ projectId: config.projectId,
197
+ connection: null,
198
+ connected: true,
199
+ hubMode: deployConfig.hub?.mode ?? null,
200
+ runtimeMode,
201
+ runtimeRegistration,
202
+ runtimeAttached: runtimeMode !== "none" && (!registrationEnabled || Boolean(marketSettings?.projectId)),
203
+ runtimeReady,
204
+ runnerReady: runtimeReady,
205
+ projectApiReady: deployConfig.runtime?.mode !== "none",
206
+ mode: runtimeMode === "treeseed_managed" ? "hosted" : runtimeMode === "byo_attached" ? registrationEnabled ? "hybrid" : "self_hosted" : "disconnected"
207
+ };
208
+ } catch {
209
+ }
210
+ const health = failedWorkstream ? { state: "verification_failing", label: "Verification failing", reason: failedWorkstream.verificationSummary ?? "A workstream verification failed." } : releaseReady ? { state: "release_ready", label: "Release ready", reason: "A release candidate is ready for approval." } : publishingDraft ? { state: "sharing_draft", label: "Sharing draft", reason: "A share package is ready for publishing." } : { state: "working_normally", label: "Working normally", reason: "Project workstreams and agents are operating normally." };
211
+ const recentActivity = [
212
+ ...workstreamPayload.map((entry) => ({
213
+ kind: "workstream",
214
+ id: entry.id,
215
+ title: entry.title,
216
+ status: entry.state,
217
+ timestamp: entry.updatedAt,
218
+ summary: entry.summary ?? entry.verificationSummary,
219
+ metadata: { branchName: entry.branchName, linkedItems: entry.linkedItems }
220
+ })),
221
+ ...releasePayload.map((entry) => ({
222
+ kind: "release",
223
+ id: entry.id,
224
+ title: entry.title ?? entry.version,
225
+ status: entry.state,
226
+ timestamp: entry.updatedAt,
227
+ summary: entry.summary,
228
+ metadata: { version: entry.version, releaseTag: entry.releaseTag }
229
+ })),
230
+ ...agents.messages.map((entry) => ({
231
+ kind: "agent_message",
232
+ id: entry.id,
233
+ title: entry.type,
234
+ status: entry.status,
235
+ timestamp: entry.createdAt,
236
+ summary: entry.summary,
237
+ metadata: entry.metadata ?? {}
238
+ }))
239
+ ].sort((left, right) => String(right.timestamp ?? "").localeCompare(String(left.timestamp ?? ""))).slice(0, 20);
240
+ return {
241
+ projectId: config.projectId,
242
+ teamId: String(principal?.metadata?.teamId ?? config.projectId),
243
+ health,
244
+ counts: {
245
+ objectives: direct.objectiveCount,
246
+ questions: direct.questionCount,
247
+ notes: direct.noteCount,
248
+ proposals: direct.proposalCount,
249
+ decisions: direct.decisionCount,
250
+ activeWorkstreams: workstreamPayload.filter((entry) => entry.state !== "archived").length,
251
+ agents: agents.agents.length,
252
+ releases: releasePayload.length
253
+ },
254
+ connection: {
255
+ ...projectConnection
256
+ },
257
+ nextBestAction: releaseReady ? "Review the ready release and decide whether to publish." : failedWorkstream ? "Inspect the latest failed verification and update the workstream." : "Open Direct or Workstreams to continue work.",
258
+ recentActivity
259
+ };
260
+ }
261
+ function registerProjectRoutes(app, options) {
262
+ const prefix = options.prefix ?? "";
263
+ const workflow = new TreeseedWorkflowSdk({
264
+ cwd: options.config.repoRoot,
265
+ env: process.env,
266
+ transport: "api"
267
+ });
268
+ app.get(withPrefix(prefix, "/v1/project/summary"), async (c) => {
269
+ const principal = c.get("principal");
270
+ if (!principal) return jsonError(c, 401, "Authentication required.");
271
+ return c.json({
272
+ ok: true,
273
+ payload: await summarizeProject(options.sharedSdk, options.config, principal)
274
+ });
275
+ });
276
+ app.get(withPrefix(prefix, "/v1/direct/summary"), async (c) => {
277
+ const principal = c.get("principal");
278
+ if (!principal) return jsonError(c, 401, "Authentication required.");
279
+ return c.json({ ok: true, payload: await summarizeDirect(options.sharedSdk, options.config.projectId) });
280
+ });
281
+ app.get(withPrefix(prefix, "/v1/workstreams"), async (c) => {
282
+ const principal = c.get("principal");
283
+ if (!principal) return jsonError(c, 401, "Authentication required.");
284
+ const items = await options.sharedSdk.listWorkstreams(options.config.projectId);
285
+ return c.json({
286
+ ok: true,
287
+ payload: {
288
+ projectId: options.config.projectId,
289
+ items: items.payload,
290
+ columns: ["Drafting", "Active locally", "Verifying", "Saved remotely", "In staging", "Archived"]
291
+ }
292
+ });
293
+ });
294
+ app.post(withPrefix(prefix, "/v1/workstreams"), async (c) => {
295
+ const unauthorized = requireTeamCapability(c, "manage_workstreams");
296
+ if (unauthorized) return unauthorized;
297
+ const body = await c.req.json().catch(() => ({}));
298
+ const title = typeof body.title === "string" && body.title.trim() ? body.title.trim() : "Untitled change";
299
+ const branchName = typeof body.branchName === "string" && body.branchName.trim() ? body.branchName.trim() : `task/${slugify(title)}`;
300
+ await workflow.switchTask({
301
+ branchName,
302
+ createIfMissing: true,
303
+ preview: body.preview === true
304
+ });
305
+ const workstream = await options.sharedSdk.upsertWorkstream({
306
+ projectId: options.config.projectId,
307
+ title,
308
+ summary: typeof body.summary === "string" ? body.summary : null,
309
+ state: "active_local",
310
+ branchName,
311
+ branchRef: `refs/heads/${branchName}`,
312
+ owner: typeof body.owner === "string" ? body.owner : c.get("principal")?.displayName ?? null,
313
+ linkedItems: Array.isArray(body.linkedItems) ? body.linkedItems : [],
314
+ metadata: typeof body.metadata === "object" && body.metadata ? body.metadata : {}
315
+ });
316
+ if (workstream.payload) {
317
+ await options.sharedSdk.appendWorkstreamEvent({
318
+ projectId: options.config.projectId,
319
+ workstreamId: workstream.payload.id,
320
+ kind: "created",
321
+ summary: "Workstream created and branch activated.",
322
+ data: { branchName }
323
+ });
324
+ }
325
+ return c.json({ ok: true, payload: workstream.payload }, { status: 201 });
326
+ });
327
+ app.get(withPrefix(prefix, "/v1/workstreams/:id"), async (c) => {
328
+ const principal = c.get("principal");
329
+ if (!principal) return jsonError(c, 401, "Authentication required.");
330
+ const detail = await options.sharedSdk.getWorkstream(c.req.param("id"));
331
+ if (!detail.payload) return jsonError(c, 404, `Unknown workstream "${c.req.param("id")}".`);
332
+ return c.json({ ok: true, payload: detail.payload });
333
+ });
334
+ app.post(withPrefix(prefix, "/v1/workstreams/:id/save"), async (c) => {
335
+ const unauthorized = requireTeamCapability(c, "manage_workstreams");
336
+ if (unauthorized) return unauthorized;
337
+ const existing = await options.sharedSdk.getWorkstream(c.req.param("id"));
338
+ if (!existing.payload) return jsonError(c, 404, `Unknown workstream "${c.req.param("id")}".`);
339
+ const body = await c.req.json().catch(() => ({}));
340
+ const result = await workflow.save({
341
+ message: typeof body.message === "string" && body.message.trim() ? body.message.trim() : `Save ${existing.payload.title}`,
342
+ verify: body.verify !== false,
343
+ refreshPreview: body.refreshPreview === true
344
+ });
345
+ const updated = await options.sharedSdk.upsertWorkstream({
346
+ ...existing.payload,
347
+ state: body.verify === false ? "saved_remote" : "verifying",
348
+ lastSaveAt: nowIso(),
349
+ verificationStatus: result.ok ? "completed" : "failed",
350
+ verificationSummary: result.summary ?? null
351
+ });
352
+ await options.sharedSdk.appendWorkstreamEvent({
353
+ projectId: options.config.projectId,
354
+ workstreamId: existing.payload.id,
355
+ kind: "saved",
356
+ summary: result.summary ?? "Workstream saved.",
357
+ data: { workflow: result.payload ?? {} }
358
+ });
359
+ return c.json({ ok: true, payload: updated.payload });
360
+ });
361
+ app.post(withPrefix(prefix, "/v1/workstreams/:id/stage"), async (c) => {
362
+ const unauthorized = requireTeamCapability(c, "stage_releases");
363
+ if (unauthorized) return unauthorized;
364
+ const existing = await options.sharedSdk.getWorkstream(c.req.param("id"));
365
+ if (!existing.payload) return jsonError(c, 404, `Unknown workstream "${c.req.param("id")}".`);
366
+ const body = await c.req.json().catch(() => ({}));
367
+ const result = await workflow.stage({
368
+ message: typeof body.message === "string" && body.message.trim() ? body.message.trim() : `Stage ${existing.payload.title}`
369
+ });
370
+ const updated = await options.sharedSdk.upsertWorkstream({
371
+ ...existing.payload,
372
+ state: "in_staging",
373
+ lastStageAt: nowIso(),
374
+ verificationStatus: result.ok ? "completed" : existing.payload.verificationStatus,
375
+ verificationSummary: result.summary ?? existing.payload.verificationSummary
376
+ });
377
+ await options.sharedSdk.appendWorkstreamEvent({
378
+ projectId: options.config.projectId,
379
+ workstreamId: existing.payload.id,
380
+ kind: "staged",
381
+ summary: result.summary ?? "Workstream moved to staging.",
382
+ data: { workflow: result.payload ?? {} }
383
+ });
384
+ return c.json({ ok: true, payload: updated.payload });
385
+ });
386
+ app.post(withPrefix(prefix, "/v1/workstreams/:id/archive"), async (c) => {
387
+ const unauthorized = requireTeamCapability(c, "manage_workstreams");
388
+ if (unauthorized) return unauthorized;
389
+ const existing = await options.sharedSdk.getWorkstream(c.req.param("id"));
390
+ if (!existing.payload) return jsonError(c, 404, `Unknown workstream "${c.req.param("id")}".`);
391
+ const updated = await options.sharedSdk.upsertWorkstream({
392
+ ...existing.payload,
393
+ state: "archived",
394
+ archivedAt: nowIso()
395
+ });
396
+ await options.sharedSdk.appendWorkstreamEvent({
397
+ projectId: options.config.projectId,
398
+ workstreamId: existing.payload.id,
399
+ kind: "archived",
400
+ summary: "Workstream archived.",
401
+ data: {}
402
+ });
403
+ return c.json({ ok: true, payload: updated.payload });
404
+ });
405
+ app.get(withPrefix(prefix, "/v1/releases"), async (c) => {
406
+ const principal = c.get("principal");
407
+ if (!principal) return jsonError(c, 401, "Authentication required.");
408
+ const releases = await options.sharedSdk.listReleases(options.config.projectId);
409
+ const history = releases.payload;
410
+ return c.json({
411
+ ok: true,
412
+ payload: {
413
+ projectId: options.config.projectId,
414
+ history,
415
+ currentProd: history.find((entry) => entry.state === "published") ?? null,
416
+ stagingCandidates: history.filter((entry) => entry.state === "ready_to_publish" || entry.state === "waiting_on_verification")
417
+ }
418
+ });
419
+ });
420
+ app.post(withPrefix(prefix, "/v1/releases"), async (c) => {
421
+ const unauthorized = requireTeamCapability(c, "stage_releases");
422
+ if (unauthorized) return unauthorized;
423
+ const body = await c.req.json().catch(() => ({}));
424
+ const workstreams = await options.sharedSdk.listWorkstreams(options.config.projectId);
425
+ const selectedIds = Array.isArray(body.workstreamIds) ? body.workstreamIds.map(String) : workstreams.payload.filter((entry) => entry.state === "in_staging").map((entry) => entry.id);
426
+ const release = await options.sharedSdk.upsertRelease({
427
+ projectId: options.config.projectId,
428
+ version: typeof body.version === "string" && body.version.trim() ? body.version.trim() : `draft-${Date.now()}`,
429
+ title: typeof body.title === "string" ? body.title : null,
430
+ state: "ready_to_publish",
431
+ summary: typeof body.summary === "string" ? body.summary : null,
432
+ workstreamIds: selectedIds,
433
+ items: selectedIds.map((workstreamId) => ({
434
+ id: `${workstreamId}-item`,
435
+ workstreamId,
436
+ model: null,
437
+ recordId: null,
438
+ summary: "Included workstream",
439
+ createdAt: nowIso(),
440
+ metadata: {}
441
+ }))
442
+ });
443
+ return c.json({ ok: true, payload: release.payload }, { status: 201 });
444
+ });
445
+ app.get(withPrefix(prefix, "/v1/releases/:id"), async (c) => {
446
+ const principal = c.get("principal");
447
+ if (!principal) return jsonError(c, 401, "Authentication required.");
448
+ const release = await options.sharedSdk.getRelease(c.req.param("id"));
449
+ if (!release.payload) return jsonError(c, 404, `Unknown release "${c.req.param("id")}".`);
450
+ return c.json({ ok: true, payload: release.payload });
451
+ });
452
+ app.post(withPrefix(prefix, "/v1/releases/:id/publish"), async (c) => {
453
+ const unauthorized = requireTeamCapability(c, "publish_releases");
454
+ if (unauthorized) return unauthorized;
455
+ const release = await options.sharedSdk.getRelease(c.req.param("id"));
456
+ if (!release.payload) return jsonError(c, 404, `Unknown release "${c.req.param("id")}".`);
457
+ const body = await c.req.json().catch(() => ({}));
458
+ const workflowResult = await workflow.release({
459
+ bump: body.bump === "major" || body.bump === "minor" ? body.bump : "patch"
460
+ });
461
+ const updated = await options.sharedSdk.upsertRelease({
462
+ ...release.payload,
463
+ state: "published",
464
+ publishedAt: nowIso(),
465
+ releaseTag: readOptionalString(workflowResult.payload ?? {}, "version", "releaseTag")
466
+ });
467
+ return c.json({ ok: true, payload: updated.payload });
468
+ });
469
+ app.post(withPrefix(prefix, "/v1/releases/:id/rollback"), async (c) => {
470
+ const unauthorized = requireTeamCapability(c, "publish_releases");
471
+ if (unauthorized) return unauthorized;
472
+ const release = await options.sharedSdk.getRelease(c.req.param("id"));
473
+ if (!release.payload) return jsonError(c, 404, `Unknown release "${c.req.param("id")}".`);
474
+ const updated = await options.sharedSdk.upsertRelease({
475
+ ...release.payload,
476
+ state: "rolled_back",
477
+ rolledBackAt: nowIso()
478
+ });
479
+ return c.json({ ok: true, payload: updated.payload });
480
+ });
481
+ app.get(withPrefix(prefix, "/v1/share/status"), async (c) => {
482
+ const principal = c.get("principal");
483
+ if (!principal) return jsonError(c, 401, "Authentication required.");
484
+ const packages = await options.sharedSdk.listSharePackages(options.config.projectId);
485
+ return c.json({
486
+ ok: true,
487
+ payload: {
488
+ projectId: options.config.projectId,
489
+ packages: packages.payload,
490
+ listing: null,
491
+ canPublish: packages.payload.some((entry) => entry.state === "ready_to_publish" || entry.state === "published")
492
+ }
493
+ });
494
+ });
495
+ for (const [path, kind] of [
496
+ ["/v1/share/export", "export"],
497
+ ["/v1/share/package-template", "template"],
498
+ ["/v1/share/package-knowledge-pack", "knowledge_pack"]
499
+ ]) {
500
+ app.post(withPrefix(prefix, path), async (c) => {
501
+ const unauthorized = requireTeamCapability(c, "manage_products");
502
+ if (unauthorized) return unauthorized;
503
+ const body = await c.req.json().catch(() => ({}));
504
+ const exportResult = kind === "export" ? await workflow.export({
505
+ directory: typeof body.directory === "string" ? body.directory : void 0
506
+ }) : null;
507
+ const packageResult = kind === "template" ? buildKnowledgeCoopTemplatePackage(options.config.repoRoot, {
508
+ id: typeof body.id === "string" ? body.id : void 0,
509
+ title: typeof body.title === "string" && body.title.trim() ? body.title.trim() : void 0,
510
+ summary: typeof body.summary === "string" ? body.summary : null,
511
+ outputRoot: typeof body.outputRoot === "string" ? body.outputRoot : null,
512
+ projectSlug: typeof body.projectSlug === "string" ? body.projectSlug : options.config.projectId,
513
+ market: {
514
+ publisherId: typeof c.get("principal")?.metadata?.teamId === "string" ? c.get("principal").metadata.teamId : null,
515
+ publisherName: typeof c.get("principal")?.displayName === "string" ? c.get("principal").displayName : null,
516
+ publishMetadata: {
517
+ projectId: options.config.projectId,
518
+ kind
519
+ }
520
+ }
521
+ }) : kind === "knowledge_pack" ? buildKnowledgeCoopKnowledgePackPackage(options.config.repoRoot, {
522
+ id: typeof body.id === "string" ? body.id : void 0,
523
+ title: typeof body.title === "string" && body.title.trim() ? body.title.trim() : void 0,
524
+ summary: typeof body.summary === "string" ? body.summary : null,
525
+ outputRoot: typeof body.outputRoot === "string" ? body.outputRoot : null,
526
+ projectSlug: typeof body.projectSlug === "string" ? body.projectSlug : options.config.projectId,
527
+ includePaths: Array.isArray(body.includePaths) ? body.includePaths.map(String) : void 0,
528
+ market: {
529
+ publisherId: typeof c.get("principal")?.metadata?.teamId === "string" ? c.get("principal").metadata.teamId : null,
530
+ publisherName: typeof c.get("principal")?.displayName === "string" ? c.get("principal").displayName : null,
531
+ publishMetadata: {
532
+ projectId: options.config.projectId,
533
+ kind
534
+ }
535
+ }
536
+ }) : null;
537
+ const item = await options.sharedSdk.upsertSharePackage({
538
+ projectId: options.config.projectId,
539
+ kind,
540
+ title: typeof body.title === "string" && body.title.trim() ? body.title.trim() : `${kind}-${Date.now()}`,
541
+ summary: kind === "export" ? exportResult?.summary ?? null : packageResult?.manifest.summary ?? null,
542
+ state: "ready_to_publish",
543
+ version: kind === "export" ? null : packageResult?.manifest.version ?? null,
544
+ outputPath: kind === "export" ? readOptionalString(exportResult?.payload ?? {}, "path", "outputPath", "directory") : packageResult?.outputRoot ?? null,
545
+ artifactKey: kind === "export" ? null : packageResult?.payloadRoot ?? null,
546
+ manifestKey: kind === "export" ? null : packageResult?.manifestPath ?? null,
547
+ metadata: kind === "export" ? typeof exportResult?.payload === "object" && exportResult.payload ? exportResult.payload : {} : {
548
+ manifest: packageResult?.manifest ?? null,
549
+ files: packageResult?.files ?? [],
550
+ payloadRoot: packageResult?.payloadRoot ?? null
551
+ }
552
+ });
553
+ return c.json({ ok: true, payload: item.payload });
554
+ });
555
+ }
556
+ app.post(withPrefix(prefix, "/v1/share/publish"), async (c) => {
557
+ const unauthorized = requireTeamCapability(c, "publish_market_listings");
558
+ if (unauthorized) return unauthorized;
559
+ const body = await c.req.json().catch(() => ({}));
560
+ const sharePackage = typeof body.packageId === "string" ? await options.sharedSdk.getSharePackage(body.packageId) : null;
561
+ if (!sharePackage?.payload) {
562
+ return jsonError(c, 404, "Unknown share package.");
563
+ }
564
+ const updated = await options.sharedSdk.upsertSharePackage({
565
+ ...sharePackage.payload,
566
+ state: "published"
567
+ });
568
+ return c.json({ ok: true, payload: updated.payload });
569
+ });
570
+ app.get(withPrefix(prefix, "/v1/agents/status"), async (c) => {
571
+ const principal = c.get("principal");
572
+ if (!principal) return jsonError(c, 401, "Authentication required.");
573
+ const payload = await summarizeAgents(options.sharedSdk, options.config.projectId);
574
+ return c.json({ ok: true, payload: { projectId: options.config.projectId, agents: payload.agents } });
575
+ });
576
+ app.get(withPrefix(prefix, "/v1/agents/messages"), async (c) => {
577
+ const principal = c.get("principal");
578
+ if (!principal) return jsonError(c, 401, "Authentication required.");
579
+ const payload = await summarizeAgents(options.sharedSdk, options.config.projectId);
580
+ return c.json({ ok: true, payload: payload.messages });
581
+ });
582
+ }
583
+ export {
584
+ registerProjectRoutes
585
+ };
@@ -0,0 +1,2 @@
1
+ import type { ApiConfig, ApiRuntimeProviders, ResolvedApiRuntimeProviders } from './types.ts';
2
+ export declare function resolveApiRuntimeProviders(config: ApiConfig, overrides?: ApiRuntimeProviders): ResolvedApiRuntimeProviders;
@@ -0,0 +1,62 @@
1
+ import { MemoryDeviceCodeAuthProvider } from "./auth/memory-provider.js";
2
+ import { D1AuthProvider } from "./auth/d1-provider.js";
3
+ import { resolveApiD1Database } from "./auth/d1-database.js";
4
+ function addProviders(target, incoming, label) {
5
+ for (const [id, value] of Object.entries(incoming ?? {})) {
6
+ if (target.has(id)) {
7
+ throw new Error(`Treeseed API runtime found duplicate ${label} provider "${id}".`);
8
+ }
9
+ target.set(id, value);
10
+ }
11
+ }
12
+ function resolveSelectedProvider(registry, selectedId, label) {
13
+ const selected = registry.get(selectedId);
14
+ if (!selected) {
15
+ throw new Error(`Treeseed API runtime could not resolve ${label} provider "${selectedId}".`);
16
+ }
17
+ return selected;
18
+ }
19
+ function resolveApiRuntimeProviders(config, overrides = {}) {
20
+ const authRegistry = /* @__PURE__ */ new Map();
21
+ const agentExecution = /* @__PURE__ */ new Map();
22
+ const agentQueue = /* @__PURE__ */ new Map();
23
+ const agentNotification = /* @__PURE__ */ new Map();
24
+ const agentRepository = /* @__PURE__ */ new Map();
25
+ const agentVerification = /* @__PURE__ */ new Map();
26
+ addProviders(authRegistry, {
27
+ memory: ({ config: runtimeConfig }) => new MemoryDeviceCodeAuthProvider(runtimeConfig),
28
+ d1: ({ config: runtimeConfig }) => new D1AuthProvider(runtimeConfig, { db: resolveApiD1Database(runtimeConfig) })
29
+ }, "auth");
30
+ addProviders(authRegistry, overrides.auth, "auth");
31
+ addProviders(agentExecution, { stub: { id: "stub" } }, "agent execution");
32
+ addProviders(agentQueue, { memory: { id: "memory" } }, "agent queue");
33
+ addProviders(agentNotification, { stub: { id: "stub" } }, "agent notification");
34
+ addProviders(agentRepository, { stub: { id: "stub" } }, "agent repository");
35
+ addProviders(agentVerification, { stub: { id: "stub" } }, "agent verification");
36
+ addProviders(agentExecution, overrides.agentExecution, "agent execution");
37
+ addProviders(agentQueue, overrides.agentQueue, "agent queue");
38
+ addProviders(agentNotification, overrides.agentNotification, "agent notification");
39
+ addProviders(agentRepository, overrides.agentRepository, "agent repository");
40
+ addProviders(agentVerification, overrides.agentVerification, "agent verification");
41
+ const authFactory = resolveSelectedProvider(authRegistry, config.providers.auth, "auth");
42
+ resolveSelectedProvider(agentExecution, config.providers.agents.execution, "agent execution");
43
+ resolveSelectedProvider(agentQueue, config.providers.agents.queue, "agent queue");
44
+ resolveSelectedProvider(agentNotification, config.providers.agents.notification, "agent notification");
45
+ resolveSelectedProvider(agentRepository, config.providers.agents.repository, "agent repository");
46
+ resolveSelectedProvider(agentVerification, config.providers.agents.verification, "agent verification");
47
+ return {
48
+ auth: authFactory({ config }),
49
+ registries: {
50
+ auth: authRegistry,
51
+ agentExecution,
52
+ agentQueue,
53
+ agentNotification,
54
+ agentRepository,
55
+ agentVerification
56
+ },
57
+ selections: config.providers
58
+ };
59
+ }
60
+ export {
61
+ resolveApiRuntimeProviders
62
+ };