Use SyncEvent service at event call sites (#26782)
This commit is contained in:
@@ -676,14 +676,12 @@ export const layer = Layer.effect(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (input.workspaceID === null) {
|
if (input.workspaceID === null) {
|
||||||
yield* Effect.sync(() =>
|
yield* sync.run(Session.Event.Updated, {
|
||||||
SyncEvent.run(Session.Event.Updated, {
|
sessionID: input.sessionID,
|
||||||
sessionID: input.sessionID,
|
info: {
|
||||||
info: {
|
workspaceID: null,
|
||||||
workspaceID: null,
|
},
|
||||||
},
|
})
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
log.info("session warp complete", {
|
log.info("session warp complete", {
|
||||||
workspaceID: input.workspaceID,
|
workspaceID: input.workspaceID,
|
||||||
|
|||||||
@@ -18,8 +18,9 @@ import { InstanceState } from "@/effect/instance-state"
|
|||||||
import { isOverflow as overflow, usable } from "./overflow"
|
import { isOverflow as overflow, usable } from "./overflow"
|
||||||
import { makeRuntime } from "@/effect/run-service"
|
import { makeRuntime } from "@/effect/run-service"
|
||||||
import { serviceUse } from "@/effect/service-use"
|
import { serviceUse } from "@/effect/service-use"
|
||||||
import { EventV2 } from "@/v2/event"
|
import { SyncEvent } from "@/sync"
|
||||||
import { SessionEvent } from "@/v2/session-event"
|
import { SessionEvent } from "@/v2/session-event"
|
||||||
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
|
|
||||||
const log = Log.create({ service: "session.compaction" })
|
const log = Log.create({ service: "session.compaction" })
|
||||||
|
|
||||||
@@ -219,6 +220,7 @@ export const layer: Layer.Layer<
|
|||||||
| Plugin.Service
|
| Plugin.Service
|
||||||
| SessionProcessor.Service
|
| SessionProcessor.Service
|
||||||
| Provider.Service
|
| Provider.Service
|
||||||
|
| SyncEvent.Service
|
||||||
> = Layer.effect(
|
> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
@@ -229,6 +231,7 @@ export const layer: Layer.Layer<
|
|||||||
const plugin = yield* Plugin.Service
|
const plugin = yield* Plugin.Service
|
||||||
const processors = yield* SessionProcessor.Service
|
const processors = yield* SessionProcessor.Service
|
||||||
const provider = yield* Provider.Service
|
const provider = yield* Provider.Service
|
||||||
|
const sync = yield* SyncEvent.Service
|
||||||
|
|
||||||
const isOverflow = Effect.fn("SessionCompaction.isOverflow")(function* (input: {
|
const isOverflow = Effect.fn("SessionCompaction.isOverflow")(function* (input: {
|
||||||
tokens: MessageV2.Assistant["tokens"]
|
tokens: MessageV2.Assistant["tokens"]
|
||||||
@@ -567,12 +570,14 @@ export const layer: Layer.Layer<
|
|||||||
parts: [],
|
parts: [],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
EventV2.run(SessionEvent.Compaction.Ended.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: input.sessionID,
|
yield* sync.run(SessionEvent.Compaction.Ended.Sync, {
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
sessionID: input.sessionID,
|
||||||
text: summary ?? "",
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
include: selected.tail_start_id,
|
text: summary ?? "",
|
||||||
})
|
include: selected.tail_start_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
yield* bus.publish(Event.Compacted, { sessionID: input.sessionID })
|
yield* bus.publish(Event.Compacted, { sessionID: input.sessionID })
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
@@ -601,11 +606,13 @@ export const layer: Layer.Layer<
|
|||||||
auto: input.auto,
|
auto: input.auto,
|
||||||
overflow: input.overflow,
|
overflow: input.overflow,
|
||||||
})
|
})
|
||||||
EventV2.run(SessionEvent.Compaction.Started.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: input.sessionID,
|
yield* sync.run(SessionEvent.Compaction.Started.Sync, {
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
sessionID: input.sessionID,
|
||||||
reason: input.auto ? "auto" : "manual",
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
})
|
reason: input.auto ? "auto" : "manual",
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return Service.of({
|
return Service.of({
|
||||||
@@ -626,6 +633,7 @@ export const defaultLayer = Layer.suspend(() =>
|
|||||||
Layer.provide(Plugin.defaultLayer),
|
Layer.provide(Plugin.defaultLayer),
|
||||||
Layer.provide(Bus.layer),
|
Layer.provide(Bus.layer),
|
||||||
Layer.provide(Config.defaultLayer),
|
Layer.provide(Config.defaultLayer),
|
||||||
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,11 @@ import { Question } from "@/question"
|
|||||||
import { errorMessage } from "@/util/error"
|
import { errorMessage } from "@/util/error"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
import { isRecord } from "@/util/record"
|
import { isRecord } from "@/util/record"
|
||||||
import { EventV2 } from "@/v2/event"
|
import { SyncEvent } from "@/sync"
|
||||||
import { SessionEvent } from "@/v2/session-event"
|
import { SessionEvent } from "@/v2/session-event"
|
||||||
import { Modelv2 } from "@/v2/model"
|
import { Modelv2 } from "@/v2/model"
|
||||||
import * as DateTime from "effect/DateTime"
|
import * as DateTime from "effect/DateTime"
|
||||||
|
import { Flag } from "@opencode-ai/core/flag/flag"
|
||||||
|
|
||||||
const DOOM_LOOP_THRESHOLD = 3
|
const DOOM_LOOP_THRESHOLD = 3
|
||||||
const log = Log.create({ service: "session.processor" })
|
const log = Log.create({ service: "session.processor" })
|
||||||
@@ -96,6 +97,7 @@ export const layer: Layer.Layer<
|
|||||||
| Image.Service
|
| Image.Service
|
||||||
| SessionSummary.Service
|
| SessionSummary.Service
|
||||||
| SessionStatus.Service
|
| SessionStatus.Service
|
||||||
|
| SyncEvent.Service
|
||||||
> = Layer.effect(
|
> = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
@@ -111,6 +113,7 @@ export const layer: Layer.Layer<
|
|||||||
const scope = yield* Scope.Scope
|
const scope = yield* Scope.Scope
|
||||||
const status = yield* SessionStatus.Service
|
const status = yield* SessionStatus.Service
|
||||||
const image = yield* Image.Service
|
const image = yield* Image.Service
|
||||||
|
const sync = yield* SyncEvent.Service
|
||||||
|
|
||||||
const create = Effect.fn("SessionProcessor.create")(function* (input: Input) {
|
const create = Effect.fn("SessionProcessor.create")(function* (input: Input) {
|
||||||
// Pre-capture snapshot before the LLM stream starts. The AI SDK
|
// Pre-capture snapshot before the LLM stream starts. The AI SDK
|
||||||
@@ -229,11 +232,13 @@ export const layer: Layer.Layer<
|
|||||||
case "reasoning-start":
|
case "reasoning-start":
|
||||||
if (value.id in ctx.reasoningMap) return
|
if (value.id in ctx.reasoningMap) return
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Reasoning.Started.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Reasoning.Started.Sync, {
|
||||||
reasoningID: value.id,
|
sessionID: ctx.sessionID,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
reasoningID: value.id,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
ctx.reasoningMap[value.id] = {
|
ctx.reasoningMap[value.id] = {
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
messageID: ctx.assistantMessage.id,
|
messageID: ctx.assistantMessage.id,
|
||||||
@@ -262,12 +267,14 @@ export const layer: Layer.Layer<
|
|||||||
case "reasoning-end":
|
case "reasoning-end":
|
||||||
if (!(value.id in ctx.reasoningMap)) return
|
if (!(value.id in ctx.reasoningMap)) return
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Reasoning.Ended.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Reasoning.Ended.Sync, {
|
||||||
reasoningID: value.id,
|
sessionID: ctx.sessionID,
|
||||||
text: ctx.reasoningMap[value.id].text,
|
reasoningID: value.id,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
text: ctx.reasoningMap[value.id].text,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
// oxlint-disable-next-line no-self-assign -- reactivity trigger
|
// oxlint-disable-next-line no-self-assign -- reactivity trigger
|
||||||
ctx.reasoningMap[value.id].text = ctx.reasoningMap[value.id].text
|
ctx.reasoningMap[value.id].text = ctx.reasoningMap[value.id].text
|
||||||
ctx.reasoningMap[value.id].time = { ...ctx.reasoningMap[value.id].time, end: Date.now() }
|
ctx.reasoningMap[value.id].time = { ...ctx.reasoningMap[value.id].time, end: Date.now() }
|
||||||
@@ -281,12 +288,14 @@ export const layer: Layer.Layer<
|
|||||||
throw new Error(`Tool call not allowed while generating summary: ${value.toolName}`)
|
throw new Error(`Tool call not allowed while generating summary: ${value.toolName}`)
|
||||||
}
|
}
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Tool.Input.Started.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Tool.Input.Started.Sync, {
|
||||||
callID: value.id,
|
sessionID: ctx.sessionID,
|
||||||
name: value.toolName,
|
callID: value.id,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
name: value.toolName,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
const part = yield* session.updatePart({
|
const part = yield* session.updatePart({
|
||||||
id: ctx.toolcalls[value.id]?.partID ?? PartID.ascending(),
|
id: ctx.toolcalls[value.id]?.partID ?? PartID.ascending(),
|
||||||
messageID: ctx.assistantMessage.id,
|
messageID: ctx.assistantMessage.id,
|
||||||
@@ -310,12 +319,14 @@ export const layer: Layer.Layer<
|
|||||||
|
|
||||||
case "tool-input-end": {
|
case "tool-input-end": {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Tool.Input.Ended.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Tool.Input.Ended.Sync, {
|
||||||
callID: value.id,
|
sessionID: ctx.sessionID,
|
||||||
text: "",
|
callID: value.id,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
text: "",
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,17 +336,19 @@ export const layer: Layer.Layer<
|
|||||||
}
|
}
|
||||||
const toolCall = yield* readToolCall(value.toolCallId)
|
const toolCall = yield* readToolCall(value.toolCallId)
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Tool.Called.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Tool.Called.Sync, {
|
||||||
callID: value.toolCallId,
|
sessionID: ctx.sessionID,
|
||||||
tool: value.toolName,
|
callID: value.toolCallId,
|
||||||
input: value.input,
|
tool: value.toolName,
|
||||||
provider: {
|
input: value.input,
|
||||||
executed: toolCall?.part.metadata?.providerExecuted === true,
|
provider: {
|
||||||
...(value.providerMetadata ? { metadata: value.providerMetadata } : {}),
|
executed: toolCall?.part.metadata?.providerExecuted === true,
|
||||||
},
|
...(value.providerMetadata ? { metadata: value.providerMetadata } : {}),
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
},
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
yield* updateToolCall(value.toolCallId, (match) => ({
|
yield* updateToolCall(value.toolCallId, (match) => ({
|
||||||
...match,
|
...match,
|
||||||
tool: value.toolName,
|
tool: value.toolName,
|
||||||
@@ -405,27 +418,29 @@ export const layer: Layer.Layer<
|
|||||||
attachments: attachments?.length ? attachments : undefined,
|
attachments: attachments?.length ? attachments : undefined,
|
||||||
}
|
}
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Tool.Success.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Tool.Success.Sync, {
|
||||||
callID: value.toolCallId,
|
sessionID: ctx.sessionID,
|
||||||
structured: output.metadata,
|
callID: value.toolCallId,
|
||||||
content: [
|
structured: output.metadata,
|
||||||
{
|
content: [
|
||||||
type: "text",
|
{
|
||||||
text: output.output,
|
type: "text",
|
||||||
|
text: output.output,
|
||||||
|
},
|
||||||
|
...(output.attachments?.map((item: MessageV2.FilePart) => ({
|
||||||
|
type: "file",
|
||||||
|
uri: item.url,
|
||||||
|
mime: item.mime,
|
||||||
|
name: item.filename,
|
||||||
|
})) ?? []),
|
||||||
|
],
|
||||||
|
provider: {
|
||||||
|
executed: toolCall?.part.metadata?.providerExecuted === true,
|
||||||
},
|
},
|
||||||
...(output.attachments?.map((item: MessageV2.FilePart) => ({
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
type: "file",
|
})
|
||||||
uri: item.url,
|
}
|
||||||
mime: item.mime,
|
|
||||||
name: item.filename,
|
|
||||||
})) ?? []),
|
|
||||||
],
|
|
||||||
provider: {
|
|
||||||
executed: toolCall?.part.metadata?.providerExecuted === true,
|
|
||||||
},
|
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
|
||||||
})
|
|
||||||
yield* completeToolCall(value.toolCallId, output)
|
yield* completeToolCall(value.toolCallId, output)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -433,18 +448,20 @@ export const layer: Layer.Layer<
|
|||||||
case "tool-error": {
|
case "tool-error": {
|
||||||
const toolCall = yield* readToolCall(value.toolCallId)
|
const toolCall = yield* readToolCall(value.toolCallId)
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Tool.Failed.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Tool.Failed.Sync, {
|
||||||
callID: value.toolCallId,
|
sessionID: ctx.sessionID,
|
||||||
error: {
|
callID: value.toolCallId,
|
||||||
type: "unknown",
|
error: {
|
||||||
message: errorMessage(value.error),
|
type: "unknown",
|
||||||
},
|
message: errorMessage(value.error),
|
||||||
provider: {
|
},
|
||||||
executed: toolCall?.part.metadata?.providerExecuted === true,
|
provider: {
|
||||||
},
|
executed: toolCall?.part.metadata?.providerExecuted === true,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
},
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
yield* failToolCall(value.toolCallId, value.error)
|
yield* failToolCall(value.toolCallId, value.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -456,17 +473,19 @@ export const layer: Layer.Layer<
|
|||||||
if (!ctx.snapshot) ctx.snapshot = yield* snapshot.track()
|
if (!ctx.snapshot) ctx.snapshot = yield* snapshot.track()
|
||||||
if (!ctx.assistantMessage.summary) {
|
if (!ctx.assistantMessage.summary) {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Step.Started.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Step.Started.Sync, {
|
||||||
agent: input.assistantMessage.agent,
|
sessionID: ctx.sessionID,
|
||||||
model: {
|
agent: input.assistantMessage.agent,
|
||||||
id: Modelv2.ID.make(ctx.model.id),
|
model: {
|
||||||
providerID: Modelv2.ProviderID.make(ctx.model.providerID),
|
id: Modelv2.ID.make(ctx.model.id),
|
||||||
variant: Modelv2.VariantID.make(input.assistantMessage.variant ?? "default"),
|
providerID: Modelv2.ProviderID.make(ctx.model.providerID),
|
||||||
},
|
variant: Modelv2.VariantID.make(input.assistantMessage.variant ?? "default"),
|
||||||
snapshot: ctx.snapshot,
|
},
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
snapshot: ctx.snapshot,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
yield* session.updatePart({
|
yield* session.updatePart({
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
@@ -486,14 +505,16 @@ export const layer: Layer.Layer<
|
|||||||
})
|
})
|
||||||
if (!ctx.assistantMessage.summary) {
|
if (!ctx.assistantMessage.summary) {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Step.Ended.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Step.Ended.Sync, {
|
||||||
finish: value.finishReason,
|
sessionID: ctx.sessionID,
|
||||||
cost: usage.cost,
|
finish: value.finishReason,
|
||||||
tokens: usage.tokens,
|
cost: usage.cost,
|
||||||
snapshot: completedSnapshot,
|
tokens: usage.tokens,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
snapshot: completedSnapshot,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.assistantMessage.finish = value.finishReason
|
ctx.assistantMessage.finish = value.finishReason
|
||||||
ctx.assistantMessage.cost += usage.cost
|
ctx.assistantMessage.cost += usage.cost
|
||||||
@@ -541,10 +562,12 @@ export const layer: Layer.Layer<
|
|||||||
case "text-start":
|
case "text-start":
|
||||||
if (!ctx.assistantMessage.summary) {
|
if (!ctx.assistantMessage.summary) {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Text.Started.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Text.Started.Sync, {
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
sessionID: ctx.sessionID,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.currentText = {
|
ctx.currentText = {
|
||||||
id: PartID.ascending(),
|
id: PartID.ascending(),
|
||||||
@@ -586,11 +609,13 @@ export const layer: Layer.Layer<
|
|||||||
)).text
|
)).text
|
||||||
if (!ctx.assistantMessage.summary) {
|
if (!ctx.assistantMessage.summary) {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Text.Ended.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Text.Ended.Sync, {
|
||||||
text: ctx.currentText.text,
|
sessionID: ctx.sessionID,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
text: ctx.currentText.text,
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
const end = Date.now()
|
const end = Date.now()
|
||||||
@@ -680,14 +705,16 @@ export const layer: Layer.Layer<
|
|||||||
}
|
}
|
||||||
if (!ctx.assistantMessage.summary) {
|
if (!ctx.assistantMessage.summary) {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Step.Failed.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: ctx.sessionID,
|
yield* sync.run(SessionEvent.Step.Failed.Sync, {
|
||||||
error: {
|
sessionID: ctx.sessionID,
|
||||||
type: "unknown",
|
error: {
|
||||||
message: errorMessage(e),
|
type: "unknown",
|
||||||
},
|
message: errorMessage(e),
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
},
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.assistantMessage.error = error
|
ctx.assistantMessage.error = error
|
||||||
yield* bus.publish(Session.Event.Error, {
|
yield* bus.publish(Session.Event.Error, {
|
||||||
@@ -732,22 +759,28 @@ export const layer: Layer.Layer<
|
|||||||
parse,
|
parse,
|
||||||
set: (info) => {
|
set: (info) => {
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Retried.Sync, {
|
const event = Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM
|
||||||
sessionID: ctx.sessionID,
|
? sync.run(SessionEvent.Retried.Sync, {
|
||||||
attempt: info.attempt,
|
sessionID: ctx.sessionID,
|
||||||
error: {
|
attempt: info.attempt,
|
||||||
message: info.message,
|
error: {
|
||||||
isRetryable: true,
|
message: info.message,
|
||||||
},
|
isRetryable: true,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
},
|
||||||
})
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
return status.set(ctx.sessionID, {
|
})
|
||||||
type: "retry",
|
: Effect.void
|
||||||
attempt: info.attempt,
|
return event.pipe(
|
||||||
message: info.message,
|
Effect.andThen(
|
||||||
action: info.action,
|
status.set(ctx.sessionID, {
|
||||||
next: info.next,
|
type: "retry",
|
||||||
})
|
attempt: info.attempt,
|
||||||
|
message: info.message,
|
||||||
|
action: info.action,
|
||||||
|
next: info.next,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@@ -788,6 +821,7 @@ export const defaultLayer = Layer.suspend(() =>
|
|||||||
Layer.provide(Image.defaultLayer),
|
Layer.provide(Image.defaultLayer),
|
||||||
Layer.provide(Bus.layer),
|
Layer.provide(Bus.layer),
|
||||||
Layer.provide(Config.defaultLayer),
|
Layer.provide(Config.defaultLayer),
|
||||||
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ import { InstanceState } from "@/effect/instance-state"
|
|||||||
import { TaskTool, type TaskPromptOps } from "@/tool/task"
|
import { TaskTool, type TaskPromptOps } from "@/tool/task"
|
||||||
import { SessionRunState } from "./run-state"
|
import { SessionRunState } from "./run-state"
|
||||||
import { EffectBridge } from "@/effect/bridge"
|
import { EffectBridge } from "@/effect/bridge"
|
||||||
import { EventV2 } from "@/v2/event"
|
import { SyncEvent } from "@/sync"
|
||||||
import { SessionEvent } from "@/v2/session-event"
|
import { SessionEvent } from "@/v2/session-event"
|
||||||
import { Modelv2 } from "@/v2/model"
|
import { Modelv2 } from "@/v2/model"
|
||||||
import { AgentAttachment, FileAttachment, Source } from "@/v2/session-prompt"
|
import { AgentAttachment, FileAttachment, Source } from "@/v2/session-prompt"
|
||||||
@@ -118,6 +118,7 @@ export const layer = Layer.effect(
|
|||||||
const summary = yield* SessionSummary.Service
|
const summary = yield* SessionSummary.Service
|
||||||
const sys = yield* SystemPrompt.Service
|
const sys = yield* SystemPrompt.Service
|
||||||
const llm = yield* LLM.Service
|
const llm = yield* LLM.Service
|
||||||
|
const sync = yield* SyncEvent.Service
|
||||||
const runner = Effect.fn("SessionPrompt.runner")(function* () {
|
const runner = Effect.fn("SessionPrompt.runner")(function* () {
|
||||||
return yield* EffectBridge.make()
|
return yield* EffectBridge.make()
|
||||||
})
|
})
|
||||||
@@ -809,12 +810,14 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
yield* sessions.updatePart(part)
|
yield* sessions.updatePart(part)
|
||||||
EventV2.run(SessionEvent.Shell.Started.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: input.sessionID,
|
yield* sync.run(SessionEvent.Shell.Started.Sync, {
|
||||||
timestamp: DateTime.makeUnsafe(started),
|
sessionID: input.sessionID,
|
||||||
callID,
|
timestamp: DateTime.makeUnsafe(started),
|
||||||
command: input.command,
|
callID,
|
||||||
})
|
command: input.command,
|
||||||
|
})
|
||||||
|
}
|
||||||
return { msg, part, cwd: ctx.directory }
|
return { msg, part, cwd: ctx.directory }
|
||||||
}).pipe(Effect.ensuring(markReady))
|
}).pipe(Effect.ensuring(markReady))
|
||||||
|
|
||||||
@@ -830,12 +833,14 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|||||||
output += "\n\n" + ["<metadata>", "User aborted the command", "</metadata>"].join("\n")
|
output += "\n\n" + ["<metadata>", "User aborted the command", "</metadata>"].join("\n")
|
||||||
}
|
}
|
||||||
const completed = Date.now()
|
const completed = Date.now()
|
||||||
EventV2.run(SessionEvent.Shell.Ended.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: input.sessionID,
|
yield* sync.run(SessionEvent.Shell.Ended.Sync, {
|
||||||
timestamp: DateTime.makeUnsafe(completed),
|
sessionID: input.sessionID,
|
||||||
callID: part.callID,
|
timestamp: DateTime.makeUnsafe(completed),
|
||||||
output,
|
callID: part.callID,
|
||||||
})
|
output,
|
||||||
|
})
|
||||||
|
}
|
||||||
if (!msg.time.completed) {
|
if (!msg.time.completed) {
|
||||||
msg.time.completed = completed
|
msg.time.completed = completed
|
||||||
yield* sessions.updateMessage(msg)
|
yield* sessions.updateMessage(msg)
|
||||||
@@ -975,34 +980,26 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (current?.agent !== info.agent) {
|
if (current?.agent !== info.agent) {
|
||||||
EventV2.run(
|
yield* sync.run(SessionEvent.AgentSwitched.Sync, {
|
||||||
SessionEvent.AgentSwitched.Sync,
|
sessionID: input.sessionID,
|
||||||
{
|
timestamp: DateTime.makeUnsafe(info.time.created),
|
||||||
sessionID: input.sessionID,
|
agent: info.agent,
|
||||||
timestamp: DateTime.makeUnsafe(info.time.created),
|
})
|
||||||
agent: info.agent,
|
|
||||||
},
|
|
||||||
{ bypassExperimentalEventSystem: true },
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
current?.model?.providerID !== info.model.providerID ||
|
current?.model?.providerID !== info.model.providerID ||
|
||||||
current.model.id !== info.model.modelID ||
|
current.model.id !== info.model.modelID ||
|
||||||
(current.model.variant === "default" ? undefined : current.model.variant) !== info.model.variant
|
(current.model.variant === "default" ? undefined : current.model.variant) !== info.model.variant
|
||||||
) {
|
) {
|
||||||
EventV2.run(
|
yield* sync.run(SessionEvent.ModelSwitched.Sync, {
|
||||||
SessionEvent.ModelSwitched.Sync,
|
sessionID: input.sessionID,
|
||||||
{
|
timestamp: DateTime.makeUnsafe(info.time.created),
|
||||||
sessionID: input.sessionID,
|
model: {
|
||||||
timestamp: DateTime.makeUnsafe(info.time.created),
|
id: Modelv2.ID.make(info.model.modelID),
|
||||||
model: {
|
providerID: Modelv2.ProviderID.make(info.model.providerID),
|
||||||
id: Modelv2.ID.make(info.model.modelID),
|
variant: Modelv2.VariantID.make(info.model.variant ?? "default"),
|
||||||
providerID: Modelv2.ProviderID.make(info.model.providerID),
|
|
||||||
variant: Modelv2.VariantID.make(info.model.variant ?? "default"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{ bypassExperimentalEventSystem: true },
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
yield* Effect.addFinalizer(() => instruction.clear(info.id))
|
yield* Effect.addFinalizer(() => instruction.clear(info.id))
|
||||||
@@ -1371,23 +1368,27 @@ NOTE: At any point in time through this workflow you should feel free to ask the
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
EventV2.run(SessionEvent.Prompted.Sync, {
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
sessionID: input.sessionID,
|
yield* sync.run(SessionEvent.Prompted.Sync, {
|
||||||
timestamp: DateTime.makeUnsafe(info.time.created),
|
|
||||||
prompt: {
|
|
||||||
text: nextPrompt.text.join("\n"),
|
|
||||||
files: nextPrompt.files,
|
|
||||||
agents: nextPrompt.agents,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
for (const text of nextPrompt.synthetic) {
|
|
||||||
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
|
||||||
EventV2.run(SessionEvent.Synthetic.Sync, {
|
|
||||||
sessionID: input.sessionID,
|
sessionID: input.sessionID,
|
||||||
timestamp: DateTime.makeUnsafe(info.time.created),
|
timestamp: DateTime.makeUnsafe(info.time.created),
|
||||||
text,
|
prompt: {
|
||||||
|
text: nextPrompt.text.join("\n"),
|
||||||
|
files: nextPrompt.files,
|
||||||
|
agents: nextPrompt.agents,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
for (const text of nextPrompt.synthetic) {
|
||||||
|
// TODO(v2): Temporary dual-write while migrating session messages to v2 events.
|
||||||
|
if (Flag.OPENCODE_EXPERIMENTAL_EVENT_SYSTEM) {
|
||||||
|
yield* sync.run(SessionEvent.Synthetic.Sync, {
|
||||||
|
sessionID: input.sessionID,
|
||||||
|
timestamp: DateTime.makeUnsafe(info.time.created),
|
||||||
|
text,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { info, parts }
|
return { info, parts }
|
||||||
}, Effect.scoped)
|
}, Effect.scoped)
|
||||||
@@ -1820,6 +1821,7 @@ export const defaultLayer = Layer.suspend(() =>
|
|||||||
LLM.defaultLayer,
|
LLM.defaultLayer,
|
||||||
Bus.layer,
|
Bus.layer,
|
||||||
CrossSpawnSpawner.defaultLayer,
|
CrossSpawnSpawner.defaultLayer,
|
||||||
|
SyncEvent.defaultLayer,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { SessionEvent } from "./session-event"
|
|||||||
import { V2Schema } from "./schema"
|
import { V2Schema } from "./schema"
|
||||||
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
|
import { optionalOmitUndefined } from "@opencode-ai/core/schema"
|
||||||
import { Modelv2 } from "./model"
|
import { Modelv2 } from "./model"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
|
||||||
export const Delivery = Schema.Literals(["immediate", "deferred"]).annotate({
|
export const Delivery = Schema.Literals(["immediate", "deferred"]).annotate({
|
||||||
identifier: "Session.Delivery",
|
identifier: "Session.Delivery",
|
||||||
@@ -113,6 +114,7 @@ export class Service extends Context.Service<Service, Interface>()("@opencode/v2
|
|||||||
export const layer = Layer.effect(
|
export const layer = Layer.effect(
|
||||||
Service,
|
Service,
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
const sync = yield* SyncEvent.Service
|
||||||
const decodeMessage = Schema.decodeUnknownSync(SessionMessage.Message)
|
const decodeMessage = Schema.decodeUnknownSync(SessionMessage.Message)
|
||||||
|
|
||||||
const decode = (row: typeof SessionMessageTable.$inferSelect) =>
|
const decode = (row: typeof SessionMessageTable.$inferSelect) =>
|
||||||
@@ -269,26 +271,18 @@ export const layer = Layer.effect(
|
|||||||
shell: Effect.fn("V2Session.shell")(function* (_input) {}),
|
shell: Effect.fn("V2Session.shell")(function* (_input) {}),
|
||||||
skill: Effect.fn("V2Session.skill")(function* (_input) {}),
|
skill: Effect.fn("V2Session.skill")(function* (_input) {}),
|
||||||
switchAgent: Effect.fn("V2Session.switchAgent")(function* (input) {
|
switchAgent: Effect.fn("V2Session.switchAgent")(function* (input) {
|
||||||
EventV2.run(
|
yield* sync.run(SessionEvent.AgentSwitched.Sync, {
|
||||||
SessionEvent.AgentSwitched.Sync,
|
sessionID: input.sessionID,
|
||||||
{
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
sessionID: input.sessionID,
|
agent: input.agent,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
})
|
||||||
agent: input.agent,
|
|
||||||
},
|
|
||||||
{ bypassExperimentalEventSystem: true },
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
switchModel: Effect.fn("V2Session.switchModel")(function* (input) {
|
switchModel: Effect.fn("V2Session.switchModel")(function* (input) {
|
||||||
EventV2.run(
|
yield* sync.run(SessionEvent.ModelSwitched.Sync, {
|
||||||
SessionEvent.ModelSwitched.Sync,
|
sessionID: input.sessionID,
|
||||||
{
|
timestamp: DateTime.makeUnsafe(Date.now()),
|
||||||
sessionID: input.sessionID,
|
model: input.model,
|
||||||
timestamp: DateTime.makeUnsafe(Date.now()),
|
})
|
||||||
model: input.model,
|
|
||||||
},
|
|
||||||
{ bypassExperimentalEventSystem: true },
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
subagent: Effect.fn("V2Session.subagent")(function* (input) {
|
subagent: Effect.fn("V2Session.subagent")(function* (input) {
|
||||||
const parent = yield* result.get(input.parentID)
|
const parent = yield* result.get(input.parentID)
|
||||||
@@ -319,6 +313,6 @@ export const layer = Layer.effect(
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
export const defaultLayer = layer
|
export const defaultLayer = layer.pipe(Layer.provide(SyncEvent.defaultLayer))
|
||||||
|
|
||||||
export * as SessionV2 from "./session"
|
export * as SessionV2 from "./session"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import { ProviderTest } from "../fake/provider"
|
|||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
||||||
import { TestConfig } from "../fixture/config"
|
import { TestConfig } from "../fixture/config"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
@@ -223,6 +224,7 @@ const deps = Layer.mergeAll(
|
|||||||
Plugin.defaultLayer,
|
Plugin.defaultLayer,
|
||||||
Bus.layer,
|
Bus.layer,
|
||||||
Config.defaultLayer,
|
Config.defaultLayer,
|
||||||
|
SyncEvent.defaultLayer,
|
||||||
)
|
)
|
||||||
|
|
||||||
const env = Layer.mergeAll(
|
const env = Layer.mergeAll(
|
||||||
@@ -269,6 +271,7 @@ function compactionProcessLayer(options?: CompactionProcessOptions) {
|
|||||||
Layer.provide(status),
|
Layer.provide(status),
|
||||||
Layer.provide(bus),
|
Layer.provide(bus),
|
||||||
Layer.provide(options?.config ?? Config.defaultLayer),
|
Layer.provide(options?.config ?? Config.defaultLayer),
|
||||||
|
Layer.provide(SyncEvent.defaultLayer),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
|||||||
import { provideTmpdirServer } from "../fixture/fixture"
|
import { provideTmpdirServer } from "../fixture/fixture"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
import { raw, reply, TestLLMServer } from "../lib/llm-server"
|
import { raw, reply, TestLLMServer } from "../lib/llm-server"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
@@ -166,6 +167,7 @@ const deps = Layer.mergeAll(
|
|||||||
LLM.defaultLayer,
|
LLM.defaultLayer,
|
||||||
Provider.defaultLayer,
|
Provider.defaultLayer,
|
||||||
status,
|
status,
|
||||||
|
SyncEvent.defaultLayer,
|
||||||
).pipe(Layer.provideMerge(infra))
|
).pipe(Layer.provideMerge(infra))
|
||||||
const env = Layer.mergeAll(
|
const env = Layer.mergeAll(
|
||||||
TestLLMServer.layer,
|
TestLLMServer.layer,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ import { Reference } from "../../src/reference/reference"
|
|||||||
import { provideTmpdirInstance, provideTmpdirServer } from "../fixture/fixture"
|
import { provideTmpdirInstance, provideTmpdirServer } from "../fixture/fixture"
|
||||||
import { testEffect } from "../lib/effect"
|
import { testEffect } from "../lib/effect"
|
||||||
import { reply, TestLLMServer } from "../lib/llm-server"
|
import { reply, TestLLMServer } from "../lib/llm-server"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
@@ -174,6 +175,7 @@ function makeHttp() {
|
|||||||
mcp,
|
mcp,
|
||||||
AppFileSystem.defaultLayer,
|
AppFileSystem.defaultLayer,
|
||||||
status,
|
status,
|
||||||
|
SyncEvent.defaultLayer,
|
||||||
).pipe(Layer.provideMerge(infra))
|
).pipe(Layer.provideMerge(infra))
|
||||||
const question = Question.layer.pipe(Layer.provideMerge(deps))
|
const question = Question.layer.pipe(Layer.provideMerge(deps))
|
||||||
const todo = Todo.layer.pipe(Layer.provideMerge(deps))
|
const todo = Todo.layer.pipe(Layer.provideMerge(deps))
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import { CrossSpawnSpawner } from "@opencode-ai/core/cross-spawn-spawner"
|
|||||||
import { Ripgrep } from "../../src/file/ripgrep"
|
import { Ripgrep } from "../../src/file/ripgrep"
|
||||||
import { Format } from "../../src/format"
|
import { Format } from "../../src/format"
|
||||||
import { Reference } from "../../src/reference/reference"
|
import { Reference } from "../../src/reference/reference"
|
||||||
|
import { SyncEvent } from "@/sync"
|
||||||
|
|
||||||
void Log.init({ print: false })
|
void Log.init({ print: false })
|
||||||
|
|
||||||
@@ -124,6 +125,7 @@ function makeHttp() {
|
|||||||
mcp,
|
mcp,
|
||||||
AppFileSystem.defaultLayer,
|
AppFileSystem.defaultLayer,
|
||||||
status,
|
status,
|
||||||
|
SyncEvent.defaultLayer,
|
||||||
).pipe(Layer.provideMerge(infra))
|
).pipe(Layer.provideMerge(infra))
|
||||||
const question = Question.layer.pipe(Layer.provideMerge(deps))
|
const question = Question.layer.pipe(Layer.provideMerge(deps))
|
||||||
const todo = Todo.layer.pipe(Layer.provideMerge(deps))
|
const todo = Todo.layer.pipe(Layer.provideMerge(deps))
|
||||||
|
|||||||
Reference in New Issue
Block a user