fix(core): remove session import cycle (#37596)
Co-authored-by: James Long <17031+jlongster@users.noreply.github.com>
This commit is contained in:
co-authored by
James Long
parent
deb5b144c3
commit
529d55b1c3
@@ -74,7 +74,7 @@ type MakeInput<
|
|||||||
T extends Tag | undefined = undefined,
|
T extends Tag | undefined = undefined,
|
||||||
> = NodeIdentity & {
|
> = NodeIdentity & {
|
||||||
readonly layer: Implementation
|
readonly layer: Implementation
|
||||||
readonly deps: (Items | (() => Items)) & CheckDependencies<Implementation, NoInfer<Items>>
|
readonly deps: Items & CheckDependencies<Implementation, NoInfer<Items>>
|
||||||
readonly tag?: T
|
readonly tag?: T
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,9 +90,7 @@ export function make<
|
|||||||
name: input.service !== undefined ? input.service.key : input.name,
|
name: input.service !== undefined ? input.service.key : input.name,
|
||||||
service: input.service,
|
service: input.service,
|
||||||
implementation: input.layer,
|
implementation: input.layer,
|
||||||
get dependencies() {
|
dependencies: input.deps,
|
||||||
return typeof input.deps === "function" ? input.deps() : input.deps
|
|
||||||
},
|
|
||||||
tag: input.tag,
|
tag: input.tag,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { Permission } from "@opencode-ai/schema/permission"
|
|||||||
import { EventV2 } from "./event"
|
import { EventV2 } from "./event"
|
||||||
import { Location } from "./location"
|
import { Location } from "./location"
|
||||||
import { AgentV2 } from "./agent"
|
import { AgentV2 } from "./agent"
|
||||||
import { SessionV2 } from "./session"
|
import { SessionErrors } from "./session/error"
|
||||||
|
import { SessionSchema } from "./session/schema"
|
||||||
import { SessionStore } from "./session/store"
|
import { SessionStore } from "./session/store"
|
||||||
import { Wildcard } from "./util/wildcard"
|
import { Wildcard } from "./util/wildcard"
|
||||||
import { PermissionSaved } from "./permission/saved"
|
import { PermissionSaved } from "./permission/saved"
|
||||||
@@ -98,11 +99,11 @@ export function merge(...rulesets: Permission.Ruleset[]): Permission.Ruleset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Interface {
|
export interface Interface {
|
||||||
readonly ask: (input: AssertInput) => Effect.Effect<AskResult, SessionV2.NotFoundError>
|
readonly ask: (input: AssertInput) => Effect.Effect<AskResult, SessionErrors.NotFoundError>
|
||||||
readonly assert: (input: AssertInput) => Effect.Effect<void, Error | SessionV2.NotFoundError>
|
readonly assert: (input: AssertInput) => Effect.Effect<void, Error | SessionErrors.NotFoundError>
|
||||||
readonly reply: (input: ReplyInput) => Effect.Effect<void, NotFoundError>
|
readonly reply: (input: ReplyInput) => Effect.Effect<void, NotFoundError>
|
||||||
readonly get: (id: ID) => Effect.Effect<Request | undefined>
|
readonly get: (id: ID) => Effect.Effect<Request | undefined>
|
||||||
readonly forSession: (sessionID: SessionV2.ID) => Effect.Effect<ReadonlyArray<Request>>
|
readonly forSession: (sessionID: SessionSchema.ID) => Effect.Effect<ReadonlyArray<Request>>
|
||||||
readonly list: () => Effect.Effect<ReadonlyArray<Request>>
|
readonly list: () => Effect.Effect<ReadonlyArray<Request>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,9 +143,12 @@ const layer = Layer.effect(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const configured = Effect.fn("PermissionV2.configured")(function* (sessionID: SessionV2.ID, agentID?: AgentV2.ID) {
|
const configured = Effect.fn("PermissionV2.configured")(function* (
|
||||||
|
sessionID: SessionSchema.ID,
|
||||||
|
agentID?: AgentV2.ID,
|
||||||
|
) {
|
||||||
const session = yield* sessions.get(sessionID)
|
const session = yield* sessions.get(sessionID)
|
||||||
if (!session) return yield* new SessionV2.NotFoundError({ sessionID })
|
if (!session) return yield* new SessionErrors.NotFoundError({ sessionID })
|
||||||
const agent = yield* agents.resolve(agentID ?? session.agent)
|
const agent = yield* agents.resolve(agentID ?? session.agent)
|
||||||
return agent?.permissions ?? missingAgentPermissions
|
return agent?.permissions ?? missingAgentPermissions
|
||||||
})
|
})
|
||||||
@@ -301,7 +305,7 @@ const layer = Layer.effect(
|
|||||||
return pending.get(id)?.request
|
return pending.get(id)?.request
|
||||||
})
|
})
|
||||||
|
|
||||||
const forSession = Effect.fn("PermissionV2.forSession")(function* (sessionID: SessionV2.ID) {
|
const forSession = Effect.fn("PermissionV2.forSession")(function* (sessionID: SessionSchema.ID) {
|
||||||
return Array.from(pending.values(), (item) => item.request).filter((request) => request.sessionID === sessionID)
|
return Array.from(pending.values(), (item) => item.request).filter((request) => request.sessionID === sessionID)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import { fromRow } from "./session/info"
|
|||||||
import { SessionRunner } from "./session/runner/index"
|
import { SessionRunner } from "./session/runner/index"
|
||||||
import { SessionStore } from "./session/store"
|
import { SessionStore } from "./session/store"
|
||||||
import { SessionExecution } from "./session/execution"
|
import { SessionExecution } from "./session/execution"
|
||||||
|
import { MessageDecodeError, NotFoundError } from "./session/error"
|
||||||
import { makeGlobalNode } from "./effect/app-node"
|
import { makeGlobalNode } from "./effect/app-node"
|
||||||
import { LocationServiceMap } from "./location-service-map"
|
import { LocationServiceMap } from "./location-service-map"
|
||||||
import { MessageDecodeError } from "./session/error"
|
|
||||||
import { SessionEvent } from "./session/event"
|
import { SessionEvent } from "./session/event"
|
||||||
import { SessionPending } from "./session/pending"
|
import { SessionPending } from "./session/pending"
|
||||||
import { SessionGenerate } from "./session/generate"
|
import { SessionGenerate } from "./session/generate"
|
||||||
@@ -108,10 +108,6 @@ type ForkInput = {
|
|||||||
messageID?: SessionMessage.ID
|
messageID?: SessionMessage.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Session.NotFoundError", {
|
|
||||||
sessionID: SessionSchema.ID,
|
|
||||||
}) {}
|
|
||||||
|
|
||||||
export class OperationUnavailableError extends Schema.TaggedErrorClass<OperationUnavailableError>()(
|
export class OperationUnavailableError extends Schema.TaggedErrorClass<OperationUnavailableError>()(
|
||||||
"Session.OperationUnavailableError",
|
"Session.OperationUnavailableError",
|
||||||
{
|
{
|
||||||
@@ -119,7 +115,7 @@ export class OperationUnavailableError extends Schema.TaggedErrorClass<Operation
|
|||||||
},
|
},
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
export { MessageDecodeError } from "./session/error"
|
export { MessageDecodeError, NotFoundError }
|
||||||
|
|
||||||
export class PromptConflictError extends Schema.TaggedErrorClass<PromptConflictError>()("Session.PromptConflictError", {
|
export class PromptConflictError extends Schema.TaggedErrorClass<PromptConflictError>()("Session.PromptConflictError", {
|
||||||
sessionID: SessionSchema.ID,
|
sessionID: SessionSchema.ID,
|
||||||
@@ -1033,8 +1029,7 @@ const SHELL_MAX_CAPTURE_BYTES = 1024 * 1024
|
|||||||
export const node = makeGlobalNode({
|
export const node = makeGlobalNode({
|
||||||
service: Service,
|
service: Service,
|
||||||
layer: layer.pipe(Layer.orDie),
|
layer: layer.pipe(Layer.orDie),
|
||||||
// Defer the execution node across the Session/runner module cycle until the graph is compiled.
|
deps: [
|
||||||
deps: () => [
|
|
||||||
Job.node,
|
Job.node,
|
||||||
Database.node,
|
Database.node,
|
||||||
EventV2.node,
|
EventV2.node,
|
||||||
|
|||||||
@@ -1,9 +1,15 @@
|
|||||||
|
export * as SessionErrors from "./error"
|
||||||
|
|
||||||
import { Schema } from "effect"
|
import { Schema } from "effect"
|
||||||
import { Agent } from "@opencode-ai/schema/agent"
|
import { Agent } from "@opencode-ai/schema/agent"
|
||||||
import { SessionMessage } from "./message"
|
import { SessionMessage } from "./message"
|
||||||
import { SessionSchema } from "./schema"
|
import { SessionSchema } from "./schema"
|
||||||
import { SessionError } from "@opencode-ai/schema/session-error"
|
import { SessionError } from "@opencode-ai/schema/session-error"
|
||||||
|
|
||||||
|
export class NotFoundError extends Schema.TaggedErrorClass<NotFoundError>()("Session.NotFoundError", {
|
||||||
|
sessionID: SessionSchema.ID,
|
||||||
|
}) {}
|
||||||
|
|
||||||
export class MessageDecodeError extends Schema.TaggedErrorClass<MessageDecodeError>()("Session.MessageDecodeError", {
|
export class MessageDecodeError extends Schema.TaggedErrorClass<MessageDecodeError>()("Session.MessageDecodeError", {
|
||||||
sessionID: SessionSchema.ID,
|
sessionID: SessionSchema.ID,
|
||||||
messageID: SessionMessage.ID,
|
messageID: SessionMessage.ID,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ const b = make({ service: B, layer: bLayer, deps: [a] })
|
|||||||
const c = make({ service: C, layer: cLayer, deps: [a, b] })
|
const c = make({ service: C, layer: cLayer, deps: [a, b] })
|
||||||
const failing = make({ service: A, layer: failingA, deps: [] })
|
const failing = make({ service: A, layer: failingA, deps: [] })
|
||||||
const dependent = make({ service: B, layer: bLayer, deps: [failing] })
|
const dependent = make({ service: B, layer: bLayer, deps: [failing] })
|
||||||
make({ service: B, layer: bLayer, deps: () => [a] })
|
|
||||||
const inputA = LayerNode.unbound(A, tags.values.app)
|
const inputA = LayerNode.unbound(A, tags.values.app)
|
||||||
const inputDependent = make({ service: B, layer: bLayer, deps: [inputA] })
|
const inputDependent = make({ service: B, layer: bLayer, deps: [inputA] })
|
||||||
|
|
||||||
@@ -47,9 +46,6 @@ make({ service: A, name: "a", layer: aLayer, deps: [] })
|
|||||||
// @ts-expect-error B requires A
|
// @ts-expect-error B requires A
|
||||||
make({ service: B, layer: bLayer, deps: [] })
|
make({ service: B, layer: bLayer, deps: [] })
|
||||||
|
|
||||||
// @ts-expect-error Lazy dependencies must still provide A
|
|
||||||
make({ service: B, layer: bLayer, deps: () => [] })
|
|
||||||
|
|
||||||
// @ts-expect-error C requires A and B
|
// @ts-expect-error C requires A and B
|
||||||
make({ service: C, layer: cLayer, deps: [a] })
|
make({ service: C, layer: cLayer, deps: [a] })
|
||||||
|
|
||||||
|
|||||||
@@ -37,12 +37,6 @@ describe("layer node", () => {
|
|||||||
expect(await Effect.runPromise(program)).toBe("hello production")
|
expect(await Effect.runPromise(program)).toBe("hello production")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("resolves lazy dependencies when compiling", async () => {
|
|
||||||
const greeting = make({ service: Greeting, layer: greetingLayer, deps: () => [value] })
|
|
||||||
const program = Effect.map(Greeting, (item) => item.value).pipe(Effect.provide(build(greeting)))
|
|
||||||
expect(await Effect.runPromise(program)).toBe("hello production")
|
|
||||||
})
|
|
||||||
|
|
||||||
test("exposes roots but hides transitive dependencies", () => {
|
test("exposes roots but hides transitive dependencies", () => {
|
||||||
const layer = build(LayerNode.group([greeting]))
|
const layer = build(LayerNode.group([greeting]))
|
||||||
const check: Layer.Layer<Greeting> = layer
|
const check: Layer.Layer<Greeting> = layer
|
||||||
|
|||||||
Reference in New Issue
Block a user