Dashboard › opencode › Session 0gn8mvDqdnbu
0gn8mvDqdnbuGUBogPerform a frozen, read-only security and adversarial-concurrency review of repository /home/byk/Code/opencode using ONLY immutable Git objects. Exact base SHA: ab9408c81cc9916177641ae8e9f08624fc27b5b9. Exact head SHA: dc661f30cb2055ad6248fdc59b14998d834497be. Exact binary patch SHA-256: e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb. Built artifact path/hash is supporting evidence only: packages/opencode/dist/opencode-linux-x64/bin/opencode SHA-256 70a250ea7a1f6dba86d329cf09f728db00af41035975d3fb38a548ff7c9003d2. Do not read the worktree, mutate files, run services, or touch databases. Inspect every changed file via git show <sha>:<path> and git diff <base> <head>. Verify ancestry and diff-check. Focus on multi-process races, claim fencing, stale-owner behavior, replay ownership separation, duplicate synthetic continuation injection, cross-Session ID or FK confusion, startup denial of service from malformed/stale rows, untrusted durable event payloads, crash windows around provider dispatch and tool side effects, data disclosure to model history, execution-row cleanup, and migration constraints. Confirm Core never imports Server and no legacy SessionPrompt path is used. Classify findings as PASS, CONCERN, or MUST-FIX with precise HEAD file:line evidence. If blocked, quote exact command/error. Return a substantive report and end with exactly MERGE or DO-NOT-MERGE.
commit commit base ab9408c81cc9916177641ae8e9f08624fc27b5b9 base-tree d3323b963b9ef2de41a7ff9884299c74b93886ba base-parents 2e9407768d1401a25b69febb36657638a779053a base-subject feat(app): adapt vertical tab density head dc661f30cb2055ad6248fdc59b14998d834497be head-tree 261416647ae3fcff79038dddd095b0c8136e0f45 head-parents ab9408c81cc9916177641ae8e9f08624fc27b5b9 head-subject feat(core): recover interrupted sessions ancestry-exit 0
e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb - diff-check-exit 0
M .lore.md M packages/core/schema.json M packages/core/src/database/migration.gen.ts A packages/core/src/database/migration/20260914170650_session-recovery.ts A packages/core/src/database/migration/20260914214636_session-execution.ts A packages/core/src/database/migration/20260914222526_session-execution-phase.ts M packages/core/src/database/schema.gen.ts M packages/core/src/session/compaction.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/message-updater.ts M packages/core/src/session/projector.ts A packages/core/src/session/recovery.ts M packages/core/src/session/runner/llm.ts M packages/core/src/session/runner/publish-llm-event.ts M packages/core/src/session/sql.ts A packages/core/test/session-execution-local.test.ts M packages/core/test/session-projector.test.ts M packages/core/test/session-runner-tool-events.test.ts M packages/core/test/session-runner.test.ts M packages/opencode/src/server/routes/instance/httpapi/server.ts M packages/schema/src/sessi...
diff --git a/packages/core/src/database/migration.gen.ts b/packages/core/src/database/migration.gen.ts
index e6ea4eaa14..4dc7ffd5d1 100644
--- a/packages/core/src/database/migration.gen.ts
+++ b/packages/core/src/database/migration.gen.ts
@@ -1,44 +1,47 @@
import type { DatabaseMigration } from "./migration"
export const migrations = (
await Promise.all([
import("./migration/20260127222353_familiar_lady_ursula"),
import("./migration/20260211171708_add_project_commands"),
import("./migration/20260213144116_wakeful_the_professor"),
import("./migration/20260225215848_workspace"),
import("./migration/20260227213759_add_session_workspace_id"),
import("./migration/20260228203230_blue_harpoon"),
import("./migration/20260303231226_add_workspace_fields"),
import("./migration/20260309230000_move_org_to_state"),
import("./migration/20260312043431_session_message_cursor"),
import("./migration/20260323234822_events"),
...
diff --git a/packages/core/src/session/execution/local.ts b/packages/core/src/session/execution/local.ts
index d874c40832..100c7ea97d 100644
--- a/packages/core/src/session/execution/local.ts
+++ b/packages/core/src/session/execution/local.ts
@@ -1,46 +1,82 @@
-import { Cause, Effect, Layer } from "effect"
+import { and, eq } from "drizzle-orm"
+import { Cause, Effect, Exit, Layer } from "effect"
+import { Database } from "../../database/database"
import { LocationServiceMap } from "../../location-service-map"
import { makeGlobalNode } from "../../effect/app-node"
import { SessionRunCoordinator } from "../run-coordinator"
import { SessionRunner } from "../runner"
import { SessionSchema } from "../schema"
import { SessionStore } from "../store"
import { SessionExecution } from "../execution"
+import { SessionExecutionTable } from "../sql"
/** Current-process routing for implicit-local Locations. Future remote placement belongs here. */
+export const make = ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0a237bcc7001uVQg0MOD6hY3yt
295 ),
296 ),
297 ),
298 ).pipe(FiberSet.run(toolFibers))
299 }),
300 ),
301 Effect.ensuring(withPublication(publisher.flush())),
302 )
303
304 return yield* Effect.uninterruptibleMask((restore) =>
305 Effect.gen(function* () {
306 yield* db
307 .update(SessionExecutionTable)
308 .set({ phase: "unknown", assistant_message_id: null })
309 .where(eq(SessionExecutionTable.session_id, session.id))
310 .run()
311 .pipe(Effect.orDie)
312 const stream = yield* restore(providerStream).pipe(Effect.exit)
313 const failure =
314 stream._tag === "Failure" ? ...
1 import {
2 LLM,
3 LLMClient,
4 LLMError,
5 LLMEvent,
6 Message,
7 SystemPart,
8 isContextOverflowFailure,
9 type ProviderErrorEvent,
10 } from "@opencode-ai/llm"
11 import { Cause, DateTime, Effect, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
12 import { and, eq } from "drizzle-orm"
13 import { AgentV2 } from "../../agent"
14 import { Config } from "../../config"
15 import { Database } from "../../database/database"
16 import { EventV2 } from "../../event"
17 import { Location } from "../../location"
18 import { ModelV2 } from "../../model"
19 import { PermissionV2 } from "../../permission"
20 import { ProviderV2 } from "../../provider"
21 import { QuestionV2 } from "../../question"
22 import { SystemContext } from "../../system-context/index"
23 import { SystemContextRegistry } from "../../system-context/registry"
24 import { SkillGuidance } from ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0a23827320016FVMuwBfmOK3Ku
entries: readonly Entry[],
tokens: number,
): { readonly head: string; readonly recent: string } | undefined => {
const conversation = entries
.filter((entry) => entry.message.type !== "compaction")
.map((entry) => serialize(entry.message))
.filter(Boolean)
if (conversation.length === 0) return
let total = 0
let split = conversation.length
for (let index = conversation.length - 1; index >= 0; index--) {
const next = total + Token.estimate(conversation[index])
if (next > tokens) break
total = next
split = index
}
return {
head: conversation.slice(0, split).join("\n\n"),
recent: conversation.slice(split).join("\n\n"),
}
}
export const buildPrompt = (input: { readonly previousSummary?: string; readonly context: readonly string[] }) => {
const conversation = `Here is the conversation so ...
1 export * as SessionProjector from "./projector"
2
3 import { and, desc, eq, gt, or, sql } from "drizzle-orm"
4 import { DateTime, Effect, Layer, Schema } from "effect"
5 import { Database } from "../database/database"
6 import { EventV2 } from "../event"
7 import { makeGlobalNode } from "../effect/app-node"
8 import { SessionEvent } from "./event"
9 import { SessionV1 } from "../v1/session"
10 import { WorkspaceTable } from "../control-plane/workspace.sql"
11 import { SessionMessage } from "./message"
12 import { SessionMessageUpdater } from "./message-updater"
13 import { SessionInput } from "./input"
14 import { WorkspaceV2 } from "../workspace"
15 import {
16 MessageTable,
17 PartTable,
18 SessionInputTable,
19 SessionMessageTable,
20 SessionRecoveryTable,
21 SessionTable,
22 } from "./sql"
23 import type { DeepMutable } from "../schema"
24
25 type ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0a2389e0b001gbxLektxFBe3a2
packages/web/src/content/docs/ko/github.mdx: contains(github.event.comment.body, '/oc') ||
packages/web/src/content/docs/ko/github.mdx: contains(github.event.comment.body, '/opencode')
packages/web/src/content/docs/ko/github.mdx:OpenCode는 아래 GitHub event로 트리거할 수 있습니다.
packages/web/src/content/docs/ko/github.mdx:schedule event는 지시를 추출할 댓글이 없기 때문에 `prompt` 입력이 **필수**입니다. 또한 schedule workflow는 permission 체크용 사용자 맥락 없이 실행되므로, OpenCode가 branch나 PR을 만들게 하려면 `contents: write`와 `pull-requests: write`를 부여해야 합니다.
packages/web/src/content/docs/ko/github.mdx:`pull_request` event에서 `prompt`를 지정하지 않으면 OpenCode는 pull request 리뷰를 기본 동작으로 수행합니다.
packages/web/src/content/docs/ko/github.mdx:`issues` event 역시 지시를 추출할 댓글이 없기 때문에 `prompt` 입력이 **필수**입니다.
packages/web/src/content/docs/ko/plugins.mdx: event: async ({ event }) => ...
packages/core/src/effect/app-node-platform.ts packages/core/src/effect/app-node.ts packages/core/src/event/sql.ts
packages/core/src/event.ts
packages/core/src/event
1 export * as EventV2 from "./event"
2
3 import { Cause, Context, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect"
4 import { Event } from "@opencode-ai/schema/event"
5 import type { Data, Definition, Payload } from "@opencode-ai/schema/event"
6 import { and, asc, eq, gt, inArray } from "drizzle-orm"
7 import { Database } from "./database/database"
8 import { EventSequenceTable, EventTable } from "./event/sql"
9 import { Location } from "./location"
10 import { makeGlobalNode } from "./effect/app-node"
11 import { isDeepStrictEqual } from "node:util"
12 import { Durable } from "@opencode-ai/schema/durable-event-manifest"
13
14 export const ID = Event.ID
15 export type ID = import("@opencode-ai/schema/event").ID
16 export type { Data, Definition, Payload } from "@opencode-ai/schema/event"
17
18 export type Subscriber<D extends ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration.gen.ts:43: import("./migration/20260914170650_session-recovery"),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260914170650_session-recovery.ts:5: id: "20260914170650_session-recovery",
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260914170650_session-recovery.ts:9: CREATE TABLE \`session_recovery\` (
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260914170650_session-recovery.ts:14: CONSTRAINT \`session_recovery_pk\` PRIMARY KEY(\`session_id\`, \`assistant_message_id\`),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260914170650_session-recovery.ts:15: CONSTRAINT \`fk_session_recovery_session_id_session_id_fk\` FOREIGN KEY (\`session_id\`) REFERENCES \`session\`(\`id\`) ON DELETE ...
1 export * as SessionCompaction from "./compaction"
2
3 import { LLM, LLMError, LLMEvent, Message, type LLMRequest, type Model } from "@opencode-ai/llm"
4 import { DateTime, Effect, Stream } from "effect"
5 import type { Config } from "../config"
6 import type { EventV2 } from "../event"
7 import { SessionEvent } from "./event"
8 import { SessionMessage } from "./message"
9 import { SessionSchema } from "./schema"
10 import { Token } from "../util/token"
11
12 const DEFAULT_BUFFER = 20_000
13 const DEFAULT_KEEP_TOKENS = 8_000
14 const TOOL_OUTPUT_MAX_CHARS = 2_000
15 const SUMMARY_OUTPUT_TOKENS = 4_096
16 const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response. ...
diff --git a/packages/core/schema.json b/packages/core/schema.json
index d0eeeebd5c..072f4434d9 100644
--- a/packages/core/schema.json
+++ b/packages/core/schema.json
@@ -1,25 +1,27 @@
{
"version": "7",
"dialect": "sqlite",
- "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
- "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
+ "id": "57591e35-6f8f-4cda-932c-6a08e85caae7",
+ "prevIds": [
+ "9401e0ed-b1e3-4557-bc94-1c9b29e1e09c"
+ ],
"ddl": [
{
"name": "workspace",
"entityType": "tables"
},
{
"name": "data_migration",
"entityType": "tables"
},
{
"name": "account_state",
"entityType": "tables"
},
{
"name": "account",
"entityType": "tables"
},
{
"name": "control_account",
"entityType": "tables"
@@ -43,48 +45,56 @@
{
"name": "project_directory",
"entityType": "tables"
},
{
"name": "project",
"entityType": "tables"
...
diff --git a/packages/opencode/src/server/routes/instance/httpapi/server.ts b/packages/opencode/src/server/routes/instance/httpapi/server.ts
index fb9d2db656..748e8aa43b 100644
--- a/packages/opencode/src/server/routes/instance/httpapi/server.ts
+++ b/packages/opencode/src/server/routes/instance/httpapi/server.ts
@@ -1,148 +1,149 @@
import { Config as EffectConfig, Context, Effect, Layer } from "effect"
import { HttpApiBuilder, OpenApi } from "effect/unstable/httpapi"
import { HttpClient, HttpMiddleware, HttpRouter, HttpServer, HttpServerResponse } from "effect/unstable/http"
import * as Socket from "effect/unstable/socket/Socket"
import { FSUtil } from "@opencode-ai/core/fs-util"
import * as Observability from "@opencode-ai/core/observability"
import { Account } from "@/account/account"
import { Agent } from "@/agent/agent"
import { Auth } from "@/auth"
import { BackgroundJob } from "@/background/job"
import { Command } from "@/command"
import { Config } from ...
1 export * as SessionRunCoordinator from "./run-coordinator"
2
3 import { Deferred, Effect, Exit, Fiber, FiberSet, Scope } from "effect"
4
5 /** Serializes execution for each key while allowing different keys to run concurrently. */
6 export interface Coordinator<Key, E> {
7 /** Snapshots keys with an execution owned by this coordinator. */
8 readonly active: Effect.Effect<ReadonlySet<Key>>
9 /** Starts execution while idle or joins the active execution. */
10 readonly run: (key: Key) => Effect.Effect<void, E>
11 /** Registers one coalesced follow-up after newly recorded work. */
12 readonly wake: (key: Key) => Effect.Effect<void>
13 /** Stops active execution and waits for its cleanup. */
14 readonly interrupt: (key: Key) => Effect.Effect<void>
15 }
16
17 type Entry<E> = {
18 readonly done: Deferred.Deferred<void, E>
19 owner?: Fiber.Fiber<void, never>
20 pendingWake: ...
/usr/bin/bash: -c: line 1: syntax error near unexpected token `)'
/usr/bin/bash: -c: line 1: `printf '%s\n' '===== Core imports from Server packages/paths ====='; git grep -n -E 'from ["'"'](@opencode-ai/server|\.\.?/.*server)(/|["'"'])' dc661f30cb2055ad6248fdc59b14998d834497be -- packages/core/src || true; printf '%s\n' '===== Core SessionPrompt references ====='; git grep -n 'SessionPrompt' dc661f30cb2055ad6248fdc59b14998d834497be -- packages/core/src || true; printf '%s\n' '===== Recovery legacy-path references ====='; git grep -n -E 'SessionPrompt\.(prompt|loop)|@/session/prompt|session/processor' dc661f30cb2055ad6248fdc59b14998d834497be -- packages/core/src/session/recovery.ts packages/core/src/session/execution/local.ts packages/core/src/session/runner/llm.ts packages/server/src/routes.ts || true; printf '%s\n' '===== Event replay ownership references in recovery/execution ====='; git grep -n -E 'owner_id|ownerID|event_sequence|\.claim\(' dc661f30cb2055ad6248fdc59b14998d834497be ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/runner/llm.ts:49: * `SessionPrompt` monolith. Implement the unchecked items in small reviewed slices:
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/execution/local.ts:18: const ownerID = crypto.randomUUID()
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/execution/local.ts:22: .where(and(eq(SessionExecutionTable.session_id, sessionID), eq(SessionExecutionTable.owner_id, ownerID)))
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/execution/local.ts:32: owner_id: ownerID,
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/sql.ts:201: owner_id: text().notNull(),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/sql.ts:208: index("session_execution_owner_idx").on(table.owner_id),
diff --git a/packages/core/test/session-execution-local.test.ts b/packages/core/test/session-execution-local.test.ts
new file mode 100644
index 0000000000..f4c28ebf94
--- /dev/null
+++ b/packages/core/test/session-execution-local.test.ts
@@ -0,0 +1,80 @@
+import { describe, expect } from "bun:test"
+import { Deferred, Effect, Exit, Scope } from "effect"
+import { eq } from "drizzle-orm"
+import { Database } from "@opencode-ai/core/database/database"
+import { Project } from "@opencode-ai/core/project"
+import { ProjectTable } from "@opencode-ai/core/project/sql"
+import { AbsolutePath } from "@opencode-ai/core/schema"
+import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
+import { SessionSchema } from "@opencode-ai/core/session/schema"
+import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
+import { testEffect } from "./lib/effect"
+
+const sessionID = SessionSchema.ID.make("ses_execution_local")
+const it = ...
diff --git a/packages/core/test/session-projector.test.ts b/packages/core/test/session-projector.test.ts
index 7ebcd97314..80f5a8669f 100644
--- a/packages/core/test/session-projector.test.ts
+++ b/packages/core/test/session-projector.test.ts
@@ -1,107 +1,321 @@
import { describe, expect } from "bun:test"
-import { DateTime, Effect, Schema } from "effect"
+import { DateTime, Effect, Exit, Schema } from "effect"
import { asc, eq, sql } from "drizzle-orm"
import { Database } from "@opencode-ai/core/database/database"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { EventV2 } from "@opencode-ai/core/event"
import { EventTable } from "@opencode-ai/core/event/sql"
import { ModelV2 } from "@opencode-ai/core/model"
import { Project } from "@opencode-ai/core/project"
import { ProjectTable } from "@opencode-ai/core/project/sql"
import { ProviderV2 } from "@opencode-ai/core/provider"
...
diff --git a/packages/core/test/session-runner-tool-events.test.ts b/packages/core/test/session-runner-tool-events.test.ts
index f96ea4dea2..3362d4340b 100644
--- a/packages/core/test/session-runner-tool-events.test.ts
+++ b/packages/core/test/session-runner-tool-events.test.ts
@@ -1,127 +1,129 @@
import { expect, test } from "bun:test"
import { Effect, Schema, Stream } from "effect"
import { LLMEvent } from "@opencode-ai/llm"
import { EventV2 } from "@opencode-ai/core/event"
import { SessionEvent } from "@opencode-ai/core/session/event"
import { SessionMessage } from "@opencode-ai/core/session/message"
import { SessionV2 } from "@opencode-ai/core/session"
import { ModelV2 } from "@opencode-ai/core/model"
import { ProviderV2 } from "@opencode-ai/core/provider"
import { createLLMEventPublisher } from "@opencode-ai/core/session/runner/publish-llm-event"
const sessionID = SessionV2.ID.make("ses_tool_event_test")
const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
const ...
diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts
index cc58b43b29..5ddc34f457 100644
--- a/packages/core/test/session-runner.test.ts
+++ b/packages/core/test/session-runner.test.ts
@@ -1,353 +1,348 @@
import { describe, expect } from "bun:test"
import {
LLMClient,
LLMError,
LLMEvent,
Model,
TransportReason,
InvalidRequestReason,
type LLMClientShape,
type LLMRequest,
} from "@opencode-ai/llm"
import * as OpenAIChat from "@opencode-ai/llm/protocols/openai-chat"
import { Database } from "@opencode-ai/core/database/database"
import { makeLocationNode } from "@opencode-ai/core/effect/app-node"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNodePlatform } from "@opencode-ai/core/effect/app-node-platform"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { EventV2 } from "@opencode-ai/core/event"
import { PermissionV2 } from ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0a23ca4f6001oOBgXZCcCPYS02
<!-- lore:01a07f3f-8845-7e32-a4d3-326747130efa -->
* **PTY event priority ordering**: Always process PTY events before system-bus events in the supervisor event loop. Equal or bus-first priority looks simpler, but can report lifecycle completion before buffered terminal output is drained; exit must be emitted only after both output descriptors reach EOF.
<!-- lore:01a07f3f-880c-7b2c-88bc-ec9b5ec590e7 -->
* **PTY random unit names**: Always generate PTY transient-unit names from 128 random bits using the strict grammar \`opencode-pty-\[0-9a-f]{32}.service\`; clients must never supply names. Chose random supervisor-owned names over bus-derived or caller-provided names because caller control enables injection and unsafe cleanup, while predictable shared identities increase collision and targeting risk.
<!-- lore:01a07f3f-887d-70fe-b999-891d818f4f96 -->
* ...
diff --git a/packages/core/src/session/message-updater.ts b/packages/core/src/session/message-updater.ts
index 46118a89fe..cfcb3cd899 100644
--- a/packages/core/src/session/message-updater.ts
+++ b/packages/core/src/session/message-updater.ts
@@ -180,100 +180,146 @@ export function update(adapter: Adapter, event: SessionEvent.Event) {
draft.time.completed = event.data.timestamp
}),
)
}
})
},
"session.next.step.started": (event) => {
return Effect.gen(function* () {
const currentAssistant = yield* adapter.getCurrentAssistant()
if (currentAssistant) {
yield* adapter.updateAssistant(
produce(currentAssistant, (draft) => {
draft.time.completed = event.data.timestamp
}),
)
}
yield* adapter.appendMessage(
SessionMessage.Assistant.make({
id: ...
diff --git a/packages/core/src/session/runner/llm.ts b/packages/core/src/session/runner/llm.ts
index 874086a06b..4b4d1f9146 100644
--- a/packages/core/src/session/runner/llm.ts
+++ b/packages/core/src/session/runner/llm.ts
@@ -1,113 +1,115 @@
import {
LLM,
LLMClient,
LLMError,
LLMEvent,
Message,
SystemPart,
isContextOverflowFailure,
type ProviderErrorEvent,
} from "@opencode-ai/llm"
import { Cause, DateTime, Effect, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
+import { and, eq } from "drizzle-orm"
import { AgentV2 } from "../../agent"
import { Config } from "../../config"
import { Database } from "../../database/database"
import { EventV2 } from "../../event"
import { Location } from "../../location"
import { ModelV2 } from "../../model"
import { PermissionV2 } from "../../permission"
import { ProviderV2 } from "../../provider"
import { QuestionV2 } from "../../question"
import { SystemContext } from "../../system-context/index"
...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/execution/local.ts:68: return yield* SessionRunner.Service.use((runner) => runner.run({ sessionID, force })).pipe(
46: 46 : ToolOutput.toResultValue({ structured: tool.state.structured, content: tool.state.content })
55: 55 if (tool.state.status === "error") {
62: 62 : { error: tool.state.error, content: tool.state.content, structured: tool.state.structured },
70: 70 const assistant = (message: SessionMessage.Assistant, model: Model) => {
110: 110 Message.make({ id: message.id, role: "assistant", content: meaningful, metadata: message.metadata }),
132: 132 case "synthetic":
145: 145 case "assistant":
146: 146 return assistant(message, model)
1 import {
2 Message,
3 ToolCallPart,
4 ToolOutput,
5 ToolResultPart,
6 type ContentPart,
7 type Model,
8 type ProviderMetadata,
9 } from "@opencode-ai/llm"
10 import { SessionMessage } from "../message"
11 import type { FileAttachment } from "../prompt"
12
13 const media = (file: FileAttachment): ContentPart => ({
14 type: "media",
15 mediaType: file.mime,
16 data: file.uri,
17 filename: file.name,
18 metadata: file.description === undefined ? undefined : { description: file.description },
19 })
20
21 const toolInput = (tool: SessionMessage.AssistantTool) => {
22 if (tool.state.status !== "pending") return tool.state.input
23 try {
24 return JSON.parse(tool.state.input) as unknown
25 } catch {
26 return tool.state.input
27 }
28 }
29
30 const toolCall = (tool: SessionMessage.AssistantTool, providerMetadata: ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/opencode/src/control-plane/workspace.ts:351: .replay(
dc661f30cb2055ad6248fdc59b14998d834497be:packages/opencode/src/control-plane/workspace.ts:402: const failed = yield* events.replay(payload.syncEvent, { publish: true, ownerID: space.id }).pipe(
dc661f30cb2055ad6248fdc59b14998d834497be:packages/opencode/src/server/routes/instance/httpapi/handlers/sync.ts:51: yield* events.replayAll(payload, { ownerID, strictOwner: true })
1 import { Workspace } from "@/control-plane/workspace"
2 import * as InstanceState from "@/effect/instance-state"
3 import { Session } from "@/session/session"
4 import { Database } from "@opencode-ai/core/database/database"
5 import { EventV2 } from "@opencode-ai/core/event"
6 import { EventV2Bridge } from "@/event-v2-bridge"
7 import { EventTable } from "@opencode-ai/core/event/sql"
8 import { asc } from "drizzle-orm"
9 import { and } from "drizzle-orm"
10 import { eq } from "drizzle-orm"
11 import { lte } from "drizzle-orm"
12 import { not } from "drizzle-orm"
13 import { or } from "drizzle-orm"
14 import { Effect, Scope } from "effect"
15 import { HttpApiBuilder, HttpApiError } from "effect/unstable/httpapi"
16 import { InstanceHttpApi } from "../api"
17 import { HistoryPayload, ReplayPayload, SessionPayload } from "../groups/sync"
18
19 export const syncHandlers = ...
1 export * as SessionInput from "./input"
2
3 import { and, asc, eq, isNull, lte } from "drizzle-orm"
4 import { DateTime, Effect, Schema } from "effect"
5 import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
6 import type { Database } from "../database/database"
7 import type { EventV2 } from "../event"
8 import { SessionEvent } from "./event"
9 import { SessionMessage } from "./message"
10 import { Prompt } from "./prompt"
11 import { SessionSchema } from "./schema"
12 import { SessionInputTable, SessionMessageTable } from "./sql"
13
14 type DatabaseService = Database.Interface["db"]
15
16 export { Admitted, Delivery }
17
18 const decodePrompt = Schema.decodeUnknownSync(Prompt)
19 const encodePrompt = Schema.encodeSync(Prompt)
20
21 const fromRow = (row: typeof SessionInputTable.$inferSelect): Admitted =>
22 Admitted.make({
23 admittedSeq: ...
1 {
2 "$schema": "https://json.schemastore.org/package.json",
3 "version": "1.18.30",
4 "name": "@opencode-ai/core",
5 "type": "module",
6 "license": "MIT",
7 "private": true,
8 "scripts": {
9 "db": "bun drizzle-kit",
10 "migration": "bun run script/migration.ts",
11 "fix-node-pty": "bun run script/fix-node-pty.ts",
12 "test": "bun test --only-failures",
13 "typecheck": "tsgo --noEmit"
14 },
15 "bin": {
16 "opencode": "./bin/opencode"
17 },
18 "exports": {
19 "./effect/layer-node": "./src/effect/layer-node.ts",
20 "./effect/app-node": "./src/effect/app-node.ts",
21 "./session/runner": "./src/session/runner/index.ts",
22 "./system-context": "./src/system-context/index.ts",
23 "./*": "./src/*.ts"
24 },
25 "imports": {
26 "#sqlite": {
27 "bun": "./src/database/sqlite.bun.ts",
28 ...
head-objects-inspected 22 head dc661f30cb2055ad6248fdc59b14998d834497be parent ab9408c81cc9916177641ae8e9f08624fc27b5b9 tree 261416647ae3fcff79038dddd095b0c8136e0f45 ancestry-exit 0 e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb - diff-check-exit 0
ab9408c81cc9916177641ae8e9f08624fc27b5b9dc661f30cb2055ad6248fdc59b14998d834497bee414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb — exact match.git diff --check: passed.git show.owner_id and time_created are recorded, but recovery ignores both:
packages/core/src/session/execution/local.ts:17-37 creates a process owner and inserts its execution claim.packages/core/src/session/recovery.ts:21-24 selects every execution row without checking owner liveness or age.packages/core/src/session/recovery.ts:66-80 deletes the row without proving its owner died.packages/core/src/session/recovery.ts:33-35 deletes by execution ID alone.A second server can therefore classify the first server’s active execution as abandoned, delete its claim, inject a continuation, and start another drain.
The original runner is not fenced:
packages/core/src/session/runner/llm.ts:236-253packages/core/src/session/runner/llm.ts:306-312packages/core/src/session/runner/llm.ts:325-333packages/core/src/session/runner/llm.ts:391-402These mutations match only Session or assistant IDs. They never match the execution ID or owner, and they never require one affected row. A stale runner continues to llm.stream after a zero-row update. If another claim now exists for the same Session, the stale runner can mutate that replacement claim. ...
Date: Sep 14, 2026 * 🟡 (23:29) `SessionInput.find` queries `SessionInputTable` by `SessionMessage.ID`, terminates database defects with `Effect.orDie`, and returns either `undefined` or an `Admitted` value decoded by `fromRow`. * 🟡 (23:29) `SessionInput.admit` is idempotent by message ID: it returns an existing input when found; otherwise publishes `SessionEvent.PromptAdmitted`, requires `event…
Date: Sep 14, 2026 * 🟡 (23:26) In commit `dc661f30cb2055ad6248fdc59b14998d834497be`, `packages/core/src/session/execution/local.ts:68` delegates local execution through `SessionRunner.Service.use((runner) => runner.run({ sessionID, force }))`. * 🟡 (23:26) Provider-history lowering code maps completed tool states to `ToolResultPart.make`; when `tool.provider?.executed === true` and `tool.state.r…
Date: Sep 14, 2026 * 🔴 (23:25) User directed that local session-runner ownership be replaced with durable multi-node ownership when operating in a cluster. * 🟡 (23:25) `packages/core/src/session/runner/llm.ts` was updated to pass a `startCommit` callback to `createLLMEventPublisher`; the callback receives `assistantMessageID` and performs a database operation as part of assistant-step startup. …
Date: Sep 14, 2026 * 🔴 (23:24) User directed that supervisor event loops always process PTY events before system-bus events. Equal or bus-first priority can report lifecycle completion before buffered terminal output drains; exit must be emitted only after both output descriptors reach EOF. * 🔴 (23:24) User directed that PTY transient-unit names always use 128 random bits and the strict grammar…
Date: Sep 14, 2026 * 🟡 (23:24) `packages/core/test/session-runner.test.ts` was updated to import and exercise `EventV2`, `ContextSnapshotDecodeError`, `SessionRunnerLLM`, and `SkillGuidance`; its runner layer is built with `AppNodeBuilder.build(SessionRunnerLLM.node, [...])` and includes `SessionExecution.Service`. * 🟡 (23:24) The `SessionExecution` test wiring in `packages/core/test/session-ru…
Date: Sep 14, 2026 * 🔴 (23:23) User supplied the exact conditional type decision `export type Output<Item> = [Item] extends [never] ? never : Item extends Node<infer A, unknown, any> ? A : never`, including the required branch phrasing `"never : Item extends Node<infer A,"`. * 🔴 (23:23) User supplied the exact conditional type decision `export type Error<Item> = [Item] extends [never] ? never :…
Date: Sep 14, 2026 * 🔴 (23:23) User stated that processes which never serve the HTTP routes—specifically CLI commands and scripts—should not pay the relevant initialization cost at module load. * 🟡 (23:23) Working-tree changes in `packages/opencode/src/server/routes/instance/httpapi/server.ts` defer server-related initialization until routes are served, avoiding module-load overhead for CLI com…
Date: Sep 14, 2026 * 🟡 (23:22) Session compaction implementation defines `DEFAULT_BUFFER = 20_000`, `DEFAULT_KEEP_TOKENS = 8_000`, `TOOL_OUTPUT_MAX_CHARS = 2_000`, and `SUMMARY_OUTPUT_TOKENS = 4_096`. * 🟡 (23:22) Session compaction uses `SUMMARY_TEMPLATE`, which requires an exact Markdown structure with sections in fixed order, including `## Objective`, `## Important Details`, `## Work State` w…
Date: Sep 14, 2026 * 🟡 (23:21) Repository search at commit `dc661f30cb2055ad6248fdc59b14998d834497be` found the existing table-based session recovery/execution implementation across migrations, generated schema, runner, recovery startup wiring, and tests. * 🟡 (23:21) Migration `packages/core/src/database/migration/20260914170650_session-recovery.ts` has ID `20260914170650_session-recovery` and …
Date: Sep 14, 2026 * 🔴 (23:20) User directed that when a feature or model is not configured automatically through OpenCode, “you can always configure it yourself.” * 🔴 (23:20) User directed: “Replace inbox-local admission sequence with event-sourced prompt admission and promotion sequences.” * 🔴 (23:20) User asserted abandoned side effects are “never silently replayed”; before assembling a pro…
Date: Sep 14, 2026 * 🔴 (23:20) User reaffirmed directive: “never resume an older assistant projection.” * 🟡 (23:20) Presented `SessionProjector` code contains the explicit invariant comment: `// A newer turn supersedes stale incomplete rows; never resume an older assistant projection.`
Date: Sep 14, 2026 * 🔴 (23:19) User directed: “never resume an older assistant projection.” * 🟡 (23:19) Presented code enforces that a newer turn supersedes stale incomplete rows and rejects invalid recovery with `Effect.die(\`Recovery target is not the latest assistant: ${event.data.assistantMessageID}\`)`. * 🟡 (23:19) Presented compaction logic selects non-compaction conversation entries wit…
Date: Sep 14, 2026 * 🔴 (23:19) User directed: “Replace local ownership with durable multi-node ownership when clustered.”
Date: Sep 14, 2026 * 🔴 (23:19) User established the session-projection invariant: “A newer turn supersedes stale incomplete rows; never resume an older assistant projection.”
Date: Sep 14, 2026 * 🟡 (23:19) `packages/core/src/session/execution/local.ts` was refactored from a static local coordinator layer to export `make = Effect.fnUntraced(function* (input: { readonly run: (sessionID: SessionSchema.ID, force: boolean) => Effect.Effect<void, SessionRunner.RunError> }) { ... })`; new imports include `and`, `eq` from `drizzle-orm`, `Exit` from `effect`, `Database`, and …
Date: Sep 14, 2026 * 🟡 (23:18) Immutable Git evidence confirmed HEAD `dc661f30cb2055ad6248fdc59b14998d834497be` (`feat(core): recover interrupted sessions`, tree `261416647ae3fcff79038dddd095b0c8136e0f45`) is a direct child of base `ab9408c81cc9916177641ae8e9f08624fc27b5b9` (`feat(app): adapt vertical tab density`, tree `d3323b963b9ef2de41a7ff9884299c74b93886ba`); ancestry check exited `0`. * 🟡…
Date: Sep 14, 2026 * 🔴 [requested-review] (23:16) User requested a frozen, read-only security and adversarial-concurrency review of repository `/home/byk/Code/opencode` using ONLY immutable Git objects. * 🔴 (23:16) User specified exact base SHA `ab9408c81cc9916177641ae8e9f08624fc27b5b9` and exact head SHA `dc661f30cb2055ad6248fdc59b14998d834497be`. * 🔴 (23:16) User specified exact binary patch…