@agentrific/server 0.2.0 → 0.4.0
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/CHANGELOG.md +14 -0
- package/cli.js +161 -75
- package/cli.js.map +4 -4
- package/index.js +154 -75
- package/index.js.map +4 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @agentrific/server
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d5dd726: Add persistent project chat with native Board and Chat tabs, project titles and a Reset action that starts a fresh conversation while preserving history. Correct native route parameters, header spacing and composer placement above the keyboard and Android tab bar.
|
|
8
|
+
|
|
9
|
+
## 0.3.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 66aa463: Improve task-board navigation and display accumulated run usage. Add clearer Bash
|
|
14
|
+
command details, highlighted read-file previews and better native sheet sizing.
|
|
15
|
+
Server events now preserve complete Bash command inputs and report session usage.
|
|
16
|
+
|
|
3
17
|
## 0.2.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
package/cli.js
CHANGED
|
@@ -30,7 +30,7 @@ var init_package = __esm({
|
|
|
30
30
|
"package.json"() {
|
|
31
31
|
package_default = {
|
|
32
32
|
name: "@agentrific/server",
|
|
33
|
-
version: "0.
|
|
33
|
+
version: "0.4.0",
|
|
34
34
|
description: "Self-hosted control plane for long-running coding agents",
|
|
35
35
|
keywords: [
|
|
36
36
|
"agentrific",
|
|
@@ -345,6 +345,7 @@ var init_src = __esm({
|
|
|
345
345
|
SessionSchema = z2.object({
|
|
346
346
|
id: z2.string(),
|
|
347
347
|
taskId: z2.string().nullable().optional(),
|
|
348
|
+
kind: z2.enum(["task", "repo_chat"]).optional(),
|
|
348
349
|
projectId: z2.string(),
|
|
349
350
|
title: z2.string(),
|
|
350
351
|
status: SessionStatusSchema,
|
|
@@ -355,6 +356,11 @@ var init_src = __esm({
|
|
|
355
356
|
gitFlow: GitFlowSchema.optional(),
|
|
356
357
|
gitDelivery: GitDeliverySchema.optional(),
|
|
357
358
|
preparation: RunPreparationSchema.optional(),
|
|
359
|
+
usage: z2.object({
|
|
360
|
+
tokens: z2.number().nonnegative(),
|
|
361
|
+
turns: z2.number().int().nonnegative(),
|
|
362
|
+
tools: z2.number().int().nonnegative()
|
|
363
|
+
}).optional(),
|
|
358
364
|
createdAt: z2.string().datetime(),
|
|
359
365
|
updatedAt: z2.string().datetime()
|
|
360
366
|
});
|
|
@@ -418,6 +424,7 @@ var init_src = __esm({
|
|
|
418
424
|
}),
|
|
419
425
|
z2.object({
|
|
420
426
|
type: z2.literal("message.completed"),
|
|
427
|
+
tokens: z2.number().nonnegative().optional(),
|
|
421
428
|
model: ModelIdentitySchema.optional(),
|
|
422
429
|
sessionId: z2.string(),
|
|
423
430
|
messageId: z2.string().optional(),
|
|
@@ -1827,7 +1834,9 @@ async function boardTasks(store2, projectId) {
|
|
|
1827
1834
|
status: taskStatusForSession(session.status, session)
|
|
1828
1835
|
};
|
|
1829
1836
|
});
|
|
1830
|
-
const legacyTasks = sessions.filter(
|
|
1837
|
+
const legacyTasks = sessions.filter(
|
|
1838
|
+
(session) => session.kind !== "repo_chat" && !session.taskId && !linkedSessionIds.has(session.id)
|
|
1839
|
+
).filter((session) => !projectId || session.projectId === projectId).map(sessionTask);
|
|
1831
1840
|
return [...linkedTasks, ...legacyTasks].map((task) => TaskSchema.parse(task)).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
1832
1841
|
}
|
|
1833
1842
|
async function findTask(store2, id) {
|
|
@@ -2079,13 +2088,29 @@ var init_runtime = __esm({
|
|
|
2079
2088
|
const entry = {
|
|
2080
2089
|
session,
|
|
2081
2090
|
agent,
|
|
2082
|
-
unsubscribe:
|
|
2091
|
+
unsubscribe: () => {
|
|
2092
|
+
},
|
|
2083
2093
|
running: false,
|
|
2084
2094
|
stopping: false,
|
|
2085
2095
|
pending: []
|
|
2086
2096
|
};
|
|
2097
|
+
entry.unsubscribe = agent.subscribe((event) => {
|
|
2098
|
+
this.emit(event);
|
|
2099
|
+
if (event.type !== "message.completed" && event.type !== "tool.started") return;
|
|
2100
|
+
const usage = entry.session.usage ?? { tokens: 0, turns: 0, tools: 0 };
|
|
2101
|
+
entry.session = {
|
|
2102
|
+
...entry.session,
|
|
2103
|
+
usage: {
|
|
2104
|
+
tokens: usage.tokens + (event.type === "message.completed" ? event.tokens ?? 0 : 0),
|
|
2105
|
+
turns: usage.turns + (event.type === "message.completed" ? 1 : 0),
|
|
2106
|
+
tools: usage.tools + (event.type === "tool.started" ? 1 : 0)
|
|
2107
|
+
},
|
|
2108
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2109
|
+
};
|
|
2110
|
+
this.emit({ type: "session.updated", session: entry.session });
|
|
2111
|
+
});
|
|
2087
2112
|
if (agent.model) {
|
|
2088
|
-
entry.session = { ...session, model: agent.model };
|
|
2113
|
+
entry.session = { ...entry.session, model: agent.model };
|
|
2089
2114
|
this.emit({ type: "session.updated", session: entry.session });
|
|
2090
2115
|
}
|
|
2091
2116
|
this.active.set(session.id, entry);
|
|
@@ -3316,6 +3341,7 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3316
3341
|
sessionId,
|
|
3317
3342
|
messageId: assistantMessageId,
|
|
3318
3343
|
text,
|
|
3344
|
+
tokens: event.message.usage.totalTokens,
|
|
3319
3345
|
model: models2.identity(event.message.provider, event.message.model)
|
|
3320
3346
|
});
|
|
3321
3347
|
assistantMessageId = void 0;
|
|
@@ -3339,7 +3365,8 @@ function createPiFactory(files, models2, delivery2, createSession = createAgentS
|
|
|
3339
3365
|
sessionId,
|
|
3340
3366
|
toolCallId: event.toolCallId,
|
|
3341
3367
|
tool: event.toolName,
|
|
3342
|
-
|
|
3368
|
+
// Preserve complete Bash arguments so multiline commands can be inspected.
|
|
3369
|
+
input: event.toolName === "bash" ? JSON.stringify(event.args) : boundedJson(event.args, 1200),
|
|
3343
3370
|
filePath: filePaths.get(event.toolCallId)
|
|
3344
3371
|
});
|
|
3345
3372
|
return true;
|
|
@@ -4097,6 +4124,44 @@ var init_worktree_cleanup = __esm({
|
|
|
4097
4124
|
}
|
|
4098
4125
|
});
|
|
4099
4126
|
|
|
4127
|
+
// src/repo-chat.ts
|
|
4128
|
+
import { randomUUID as randomUUID8 } from "node:crypto";
|
|
4129
|
+
import { Effect as Effect16 } from "effect";
|
|
4130
|
+
function openRepoChat(store2, projectId, workspacePath, defaultModel, reset = false) {
|
|
4131
|
+
return withTaskLock(`repo-chat:${projectId}`, async () => {
|
|
4132
|
+
const existing = (await Effect16.runPromise(store2.list())).filter((session2) => session2.projectId === projectId && session2.kind === "repo_chat").sort((a, b) => b.createdAt.localeCompare(a.createdAt))[0];
|
|
4133
|
+
if (existing && !reset) return existing;
|
|
4134
|
+
if (existing?.status === "running")
|
|
4135
|
+
throw new Error("Wait for the current reply before resetting chat.");
|
|
4136
|
+
const model = await defaultModel();
|
|
4137
|
+
if (!model) throw new Error("Choose an available default model in Settings before chatting.");
|
|
4138
|
+
const now = new Date(
|
|
4139
|
+
Math.max(Date.now(), existing ? Date.parse(existing.createdAt) + 1 : 0)
|
|
4140
|
+
).toISOString();
|
|
4141
|
+
const session = {
|
|
4142
|
+
id: randomUUID8(),
|
|
4143
|
+
projectId,
|
|
4144
|
+
kind: "repo_chat",
|
|
4145
|
+
title: workspacePath.split("/").at(-1) ?? projectId,
|
|
4146
|
+
status: "waiting_for_input",
|
|
4147
|
+
agent: "pi",
|
|
4148
|
+
model,
|
|
4149
|
+
branch: null,
|
|
4150
|
+
workspacePath,
|
|
4151
|
+
createdAt: now,
|
|
4152
|
+
updatedAt: now
|
|
4153
|
+
};
|
|
4154
|
+
await Effect16.runPromise(store2.append({ type: "session.updated", session }));
|
|
4155
|
+
return session;
|
|
4156
|
+
});
|
|
4157
|
+
}
|
|
4158
|
+
var init_repo_chat = __esm({
|
|
4159
|
+
"src/repo-chat.ts"() {
|
|
4160
|
+
"use strict";
|
|
4161
|
+
init_tasks();
|
|
4162
|
+
}
|
|
4163
|
+
});
|
|
4164
|
+
|
|
4100
4165
|
// src/index.ts
|
|
4101
4166
|
var index_exports = {};
|
|
4102
4167
|
import Fastify from "fastify";
|
|
@@ -4104,18 +4169,18 @@ import pretty from "pino-pretty";
|
|
|
4104
4169
|
import { styleText as styleText4 } from "node:util";
|
|
4105
4170
|
import cors from "@fastify/cors";
|
|
4106
4171
|
import multipart from "@fastify/multipart";
|
|
4107
|
-
import { randomUUID as
|
|
4172
|
+
import { randomUUID as randomUUID9 } from "node:crypto";
|
|
4108
4173
|
import { execFile as execFile3 } from "node:child_process";
|
|
4109
4174
|
import { promisify as promisify3 } from "node:util";
|
|
4110
4175
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
4111
4176
|
import { homedir as homedir4 } from "node:os";
|
|
4112
4177
|
import path11 from "node:path";
|
|
4113
4178
|
import { WebSocket } from "ws";
|
|
4114
|
-
import { Effect as
|
|
4179
|
+
import { Effect as Effect17, ManagedRuntime } from "effect";
|
|
4115
4180
|
import { z as z18 } from "zod";
|
|
4116
4181
|
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
|
|
4117
4182
|
function publish(event, workflowUpdate = false) {
|
|
4118
|
-
const previous = event.type === "session.updated" ?
|
|
4183
|
+
const previous = event.type === "session.updated" ? Effect17.runSync(store.get(event.session.id)) : void 0;
|
|
4119
4184
|
const emittedEvent = event.type === "session.updated" && previous && !workflowUpdate ? {
|
|
4120
4185
|
type: "session.updated",
|
|
4121
4186
|
session: {
|
|
@@ -4125,7 +4190,7 @@ function publish(event, workflowUpdate = false) {
|
|
|
4125
4190
|
}
|
|
4126
4191
|
} : event;
|
|
4127
4192
|
const notification = emittedEvent.type === "session.updated" ? push.pending(emittedEvent.session, previous) : void 0;
|
|
4128
|
-
const record =
|
|
4193
|
+
const record = Effect17.runSync(store.append(emittedEvent, notification));
|
|
4129
4194
|
const message = JSON.stringify({ type: "event", record });
|
|
4130
4195
|
for (const client of security.sockets.keys()) {
|
|
4131
4196
|
if (!(client instanceof WebSocket) || client.readyState !== WebSocket.OPEN) continue;
|
|
@@ -4165,8 +4230,8 @@ async function selectSessionModel(input, hasImages) {
|
|
|
4165
4230
|
}
|
|
4166
4231
|
}
|
|
4167
4232
|
async function resolveSessionProject(projectId) {
|
|
4168
|
-
const projectsRoot = await
|
|
4169
|
-
const project = (await
|
|
4233
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4234
|
+
const project = (await Effect17.runPromise(discoverProjects(projectsRoot, store))).find(
|
|
4170
4235
|
(item) => item.id === projectId && item.available
|
|
4171
4236
|
);
|
|
4172
4237
|
if (!project)
|
|
@@ -4208,7 +4273,7 @@ async function createAndStartSession(input) {
|
|
|
4208
4273
|
const projectPath = location.projectPath;
|
|
4209
4274
|
let files;
|
|
4210
4275
|
try {
|
|
4211
|
-
files = await
|
|
4276
|
+
files = await Effect17.runPromise(
|
|
4212
4277
|
attachments.resolve(input.projectId, input.attachmentIds ?? [])
|
|
4213
4278
|
);
|
|
4214
4279
|
} catch (error) {
|
|
@@ -4221,17 +4286,18 @@ async function createAndStartSession(input) {
|
|
|
4221
4286
|
if (selection.error !== void 0) return { error: selection.error, status: selection.status };
|
|
4222
4287
|
const selectedModel = selection.model;
|
|
4223
4288
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4224
|
-
const task = input.taskId ? await
|
|
4289
|
+
const task = input.taskId ? await Effect17.runPromise(store.getTask(input.taskId)) : void 0;
|
|
4225
4290
|
if (input.taskId && sessionTaskUnavailable(task, input.projectId))
|
|
4226
4291
|
return { error: "Task not found.", status: 404 };
|
|
4227
4292
|
const session = SessionSchema.parse({
|
|
4228
|
-
id:
|
|
4293
|
+
id: randomUUID9(),
|
|
4229
4294
|
taskId: input.taskId ?? null,
|
|
4230
4295
|
projectId: input.projectId,
|
|
4231
4296
|
title: task?.title ?? input.prompt.slice(0, 80),
|
|
4232
4297
|
status: "starting",
|
|
4233
4298
|
agent: input.agent,
|
|
4234
4299
|
model: selectedModel,
|
|
4300
|
+
usage: { tokens: 0, turns: 0, tools: 0 },
|
|
4235
4301
|
gitFlow: input.gitFlow,
|
|
4236
4302
|
branch: null,
|
|
4237
4303
|
createdAt: now,
|
|
@@ -4250,7 +4316,7 @@ async function createAndStartSession(input) {
|
|
|
4250
4316
|
);
|
|
4251
4317
|
} catch (error) {
|
|
4252
4318
|
if (task)
|
|
4253
|
-
await
|
|
4319
|
+
await Effect17.runPromise(
|
|
4254
4320
|
store.saveTask({ ...task, sessionId: session.id, updatedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4255
4321
|
);
|
|
4256
4322
|
return {
|
|
@@ -4259,7 +4325,7 @@ async function createAndStartSession(input) {
|
|
|
4259
4325
|
};
|
|
4260
4326
|
}
|
|
4261
4327
|
try {
|
|
4262
|
-
await
|
|
4328
|
+
await Effect17.runPromise(runtime.send(running, input.prompt, files));
|
|
4263
4329
|
} catch (error) {
|
|
4264
4330
|
publish({
|
|
4265
4331
|
type: "session.updated",
|
|
@@ -4268,18 +4334,18 @@ async function createAndStartSession(input) {
|
|
|
4268
4334
|
publish({ type: "error", sessionId: session.id, message: String(error) });
|
|
4269
4335
|
return { error: "Could not start the agent session.", status: 500 };
|
|
4270
4336
|
}
|
|
4271
|
-
return { session: await
|
|
4337
|
+
return { session: await Effect17.runPromise(store.get(session.id)), status: 201 };
|
|
4272
4338
|
}
|
|
4273
4339
|
function startTask(id, instructionsOverride, automated = false) {
|
|
4274
4340
|
return withTaskLock(
|
|
4275
4341
|
"agent-dispatch",
|
|
4276
4342
|
() => withTaskLock(id, async () => {
|
|
4277
4343
|
maintenance.assertReady();
|
|
4278
|
-
const task = await
|
|
4344
|
+
const task = await Effect17.runPromise(store.getTask(id));
|
|
4279
4345
|
if (!task || task.deletedAt) throw new TaskActionError("Task not found.", 404);
|
|
4280
4346
|
if (automated) {
|
|
4281
|
-
const state = await
|
|
4282
|
-
if (activeAgents(await
|
|
4347
|
+
const state = await Effect17.runPromise(store.automation());
|
|
4348
|
+
if (activeAgents(await Effect17.runPromise(store.list())) >= state.config.parallelism) return;
|
|
4283
4349
|
const backlog = await boardTasks(store);
|
|
4284
4350
|
if (!backlog.some((item) => item.id === id && item.status === "backlog")) return;
|
|
4285
4351
|
}
|
|
@@ -4287,7 +4353,7 @@ function startTask(id, instructionsOverride, automated = false) {
|
|
|
4287
4353
|
const prompt = instructions ? `${task.title}
|
|
4288
4354
|
|
|
4289
4355
|
${instructions}` : task.title;
|
|
4290
|
-
const linkedSession = task.sessionId ? await
|
|
4356
|
+
const linkedSession = task.sessionId ? await Effect17.runPromise(store.get(task.sessionId)) : void 0;
|
|
4291
4357
|
if (linkedSession && (["starting", "running", "queued"].includes(linkedSession.status) || linkedSession.gitDelivery?.status === "pending"))
|
|
4292
4358
|
throw new TaskActionError("This task already has a running agent.", 409);
|
|
4293
4359
|
const result = await createAndStartSession({
|
|
@@ -4301,7 +4367,7 @@ ${instructions}` : task.title;
|
|
|
4301
4367
|
});
|
|
4302
4368
|
if (!result.session)
|
|
4303
4369
|
throw new TaskActionError(result.error ?? "Could not start task.", result.status);
|
|
4304
|
-
const updatedTask = await
|
|
4370
|
+
const updatedTask = await Effect17.runPromise(
|
|
4305
4371
|
store.saveTask({
|
|
4306
4372
|
...task,
|
|
4307
4373
|
status: taskStatusForSession(result.session.status, result.session),
|
|
@@ -4314,7 +4380,7 @@ ${instructions}` : task.title;
|
|
|
4314
4380
|
);
|
|
4315
4381
|
}
|
|
4316
4382
|
async function withSessionTaskLock(id, operation) {
|
|
4317
|
-
const session = await
|
|
4383
|
+
const session = await Effect17.runPromise(store.get(id));
|
|
4318
4384
|
const lockId = session ? await sessionTaskLockId(store, session) : `session-${id}`;
|
|
4319
4385
|
return withTaskLock(lockId, operation);
|
|
4320
4386
|
}
|
|
@@ -4343,6 +4409,7 @@ var init_index = __esm({
|
|
|
4343
4409
|
init_store();
|
|
4344
4410
|
init_tasks();
|
|
4345
4411
|
init_worktree_cleanup();
|
|
4412
|
+
init_repo_chat();
|
|
4346
4413
|
host = process.env.AGENTRIFIC_HOST ?? "127.0.0.1";
|
|
4347
4414
|
port = Number(process.env.AGENTRIFIC_PORT ?? 4318);
|
|
4348
4415
|
defaultProjectsRoot = path11.resolve(process.env.AGENTRIFIC_PROJECTS_DIR ?? "./projects");
|
|
@@ -4374,7 +4441,7 @@ var init_index = __esm({
|
|
|
4374
4441
|
releaseLock = await acquireServerLock(dataRoot);
|
|
4375
4442
|
effects = ManagedRuntime.make(SessionStore.layer(path11.join(dataRoot, "sessions.sqlite")));
|
|
4376
4443
|
store = await effects.runPromise(SessionStore);
|
|
4377
|
-
security = new ServerSecurity(store, await
|
|
4444
|
+
security = new ServerSecurity(store, await Effect17.runPromise(store.serverIdentity()));
|
|
4378
4445
|
testPushUrl = process.env.AGENTRIFIC_PUSH_TEST_URL;
|
|
4379
4446
|
if (testPushUrl && (process.env.AGENTRIFIC_TEST_MODE !== "1" || !new URL(testPushUrl).hostname.endsWith(".ts.net") || new URL(testPushUrl).protocol !== "https:"))
|
|
4380
4447
|
throw new Error("Test relay requires explicit test mode and Tailscale HTTPS.");
|
|
@@ -4397,10 +4464,10 @@ var init_index = __esm({
|
|
|
4397
4464
|
Attachments.layer(path11.join(dataRoot, "attachments"), store)
|
|
4398
4465
|
);
|
|
4399
4466
|
attachments = await attachmentEffects.runPromise(Attachments);
|
|
4400
|
-
await
|
|
4467
|
+
await Effect17.runPromise(attachments.cleanup());
|
|
4401
4468
|
attachmentCleanup = setInterval(
|
|
4402
4469
|
() => {
|
|
4403
|
-
void
|
|
4470
|
+
void Effect17.runPromise(attachments.cleanup()).catch((error) => app.log.error(error));
|
|
4404
4471
|
},
|
|
4405
4472
|
60 * 60 * 1e3
|
|
4406
4473
|
);
|
|
@@ -4411,14 +4478,14 @@ var init_index = __esm({
|
|
|
4411
4478
|
app.post(
|
|
4412
4479
|
"/api/projects/:projectId/attachments",
|
|
4413
4480
|
async (request, reply) => {
|
|
4414
|
-
const root = await
|
|
4415
|
-
const projects = await
|
|
4481
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4482
|
+
const projects = await Effect17.runPromise(discoverProjects(root, store));
|
|
4416
4483
|
if (!projects.some((project) => project.id === request.params.projectId && project.available))
|
|
4417
4484
|
return reply.code(404).send({ error: "Project not found." });
|
|
4418
4485
|
try {
|
|
4419
4486
|
const file = await request.file();
|
|
4420
4487
|
if (!file) return reply.code(400).send({ error: "Choose a file to upload." });
|
|
4421
|
-
return reply.code(201).send(await
|
|
4488
|
+
return reply.code(201).send(await Effect17.runPromise(attachments.upload(request.params.projectId, file)));
|
|
4422
4489
|
} catch (error) {
|
|
4423
4490
|
return reply.code(400).send({ error: error instanceof Error ? error.message : "Upload failed." });
|
|
4424
4491
|
}
|
|
@@ -4427,7 +4494,7 @@ var init_index = __esm({
|
|
|
4427
4494
|
app.get(
|
|
4428
4495
|
"/api/projects/:projectId/attachments/:id/content",
|
|
4429
4496
|
async (request, reply) => {
|
|
4430
|
-
const attachment = await
|
|
4497
|
+
const attachment = await Effect17.runPromise(store.getAttachment(request.params.id));
|
|
4431
4498
|
if (!attachment || attachment.projectId !== request.params.projectId)
|
|
4432
4499
|
return reply.code(404).send({ error: "Attachment not found." });
|
|
4433
4500
|
const preview = request.query.preview === "1" && attachment.kind === "image";
|
|
@@ -4445,8 +4512,8 @@ var init_index = __esm({
|
|
|
4445
4512
|
publish: (event) => publish(event, true),
|
|
4446
4513
|
draft: deliveryWriter(models),
|
|
4447
4514
|
projectPath: async (session) => {
|
|
4448
|
-
const projectsRoot = await
|
|
4449
|
-
const project = (await
|
|
4515
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4516
|
+
const project = (await Effect17.runPromise(discoverProjects(projectsRoot, store))).find(
|
|
4450
4517
|
(item) => item.id === session.projectId && item.available
|
|
4451
4518
|
);
|
|
4452
4519
|
if (!project) throw new Error("Reconnect the project folder before delivering changes.");
|
|
@@ -4460,7 +4527,7 @@ var init_index = __esm({
|
|
|
4460
4527
|
publish,
|
|
4461
4528
|
worktreesRoot,
|
|
4462
4529
|
path11.join(dataRoot, "pi"),
|
|
4463
|
-
(sessionId, messageId) =>
|
|
4530
|
+
(sessionId, messageId) => Effect17.runSync(store.hasMessage(sessionId, messageId)),
|
|
4464
4531
|
(session) => delivery.settled(session.id)
|
|
4465
4532
|
)
|
|
4466
4533
|
);
|
|
@@ -4495,7 +4562,7 @@ var init_index = __esm({
|
|
|
4495
4562
|
() => maintenance.ready(),
|
|
4496
4563
|
(error) => app.log.error(error)
|
|
4497
4564
|
);
|
|
4498
|
-
for (const session of await
|
|
4565
|
+
for (const session of await Effect17.runPromise(store.list())) {
|
|
4499
4566
|
if (session.gitDelivery?.status === "pending") {
|
|
4500
4567
|
publish(
|
|
4501
4568
|
{
|
|
@@ -4525,7 +4592,7 @@ var init_index = __esm({
|
|
|
4525
4592
|
await delivery.recover();
|
|
4526
4593
|
app.get("/api/connections", async () => githubCliStatus());
|
|
4527
4594
|
app.get("/api/settings", async () => ({
|
|
4528
|
-
projectsRoot: await
|
|
4595
|
+
projectsRoot: await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot)),
|
|
4529
4596
|
defaultModel: await models.defaultIdentity()
|
|
4530
4597
|
}));
|
|
4531
4598
|
app.get("/api/models", async (_request, reply) => {
|
|
@@ -4553,7 +4620,7 @@ var init_index = __esm({
|
|
|
4553
4620
|
try {
|
|
4554
4621
|
const defaultModel = await models.setDefault(parsed.data);
|
|
4555
4622
|
return {
|
|
4556
|
-
projectsRoot: await
|
|
4623
|
+
projectsRoot: await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot)),
|
|
4557
4624
|
defaultModel
|
|
4558
4625
|
};
|
|
4559
4626
|
} catch {
|
|
@@ -4566,13 +4633,13 @@ var init_index = __esm({
|
|
|
4566
4633
|
"/api/sessions/:id/model",
|
|
4567
4634
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4568
4635
|
const parsed = ModelReferenceSchema.safeParse(request.body);
|
|
4569
|
-
const session = await
|
|
4636
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4570
4637
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4571
4638
|
if (!parsed.success)
|
|
4572
4639
|
return reply.code(400).send({ error: "Choose a model from the server catalogue." });
|
|
4573
4640
|
try {
|
|
4574
4641
|
await models.resolve(parsed.data);
|
|
4575
|
-
return await
|
|
4642
|
+
return await Effect17.runPromise(runtime.setModel(session, parsed.data));
|
|
4576
4643
|
} catch {
|
|
4577
4644
|
return reply.code(409).send({
|
|
4578
4645
|
error: "Could not change the model. Wait for the current reply to finish and choose an available model."
|
|
@@ -4583,21 +4650,21 @@ var init_index = __esm({
|
|
|
4583
4650
|
app.post("/api/settings", async (request, reply) => {
|
|
4584
4651
|
const parsed = UpdateSettingsSchema.safeParse(request.body);
|
|
4585
4652
|
if (!parsed.success) return reply.code(400).send({ error: "Enter a projects folder." });
|
|
4586
|
-
const result = await
|
|
4587
|
-
resolveProjectsRoot(parsed.data.projectsRoot).pipe(
|
|
4653
|
+
const result = await Effect17.runPromise(
|
|
4654
|
+
resolveProjectsRoot(parsed.data.projectsRoot).pipe(Effect17.result)
|
|
4588
4655
|
);
|
|
4589
4656
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4590
|
-
return
|
|
4657
|
+
return Effect17.runPromise(store.setProjectsRoot(result.success));
|
|
4591
4658
|
});
|
|
4592
4659
|
app.get("/api/projects", async (_request, reply) => {
|
|
4593
|
-
const root = await
|
|
4594
|
-
const result = await
|
|
4660
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4661
|
+
const result = await Effect17.runPromise(discoverProjects(root, store).pipe(Effect17.result));
|
|
4595
4662
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4596
4663
|
return result.success;
|
|
4597
4664
|
});
|
|
4598
4665
|
app.get("/api/projects/:id", async (request, reply) => {
|
|
4599
|
-
const root = await
|
|
4600
|
-
const result = await
|
|
4666
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4667
|
+
const result = await Effect17.runPromise(discoverProjects(root, store).pipe(Effect17.result));
|
|
4601
4668
|
if (result._tag === "Failure") return reply.code(400).send({ error: result.failure.message });
|
|
4602
4669
|
const project = result.success.find((item) => item.id === request.params.id);
|
|
4603
4670
|
return project ?? reply.code(404).send({ error: "Project not found on this server." });
|
|
@@ -4606,13 +4673,13 @@ var init_index = __esm({
|
|
|
4606
4673
|
const parsed = z18.object({ path: z18.string().trim().min(1).max(4096) }).safeParse(request.body);
|
|
4607
4674
|
if (!parsed.success)
|
|
4608
4675
|
return reply.code(400).send({ error: "Enter the project's new folder path." });
|
|
4609
|
-
const root = await
|
|
4610
|
-
const result = await
|
|
4611
|
-
reconnectProject(root, request.params.id, parsed.data.path, store).pipe(
|
|
4676
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4677
|
+
const result = await Effect17.runPromise(
|
|
4678
|
+
reconnectProject(root, request.params.id, parsed.data.path, store).pipe(Effect17.result)
|
|
4612
4679
|
);
|
|
4613
4680
|
return result._tag === "Failure" ? reply.code(400).send({ error: result.failure.message }) : result.success;
|
|
4614
4681
|
});
|
|
4615
|
-
app.get("/api/sessions", () =>
|
|
4682
|
+
app.get("/api/sessions", () => Effect17.runPromise(store.list()));
|
|
4616
4683
|
app.get(
|
|
4617
4684
|
"/api/tasks",
|
|
4618
4685
|
(request) => boardTasks(store, request.query.projectId)
|
|
@@ -4625,15 +4692,15 @@ var init_index = __esm({
|
|
|
4625
4692
|
if (!github.githubCli.authenticated)
|
|
4626
4693
|
return reply.code(400).send({ error: "GitHub CLI must be authenticated on the server to open a PR." });
|
|
4627
4694
|
}
|
|
4628
|
-
const projectsRoot = await
|
|
4629
|
-
const projects = await
|
|
4630
|
-
discoverProjects(projectsRoot, store).pipe(
|
|
4695
|
+
const projectsRoot = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4696
|
+
const projects = await Effect17.runPromise(
|
|
4697
|
+
discoverProjects(projectsRoot, store).pipe(Effect17.result)
|
|
4631
4698
|
);
|
|
4632
4699
|
if (projects._tag === "Failure" || !projects.success.some((project) => project.id === parsed.data.projectId && project.available))
|
|
4633
4700
|
return reply.code(404).send({ error: "Project not found in the server's projects folder." });
|
|
4634
4701
|
let files;
|
|
4635
4702
|
try {
|
|
4636
|
-
files = await
|
|
4703
|
+
files = await Effect17.runPromise(
|
|
4637
4704
|
attachments.resolve(parsed.data.projectId, parsed.data.attachmentIds ?? [])
|
|
4638
4705
|
);
|
|
4639
4706
|
} catch (error) {
|
|
@@ -4641,7 +4708,7 @@ var init_index = __esm({
|
|
|
4641
4708
|
}
|
|
4642
4709
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4643
4710
|
const task = TaskSchema.parse({
|
|
4644
|
-
id:
|
|
4711
|
+
id: randomUUID9(),
|
|
4645
4712
|
...parsed.data,
|
|
4646
4713
|
attachments: files,
|
|
4647
4714
|
status: "backlog",
|
|
@@ -4649,7 +4716,7 @@ var init_index = __esm({
|
|
|
4649
4716
|
createdAt: now,
|
|
4650
4717
|
updatedAt: now
|
|
4651
4718
|
});
|
|
4652
|
-
return reply.code(201).send(await
|
|
4719
|
+
return reply.code(201).send(await Effect17.runPromise(store.saveTask(task)));
|
|
4653
4720
|
});
|
|
4654
4721
|
app.patch("/api/tasks/:id", async (request, reply) => {
|
|
4655
4722
|
const parsed = UpdateTaskSchema.safeParse(request.body);
|
|
@@ -4664,8 +4731,8 @@ var init_index = __esm({
|
|
|
4664
4731
|
return reply.code(400).send({ error: "GitHub CLI must be authenticated on the server to open a PR." });
|
|
4665
4732
|
}
|
|
4666
4733
|
return await editTask(store, request.params.id, parsed.data, async (projectId, ids) => {
|
|
4667
|
-
const result = await
|
|
4668
|
-
attachments.resolve(projectId, ids).pipe(
|
|
4734
|
+
const result = await Effect17.runPromise(
|
|
4735
|
+
attachments.resolve(projectId, ids).pipe(Effect17.result)
|
|
4669
4736
|
);
|
|
4670
4737
|
if (result._tag === "Failure") throw new TaskActionError(result.failure.message, 400);
|
|
4671
4738
|
return result.success;
|
|
@@ -4679,11 +4746,11 @@ var init_index = __esm({
|
|
|
4679
4746
|
app.delete("/api/tasks/:id", async (request, reply) => {
|
|
4680
4747
|
try {
|
|
4681
4748
|
await deleteTask(store, request.params.id, async (session) => {
|
|
4682
|
-
await
|
|
4749
|
+
await Effect17.runPromise(runtime.forget(session.id));
|
|
4683
4750
|
await worktreeCleanup.remove(session);
|
|
4684
4751
|
await removeRunFiles(dataRoot, session.id);
|
|
4685
4752
|
});
|
|
4686
|
-
await
|
|
4753
|
+
await Effect17.runPromise(attachments.cleanup());
|
|
4687
4754
|
return reply.code(204).send();
|
|
4688
4755
|
} catch (error) {
|
|
4689
4756
|
if (error instanceof TaskActionError)
|
|
@@ -4692,14 +4759,14 @@ var init_index = __esm({
|
|
|
4692
4759
|
}
|
|
4693
4760
|
});
|
|
4694
4761
|
app.get("/api/sessions/:id", async (request, reply) => {
|
|
4695
|
-
const session = await
|
|
4762
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4696
4763
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4697
4764
|
return session;
|
|
4698
4765
|
});
|
|
4699
4766
|
app.post(
|
|
4700
4767
|
"/api/sessions/:id/delivery",
|
|
4701
4768
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4702
|
-
const session = await
|
|
4769
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4703
4770
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4704
4771
|
try {
|
|
4705
4772
|
const result = await delivery.retry(session.id);
|
|
@@ -4716,10 +4783,10 @@ var init_index = __esm({
|
|
|
4716
4783
|
try {
|
|
4717
4784
|
const prepared = await preparation.retry(request.params.id);
|
|
4718
4785
|
preparedSession = prepared.session;
|
|
4719
|
-
const files = await
|
|
4786
|
+
const files = await Effect17.runPromise(
|
|
4720
4787
|
attachments.resolve(prepared.session.projectId, prepared.input.attachmentIds ?? [])
|
|
4721
4788
|
);
|
|
4722
|
-
await
|
|
4789
|
+
await Effect17.runPromise(runtime.send(prepared.session, prepared.input.prompt, files));
|
|
4723
4790
|
return reply.code(202).send(prepared.session);
|
|
4724
4791
|
} catch (error) {
|
|
4725
4792
|
if (preparedSession) {
|
|
@@ -4742,9 +4809,28 @@ var init_index = __esm({
|
|
|
4742
4809
|
app.get("/api/sessions/:id/events", async (request, reply) => {
|
|
4743
4810
|
const query = HistoryQuery.safeParse(request.query);
|
|
4744
4811
|
if (!query.success) return reply.code(400).send({ error: "Invalid history cursor or limit." });
|
|
4745
|
-
const session = await
|
|
4812
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4746
4813
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4747
|
-
return
|
|
4814
|
+
return Effect17.runPromise(store.history(session.id, query.data.after, query.data.limit));
|
|
4815
|
+
});
|
|
4816
|
+
app.post("/api/projects/:id/chat", async (request, reply) => {
|
|
4817
|
+
maintenance.assertReady();
|
|
4818
|
+
const location = await resolveSessionProject(request.params.id);
|
|
4819
|
+
if (location.error !== void 0)
|
|
4820
|
+
return reply.code(location.status).send({ error: location.error });
|
|
4821
|
+
const parsed = z18.object({ reset: z18.boolean().optional() }).safeParse(request.body ?? {});
|
|
4822
|
+
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
4823
|
+
try {
|
|
4824
|
+
return await openRepoChat(
|
|
4825
|
+
store,
|
|
4826
|
+
request.params.id,
|
|
4827
|
+
location.projectPath,
|
|
4828
|
+
() => models.defaultIdentity(),
|
|
4829
|
+
parsed.data.reset
|
|
4830
|
+
);
|
|
4831
|
+
} catch (error) {
|
|
4832
|
+
return reply.code(409).send({ error: String(error) });
|
|
4833
|
+
}
|
|
4748
4834
|
});
|
|
4749
4835
|
app.post("/api/sessions", async (request, reply) => {
|
|
4750
4836
|
const parsed = CreateSessionSchema.safeParse(request.body);
|
|
@@ -4780,8 +4866,8 @@ var init_index = __esm({
|
|
|
4780
4866
|
const parsed = AutomationConfigSchema.safeParse(request.body);
|
|
4781
4867
|
if (!parsed.success)
|
|
4782
4868
|
return reply.code(400).send({ error: parsed.error.issues.map((issue) => issue.message).join(" ") });
|
|
4783
|
-
const root = await
|
|
4784
|
-
const projects = await
|
|
4869
|
+
const root = await Effect17.runPromise(store.projectsRoot(defaultProjectsRoot));
|
|
4870
|
+
const projects = await Effect17.runPromise(discoverProjects(root, store));
|
|
4785
4871
|
if (parsed.data.projectIds.some(
|
|
4786
4872
|
(id) => !projects.some((project) => project.id === id && project.available)
|
|
4787
4873
|
))
|
|
@@ -4809,7 +4895,7 @@ var init_index = __esm({
|
|
|
4809
4895
|
app.post(
|
|
4810
4896
|
"/api/sessions/:id/messages",
|
|
4811
4897
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4812
|
-
const session = await
|
|
4898
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4813
4899
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4814
4900
|
const parsed = SendMessageSchema.safeParse(request.body);
|
|
4815
4901
|
if (!parsed.success) return reply.code(400).send({ error: parsed.error.flatten() });
|
|
@@ -4817,10 +4903,10 @@ var init_index = __esm({
|
|
|
4817
4903
|
await delivery.assertCanWork(session.id);
|
|
4818
4904
|
if (session.preparation && session.preparation.status !== "ready")
|
|
4819
4905
|
throw new Error("Retry workspace preparation before sending a message.");
|
|
4820
|
-
const files = await
|
|
4906
|
+
const files = await Effect17.runPromise(
|
|
4821
4907
|
attachments.resolve(session.projectId, parsed.data.attachmentIds ?? [])
|
|
4822
4908
|
);
|
|
4823
|
-
await
|
|
4909
|
+
await Effect17.runPromise(
|
|
4824
4910
|
runtime.send(
|
|
4825
4911
|
session,
|
|
4826
4912
|
parsed.data.message,
|
|
@@ -4838,15 +4924,15 @@ var init_index = __esm({
|
|
|
4838
4924
|
app.post(
|
|
4839
4925
|
"/api/sessions/:id/stop",
|
|
4840
4926
|
async (request, reply) => withSessionTaskLock(request.params.id, async () => {
|
|
4841
|
-
const session = await
|
|
4927
|
+
const session = await Effect17.runPromise(store.get(request.params.id));
|
|
4842
4928
|
if (!session) return reply.code(404).send({ error: "Session not found." });
|
|
4843
|
-
await
|
|
4929
|
+
await Effect17.runPromise(runtime.stop(session));
|
|
4844
4930
|
return reply.code(202).send({ accepted: true });
|
|
4845
4931
|
})
|
|
4846
4932
|
);
|
|
4847
4933
|
attachEventServer(app, security, allowedOrigins, () => ({
|
|
4848
4934
|
type: "snapshot",
|
|
4849
|
-
sessions:
|
|
4935
|
+
sessions: Effect17.runSync(store.list())
|
|
4850
4936
|
}));
|
|
4851
4937
|
closeControl = await startControl(
|
|
4852
4938
|
dataRoot,
|
|
@@ -4858,7 +4944,7 @@ var init_index = __esm({
|
|
|
4858
4944
|
connectedDevices: new Set(security.sockets.values()).size,
|
|
4859
4945
|
connectedSockets: security.sockets.size,
|
|
4860
4946
|
memory: process.memoryUsage().rss,
|
|
4861
|
-
activeRuns: activeAgents(
|
|
4947
|
+
activeRuns: activeAgents(Effect17.runSync(store.list())),
|
|
4862
4948
|
maintenance: maintenance.status()
|
|
4863
4949
|
}),
|
|
4864
4950
|
maintenance
|
|
@@ -4876,7 +4962,7 @@ var init_index = __esm({
|
|
|
4876
4962
|
clearInterval(automationTimer);
|
|
4877
4963
|
await withTaskLock("automation", async () => {
|
|
4878
4964
|
});
|
|
4879
|
-
await
|
|
4965
|
+
await Effect17.runPromise(runtime.close());
|
|
4880
4966
|
await agentEffects.dispose();
|
|
4881
4967
|
await attachmentEffects.dispose();
|
|
4882
4968
|
await effects.dispose();
|