@codeworksh/harness 0.0.1-dev.20260824182323 → 0.0.1-dev.20260824191503

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
@@ -76,6 +76,21 @@ pnpm dlx @codeworksh/harness@dev \
76
76
  "Inspect this repository"
77
77
  ```
78
78
 
79
+ The streamed response remains clean on stdout for piping. Session context and the per-run model usage summary are written to stderr:
80
+
81
+ ```text
82
+ session ses_...
83
+ sandbox local · /workspace/project
84
+
85
+ ── response ─────────────────────────────────────────────────────────────
86
+ The response streams here.
87
+ ── usage ────────────────────────────────────────────────────────────────
88
+ model openai/gpt-5.5
89
+ tokens 12,400 input · 820 output · 13,220 total
90
+ cache 9,600 read · 0 write
91
+ cost $0.014200 · 1 turn
92
+ ```
93
+
79
94
  `modelgen [path]` uses Aikit's model generator and writes `./models.gen.json` by default. Set `CODEWORK_MODELS_FILE` or pass a path when you keep the catalog elsewhere.
80
95
 
81
96
  The CLI prints the session ID to stderr. Provider, model, and thinking settings are stored with the session, so use the same home directory and session ID to continue it:
package/cli/index.mjs CHANGED
@@ -1,10 +1,49 @@
1
1
  #!/usr/bin/env node
2
- import { a as create, d as ID, i as Drivers, l as layer, n as create$1, o as register, t as attach } from "../session-CF1B0VEr.mjs";
3
- import { Effect, Fiber, Option, Queue, Schema, Stream } from "effect";
2
+ import { a as create, d as LLMEnded, f as LLMTextDelta, i as Drivers, l as layer, m as ID, n as create$1, o as register, p as TurnEnded, t as attach } from "../session-DZZfASHn.mjs";
3
+ import { Effect, Fiber, Option, Queue, Ref, Schema, Stream } from "effect";
4
4
  import { generateModels } from "@codeworksh/aikit/modelgen";
5
5
  import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
6
6
  import * as NodeServices from "@effect/platform-node/NodeServices";
7
7
  import { Argument, Command, Flag } from "effect/unstable/cli";
8
+ //#region src/cli/output.ts
9
+ const emptyUsage = {
10
+ turns: 0,
11
+ input: 0,
12
+ output: 0,
13
+ reasoning: 0,
14
+ cacheRead: 0,
15
+ cacheWrite: 0,
16
+ totalTokens: 0,
17
+ cost: 0
18
+ };
19
+ const addUsage = (summary, message) => ({
20
+ turns: summary.turns + 1,
21
+ provider: message.provider.id,
22
+ model: message.responseModel ?? message.model,
23
+ input: summary.input + message.usage.input,
24
+ output: summary.output + message.usage.output,
25
+ reasoning: summary.reasoning + (message.usage.reasoning ?? 0),
26
+ cacheRead: summary.cacheRead + message.usage.cacheRead,
27
+ cacheWrite: summary.cacheWrite + message.usage.cacheWrite,
28
+ totalTokens: summary.totalTokens + message.usage.totalTokens,
29
+ cost: summary.cost + message.usage.cost.total
30
+ });
31
+ const number = new Intl.NumberFormat("en-US");
32
+ const row = (label, value) => `${label.padEnd(9)}${value}\n`;
33
+ const formatCost = (cost) => `$${cost.toFixed(cost > 0 && cost < .01 ? 6 : 4)}`;
34
+ const divider = (label, columns = 72) => {
35
+ const width = Math.min(100, Math.max(32, columns));
36
+ const start = `── ${label} `;
37
+ return `${start}${"─".repeat(Math.max(2, width - start.length))}\n`;
38
+ };
39
+ const header = (input) => `${row("session", input.sessionId)}${row("sandbox", `${input.sandbox} · ${input.directory}`)}\n${divider("response", input.columns)}`;
40
+ const usage = (summary, columns) => {
41
+ const turns = `${summary.turns} ${summary.turns === 1 ? "turn" : "turns"}`;
42
+ const model = summary.provider === void 0 || summary.model === void 0 ? "unknown" : `${summary.provider}/${summary.model}`;
43
+ const reasoning = summary.reasoning === 0 ? "" : ` · ${number.format(summary.reasoning)} reasoning`;
44
+ return `${divider("usage", columns)}${row("model", model)}${row("tokens", `${number.format(summary.input)} input · ${number.format(summary.output)} output${reasoning} · ${number.format(summary.totalTokens)} total`)}${row("cache", `${number.format(summary.cacheRead)} read · ${number.format(summary.cacheWrite)} write`)}${row("cost", `${formatCost(summary.cost)} · ${turns}`)}`;
45
+ };
46
+ //#endregion
8
47
  //#region src/cli/index.ts
9
48
  const thinkingLevels = [
10
49
  "off",
@@ -22,14 +61,35 @@ const sandboxDrivers = [
22
61
  ];
23
62
  const writeOut = (value) => Effect.sync(() => void process.stdout.write(value));
24
63
  const writeError = (value) => Effect.sync(() => void process.stderr.write(value));
64
+ const terminalColumns = () => process.stderr.isTTY ? process.stderr.columns ?? 72 : 72;
25
65
  var InvalidInputError = class extends Schema.TaggedError()("CLI.InvalidInputError", { message: Schema.String }) {};
26
66
  var ModelgenError = class extends Schema.TaggedError()("CLI.ModelgenError", { cause: Schema.Defect() }) {};
27
- const render = (ended) => Effect.fn("CLI.render")(function* (event) {
28
- if (event.type === "session.llm.text.delta") {
67
+ const initialRenderState = {
68
+ usage: emptyUsage,
69
+ textSeen: false,
70
+ textEndsWithNewline: false
71
+ };
72
+ const isTextDelta = Schema.is(LLMTextDelta);
73
+ const isLLMEnded = Schema.is(LLMEnded);
74
+ const isTurnEnded = Schema.is(TurnEnded);
75
+ const render = (ended, state) => Effect.fn("CLI.render")(function* (event) {
76
+ if (isTextDelta(event)) {
29
77
  yield* writeOut(event.data.delta);
78
+ if (event.data.delta.length > 0) yield* Ref.update(state, (current) => ({
79
+ ...current,
80
+ textSeen: true,
81
+ textEndsWithNewline: event.data.delta.endsWith("\n")
82
+ }));
83
+ return;
84
+ }
85
+ if (isLLMEnded(event)) {
86
+ yield* Ref.update(state, (current) => ({
87
+ ...current,
88
+ usage: addUsage(current.usage, event.data.message)
89
+ }));
30
90
  return;
31
91
  }
32
- if (event.type === "session.turn.ended") yield* Queue.offer(ended, event.data.messageId);
92
+ if (isTurnEnded(event)) yield* Queue.offer(ended, event.data.messageId);
33
93
  });
34
94
  const awaitMessage = Effect.fn("CLI.awaitMessage")(function* (ended, messageId) {
35
95
  while ((yield* Queue.take(ended)) !== messageId);
@@ -87,13 +147,23 @@ const run = Command.make("run", {
87
147
  });
88
148
  }
89
149
  const ended = yield* Queue.unbounded();
90
- const printer = yield* handle.events().pipe(Stream.runForEach(render(ended)), Effect.forkScoped({ startImmediately: true }));
91
- yield* writeError(`session ${handle.id}\n`);
150
+ const renderState = yield* Ref.make(initialRenderState);
151
+ const printer = yield* handle.events().pipe(Stream.runForEach(render(ended, renderState)), Effect.forkScoped({ startImmediately: true }));
152
+ const info = yield* handle.info;
153
+ const columns = terminalColumns();
154
+ yield* writeError(header({
155
+ sessionId: handle.id,
156
+ sandbox: info.sandbox?.driver ?? "local",
157
+ directory: info.directory,
158
+ columns
159
+ }));
92
160
  yield* handle.run(prompt);
93
161
  const leaf = (yield* handle.path()).at(-1);
94
162
  if (leaf !== void 0) yield* awaitMessage(ended, leaf.entry.id);
95
163
  yield* Fiber.interrupt(printer);
96
- yield* writeOut("\n");
164
+ const rendered = yield* Ref.get(renderState);
165
+ if (rendered.textSeen && !rendered.textEndsWithNewline) yield* writeOut("\n");
166
+ yield* writeError(usage(rendered.usage, columns));
97
167
  }).pipe(Effect.provide(layer({
98
168
  ...Option.isNone(shared.home) ? {} : { home: shared.home.value },
99
169
  ...Option.isNone(shared.database) ? {} : { database: shared.database.value },
package/cli/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["Sandbox.create","Sandbox.register","Session.attach","Session.create","Harness.layer"],"sources":["../../../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { generateModels } from \"@codeworksh/aikit/modelgen\";\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\";\nimport * as NodeServices from \"@effect/platform-node/NodeServices\";\nimport { Effect, Fiber, Option, Queue, Schema, Stream } from \"effect\";\nimport { Argument, CliError, Command, Flag } from \"effect/unstable/cli\";\nimport { Harness } from \"../effect/harness.ts\";\nimport { Sandbox } from \"../effect/sandbox.ts\";\nimport { Session } from \"../effect/session.ts\";\nimport type { EventSchema } from \"../event/schema.ts\";\n\nconst thinkingLevels = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nconst sandboxDrivers = [\"local\", \"daytona\", \"vercel\"] as const;\n\nconst writeOut = (value: string) => Effect.sync(() => void process.stdout.write(value));\nconst writeError = (value: string) => Effect.sync(() => void process.stderr.write(value));\n\nclass InvalidInputError extends Schema.TaggedError<InvalidInputError>()(\"CLI.InvalidInputError\", {\n\tmessage: Schema.String,\n}) {}\n\nclass ModelgenError extends Schema.TaggedError<ModelgenError>()(\"CLI.ModelgenError\", {\n\tcause: Schema.Defect(),\n}) {}\n\nconst render = (ended: Queue.Queue<string>) =>\n\tEffect.fn(\"CLI.render\")(function* (event: EventSchema.Payload) {\n\t\tif (event.type === \"session.llm.text.delta\") {\n\t\t\tyield* writeOut((event.data as { readonly delta: string }).delta);\n\t\t\treturn;\n\t\t}\n\t\tif (event.type === \"session.turn.ended\") {\n\t\t\tyield* Queue.offer(ended, (event.data as { readonly messageId: string }).messageId);\n\t\t}\n\t});\n\nconst awaitMessage = Effect.fn(\"CLI.awaitMessage\")(function* (ended: Queue.Queue<string>, messageId: string) {\n\twhile ((yield* Queue.take(ended)) !== messageId) {\n\t\t// Earlier turns in the same drain are expected when a tool call continues.\n\t}\n});\n\nconst selectSandbox = Effect.fn(\"CLI.selectSandbox\")(function* (\n\tdriver: (typeof sandboxDrivers)[number],\n\tproviderResourceId?: string,\n) {\n\tif (driver === \"local\") return undefined;\n\tif (driver === \"daytona\") {\n\t\treturn providerResourceId === undefined\n\t\t\t? yield* Sandbox.create({ driver })\n\t\t\t: yield* Sandbox.register({ driver, providerResourceId });\n\t}\n\treturn providerResourceId === undefined\n\t\t? yield* Sandbox.create({ driver })\n\t\t: yield* Sandbox.register({ driver, providerResourceId });\n});\n\nconst root = Command.make(\"codework\").pipe(\n\tCommand.withSharedFlags({\n\t\thome: Flag.string(\"home\").pipe(Flag.withDescription(\"Harness data directory\"), Flag.optional),\n\t\tdatabase: Flag.string(\"database\").pipe(Flag.withDescription(\"SQLite path or :memory:\"), Flag.optional),\n\t}),\n\tCommand.withDescription(\"Run Codework agent sessions\"),\n);\n\nconst run = Command.make(\n\t\"run\",\n\t{\n\t\tprompt: Argument.string(\"prompt\").pipe(Argument.withDescription(\"Prompt for the agent\")),\n\t\tsession: Flag.string(\"session\").pipe(\n\t\t\tFlag.withAlias(\"s\"),\n\t\t\tFlag.withDescription(\"Continue an existing session ID\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tcwd: Flag.string(\"cwd\").pipe(\n\t\t\tFlag.withAlias(\"C\"),\n\t\t\tFlag.withDescription(\"Working directory for a new session\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tsandbox: Flag.choice(\"sandbox\", sandboxDrivers).pipe(\n\t\t\tFlag.withDescription(\"Sandbox for a new session (default: local)\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tsandboxProviderId: Flag.string(\"sandbox-provider-id\").pipe(\n\t\t\tFlag.withDescription(\"Provider ID of an existing sandbox\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tprovider: Flag.string(\"provider\").pipe(Flag.withDescription(\"Model catalog provider ID\"), Flag.optional),\n\t\tmodel: Flag.string(\"model\").pipe(Flag.withDescription(\"Model ID\"), Flag.optional),\n\t\tthinking: Flag.choice(\"thinking\", thinkingLevels).pipe(Flag.withDescription(\"Thinking level\"), Flag.optional),\n\t},\n\tEffect.fn(\"CLI.run\")(function* ({ prompt, session, cwd, sandbox, sandboxProviderId, provider, model, thinking }) {\n\t\tconst shared = yield* root;\n\t\tif (\n\t\t\tOption.isSome(session) &&\n\t\t\t(Option.isSome(cwd) || Option.isSome(sandbox) || Option.isSome(sandboxProviderId))\n\t\t) {\n\t\t\treturn yield* new InvalidInputError({\n\t\t\t\tmessage: \"--cwd, --sandbox, and --sandbox-provider-id can only be used when creating a new session\",\n\t\t\t});\n\t\t}\n\t\tif (Option.isSome(provider) !== Option.isSome(model)) {\n\t\t\treturn yield* new InvalidInputError({ message: \"--provider and --model must be provided together\" });\n\t\t}\n\t\tif (Option.isSome(sandboxProviderId) && (Option.isNone(sandbox) || sandbox.value === \"local\")) {\n\t\t\treturn yield* new InvalidInputError({ message: \"--sandbox-provider-id requires a remote --sandbox\" });\n\t\t}\n\t\tconst program = Effect.gen(function* () {\n\t\t\tconst runtime = {\n\t\t\t\t...(Option.isNone(provider) || Option.isNone(model)\n\t\t\t\t\t? {}\n\t\t\t\t\t: { model: { provider: provider.value, id: model.value } }),\n\t\t\t\t...(Option.isNone(thinking) ? {} : { thinkingLevel: thinking.value }),\n\t\t\t};\n\t\t\tlet handle: Session.Handle;\n\t\t\tif (Option.isSome(session)) {\n\t\t\t\thandle = yield* Session.attach({ sessionId: Session.SessionSchema.ID.make(session.value), ...runtime });\n\t\t\t} else {\n\t\t\t\tconst selectedSandbox = Option.getOrElse(sandbox, () => \"local\" as const);\n\t\t\t\tconst selected = yield* selectSandbox(selectedSandbox, Option.getOrUndefined(sandboxProviderId));\n\t\t\t\thandle = yield* Session.create({\n\t\t\t\t\ttitle: \"CLI\",\n\t\t\t\t\t...runtime,\n\t\t\t\t\t...(selected === undefined ? {} : { sandbox: selected }),\n\t\t\t\t\t...(Option.isNone(cwd) ? {} : { directory: cwd.value }),\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst ended = yield* Queue.unbounded<string>();\n\t\t\tconst printer = yield* handle\n\t\t\t\t.events()\n\t\t\t\t.pipe(Stream.runForEach(render(ended)), Effect.forkScoped({ startImmediately: true }));\n\n\t\t\tyield* writeError(`session ${handle.id}\\n`);\n\t\t\tyield* handle.run(prompt);\n\t\t\tconst path = yield* handle.path();\n\t\t\tconst leaf = path.at(-1);\n\t\t\tif (leaf !== undefined) yield* awaitMessage(ended, leaf.entry.id);\n\t\t\tyield* Fiber.interrupt(printer);\n\t\t\tyield* writeOut(\"\\n\");\n\t\t});\n\n\t\treturn yield* program.pipe(\n\t\t\tEffect.provide(\n\t\t\t\tHarness.layer({\n\t\t\t\t\t...(Option.isNone(shared.home) ? {} : { home: shared.home.value }),\n\t\t\t\t\t...(Option.isNone(shared.database) ? {} : { database: shared.database.value }),\n\t\t\t\t\tsandboxes: [Sandbox.Drivers.daytona(), Sandbox.Drivers.vercel()],\n\t\t\t\t}),\n\t\t\t),\n\t\t\tEffect.scoped,\n\t\t);\n\t}),\n).pipe(\n\tCommand.withDescription(\"Start or continue an agent session\"),\n\tCommand.withExamples([\n\t\t{ command: 'codework run \"Inspect the failing tests\"', description: \"Create a session\" },\n\t\t{\n\t\t\tcommand: 'codework run --provider openai --model gpt-5.5 --thinking high \"Inspect the failing tests\"',\n\t\t\tdescription: \"Create a session with an explicit model\",\n\t\t},\n\t\t{\n\t\t\tcommand: 'codework run --sandbox daytona \"Inspect the repository\"',\n\t\t\tdescription: \"Create a Daytona sandbox and session\",\n\t\t},\n\t\t{\n\t\t\tcommand: 'codework run --sandbox daytona --sandbox-provider-id <id> \"Inspect the repository\"',\n\t\t\tdescription: \"Use an existing remote sandbox\",\n\t\t},\n\t\t{\n\t\t\tcommand: 'codework run --session <id> --provider openai --model gpt-5.6-luna \"Now fix them\"',\n\t\t\tdescription: \"Continue a session with explicit model bindings\",\n\t\t},\n\t]),\n);\n\nconst modelgen = Command.make(\n\t\"modelgen\",\n\t{\n\t\tpath: Argument.string(\"path\").pipe(\n\t\t\tArgument.withDescription(\"Output path; defaults to CODEWORK_MODELS_FILE or ./models.gen.json\"),\n\t\t\tArgument.optional,\n\t\t),\n\t},\n\tEffect.fn(\"CLI.modelgen\")(function* ({ path }) {\n\t\tconst generated = yield* Effect.tryPromise({\n\t\t\ttry: () => generateModels(Option.isNone(path) ? {} : { path: path.value }),\n\t\t\tcatch: (cause) => new ModelgenError({ cause }),\n\t\t});\n\t\tyield* writeOut(`Generated model catalog at ${generated}\\n`);\n\t}),\n).pipe(\n\tCommand.withDescription(\"Generate the Aikit model catalog\"),\n\tCommand.withExamples([{ command: \"codework modelgen\", description: \"Generate models.gen.json\" }]),\n);\n\nexport const command = root.pipe(Command.withSubcommands([run, modelgen]));\n\nexport const main: Effect.Effect<void, Command.Error<typeof command> | CliError.CliError> = Command.run(command, {\n\tversion: \"0.0.1\",\n}).pipe(Effect.provide(NodeServices.layer));\n\nNodeRuntime.runMain(main);\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["EventList.LLMTextDelta","EventList.LLMEnded","EventList.TurnEnded","Sandbox.create","Sandbox.register","Session.attach","Session.create","Harness.layer"],"sources":["../../../src/cli/output.ts","../../../src/cli/index.ts"],"sourcesContent":["import type { Message } from \"@codeworksh/aikit\";\n\nexport interface UsageSummary {\n\treadonly turns: number;\n\treadonly provider?: string;\n\treadonly model?: string;\n\treadonly input: number;\n\treadonly output: number;\n\treadonly reasoning: number;\n\treadonly cacheRead: number;\n\treadonly cacheWrite: number;\n\treadonly totalTokens: number;\n\treadonly cost: number;\n}\n\nexport const emptyUsage: UsageSummary = {\n\tturns: 0,\n\tinput: 0,\n\toutput: 0,\n\treasoning: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: 0,\n};\n\nexport const addUsage = (summary: UsageSummary, message: Message.AssistantMessage): UsageSummary => ({\n\tturns: summary.turns + 1,\n\tprovider: message.provider.id,\n\tmodel: message.responseModel ?? message.model,\n\tinput: summary.input + message.usage.input,\n\toutput: summary.output + message.usage.output,\n\treasoning: summary.reasoning + (message.usage.reasoning ?? 0),\n\tcacheRead: summary.cacheRead + message.usage.cacheRead,\n\tcacheWrite: summary.cacheWrite + message.usage.cacheWrite,\n\ttotalTokens: summary.totalTokens + message.usage.totalTokens,\n\tcost: summary.cost + message.usage.cost.total,\n});\n\nconst number = new Intl.NumberFormat(\"en-US\");\nconst row = (label: string, value: string) => `${label.padEnd(9)}${value}\\n`;\n\nconst formatCost = (cost: number): string => `$${cost.toFixed(cost > 0 && cost < 0.01 ? 6 : 4)}`;\n\nexport const divider = (label: string, columns = 72): string => {\n\tconst width = Math.min(100, Math.max(32, columns));\n\tconst start = `── ${label} `;\n\treturn `${start}${\"─\".repeat(Math.max(2, width - start.length))}\\n`;\n};\n\nexport const header = (input: {\n\treadonly sessionId: string;\n\treadonly sandbox: string;\n\treadonly directory: string;\n\treadonly columns?: number;\n}): string =>\n\t`${row(\"session\", input.sessionId)}${row(\"sandbox\", `${input.sandbox} · ${input.directory}`)}\\n${divider(\n\t\t\"response\",\n\t\tinput.columns,\n\t)}`;\n\nexport const usage = (summary: UsageSummary, columns?: number): string => {\n\tconst turns = `${summary.turns} ${summary.turns === 1 ? \"turn\" : \"turns\"}`;\n\tconst model =\n\t\tsummary.provider === undefined || summary.model === undefined\n\t\t\t? \"unknown\"\n\t\t\t: `${summary.provider}/${summary.model}`;\n\tconst reasoning = summary.reasoning === 0 ? \"\" : ` · ${number.format(summary.reasoning)} reasoning`;\n\treturn `${divider(\"usage\", columns)}${row(\"model\", model)}${row(\n\t\t\"tokens\",\n\t\t`${number.format(summary.input)} input · ${number.format(summary.output)} output${reasoning} · ${number.format(\n\t\t\tsummary.totalTokens,\n\t\t)} total`,\n\t)}${row(\"cache\", `${number.format(summary.cacheRead)} read · ${number.format(summary.cacheWrite)} write`)}${row(\n\t\t\"cost\",\n\t\t`${formatCost(summary.cost)} · ${turns}`,\n\t)}`;\n};\n","#!/usr/bin/env node\n\nimport { generateModels } from \"@codeworksh/aikit/modelgen\";\nimport * as NodeRuntime from \"@effect/platform-node/NodeRuntime\";\nimport * as NodeServices from \"@effect/platform-node/NodeServices\";\nimport { Effect, Fiber, Option, Queue, Ref, Schema, Stream } from \"effect\";\nimport { Argument, CliError, Command, Flag } from \"effect/unstable/cli\";\nimport { Harness } from \"../effect/harness.ts\";\nimport { Sandbox } from \"../effect/sandbox.ts\";\nimport { Session } from \"../effect/session.ts\";\nimport { EventList } from \"../event/list.ts\";\nimport type { EventSchema } from \"../event/schema.ts\";\nimport { addUsage, emptyUsage, header, usage, type UsageSummary } from \"./output.ts\";\n\nconst thinkingLevels = [\"off\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"] as const;\nconst sandboxDrivers = [\"local\", \"daytona\", \"vercel\"] as const;\n\nconst writeOut = (value: string) => Effect.sync(() => void process.stdout.write(value));\nconst writeError = (value: string) => Effect.sync(() => void process.stderr.write(value));\nconst terminalColumns = () => (process.stderr.isTTY ? (process.stderr.columns ?? 72) : 72);\n\nclass InvalidInputError extends Schema.TaggedError<InvalidInputError>()(\"CLI.InvalidInputError\", {\n\tmessage: Schema.String,\n}) {}\n\nclass ModelgenError extends Schema.TaggedError<ModelgenError>()(\"CLI.ModelgenError\", {\n\tcause: Schema.Defect(),\n}) {}\n\ninterface RenderState {\n\treadonly usage: UsageSummary;\n\treadonly textSeen: boolean;\n\treadonly textEndsWithNewline: boolean;\n}\n\nconst initialRenderState: RenderState = {\n\tusage: emptyUsage,\n\ttextSeen: false,\n\ttextEndsWithNewline: false,\n};\n\nconst isTextDelta = Schema.is(EventList.LLMTextDelta);\nconst isLLMEnded = Schema.is(EventList.LLMEnded);\nconst isTurnEnded = Schema.is(EventList.TurnEnded);\n\nconst render = (ended: Queue.Queue<string>, state: Ref.Ref<RenderState>) =>\n\tEffect.fn(\"CLI.render\")(function* (event: EventSchema.Payload) {\n\t\tif (isTextDelta(event)) {\n\t\t\tyield* writeOut(event.data.delta);\n\t\t\tif (event.data.delta.length > 0) {\n\t\t\t\tyield* Ref.update(state, (current) => ({\n\t\t\t\t\t...current,\n\t\t\t\t\ttextSeen: true,\n\t\t\t\t\ttextEndsWithNewline: event.data.delta.endsWith(\"\\n\"),\n\t\t\t\t}));\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (isLLMEnded(event)) {\n\t\t\tyield* Ref.update(state, (current) => ({ ...current, usage: addUsage(current.usage, event.data.message) }));\n\t\t\treturn;\n\t\t}\n\t\tif (isTurnEnded(event)) {\n\t\t\tyield* Queue.offer(ended, event.data.messageId);\n\t\t}\n\t});\n\nconst awaitMessage = Effect.fn(\"CLI.awaitMessage\")(function* (ended: Queue.Queue<string>, messageId: string) {\n\twhile ((yield* Queue.take(ended)) !== messageId) {\n\t\t// Earlier turns in the same drain are expected when a tool call continues.\n\t}\n});\n\nconst selectSandbox = Effect.fn(\"CLI.selectSandbox\")(function* (\n\tdriver: (typeof sandboxDrivers)[number],\n\tproviderResourceId?: string,\n) {\n\tif (driver === \"local\") return undefined;\n\tif (driver === \"daytona\") {\n\t\treturn providerResourceId === undefined\n\t\t\t? yield* Sandbox.create({ driver })\n\t\t\t: yield* Sandbox.register({ driver, providerResourceId });\n\t}\n\treturn providerResourceId === undefined\n\t\t? yield* Sandbox.create({ driver })\n\t\t: yield* Sandbox.register({ driver, providerResourceId });\n});\n\nconst root = Command.make(\"codework\").pipe(\n\tCommand.withSharedFlags({\n\t\thome: Flag.string(\"home\").pipe(Flag.withDescription(\"Harness data directory\"), Flag.optional),\n\t\tdatabase: Flag.string(\"database\").pipe(Flag.withDescription(\"SQLite path or :memory:\"), Flag.optional),\n\t}),\n\tCommand.withDescription(\"Run Codework agent sessions\"),\n);\n\nconst run = Command.make(\n\t\"run\",\n\t{\n\t\tprompt: Argument.string(\"prompt\").pipe(Argument.withDescription(\"Prompt for the agent\")),\n\t\tsession: Flag.string(\"session\").pipe(\n\t\t\tFlag.withAlias(\"s\"),\n\t\t\tFlag.withDescription(\"Continue an existing session ID\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tcwd: Flag.string(\"cwd\").pipe(\n\t\t\tFlag.withAlias(\"C\"),\n\t\t\tFlag.withDescription(\"Working directory for a new session\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tsandbox: Flag.choice(\"sandbox\", sandboxDrivers).pipe(\n\t\t\tFlag.withDescription(\"Sandbox for a new session (default: local)\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tsandboxProviderId: Flag.string(\"sandbox-provider-id\").pipe(\n\t\t\tFlag.withDescription(\"Provider ID of an existing sandbox\"),\n\t\t\tFlag.optional,\n\t\t),\n\t\tprovider: Flag.string(\"provider\").pipe(Flag.withDescription(\"Model catalog provider ID\"), Flag.optional),\n\t\tmodel: Flag.string(\"model\").pipe(Flag.withDescription(\"Model ID\"), Flag.optional),\n\t\tthinking: Flag.choice(\"thinking\", thinkingLevels).pipe(Flag.withDescription(\"Thinking level\"), Flag.optional),\n\t},\n\tEffect.fn(\"CLI.run\")(function* ({ prompt, session, cwd, sandbox, sandboxProviderId, provider, model, thinking }) {\n\t\tconst shared = yield* root;\n\t\tif (\n\t\t\tOption.isSome(session) &&\n\t\t\t(Option.isSome(cwd) || Option.isSome(sandbox) || Option.isSome(sandboxProviderId))\n\t\t) {\n\t\t\treturn yield* new InvalidInputError({\n\t\t\t\tmessage: \"--cwd, --sandbox, and --sandbox-provider-id can only be used when creating a new session\",\n\t\t\t});\n\t\t}\n\t\tif (Option.isSome(provider) !== Option.isSome(model)) {\n\t\t\treturn yield* new InvalidInputError({ message: \"--provider and --model must be provided together\" });\n\t\t}\n\t\tif (Option.isSome(sandboxProviderId) && (Option.isNone(sandbox) || sandbox.value === \"local\")) {\n\t\t\treturn yield* new InvalidInputError({ message: \"--sandbox-provider-id requires a remote --sandbox\" });\n\t\t}\n\t\tconst program = Effect.gen(function* () {\n\t\t\tconst runtime = {\n\t\t\t\t...(Option.isNone(provider) || Option.isNone(model)\n\t\t\t\t\t? {}\n\t\t\t\t\t: { model: { provider: provider.value, id: model.value } }),\n\t\t\t\t...(Option.isNone(thinking) ? {} : { thinkingLevel: thinking.value }),\n\t\t\t};\n\t\t\tlet handle: Session.Handle;\n\t\t\tif (Option.isSome(session)) {\n\t\t\t\thandle = yield* Session.attach({ sessionId: Session.SessionSchema.ID.make(session.value), ...runtime });\n\t\t\t} else {\n\t\t\t\tconst selectedSandbox = Option.getOrElse(sandbox, () => \"local\" as const);\n\t\t\t\tconst selected = yield* selectSandbox(selectedSandbox, Option.getOrUndefined(sandboxProviderId));\n\t\t\t\thandle = yield* Session.create({\n\t\t\t\t\ttitle: \"CLI\",\n\t\t\t\t\t...runtime,\n\t\t\t\t\t...(selected === undefined ? {} : { sandbox: selected }),\n\t\t\t\t\t...(Option.isNone(cwd) ? {} : { directory: cwd.value }),\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst ended = yield* Queue.unbounded<string>();\n\t\t\tconst renderState = yield* Ref.make(initialRenderState);\n\t\t\tconst printer = yield* handle\n\t\t\t\t.events()\n\t\t\t\t.pipe(Stream.runForEach(render(ended, renderState)), Effect.forkScoped({ startImmediately: true }));\n\n\t\t\tconst info = yield* handle.info;\n\t\t\tconst columns = terminalColumns();\n\t\t\tyield* writeError(\n\t\t\t\theader({\n\t\t\t\t\tsessionId: handle.id,\n\t\t\t\t\tsandbox: info.sandbox?.driver ?? \"local\",\n\t\t\t\t\tdirectory: info.directory,\n\t\t\t\t\tcolumns,\n\t\t\t\t}),\n\t\t\t);\n\t\t\tyield* handle.run(prompt);\n\t\t\tconst path = yield* handle.path();\n\t\t\tconst leaf = path.at(-1);\n\t\t\tif (leaf !== undefined) yield* awaitMessage(ended, leaf.entry.id);\n\t\t\tyield* Fiber.interrupt(printer);\n\t\t\tconst rendered = yield* Ref.get(renderState);\n\t\t\tif (rendered.textSeen && !rendered.textEndsWithNewline) yield* writeOut(\"\\n\");\n\t\t\tyield* writeError(usage(rendered.usage, columns));\n\t\t});\n\n\t\treturn yield* program.pipe(\n\t\t\tEffect.provide(\n\t\t\t\tHarness.layer({\n\t\t\t\t\t...(Option.isNone(shared.home) ? {} : { home: shared.home.value }),\n\t\t\t\t\t...(Option.isNone(shared.database) ? {} : { database: shared.database.value }),\n\t\t\t\t\tsandboxes: [Sandbox.Drivers.daytona(), Sandbox.Drivers.vercel()],\n\t\t\t\t}),\n\t\t\t),\n\t\t\tEffect.scoped,\n\t\t);\n\t}),\n).pipe(\n\tCommand.withDescription(\"Start or continue an agent session\"),\n\tCommand.withExamples([\n\t\t{ command: 'codework run \"Inspect the failing tests\"', description: \"Create a session\" },\n\t\t{\n\t\t\tcommand: 'codework run --provider openai --model gpt-5.5 --thinking high \"Inspect the failing tests\"',\n\t\t\tdescription: \"Create a session with an explicit model\",\n\t\t},\n\t\t{\n\t\t\tcommand: 'codework run --sandbox daytona \"Inspect the repository\"',\n\t\t\tdescription: \"Create a Daytona sandbox and session\",\n\t\t},\n\t\t{\n\t\t\tcommand: 'codework run --sandbox daytona --sandbox-provider-id <id> \"Inspect the repository\"',\n\t\t\tdescription: \"Use an existing remote sandbox\",\n\t\t},\n\t\t{\n\t\t\tcommand: 'codework run --session <id> --provider openai --model gpt-5.6-luna \"Now fix them\"',\n\t\t\tdescription: \"Continue a session with explicit model bindings\",\n\t\t},\n\t]),\n);\n\nconst modelgen = Command.make(\n\t\"modelgen\",\n\t{\n\t\tpath: Argument.string(\"path\").pipe(\n\t\t\tArgument.withDescription(\"Output path; defaults to CODEWORK_MODELS_FILE or ./models.gen.json\"),\n\t\t\tArgument.optional,\n\t\t),\n\t},\n\tEffect.fn(\"CLI.modelgen\")(function* ({ path }) {\n\t\tconst generated = yield* Effect.tryPromise({\n\t\t\ttry: () => generateModels(Option.isNone(path) ? {} : { path: path.value }),\n\t\t\tcatch: (cause) => new ModelgenError({ cause }),\n\t\t});\n\t\tyield* writeOut(`Generated model catalog at ${generated}\\n`);\n\t}),\n).pipe(\n\tCommand.withDescription(\"Generate the Aikit model catalog\"),\n\tCommand.withExamples([{ command: \"codework modelgen\", description: \"Generate models.gen.json\" }]),\n);\n\nexport const command = root.pipe(Command.withSubcommands([run, modelgen]));\n\nexport const main: Effect.Effect<void, Command.Error<typeof command> | CliError.CliError> = Command.run(command, {\n\tversion: \"0.0.1\",\n}).pipe(Effect.provide(NodeServices.layer));\n\nNodeRuntime.runMain(main);\n"],"mappings":"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"}
package/effect.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.mjs";
2
- import { c as harness_exports, r as session_exports, s as sandbox_exports, u as tool_exports } from "./session-CF1B0VEr.mjs";
2
+ import { c as harness_exports, r as session_exports, s as sandbox_exports, u as tool_exports } from "./session-DZZfASHn.mjs";
3
3
  //#region src/effect/tools.ts
4
4
  var tools_exports = /* @__PURE__ */ __exportAll({
5
5
  Tools: () => tools_exports,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeworksh/harness",
3
- "version": "0.0.1-dev.20260824182323",
3
+ "version": "0.0.1-dev.20260824191503",
4
4
  "description": "AI agent harness — agent loop, tools, and code execution sandbox.",
5
5
  "keywords": [
6
6
  "agent",
@@ -985,7 +985,6 @@ const layer$20 = Layer.effect(Service$13, Effect.gen(function* () {
985
985
  const sql = yield* SqlClient.SqlClient;
986
986
  const pubsub = {
987
987
  all: yield* PubSub.unbounded(),
988
- durable: /* @__PURE__ */ new Map(),
989
988
  typed: /* @__PURE__ */ new Map()
990
989
  };
991
990
  const projectors = /* @__PURE__ */ new Map();
@@ -993,7 +992,6 @@ const layer$20 = Layer.effect(Service$13, Effect.gen(function* () {
993
992
  yield* Effect.addFinalizer(() => Effect.gen(function* () {
994
993
  yield* PubSub.shutdown(pubsub.all);
995
994
  yield* Effect.forEach(pubsub.typed.values(), PubSub.shutdown, { discard: true });
996
- yield* Effect.forEach(pubsub.durable.values(), (wakes) => Effect.forEach(wakes, PubSub.shutdown, { discard: true }), { discard: true });
997
995
  }));
998
996
  const observe = (event, observer) => Effect.suspend(() => observer(event)).pipe(Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), (cause) => Effect.logError("Event listener failed", {
999
997
  eventID: event.id,
@@ -1097,7 +1095,7 @@ const layer$20 = Layer.effect(Service$13, Effect.gen(function* () {
1097
1095
  message: `Expected string aggregate field ${durable.aggregate}`
1098
1096
  }));
1099
1097
  const encoded = yield* Schema.encodeUnknownEffect(definition.data)(event.data);
1100
- const committed = yield* sql.withTransaction(Effect.gen(function* () {
1098
+ return yield* sql.withTransaction(Effect.gen(function* () {
1101
1099
  const { seq } = yield* bumpSequence(aggregateId);
1102
1100
  const existing = yield* findEventById(event.id);
1103
1101
  if (Option.isSome(existing)) return yield* Effect.die(new InvalidDurableEventError({
@@ -1122,8 +1120,6 @@ const layer$20 = Layer.effect(Service$13, Effect.gen(function* () {
1122
1120
  });
1123
1121
  return payload;
1124
1122
  }));
1125
- yield* Effect.forEach(pubsub.durable.get(aggregateId) ?? [], (wake) => PubSub.publish(wake, void 0), { discard: true });
1126
- return committed;
1127
1123
  }, Effect.orDie, Effect.uninterruptible);
1128
1124
  const publishEvent = Effect.fn("Event.publishEvent")(function* (definition, event) {
1129
1125
  if (definition.durable) {
@@ -1175,19 +1171,32 @@ const layer$20 = Layer.effect(Service$13, Effect.gen(function* () {
1175
1171
  const subscribe = (definition) => Stream.unwrap(getOrCreate(definition).pipe(Effect.map((created) => Stream.fromPubSub(created)))).pipe(Stream.map((event) => event));
1176
1172
  const streamAll = () => Stream.fromPubSub(pubsub.all);
1177
1173
  const stream = (input) => {
1178
- const tail = streamAll().pipe(Stream.filter((event) => event.data.sessionId === input.sessionId));
1179
- if (input.after === void 0) return tail;
1180
- return Stream.paginate(input.after, Effect.fn("Event.stream.history")(function* (after) {
1181
- const page = yield* readAggregate({
1182
- aggregateId: input.sessionId,
1183
- after,
1184
- limit: 100,
1185
- manifest: Manifest
1186
- });
1187
- const last = page.events.at(-1);
1188
- const next = page.hasMore && last?.durable !== void 0 ? Option.some(last.durable.seq) : Option.none();
1189
- return [page.events, next];
1190
- })).pipe(Stream.map((event) => event), Stream.concat(tail));
1174
+ const forSession = (event) => event.data.sessionId === input.sessionId;
1175
+ const after = input.after;
1176
+ if (after === void 0) return streamAll().pipe(Stream.filter(forSession));
1177
+ return Stream.unwrap(Effect.gen(function* () {
1178
+ const subscription = yield* PubSub.subscribe(pubsub.all);
1179
+ const watermark = yield* Ref.make(after);
1180
+ const history = Stream.paginate(after, Effect.fn("Event.stream.history")(function* (after) {
1181
+ const page = yield* readAggregate({
1182
+ aggregateId: input.sessionId,
1183
+ after,
1184
+ limit: 100,
1185
+ manifest: Manifest
1186
+ });
1187
+ const last = page.events.at(-1);
1188
+ const next = page.hasMore && last?.durable !== void 0 ? Option.some(last.durable.seq) : Option.none();
1189
+ return [page.events, next];
1190
+ })).pipe(Stream.map((event) => event), Stream.tap((event) => {
1191
+ const seq = event.durable?.seq;
1192
+ return seq === void 0 ? Effect.void : Ref.update(watermark, (current) => Math.max(current, seq));
1193
+ }));
1194
+ const tail = Stream.fromSubscription(subscription).pipe(Stream.filter(forSession), Stream.filterEffect((event) => {
1195
+ const seq = event.durable?.seq;
1196
+ return seq === void 0 ? Effect.succeed(true) : Ref.get(watermark).pipe(Effect.map((replayed) => seq > replayed));
1197
+ }));
1198
+ return history.pipe(Stream.concat(tail));
1199
+ }));
1191
1200
  };
1192
1201
  const listen = (listener) => Effect.sync(() => {
1193
1202
  listeners.push(listener);
@@ -7265,6 +7274,6 @@ const attach = Effect.fn("Session.attach")(function* (input) {
7265
7274
  return found.value;
7266
7275
  });
7267
7276
  //#endregion
7268
- export { create$2 as a, harness_exports as c, ID$4 as d, Drivers as i, layer as l, create$1 as n, register as o, session_exports as r, sandbox_exports as s, attach as t, tool_exports as u };
7277
+ export { create$2 as a, harness_exports as c, LLMEnded as d, LLMTextDelta as f, Drivers as i, layer as l, ID$4 as m, create$1 as n, register as o, TurnEnded as p, session_exports as r, sandbox_exports as s, attach as t, tool_exports as u };
7269
7278
 
7270
- //# sourceMappingURL=session-CF1B0VEr.mjs.map
7279
+ //# sourceMappingURL=session-DZZfASHn.mjs.map