@treeseed/core 0.6.38 → 0.6.39

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 CHANGED
@@ -52,8 +52,8 @@ npm run dev
52
52
  npm run dev:web
53
53
  npm run dev:api
54
54
  npm run dev:worker
55
- npm run dev:workday-start
56
- npm run dev:workday-report
55
+ npm run dev:workday-manager
56
+ npm run dev:worker-runner
57
57
  npm run fixtures:check
58
58
  npm run build:dist
59
59
  npm run test:unit
@@ -67,7 +67,7 @@ What they do:
67
67
  - `dev`: starts the integrated Astro UI and Hono API local runtime from `core`
68
68
  - `dev:web`: starts only the Astro UI dev surface through the `core` runtime
69
69
  - `dev:api`: starts only the Hono API dev surface through the `core` runtime
70
- - `dev:worker`, `dev:workday-start`, `dev:workday-report`: start the worker-service entrypoints from `core`
70
+ - `dev:workday-manager`, `dev:worker-runner`: start the scheduled manager and worker runner entrypoints from `core`
71
71
  - `fixtures:check`: verifies that the pinned shared fixture is initialized and usable
72
72
  - `build:dist`: builds the publishable `dist/` package output
73
73
  - `test:unit`: runs package unit tests with Vitest
package/dist/agent.d.ts CHANGED
@@ -3,6 +3,7 @@ export { listTreeseedAgentCommands, renderTreeseedAgentHelp, runTreeseedAgentCli
3
3
  export { resolveAgentHandler, listRegisteredAgentHandlers } from './agents/registry.ts';
4
4
  export { resolveAgentRuntimeProviders } from './agent-runtime.ts';
5
5
  export { runWorkerCycle, startWorkerLoop } from './services/worker.ts';
6
+ export { runScheduledWorkdayManager } from './services/workday-manager.ts';
6
7
  export { runWorkdayStart } from './services/workday-start.ts';
7
8
  export { runWorkdayReport } from './services/workday-report.ts';
8
9
  export { parseAgentMessagePayload, AGENT_MESSAGE_TYPES } from './agents/contracts/messages.ts';
package/dist/agent.js CHANGED
@@ -3,6 +3,7 @@ import { listTreeseedAgentCommands, renderTreeseedAgentHelp, runTreeseedAgentCli
3
3
  import { resolveAgentHandler, listRegisteredAgentHandlers } from "./agents/registry.js";
4
4
  import { resolveAgentRuntimeProviders } from "./agent-runtime.js";
5
5
  import { runWorkerCycle, startWorkerLoop } from "./services/worker.js";
6
+ import { runScheduledWorkdayManager } from "./services/workday-manager.js";
6
7
  import { runWorkdayStart } from "./services/workday-start.js";
7
8
  import { runWorkdayReport } from "./services/workday-report.js";
8
9
  import { parseAgentMessagePayload, AGENT_MESSAGE_TYPES } from "./agents/contracts/messages.js";
@@ -15,6 +16,7 @@ export {
15
16
  renderTreeseedAgentHelp,
16
17
  resolveAgentHandler,
17
18
  resolveAgentRuntimeProviders,
19
+ runScheduledWorkdayManager,
18
20
  runTreeseedAgentCli,
19
21
  runWorkdayReport,
20
22
  runWorkdayStart,
@@ -122,7 +122,77 @@ function normalizeExecution(value, diagnostics, slug) {
122
122
  cooldownSeconds: ensurePositiveNumber(next.cooldownSeconds, "execution.cooldownSeconds", diagnostics, slug, 30, true),
123
123
  leaseSeconds: ensurePositiveNumber(next.leaseSeconds, "execution.leaseSeconds", diagnostics, slug, 300),
124
124
  retryLimit: ensurePositiveNumber(next.retryLimit, "execution.retryLimit", diagnostics, slug, 3, true),
125
- branchPrefix: ensureString(next.branchPrefix ?? "agent", "execution.branchPrefix", diagnostics, slug) || "agent"
125
+ branchPrefix: ensureString(next.branchPrefix ?? "agent", "execution.branchPrefix", diagnostics, slug) || "agent",
126
+ providerProfile: normalizeProviderProfile(next.providerProfile, diagnostics, slug)
127
+ };
128
+ }
129
+ function normalizeWeightedLaneList(value, field, diagnostics, slug) {
130
+ if (value === void 0) return [];
131
+ if (!Array.isArray(value)) {
132
+ diagnostics.push({
133
+ severity: "error",
134
+ slug,
135
+ field,
136
+ message: `Expected ${field} to be an array.`
137
+ });
138
+ return [];
139
+ }
140
+ return value.flatMap((entry, index) => {
141
+ if (!isPlainObject(entry)) {
142
+ diagnostics.push({
143
+ severity: "error",
144
+ slug,
145
+ field: `${field}[${index}]`,
146
+ message: "Expected provider lane entry to be an object."
147
+ });
148
+ return [];
149
+ }
150
+ return [{
151
+ providerId: typeof entry.providerId === "string" ? entry.providerId : void 0,
152
+ provider: typeof entry.provider === "string" ? entry.provider : void 0,
153
+ laneId: typeof entry.laneId === "string" ? entry.laneId : void 0,
154
+ model: typeof entry.model === "string" ? entry.model : void 0,
155
+ modelClass: typeof entry.modelClass === "string" ? entry.modelClass : void 0,
156
+ weight: ensurePositiveNumber(entry.weight, `${field}[${index}].weight`, diagnostics, slug, 1, true),
157
+ reason: typeof entry.reason === "string" ? entry.reason : void 0,
158
+ maxQualityPenalty: typeof entry.maxQualityPenalty === "number" ? entry.maxQualityPenalty : void 0
159
+ }];
160
+ });
161
+ }
162
+ function normalizeProviderProfile(value, diagnostics, slug) {
163
+ if (value === void 0) return void 0;
164
+ if (!isPlainObject(value)) {
165
+ diagnostics.push({
166
+ severity: "error",
167
+ slug,
168
+ field: "execution.providerProfile",
169
+ message: "Expected execution.providerProfile to be an object."
170
+ });
171
+ return void 0;
172
+ }
173
+ const fallbackPolicy = typeof value.fallbackPolicy === "string" ? value.fallbackPolicy : "allow_substitution";
174
+ if (!["allow_substitution", "require_same_model_class", "fail_if_unavailable", "ask_for_approval"].includes(fallbackPolicy)) {
175
+ diagnostics.push({
176
+ severity: "error",
177
+ slug,
178
+ field: "execution.providerProfile.fallbackPolicy",
179
+ message: `Unsupported fallback policy "${String(value.fallbackPolicy)}".`
180
+ });
181
+ }
182
+ return {
183
+ requiredCapabilities: Array.isArray(value.requiredCapabilities) ? value.requiredCapabilities.filter((entry) => typeof entry === "string" && entry.trim().length > 0) : [],
184
+ preferredLanes: normalizeWeightedLaneList(value.preferredLanes, "execution.providerProfile.preferredLanes", diagnostics, slug),
185
+ acceptableFallbacks: normalizeWeightedLaneList(value.acceptableFallbacks, "execution.providerProfile.acceptableFallbacks", diagnostics, slug).map((entry) => ({
186
+ providerId: entry.providerId,
187
+ provider: entry.provider,
188
+ laneId: entry.laneId,
189
+ model: entry.model,
190
+ modelClass: entry.modelClass,
191
+ maxQualityPenalty: entry.maxQualityPenalty
192
+ })),
193
+ disallowedProviders: Array.isArray(value.disallowedProviders) ? value.disallowedProviders.filter((entry) => typeof entry === "string") : void 0,
194
+ disallowedRegions: Array.isArray(value.disallowedRegions) ? value.disallowedRegions.filter((entry) => typeof entry === "string") : void 0,
195
+ fallbackPolicy
126
196
  };
127
197
  }
128
198
  function normalizeOutputs(value, _diagnostics, _slug) {
@@ -58,17 +58,7 @@ function registerAgentRoutes(app, options) {
58
58
  app.post(withPrefix(prefix, "/workdays/start"), async (c) => {
59
59
  const unauthorized = authorizeRequest(c, options);
60
60
  if (unauthorized) return unauthorized;
61
- const body = await c.req.json().catch(() => ({}));
62
- const graphRefresh = await options.sdk.refreshGraph();
63
- const result = await options.sdk.startWorkDay({
64
- id: typeof body.id === "string" ? body.id : void 0,
65
- projectId: String(body.projectId ?? options.projectId ?? "treeseed-market"),
66
- capacityBudget: body.capacityBudget === void 0 ? void 0 : Number(body.capacityBudget),
67
- graphVersion: typeof body.graphVersion === "string" ? body.graphVersion : graphRefresh.snapshotRoot,
68
- summary: body.summary ?? { graphRefresh },
69
- actor: actor(body, defaultActor)
70
- });
71
- return c.json(result);
61
+ return jsonError(c, 410, "Starting workdays through /agent/workdays/start is deprecated. Use project workday policy and workday requests instead.");
72
62
  });
73
63
  app.post(withPrefix(prefix, "/workdays/:id/close"), async (c) => {
74
64
  const unauthorized = authorizeRequest(c, options);
@@ -8,6 +8,7 @@ const require = createRequire(import.meta.url);
8
8
  const srcRoot = resolve(packageRoot, 'src');
9
9
  const scriptsRoot = resolve(packageRoot, 'scripts');
10
10
  const distRoot = resolve(packageRoot, 'dist');
11
+ const workspaceSdkDistRoot = resolve(packageRoot, '..', 'sdk', 'dist');
11
12
  const JS_SOURCE_EXTENSIONS = new Set(['.ts', '.ts']);
12
13
  const COPY_EXTENSIONS = new Set(['.astro', '.css', '.d.js', '.js', '.json', '.jsonc', '.ts', '.yaml', '.yml']);
13
14
  function walkFiles(root) {
@@ -202,6 +203,18 @@ function rewriteDeclarations() {
202
203
  writeFileSync(filePath, rewriteRuntimeSpecifiers(contents), 'utf8');
203
204
  }
204
205
  }
206
+ function relativePathForTsconfig(fromRoot, targetPath) {
207
+ return relative(fromRoot, targetPath).replaceAll('\\', '/');
208
+ }
209
+ function resolveWorkspaceSdkDeclarationPaths() {
210
+ if (!existsSync(resolve(workspaceSdkDistRoot, 'index.d.ts'))) {
211
+ return null;
212
+ }
213
+ return {
214
+ '@treeseed/sdk': [relativePathForTsconfig(packageRoot, resolve(workspaceSdkDistRoot, 'index.d.ts'))],
215
+ '@treeseed/sdk/*': [relativePathForTsconfig(packageRoot, resolve(workspaceSdkDistRoot, '*.d.ts'))],
216
+ };
217
+ }
205
218
  function emitTypeDeclarations() {
206
219
  const sourceFiles = [
207
220
  resolve(srcRoot, 'types/astro-build.d.ts'),
@@ -228,11 +241,13 @@ function emitTypeDeclarations() {
228
241
  }
229
242
  const compilerOptions = {
230
243
  allowImportingTsExtensions: true,
244
+ baseUrl: packageRoot,
231
245
  declaration: true,
232
246
  emitDeclarationOnly: true,
233
247
  module: ts.ModuleKind.ESNext,
234
248
  moduleResolution: ts.ModuleResolutionKind.Bundler,
235
249
  outDir: distRoot,
250
+ paths: resolveWorkspaceSdkDeclarationPaths() ?? undefined,
236
251
  rootDir: srcRoot,
237
252
  skipLibCheck: true,
238
253
  target: ts.ScriptTarget.ES2022,
@@ -28,6 +28,12 @@ export declare function buildTaskContext(sdk: AgentSdk, taskId: string): Promise
28
28
  graph: Record<string, unknown> | null;
29
29
  }>;
30
30
  export declare function seedRootTasks(sdk: AgentSdk, workDayId: string): Promise<any[]>;
31
+ export declare function seedGraphRefreshTask(sdk: AgentSdk, request: {
32
+ workDayId: string;
33
+ projectId: string;
34
+ repositoryId?: string | null;
35
+ actor?: string;
36
+ }): Promise<import("@treeseed/sdk").SdkTaskEntity>;
31
37
  export declare function startAndSeedWorkday(sdk: AgentSdk, request: {
32
38
  id?: string;
33
39
  projectId: string;
@@ -47,7 +53,14 @@ export declare function resolveManagerConfig(): {
47
53
  export declare function resolveWorkerConfig(): {
48
54
  workerId: string;
49
55
  batchSize: number;
56
+ maxLocalWorkers: number;
57
+ runnerServiceName: string;
58
+ volumeRoot: string;
59
+ volumeIdentity: string;
60
+ projectId: string;
61
+ environment: string;
50
62
  visibilityTimeoutMs: number;
51
63
  pollIntervalMs: number;
64
+ idleExitMs: number;
52
65
  leaseSeconds: number;
53
66
  };
@@ -135,21 +135,41 @@ async function seedRootTasks(sdk, workDayId) {
135
135
  }
136
136
  return created;
137
137
  }
138
+ async function seedGraphRefreshTask(sdk, request) {
139
+ const task = await sdk.createTask({
140
+ workDayId: request.workDayId,
141
+ agentId: "system",
142
+ type: "refresh_project_graph",
143
+ priority: 1e3,
144
+ idempotencyKey: `${request.workDayId}:refresh_project_graph`,
145
+ payload: {
146
+ projectId: request.projectId,
147
+ repositoryId: request.repositoryId ?? request.projectId
148
+ },
149
+ graphVersion: null,
150
+ actor: request.actor ?? "manager"
151
+ });
152
+ return task.payload;
153
+ }
138
154
  async function startAndSeedWorkday(sdk, request) {
139
- const graphRefresh = await sdk.refreshGraph();
140
155
  const workDay = await sdk.startWorkDay({
141
156
  id: request.id,
142
157
  projectId: request.projectId,
143
158
  capacityBudget: request.capacityBudget,
144
- graphVersion: graphRefresh.snapshotRoot,
145
- summary: { graphRefresh },
159
+ graphVersion: null,
160
+ summary: { graphRefresh: { state: "queued" } },
146
161
  actor: request.actor ?? "manager"
147
162
  });
163
+ const graphTask = workDay.payload ? await seedGraphRefreshTask(sdk, {
164
+ workDayId: String(workDay.payload.id),
165
+ projectId: request.projectId,
166
+ actor: request.actor ?? "manager"
167
+ }) : null;
148
168
  const tasks = workDay.payload ? await seedRootTasks(sdk, String(workDay.payload.id)) : [];
149
169
  return {
150
170
  ok: true,
151
171
  workDay: workDay.payload,
152
- seededTasks: tasks.map((entry) => entry.payload).filter(Boolean)
172
+ seededTasks: [graphTask, ...tasks.map((entry) => entry.payload).filter(Boolean)].filter(Boolean)
153
173
  };
154
174
  }
155
175
  function resolveManagerConfig() {
@@ -163,9 +183,16 @@ function resolveManagerConfig() {
163
183
  function resolveWorkerConfig() {
164
184
  return {
165
185
  workerId: process.env.TREESEED_WORKER_ID?.trim() || `worker-${process.pid}`,
166
- batchSize: integerFromEnv("TREESEED_QUEUE_BATCH_SIZE", 1),
186
+ batchSize: integerFromEnv("TREESEED_QUEUE_BATCH_SIZE", integerFromEnv("TREESEED_RUNNER_MAX_LOCAL_WORKERS", 4)),
187
+ maxLocalWorkers: integerFromEnv("TREESEED_RUNNER_MAX_LOCAL_WORKERS", 4),
188
+ runnerServiceName: process.env.TREESEED_RUNNER_SERVICE_NAME?.trim() || process.env.RAILWAY_SERVICE_NAME?.trim() || `worker-runner-${process.pid}`,
189
+ volumeRoot: process.env.TREESEED_RUNNER_VOLUME_ROOT?.trim() || process.env.RAILWAY_VOLUME_MOUNT_PATH?.trim() || ".treeseed-runner",
190
+ volumeIdentity: process.env.TREESEED_RUNNER_VOLUME_ID?.trim() || process.env.RAILWAY_VOLUME_ID?.trim() || process.env.RAILWAY_VOLUME_NAME?.trim() || "local-runner-volume",
191
+ projectId: process.env.TREESEED_PROJECT_ID?.trim() || "treeseed-market",
192
+ environment: process.env.TREESEED_DEPLOY_ENVIRONMENT?.trim() || (process.env.NODE_ENV === "production" ? "prod" : "local"),
167
193
  visibilityTimeoutMs: integerFromEnv("TREESEED_QUEUE_VISIBILITY_TIMEOUT_MS", 12e4),
168
194
  pollIntervalMs: integerFromEnv("TREESEED_WORKER_POLL_INTERVAL_MS", 5e3),
195
+ idleExitMs: integerFromEnv("TREESEED_WORKER_IDLE_EXIT_MS", 0),
169
196
  leaseSeconds: integerFromEnv("TREESEED_TASK_LEASE_SECONDS", 120)
170
197
  };
171
198
  }
@@ -179,6 +206,7 @@ export {
179
206
  resolveManagerConfig,
180
207
  resolveServiceRepoRoot,
181
208
  resolveWorkerConfig,
209
+ seedGraphRefreshTask,
182
210
  seedRootTasks,
183
211
  startAndSeedWorkday
184
212
  };
@@ -1,5 +1,6 @@
1
1
  export { runManagerAction, runManagerCycle, startManagerLoop } from './manager.ts';
2
2
  export { runWorkerCycle, startWorkerLoop } from './worker.ts';
3
+ export { runScheduledWorkdayManager } from './workday-manager.ts';
3
4
  export { runWorkdayStart } from './workday-start.ts';
4
5
  export { runWorkdayReport } from './workday-report.ts';
5
6
  export { createWorkerPoolScaler, RailwayWorkerPoolScaler, NoopWorkerPoolScaler } from './worker-pool-scaler.ts';
@@ -1,5 +1,6 @@
1
1
  import { runManagerAction, runManagerCycle, startManagerLoop } from "./manager.js";
2
2
  import { runWorkerCycle, startWorkerLoop } from "./worker.js";
3
+ import { runScheduledWorkdayManager } from "./workday-manager.js";
3
4
  import { runWorkdayStart } from "./workday-start.js";
4
5
  import { runWorkdayReport } from "./workday-report.js";
5
6
  import { createWorkerPoolScaler, RailwayWorkerPoolScaler, NoopWorkerPoolScaler } from "./worker-pool-scaler.js";
@@ -9,6 +10,7 @@ export {
9
10
  createWorkerPoolScaler,
10
11
  runManagerAction,
11
12
  runManagerCycle,
13
+ runScheduledWorkdayManager,
12
14
  runWorkdayReport,
13
15
  runWorkdayStart,
14
16
  runWorkerCycle,
@@ -48,7 +48,7 @@ export declare function runManagerAction(options?: {
48
48
  mode: "reconcile";
49
49
  managerId: string;
50
50
  projectId: string;
51
- environment: "local" | "staging" | "prod";
51
+ environment: "local" | "prod" | "staging";
52
52
  insideWorkWindow: boolean;
53
53
  workPolicy: WorkdayPolicy;
54
54
  workDay: Record<string, unknown>;
@@ -97,8 +97,18 @@ export declare function runManagerAction(options?: {
97
97
  reportVersion: string;
98
98
  title: string;
99
99
  };
100
+ capacity: {
101
+ providerSplit: any;
102
+ grantedDailyCredits: number;
103
+ reservedCredits: number;
104
+ consumedCredits: number;
105
+ remainingDailyCredits: number | null;
106
+ providerCount: number;
107
+ laneCount: number;
108
+ grantCount: number;
109
+ };
100
110
  projectId: string;
101
- environment: "local" | "staging" | "prod";
111
+ environment: "local" | "prod" | "staging";
102
112
  workDayId: string;
103
113
  state: string;
104
114
  totalTasks: number;
@@ -164,8 +174,18 @@ export declare function runManagerAction(options?: {
164
174
  reportVersion: string;
165
175
  title: string;
166
176
  };
177
+ capacity: {
178
+ providerSplit: any;
179
+ grantedDailyCredits: number;
180
+ reservedCredits: number;
181
+ consumedCredits: number;
182
+ remainingDailyCredits: number | null;
183
+ providerCount: number;
184
+ laneCount: number;
185
+ grantCount: number;
186
+ };
167
187
  projectId: string;
168
- environment: "local" | "staging" | "prod";
188
+ environment: "local" | "prod" | "staging";
169
189
  workDayId: string;
170
190
  state: string;
171
191
  totalTasks: number;
@@ -226,7 +246,7 @@ export declare function runManagerCycle(options?: {
226
246
  mode: "reconcile";
227
247
  managerId: string;
228
248
  projectId: string;
229
- environment: "local" | "staging" | "prod";
249
+ environment: "local" | "prod" | "staging";
230
250
  insideWorkWindow: boolean;
231
251
  workPolicy: WorkdayPolicy;
232
252
  workDay: Record<string, unknown>;