@opencode/server 0.0.0-dev-19795 → 0.0.0-dev-19812

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 (101) hide show
  1. package/dist/api.js +10 -9
  2. package/dist/auth.js +12 -17
  3. package/dist/chunks/process-172x6jgf.js +139 -0
  4. package/dist/chunks/process-18k6g42r.js +36 -0
  5. package/dist/chunks/process-1crq7hen.js +13 -0
  6. package/dist/chunks/process-27edjygf.js +33 -0
  7. package/dist/chunks/process-3esm6t0a.js +156 -0
  8. package/dist/chunks/process-3ra48t04.js +26 -0
  9. package/dist/chunks/process-8j6eh3dh.js +55 -0
  10. package/dist/chunks/process-910kejdj.js +37 -0
  11. package/dist/chunks/process-9rqn6x4v.js +16 -0
  12. package/dist/chunks/process-b0dj656z.js +13 -0
  13. package/dist/chunks/process-b4s3wxap.js +23 -0
  14. package/dist/chunks/process-c23ng5tp.js +17 -0
  15. package/dist/chunks/process-d0efjmp3.js +83 -0
  16. package/dist/chunks/process-d5nhwenp.js +22 -0
  17. package/dist/chunks/process-dhcrxjg3.js +163 -0
  18. package/dist/chunks/process-fa3dg36p.js +15 -0
  19. package/dist/chunks/process-gw2hwgqh.js +20 -0
  20. package/dist/chunks/process-h965rd83.js +25 -0
  21. package/dist/chunks/process-hezj1jdf.js +7 -0
  22. package/dist/chunks/process-j26nh0zz.js +40 -0
  23. package/dist/chunks/process-ja6yvede.js +22 -0
  24. package/dist/chunks/process-k319q0y1.js +39 -0
  25. package/dist/chunks/process-k8tgqrb7.js +61 -0
  26. package/dist/chunks/process-kadtbwy3.js +77 -0
  27. package/dist/chunks/process-kb9735wh.js +26 -0
  28. package/dist/chunks/process-kgbwhj3q.js +149 -0
  29. package/dist/chunks/process-kheky2r0.js +35 -0
  30. package/dist/chunks/process-knez9aqv.js +13 -0
  31. package/dist/chunks/process-ks9pc2xc.js +19 -0
  32. package/dist/chunks/process-m5dqr0xs.js +21 -0
  33. package/dist/chunks/process-m6wvgbfq.js +60 -0
  34. package/dist/chunks/process-m8y6333k.js +346 -0
  35. package/dist/chunks/process-mx2h5aqn.js +59 -0
  36. package/dist/chunks/process-prrj1pgd.js +42 -0
  37. package/dist/chunks/process-q4bn6dy8.js +39 -0
  38. package/dist/chunks/process-qc2xtz00.js +34 -0
  39. package/dist/chunks/process-qswqf30g.js +25 -0
  40. package/dist/chunks/process-rkdaz036.js +32 -0
  41. package/dist/chunks/process-smaf4a18.js +28 -0
  42. package/dist/chunks/process-t7k3q1ah.js +23 -0
  43. package/dist/chunks/process-t8yzmczr.js +18 -0
  44. package/dist/chunks/process-tp6dn9dc.js +99 -0
  45. package/dist/chunks/process-w0mvbrhx.js +22 -0
  46. package/dist/chunks/process-wt4btb09.js +13 -0
  47. package/dist/chunks/process-xettb16v.js +22 -0
  48. package/dist/chunks/process-xn1y01ws.js +29 -0
  49. package/dist/chunks/process-xsrtpxyd.js +23 -0
  50. package/dist/chunks/process-yc7nft6q.js +5 -0
  51. package/dist/chunks/process-zyv89x5n.js +30 -0
  52. package/dist/cors.js +10 -36
  53. package/dist/event-feed.js +20 -60
  54. package/dist/fetch.js +53 -11
  55. package/dist/handlers/agent.js +11 -17
  56. package/dist/handlers/command.js +11 -5
  57. package/dist/handlers/config.js +11 -16
  58. package/dist/handlers/credential.js +11 -14
  59. package/dist/handlers/debug.js +11 -11
  60. package/dist/handlers/event.js +12 -27
  61. package/dist/handlers/form.js +11 -9
  62. package/dist/handlers/fs.js +11 -22
  63. package/dist/handlers/generate.js +11 -15
  64. package/dist/handlers/integration.js +10 -142
  65. package/dist/handlers/location.js +11 -16
  66. package/dist/handlers/mcp.js +11 -31
  67. package/dist/handlers/message.js +10 -42
  68. package/dist/handlers/migration.js +11 -7
  69. package/dist/handlers/model.js +11 -14
  70. package/dist/handlers/permission.js +11 -57
  71. package/dist/handlers/persistent-pty.js +12 -135
  72. package/dist/handlers/plugin.js +11 -47
  73. package/dist/handlers/project.js +11 -9
  74. package/dist/handlers/provider.js +11 -20
  75. package/dist/handlers/pty-socket.js +6 -4
  76. package/dist/handlers/pty.js +13 -107
  77. package/dist/handlers/reference.js +11 -5
  78. package/dist/handlers/rpc.js +11 -15
  79. package/dist/handlers/server.js +12 -13
  80. package/dist/handlers/session-error.js +12 -26
  81. package/dist/handlers/session.js +10 -311
  82. package/dist/handlers/shell.js +11 -25
  83. package/dist/handlers/skill.js +11 -5
  84. package/dist/handlers/vcs.js +11 -19
  85. package/dist/handlers/websearch.js +11 -31
  86. package/dist/handlers/worktree.js +11 -37
  87. package/dist/handlers.js +46 -33
  88. package/dist/location.js +15 -49
  89. package/dist/middleware/authorization.js +11 -49
  90. package/dist/middleware/form-location.js +10 -25
  91. package/dist/middleware/schema-error.js +8 -15
  92. package/dist/middleware/session-location.js +10 -19
  93. package/dist/options.js +5 -1
  94. package/dist/process.js +108 -28
  95. package/dist/pty-environment.js +12 -9
  96. package/dist/request-tracing.js +6 -2
  97. package/dist/routes.js +52 -118
  98. package/dist/server-info.js +12 -22
  99. package/dist/service-status.js +8 -11
  100. package/dist/workerd.js +80 -12
  101. package/package.json +8 -8
@@ -0,0 +1,149 @@
1
+ import {
2
+ layer2
3
+ } from "./process-8j6eh3dh.js";
4
+ import {
5
+ formLocationLayer2
6
+ } from "./process-27edjygf.js";
7
+ import {
8
+ sessionLocationLayer2
9
+ } from "./process-kb9735wh.js";
10
+ import {
11
+ Api2
12
+ } from "./process-ks9pc2xc.js";
13
+ import {
14
+ Config2
15
+ } from "./process-zyv89x5n.js";
16
+ import {
17
+ CorsConfig2
18
+ } from "./process-q4bn6dy8.js";
19
+ import {
20
+ layer5
21
+ } from "./process-18k6g42r.js";
22
+ import {
23
+ node2
24
+ } from "./process-t7k3q1ah.js";
25
+ import {
26
+ handlers2
27
+ } from "./process-tp6dn9dc.js";
28
+ import {
29
+ authorizationLayer2
30
+ } from "./process-m6wvgbfq.js";
31
+ import {
32
+ schemaErrorLayer2
33
+ } from "./process-t8yzmczr.js";
34
+
35
+ // src/routes.ts
36
+ import { Database } from "@opencode/core/database/database";
37
+ import { V1Migration } from "@opencode/core/database/v1-migration";
38
+ import { App } from "@opencode/core/app";
39
+ import { LayerNode } from "@opencode/util/effect/layer-node";
40
+ import { httpClient } from "@opencode/util/effect/app-node-platform";
41
+ import { AppNodeBuilder } from "@opencode/core/effect/app-node-builder";
42
+ import { Bus } from "@opencode/core/bus";
43
+ import { EventLogger } from "@opencode/core/event-logger";
44
+ import { FileSystemSearch } from "@opencode/core/filesystem/search";
45
+ import { Credential } from "@opencode/core/credential";
46
+ import { Config } from "@opencode/core/config";
47
+ import { PermissionSaved } from "@opencode/core/permission/saved";
48
+ import { PtyTicket } from "@opencode/core/pty/ticket";
49
+ import { PersistentPty } from "@opencode/core/persistent-pty";
50
+ import { Project } from "@opencode/core/project";
51
+ import { Worktree } from "@opencode/core/worktree";
52
+ import { Session } from "@opencode/core/session";
53
+ import { Instance } from "@opencode/core/instance/service";
54
+ import { SessionTransfer } from "@opencode/core/session/transfer";
55
+ import { ShellSelect } from "@opencode/core/shell/select";
56
+ import { Job } from "@opencode/core/job";
57
+ import { Mcp } from "@opencode/core/mcp/index";
58
+ import { Global } from "@opencode/util/global";
59
+ import { InstructionDiscovery } from "@opencode/core/instruction-discovery";
60
+ import { LocationServiceMap } from "@opencode/core/location-service-map";
61
+ import { LocationActivity } from "@opencode/core/location-activity";
62
+ import { ModelsDev } from "@opencode/core/models-dev";
63
+ import { SessionRestart } from "@opencode/core/session/execution/restart";
64
+ import { PluginUpdate } from "@opencode/core/plugin/update";
65
+ import { SdkPlugins } from "@opencode/core/plugin/sdk";
66
+ import { WellKnown } from "@opencode/core/wellknown";
67
+ import { Workspace } from "@opencode/core/workspace";
68
+ import { Watcher } from "@opencode/core/filesystem/watcher";
69
+ import { HttpRouter } from "effect/unstable/http";
70
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
71
+ import { Context, Effect, Layer, Option } from "effect";
72
+ var applicationServiceNodes = [
73
+ Global.node,
74
+ Database.node,
75
+ Bus.node,
76
+ EventLogger.node,
77
+ httpClient,
78
+ Job.node,
79
+ Project.node,
80
+ Worktree.node,
81
+ Session.node,
82
+ Instance.node,
83
+ SessionTransfer.node,
84
+ SdkPlugins.node,
85
+ PluginUpdate.node,
86
+ PermissionSaved.node,
87
+ PtyTicket.node,
88
+ PersistentPty.node,
89
+ Credential.node,
90
+ WellKnown.node,
91
+ node2,
92
+ LocationServiceMap.node,
93
+ LocationActivity.node,
94
+ SessionRestart.node,
95
+ Workspace.node
96
+ ];
97
+ var applicationServices = LayerNode.group(applicationServiceNodes);
98
+ function createRoutes2(options = {}, serviceURLs = () => [], overrides = []) {
99
+ return makeRoutes(options.password ? Config2.configLayer({ password: Option.some(options.password) }) : Config2.layer, options, serviceURLs, overrides);
100
+ }
101
+ function createEmbeddedRoutes2(options = {}, overrides = [], instances) {
102
+ return makeRoutes(Config2.configLayer({ password: Option.none() }), options, () => [], overrides, instances);
103
+ }
104
+ function makeRoutes(auth, options, serviceURLs, overrides, instances) {
105
+ const standard = [
106
+ Database.node.replace(Database.configured(options.database)),
107
+ PersistentPty.node.replace(PersistentPty.configured(options.pty)),
108
+ Bus.node.replace(Bus.configured({ persist: options.events?.persist })),
109
+ App.node.replace(App.configured(options.app)),
110
+ ModelsDev.node.replace(ModelsDev.configured(options.models)),
111
+ Watcher.node.replace(Watcher.configured({ enabled: options.fs?.filewatcher })),
112
+ FileSystemSearch.node.replace(FileSystemSearch.configured({ fff: options.fs?.fff })),
113
+ Global.node.replace(Global.layerWith(options.config?.directory ? { config: options.config.directory } : {})),
114
+ Config.node.replace(Config.configured({
115
+ project: options.config?.project,
116
+ file: options.config?.file,
117
+ content: options.config?.content
118
+ })),
119
+ InstructionDiscovery.node.replace(InstructionDiscovery.configured({ project: options.config?.project })),
120
+ ShellSelect.node.replace(ShellSelect.configured({ gitbash: options.windows?.gitbash })),
121
+ Mcp.node.replace(Mcp.configured({
122
+ clientInfo: {
123
+ name: options.app?.name ?? "opencode",
124
+ version: options.app?.version ?? "unknown"
125
+ }
126
+ }))
127
+ ];
128
+ const build = (overrides) => {
129
+ const replacements = [
130
+ ...standard,
131
+ ...instances ? [Instance.node.replace(instances(() => replacements))] : [],
132
+ ...overrides
133
+ ];
134
+ return AppNodeBuilder.build(applicationServices, replacements);
135
+ };
136
+ const serviceLayer = options.simulation ? Layer.unwrap(Effect.gen(function* () {
137
+ const { simulationReplacements } = yield* Effect.promise(() => import("@opencode/simulation/backend"));
138
+ const simulation = yield* simulationReplacements({ version: App.make(options.app).version });
139
+ return build([...overrides, ...simulation]);
140
+ })) : build(overrides);
141
+ return serviceLayer.pipe(Layer.flatMap((context) => {
142
+ const services = Layer.succeedContext(context);
143
+ const requestServices = Layer.merge(Layer.succeedContext(Context.pick(Database.Service, Credential.Service, PermissionSaved.Service, PluginUpdate.Service, Project.Service, WellKnown.Service)(context)), layer5(serviceURLs, Context.get(context, Global.Service).tmp, options.app));
144
+ const api = HttpApiBuilder.layer(Api2, { openapiPath: "/openapi.json" }).pipe(Layer.provide(handlers2.pipe(Layer.provide(services), Layer.provide(Layer.succeed(CorsConfig2, options)))), Layer.provide(formLocationLayer2), Layer.provide(sessionLocationLayer2), Layer.provide(layer2), Layer.provide(authorizationLayer2), Layer.provide(schemaErrorLayer2), Layer.provide(auth), HttpRouter.provideRequest(requestServices), Layer.provideMerge(services), Layer.provideMerge(HttpRouter.layer));
145
+ return Layer.merge(api, V1Migration.layer.pipe(Layer.provide(services)));
146
+ }));
147
+ }
148
+
149
+ export { createRoutes2, createEmbeddedRoutes2 };
@@ -0,0 +1,35 @@
1
+ import {
2
+ response2
3
+ } from "./process-8j6eh3dh.js";
4
+ import {
5
+ Api2
6
+ } from "./process-ks9pc2xc.js";
7
+
8
+ // src/handlers/shell.ts
9
+ import { Shell } from "@opencode/core/shell";
10
+ import { Location } from "@opencode/core/location";
11
+ import { Effect } from "effect";
12
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
13
+ import { ShellNotFoundError } from "@opencode/protocol/errors";
14
+ var ShellHandler2 = HttpApiBuilder.group(Api2, "server.shell", (handlers) => Effect.gen(function* () {
15
+ return handlers.handle("shell.list", Effect.fn(function* () {
16
+ const shell = yield* Shell.Service;
17
+ return yield* response2(shell.list());
18
+ })).handle("shell.create", Effect.fn(function* (ctx) {
19
+ const shell = yield* Shell.Service;
20
+ const location = yield* Location.Service;
21
+ return yield* response2(shell.create({ ...ctx.payload, cwd: ctx.payload.cwd || location.directory }).pipe(Effect.orDie));
22
+ })).handle("shell.get", Effect.fn(function* (ctx) {
23
+ const shell = yield* Shell.Service;
24
+ return yield* response2(shell.get(ctx.params.id).pipe(Effect.catchTag("Shell.NotFoundError", () => new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }))));
25
+ })).handle("shell.output", Effect.fn(function* (ctx) {
26
+ const shell = yield* Shell.Service;
27
+ return yield* response2(shell.output(ctx.params.id, { cursor: ctx.query.cursor, limit: ctx.query.limit }).pipe(Effect.catchTag("Shell.NotFoundError", () => new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }))));
28
+ })).handle("shell.remove", Effect.fn(function* (ctx) {
29
+ const shell = yield* Shell.Service;
30
+ yield* shell.remove(ctx.params.id).pipe(Effect.catchTag("Shell.NotFoundError", () => Effect.void));
31
+ return HttpApiSchema.NoContent.make();
32
+ }));
33
+ }));
34
+
35
+ export { ShellHandler2 };
@@ -0,0 +1,13 @@
1
+ import {
2
+ Api2
3
+ } from "./process-ks9pc2xc.js";
4
+
5
+ // src/handlers/migration.ts
6
+ import { V1Migration } from "@opencode/core/database/v1-migration";
7
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
8
+ import { Effect } from "effect";
9
+ var MigrationHandler2 = HttpApiBuilder.group(Api2, "server.migration", (handlers) => handlers.handle("migration.v1.status", Effect.fn(function* () {
10
+ return yield* V1Migration.status();
11
+ })));
12
+
13
+ export { MigrationHandler2 };
@@ -0,0 +1,19 @@
1
+ import {
2
+ LocationMiddleware2
3
+ } from "./process-8j6eh3dh.js";
4
+ import {
5
+ FormLocationMiddleware2
6
+ } from "./process-27edjygf.js";
7
+ import {
8
+ SessionLocationMiddleware2
9
+ } from "./process-kb9735wh.js";
10
+
11
+ // src/api.ts
12
+ import { makeDefaultApi } from "@opencode/protocol/api";
13
+ var Api2 = makeDefaultApi({
14
+ locationMiddleware: LocationMiddleware2,
15
+ formLocationMiddleware: FormLocationMiddleware2,
16
+ sessionLocationMiddleware: SessionLocationMiddleware2
17
+ });
18
+
19
+ export { Api2 };
@@ -0,0 +1,21 @@
1
+ import {
2
+ Api2
3
+ } from "./process-ks9pc2xc.js";
4
+ import {
5
+ Service4
6
+ } from "./process-18k6g42r.js";
7
+
8
+ // src/handlers/server.ts
9
+ import { Effect } from "effect";
10
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
11
+ var ServerHandler2 = HttpApiBuilder.group(Api2, "server.server", (handlers) => handlers.handle("server.info", () => Effect.gen(function* () {
12
+ const info = yield* Service4;
13
+ return {
14
+ version: info.app.version ?? "unknown",
15
+ pid: process.pid ?? 0,
16
+ urls: info.urls(),
17
+ paths: info.paths
18
+ };
19
+ })));
20
+
21
+ export { ServerHandler2 };
@@ -0,0 +1,60 @@
1
+ import {
2
+ Config2,
3
+ required2,
4
+ authorized2
5
+ } from "./process-zyv89x5n.js";
6
+
7
+ // src/middleware/authorization.ts
8
+ import { UnauthorizedError } from "@opencode/protocol/errors";
9
+ import { Authorization } from "@opencode/protocol/middleware/authorization";
10
+ import { Authorization as Authorization2 } from "@opencode/protocol/middleware/authorization";
11
+ import { hasPtyConnectTicketURL } from "@opencode/protocol/groups/pty";
12
+ import { hasPersistentPtyConnectTicketURL } from "@opencode/protocol/groups/persistent-pty";
13
+ import { Effect, Encoding, Layer, Redacted } from "effect";
14
+ import { HttpEffect, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
15
+ var AUTH_TOKEN_QUERY = "auth_token";
16
+ var WWW_AUTHENTICATE = 'Basic realm="Secure Area"';
17
+ function emptyCredential() {
18
+ return { username: "", password: Redacted.make("") };
19
+ }
20
+ function decodeCredential(input) {
21
+ return Effect.fromResult(Encoding.decodeBase64String(input)).pipe(Effect.match({
22
+ onFailure: emptyCredential,
23
+ onSuccess: (header) => {
24
+ const separator = header.indexOf(":");
25
+ if (separator === -1)
26
+ return emptyCredential();
27
+ return { username: header.slice(0, separator), password: Redacted.make(header.slice(separator + 1)) };
28
+ }
29
+ }));
30
+ }
31
+ function credentialFromRequest(request) {
32
+ const url = new URL(request.url, "http://localhost");
33
+ const token = url.searchParams.get(AUTH_TOKEN_QUERY);
34
+ if (token)
35
+ return decodeCredential(token);
36
+ const match = /^Basic\s+(.+)$/i.exec(request.headers.authorization ?? "");
37
+ if (match)
38
+ return decodeCredential(match[1]);
39
+ return Effect.succeed(emptyCredential());
40
+ }
41
+ function authorizedRequest2(request, config) {
42
+ return credentialFromRequest(request).pipe(Effect.map((credential) => authorized2(credential, config)));
43
+ }
44
+ var authorizationLayer2 = Layer.effect(Authorization, Effect.gen(function* () {
45
+ const config = yield* Config2;
46
+ if (!required2(config))
47
+ return Authorization.of((effect) => effect);
48
+ return Authorization.of((effect) => Effect.gen(function* () {
49
+ const request = yield* HttpServerRequest.HttpServerRequest;
50
+ const url = new URL(request.url, "http://localhost");
51
+ if (hasPtyConnectTicketURL(url) || hasPersistentPtyConnectTicketURL(url))
52
+ return yield* effect;
53
+ if (yield* authorizedRequest2(request, config))
54
+ return yield* effect;
55
+ yield* HttpEffect.appendPreResponseHandler((_request, response) => Effect.succeed(HttpServerResponse.setHeader(response, "www-authenticate", WWW_AUTHENTICATE)));
56
+ return yield* new UnauthorizedError({ message: "Authentication required" });
57
+ }));
58
+ }));
59
+
60
+ export { authorizedRequest2, authorizationLayer2, Authorization2 };
@@ -0,0 +1,346 @@
1
+ import {
2
+ missingSession2,
3
+ missingMessage2,
4
+ failedMessageDecode2,
5
+ failedSnapshot2
6
+ } from "./process-smaf4a18.js";
7
+ import {
8
+ Api2
9
+ } from "./process-ks9pc2xc.js";
10
+
11
+ // src/handlers/session.ts
12
+ import { Session } from "@opencode/core/session";
13
+ import { SessionStats } from "@opencode/core/session/stats";
14
+ import { SessionTitle } from "@opencode/core/session/title";
15
+ import { SessionTransfer } from "@opencode/core/session/transfer";
16
+ import { InstructionEntry } from "@opencode/core/session/instruction-entry";
17
+ import { Form } from "@opencode/core/form";
18
+ import { DateTime, Effect, Stream } from "effect";
19
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
20
+ import { SessionsCursor } from "@opencode/protocol/groups/session";
21
+ import {
22
+ ConflictError,
23
+ CommandExecutionError,
24
+ CommandNotFoundError,
25
+ FormAlreadySettledError,
26
+ FormInvalidAnswerError,
27
+ FormNotFoundError,
28
+ InvalidRequestError,
29
+ InvalidCursorError,
30
+ MessageNotFoundError,
31
+ ServiceUnavailableError,
32
+ SessionBusyError,
33
+ SkillNotFoundError
34
+ } from "@opencode/protocol/errors";
35
+ import { AbsolutePath } from "@opencode/core/schema";
36
+ var DefaultSessionsLimit = 50;
37
+ function missingForm(id) {
38
+ return new FormNotFoundError({ id, message: `Form not found: ${id}` });
39
+ }
40
+ var SessionHandler2 = HttpApiBuilder.group(Api2, "server.session", (handlers) => Effect.gen(function* () {
41
+ const session = yield* Session.Service;
42
+ const transfer = yield* SessionTransfer.Service;
43
+ const requireOwnedForm = Effect.fnUntraced(function* (sessionID, formID) {
44
+ const form = yield* Form.Service;
45
+ const info = yield* form.get(formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(formID)));
46
+ if (info.sessionID !== sessionID)
47
+ return yield* missingForm(formID);
48
+ return { form, info };
49
+ });
50
+ const busySession = (error) => new SessionBusyError({
51
+ sessionID: error.sessionID,
52
+ message: `Session is busy: ${error.sessionID}`
53
+ });
54
+ const pendingMutation = (effect, conflict) => effect.pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.InboxConflictError", (error) => new ConflictError({ resource: error.inboxID, message: `${conflict}: ${error.inboxID}` })), Effect.as(HttpApiSchema.NoContent.make()));
55
+ return handlers.handle("session.list", Effect.fn(function* (ctx) {
56
+ const query = ctx.query.cursor !== undefined ? yield* SessionsCursor.parse(ctx.query.cursor).pipe(Effect.mapError(() => new InvalidCursorError({ message: "Invalid cursor" }))) : ctx.query;
57
+ const page = yield* session.list({
58
+ ...query,
59
+ limit: ctx.query.limit ?? DefaultSessionsLimit
60
+ });
61
+ const sessions = page.data;
62
+ const first = sessions[0];
63
+ const last = sessions.at(-1);
64
+ return {
65
+ data: sessions,
66
+ cursor: {
67
+ previous: first ? SessionsCursor.make({
68
+ ...query,
69
+ anchor: {
70
+ id: first.id,
71
+ time: DateTime.toEpochMillis(first.time.updated),
72
+ direction: "previous"
73
+ }
74
+ }) : undefined,
75
+ next: last ? SessionsCursor.make({
76
+ ...query,
77
+ anchor: {
78
+ id: last.id,
79
+ time: DateTime.toEpochMillis(last.time.updated),
80
+ direction: "next"
81
+ }
82
+ }) : undefined
83
+ }
84
+ };
85
+ })).handle("session.stats", Effect.fn(function* (ctx) {
86
+ const timezone = ctx.query.timezone ?? "UTC";
87
+ yield* Effect.try({
88
+ try: () => new Intl.DateTimeFormat("en-US", { timeZone: timezone }),
89
+ catch: () => new InvalidRequestError({ message: `Invalid time zone: ${timezone}` })
90
+ });
91
+ return {
92
+ data: yield* SessionStats.get({
93
+ from: ctx.query.from,
94
+ to: ctx.query.to,
95
+ projectID: ctx.query.project,
96
+ timezone,
97
+ tools: ctx.query.tools
98
+ }).pipe(Effect.mapError(() => new InvalidRequestError({ message: "Stats range must end after it starts" })))
99
+ };
100
+ })).handle("session.create", Effect.fn(function* (ctx) {
101
+ return {
102
+ data: yield* session.create({
103
+ id: ctx.payload.id,
104
+ title: ctx.payload.title,
105
+ agent: ctx.payload.agent,
106
+ model: ctx.payload.model,
107
+ metadata: ctx.payload.metadata,
108
+ permissions: ctx.payload.permissions,
109
+ location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) }
110
+ }).pipe(Effect.orDie)
111
+ };
112
+ })).handle("session.import", Effect.fn(function* (ctx) {
113
+ return {
114
+ data: yield* transfer.import({
115
+ data: { info: ctx.payload.info, messages: ctx.payload.messages },
116
+ location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) }
117
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("SessionTransfer.ImportConflictError", (error) => new ConflictError({
118
+ message: `Session already exists: ${error.sessionID}`,
119
+ resource: error.sessionID
120
+ })))
121
+ };
122
+ })).handle("session.export", Effect.fn(function* (ctx) {
123
+ return {
124
+ data: yield* transfer.export({ sessionID: ctx.params.sessionID, sanitize: ctx.query.sanitize }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode2))
125
+ };
126
+ })).handle("session.active", Effect.fn(function* () {
127
+ const active = yield* session.active;
128
+ return {
129
+ data: Object.fromEntries(Array.from(active, (sessionID) => [sessionID, { type: "running" }]))
130
+ };
131
+ })).handle("session.get", Effect.fn(function* (ctx) {
132
+ return {
133
+ data: yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2))
134
+ };
135
+ })).handle("session.view", Effect.fn(function* (ctx) {
136
+ yield* session.view({ sessionID: ctx.params.sessionID, idle: DateTime.toEpochMillis(ctx.payload.idle) }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
137
+ return HttpApiSchema.NoContent.make();
138
+ })).handle("session.remove", Effect.fn(function* (ctx) {
139
+ yield* session.remove(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
140
+ return HttpApiSchema.NoContent.make();
141
+ })).handle("session.environment", Effect.fn(function* (ctx) {
142
+ yield* session.environment({ sessionID: ctx.params.sessionID, variables: ctx.payload.variables }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
143
+ return HttpApiSchema.NoContent.make();
144
+ })).handle("session.fork", Effect.fn(function* (ctx) {
145
+ return {
146
+ data: yield* session.fork({ sessionID: ctx.params.sessionID, before: ctx.payload.before }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageNotFoundError", missingMessage2), Effect.catchTag("Session.ForkEmptyError", (error) => new InvalidRequestError({ message: error.message, kind: "empty_session" })))
147
+ };
148
+ })).handle("session.switchAgent", Effect.fn(function* (ctx) {
149
+ yield* session.switchAgent({ sessionID: ctx.params.sessionID, agent: ctx.payload.agent }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
150
+ return HttpApiSchema.NoContent.make();
151
+ })).handle("session.switchModel", Effect.fn(function* (ctx) {
152
+ yield* session.switchModel({ sessionID: ctx.params.sessionID, model: ctx.payload.model }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
153
+ return HttpApiSchema.NoContent.make();
154
+ })).handle("session.update", Effect.fn(function* (ctx) {
155
+ yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
156
+ if (ctx.payload.title !== undefined) {
157
+ if (ctx.payload.title) {
158
+ yield* session.rename({ sessionID: ctx.params.sessionID, title: ctx.payload.title }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
159
+ } else {
160
+ const title = yield* SessionTitle.Service;
161
+ yield* title.generate(ctx.params.sessionID);
162
+ }
163
+ }
164
+ if (ctx.payload.permissions !== undefined)
165
+ yield* session.setPermissions({ sessionID: ctx.params.sessionID, permissions: ctx.payload.permissions }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
166
+ return HttpApiSchema.NoContent.make();
167
+ })).handle("session.move", Effect.fn(function* (ctx) {
168
+ yield* session.move({
169
+ sessionID: ctx.params.sessionID,
170
+ directory: ctx.payload.directory,
171
+ delivery: ctx.payload.delivery
172
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.DestinationNotFoundError", (error) => Effect.fail(new InvalidRequestError({ message: `Directory does not exist: ${error.directory}` }))), Effect.catchTag("Session.DestinationNotDirectoryError", (error) => Effect.fail(new InvalidRequestError({ message: `Not a directory: ${error.directory}` }))), Effect.catchTag("Session.DestinationUnavailableError", (error) => Effect.fail(new InvalidRequestError({ message: `Directory is unavailable: ${error.directory}` }))));
173
+ return HttpApiSchema.NoContent.make();
174
+ })).handle("session.prompt", Effect.fn(function* (ctx) {
175
+ return {
176
+ data: yield* session.prompt({
177
+ sessionID: ctx.params.sessionID,
178
+ id: ctx.payload.id,
179
+ text: ctx.payload.text,
180
+ files: ctx.payload.files,
181
+ agents: ctx.payload.agents,
182
+ skills: ctx.payload.skills,
183
+ metadata: ctx.payload.metadata,
184
+ delivery: ctx.payload.delivery,
185
+ resume: ctx.payload.resume
186
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.PromptConflictError", (error) => Effect.fail(new ConflictError({
187
+ message: `Prompt message ID conflicts with an existing durable record: ${error.messageID}`,
188
+ resource: error.messageID
189
+ }))), Effect.catchTag("Session.AttachmentError", (error) => Effect.fail(new InvalidRequestError({ message: error.message, field: "files" }))), Effect.catchTag("Session.SkillNotFoundError", (error) => Effect.fail(new InvalidRequestError({ message: `Skill not found: ${error.skill}`, field: "skills" }))))
190
+ };
191
+ })).handle("session.command", Effect.fn(function* (ctx) {
192
+ yield* session.command({
193
+ sessionID: ctx.params.sessionID,
194
+ command: ctx.payload.name,
195
+ text: ctx.payload.text,
196
+ files: ctx.payload.files,
197
+ agents: ctx.payload.agents,
198
+ skills: ctx.payload.skills,
199
+ delivery: ctx.payload.delivery
200
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Command.NotFoundError", (error) => Effect.fail(new CommandNotFoundError({
201
+ command: error.command,
202
+ message: error.message
203
+ }))), Effect.catchTag("Command.ExecutionError", (error) => Effect.fail(new CommandExecutionError({
204
+ command: error.command,
205
+ message: error.message
206
+ }))));
207
+ return HttpApiSchema.NoContent.make();
208
+ })).handle("session.skill", Effect.fn(function* (ctx) {
209
+ yield* session.skill({
210
+ sessionID: ctx.params.sessionID,
211
+ skill: ctx.payload.id,
212
+ resume: ctx.payload.resume
213
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.SkillNotFoundError", (error) => Effect.fail(new SkillNotFoundError({ skill: error.skill, message: `Skill not found: ${error.skill}` }))));
214
+ return HttpApiSchema.NoContent.make();
215
+ })).handle("session.synthetic", Effect.fn(function* (ctx) {
216
+ const data = yield* session.synthetic({
217
+ id: ctx.payload.id,
218
+ sessionID: ctx.params.sessionID,
219
+ text: ctx.payload.text,
220
+ description: ctx.payload.description,
221
+ metadata: ctx.payload.metadata,
222
+ delivery: ctx.payload.delivery,
223
+ resume: ctx.payload.resume
224
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.SyntheticConflictError", (error) => Effect.fail(new ConflictError({
225
+ message: `Synthetic input ID conflicts with an existing durable record: ${error.inputID}`,
226
+ resource: error.inputID
227
+ }))));
228
+ return { data };
229
+ })).handle("session.shell", Effect.fn(function* (ctx) {
230
+ yield* session.shell({ sessionID: ctx.params.sessionID, id: ctx.payload.id, command: ctx.payload.command }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
231
+ return HttpApiSchema.NoContent.make();
232
+ })).handle("session.compact", Effect.fn(function* (ctx) {
233
+ return {
234
+ data: yield* session.compact({ sessionID: ctx.params.sessionID, id: ctx.payload.id, delivery: ctx.payload.delivery }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.CompactionConflictError", (error) => Effect.fail(new ConflictError({
235
+ message: `Compaction input ID conflicts with an existing durable record: ${error.inputID}`,
236
+ resource: error.inputID
237
+ }))))
238
+ };
239
+ })).handle("session.wait", Effect.fn(function* (ctx) {
240
+ yield* session.wait(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
241
+ return HttpApiSchema.NoContent.make();
242
+ })).handle("session.revert.stage", Effect.fn(function* (ctx) {
243
+ yield* Effect.log("session.revert.stage", {
244
+ sessionID: ctx.params.sessionID,
245
+ messageID: ctx.payload.messageID,
246
+ files: ctx.payload.files
247
+ });
248
+ return {
249
+ data: yield* session.revert.stage({ ...ctx.params, ...ctx.payload }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageNotFoundError", missingMessage2), Effect.catchTag("Session.BusyError", busySession), Effect.catchTag("Snapshot.Error", failedSnapshot2("stage session revert", ctx.params.sessionID)))
250
+ };
251
+ })).handle("session.revert.clear", Effect.fn(function* (ctx) {
252
+ yield* Effect.log("session.revert.clear", { sessionID: ctx.params.sessionID });
253
+ yield* session.revert.clear(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.BusyError", busySession), Effect.catchTag("Snapshot.Error", failedSnapshot2("clear session revert", ctx.params.sessionID)));
254
+ return HttpApiSchema.NoContent.make();
255
+ })).handle("session.revert.commit", Effect.fn(function* (ctx) {
256
+ yield* Effect.log("session.revert.commit", { sessionID: ctx.params.sessionID });
257
+ yield* session.revert.commit(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.BusyError", busySession));
258
+ return HttpApiSchema.NoContent.make();
259
+ })).handle("session.context", Effect.fn(function* (ctx) {
260
+ return {
261
+ data: yield* session.context(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode2))
262
+ };
263
+ })).handle("session.diff", Effect.fn(function* (ctx) {
264
+ return {
265
+ data: yield* session.diff({ sessionID: ctx.params.sessionID, ...ctx.query }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageNotFoundError", missingMessage2), Effect.catchTag("Session.TurnRangeError", (error) => new InvalidRequestError({ message: error.message, field: error.field })), Effect.catchTag("Snapshot.Error", failedSnapshot2("diff session turn", ctx.params.sessionID)))
266
+ };
267
+ })).handle("session.inbox.list", Effect.fn(function* (ctx) {
268
+ return {
269
+ data: yield* session.inbox(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2))
270
+ };
271
+ })).handle("session.inbox.cancel", Effect.fn(function* (ctx) {
272
+ yield* session.cancelInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.InboxConflictError", () => Effect.void));
273
+ return HttpApiSchema.NoContent.make();
274
+ })).handle("session.inbox.update", Effect.fn(function* (ctx) {
275
+ return yield* pendingMutation(ctx.payload.delivery === "steer" ? session.steerInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }) : session.queueInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), `Pending input cannot change to ${ctx.payload.delivery}`);
276
+ })).handle("session.instructions.entry.list", Effect.fn(function* (ctx) {
277
+ const instructions = yield* InstructionEntry.Service;
278
+ return { data: yield* instructions.list(ctx.params.sessionID) };
279
+ })).handle("session.instructions.entry.put", Effect.fn(function* (ctx) {
280
+ const instructions = yield* InstructionEntry.Service;
281
+ yield* instructions.put({ sessionID: ctx.params.sessionID, key: ctx.params.key, value: ctx.payload.value });
282
+ return HttpApiSchema.NoContent.make();
283
+ })).handle("session.instructions.entry.remove", Effect.fn(function* (ctx) {
284
+ const instructions = yield* InstructionEntry.Service;
285
+ yield* instructions.remove({ sessionID: ctx.params.sessionID, key: ctx.params.key });
286
+ return HttpApiSchema.NoContent.make();
287
+ })).handle("session.generate", Effect.fn(function* (ctx) {
288
+ const text = yield* session.generate({ sessionID: ctx.params.sessionID, prompt: ctx.payload.prompt }).pipe(Effect.mapError((error) => error._tag === "Session.NotFoundError" ? missingSession2(error) : new ServiceUnavailableError({ message: error.message, service: "session generation" })));
289
+ return { data: { text } };
290
+ })).handle("session.log", Effect.fn(function* (ctx) {
291
+ yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
292
+ return session.log({ sessionID: ctx.params.sessionID, after: ctx.query.after, follow: ctx.query.follow }).pipe(Stream.orDie);
293
+ })).handle("session.interrupt", Effect.fn(function* (ctx) {
294
+ return { interrupted: yield* session.interrupt(ctx.params.sessionID, { resume: ctx.query.resume }) };
295
+ })).handle("session.background", Effect.fn(function* (ctx) {
296
+ yield* session.background(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
297
+ return HttpApiSchema.NoContent.make();
298
+ })).handle("session.message", Effect.fn(function* (ctx) {
299
+ yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
300
+ const message = yield* session.message(ctx.params);
301
+ if (message)
302
+ return { data: message };
303
+ return yield* new MessageNotFoundError({
304
+ sessionID: ctx.params.sessionID,
305
+ messageID: ctx.params.messageID,
306
+ message: `Message not found: ${ctx.params.messageID}`
307
+ });
308
+ })).handle("session.form.list", Effect.fn(function* (ctx) {
309
+ const form = yield* Form.Service;
310
+ return { data: yield* form.list({ sessionID: ctx.params.sessionID }) };
311
+ })).handle("session.form.create", Effect.fn(function* (ctx) {
312
+ const form = yield* Form.Service;
313
+ const created = yield* form.create({
314
+ id: ctx.payload.id,
315
+ sessionID: ctx.params.sessionID,
316
+ title: ctx.payload.title,
317
+ metadata: ctx.payload.metadata,
318
+ fields: ctx.payload.fields
319
+ }).pipe(Effect.catchTags({
320
+ "Form.AlreadyExistsError": (error) => new ConflictError({ resource: error.id, message: error.message }),
321
+ "Form.InvalidFormError": (error) => new InvalidRequestError({ message: error.message, field: "fields" })
322
+ }));
323
+ return { data: created };
324
+ })).handle("session.form.get", Effect.fn(function* (ctx) {
325
+ const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID);
326
+ const state = yield* owned.form.state(ctx.params.formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(ctx.params.formID)));
327
+ return { data: { ...owned.info, state } };
328
+ })).handle("session.form.reply", Effect.fn(function* (ctx) {
329
+ const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID);
330
+ yield* owned.form.reply({ id: ctx.params.formID, answer: ctx.payload.answer }).pipe(Effect.catchTags({
331
+ "Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
332
+ "Form.InvalidAnswerError": (error) => new FormInvalidAnswerError({ id: error.id, message: error.message }),
333
+ "Form.NotFoundError": () => missingForm(ctx.params.formID)
334
+ }));
335
+ return HttpApiSchema.NoContent.make();
336
+ })).handle("session.form.cancel", Effect.fn(function* (ctx) {
337
+ const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID);
338
+ yield* owned.form.cancel(ctx.params.formID).pipe(Effect.catchTags({
339
+ "Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
340
+ "Form.NotFoundError": () => missingForm(ctx.params.formID)
341
+ }));
342
+ return HttpApiSchema.NoContent.make();
343
+ }));
344
+ }));
345
+
346
+ export { SessionHandler2 };