diff --git a/packages/app/src/components/dialog-connect-provider.tsx b/packages/app/src/components/dialog-connect-provider.tsx index 2d3ecff7e7..71833be4a5 100644 --- a/packages/app/src/components/dialog-connect-provider.tsx +++ b/packages/app/src/components/dialog-connect-provider.tsx @@ -277,8 +277,7 @@ function ProviderConnection(props: { }) const provider = createMemo(() => ({ id: props.provider, - name: - providers.all().get(props.provider)?.name ?? controller.integration()?.name ?? props.provider, + name: providers.all().get(props.provider)?.name ?? controller.integration()?.name ?? props.provider, })) const methodLabel = (value?: { type?: string; label?: string }) => { if (!value) return "" diff --git a/packages/app/src/components/dialog-select-mcp.tsx b/packages/app/src/components/dialog-select-mcp.tsx index f73256e21e..94d03daa91 100644 --- a/packages/app/src/components/dialog-select-mcp.tsx +++ b/packages/app/src/components/dialog-select-mcp.tsx @@ -50,7 +50,8 @@ export const DialogSelectMcp: Component = () => { > {(i) => { const mcpStatus = () => - data.location.mcp.server.list({ directory: sdk().directory })?.find((server) => server.name === i.name)?.status + data.location.mcp.server.list({ directory: sdk().directory })?.find((server) => server.name === i.name) + ?.status const status = () => mcpStatus()?.status const statusLabel = () => { const key = status() ? statusLabels[status() as keyof typeof statusLabels] : undefined diff --git a/packages/app/src/components/project-settings-extensions.tsx b/packages/app/src/components/project-settings-extensions.tsx index fb77c7077e..376cd6d0b2 100644 --- a/packages/app/src/components/project-settings-extensions.tsx +++ b/packages/app/src/components/project-settings-extensions.tsx @@ -90,8 +90,8 @@ export const ProjectSettingsExtensions: Component = () => { .sort() }) const mcpEnabled = (name: string) => - data.location.mcp.server.list({ directory: directorySDK().directory })?.find((server) => server.name === name)?.status - .status === "connected" + data.location.mcp.server.list({ directory: directorySDK().directory })?.find((server) => server.name === name) + ?.status.status === "connected" const [globalPluginList] = createResource( () => serverSDK.connection.status() === "connected", @@ -196,7 +196,6 @@ export const ProjectSettingsExtensions: Component = () => { {skillRows(serverSkills())} - ) diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index 5a0d7981a0..200bd1e524 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -63,7 +63,10 @@ export async function sendFollowupDraft(input: FollowupSendInput) { const [head, ...tail] = text.split(" ") const cmd = head?.startsWith("/") ? head.slice(1) : undefined - if (cmd && input.data.location.command.list({ directory: input.draft.sessionDirectory })?.some((item) => item.name === cmd)) { + if ( + cmd && + input.data.location.command.list({ directory: input.draft.sessionDirectory })?.some((item) => item.name === cmd) + ) { setBusy() try { const messageID = Identifier.ascending("message") @@ -197,8 +200,7 @@ export function createPromptSubmit(input: PromptSubmitInput) { if (!sessionID) return Promise.resolve() input.onAbort?.() - return serverSDK.api.session.interrupt({ sessionID }) - .catch(() => {}) + return serverSDK.api.session.interrupt({ sessionID }).catch(() => {}) } const restoreCommentItems = ( diff --git a/packages/app/src/components/session/session-context-tab.tsx b/packages/app/src/components/session/session-context-tab.tsx index aa6673160b..d25e9cda5e 100644 --- a/packages/app/src/components/session/session-context-tab.tsx +++ b/packages/app/src/components/session/session-context-tab.tsx @@ -181,8 +181,7 @@ export function SessionContextTab() { { label: "context.stats.reasoningTokens", value: () => formatter().number(ctx()?.tokens.reasoning) }, { label: "context.stats.cacheTokens", - value: () => - `${formatter().number(ctx()?.tokens.cache.read)} / ${formatter().number(ctx()?.tokens.cache.write)}`, + value: () => `${formatter().number(ctx()?.tokens.cache.read)} / ${formatter().number(ctx()?.tokens.cache.write)}`, }, { label: "context.stats.userMessages", value: () => counts().user.toLocaleString(language.intl()) }, { label: "context.stats.assistantMessages", value: () => counts().assistant.toLocaleString(language.intl()) }, @@ -305,9 +304,7 @@ export function SessionContextTab() { - {(message) => ( - - )} + {(message) => } diff --git a/packages/app/src/components/status-popover-body.tsx b/packages/app/src/components/status-popover-body.tsx index 6777ed0938..03cbfa169c 100644 --- a/packages/app/src/components/status-popover-body.tsx +++ b/packages/app/src/components/status-popover-body.tsx @@ -27,15 +27,16 @@ export function StatusPopoverBody(props: { shown: boolean }) { const toggleMcp = useMcpToggle(() => sdk().directory) const mcp = () => data.location.mcp.server.list({ directory: sdk().directory }) ?? [] - const mcpNames = createMemo(() => mcp().map((server) => server.name).sort((a, b) => a.localeCompare(b))) + const mcpNames = createMemo(() => + mcp() + .map((server) => server.name) + .sort((a, b) => a.localeCompare(b)), + ) const mcpStatus = (name: string) => mcp().find((server) => server.name === name)?.status.status const mcpConnected = createMemo(() => mcpNames().filter((name) => mcpStatus(name) === "connected").length) const [pluginList] = createResource( () => (props.shown ? sdk().directory : undefined), - (directory) => - serverSDK.api.plugin - .list({ location: { directory } }) - .then((result) => result.data), + (directory) => serverSDK.api.plugin.list({ location: { directory } }).then((result) => result.data), ) const plugins = createMemo(() => (pluginList.latest ?? []).map((item) => item.id)) const pluginCount = createMemo(() => plugins().length) diff --git a/packages/app/src/context/file.tsx b/packages/app/src/context/file.tsx index d104410294..8f8d3a149f 100644 --- a/packages/app/src/context/file.tsx +++ b/packages/app/src/context/file.tsx @@ -78,16 +78,14 @@ export const { use: useFile, provider: FileProvider } = createSimpleContext({ scope, normalizeDir: path.normalizeDir, list: (dir) => - serverSDK.api.file - .list({ path: dir, location: { directory: scope() } }) - .then((x) => - x.data.map((entry) => ({ - ...entry, - name: entry.path.split("/").at(-1) ?? entry.path, - absolute: `${scope()}/${entry.path}`, - ignored: false, - })), - ), + serverSDK.api.file.list({ path: dir, location: { directory: scope() } }).then((x) => + x.data.map((entry) => ({ + ...entry, + name: entry.path.split("/").at(-1) ?? entry.path, + absolute: `${scope()}/${entry.path}`, + ignored: false, + })), + ), onError: (message) => { showToast({ variant: "error", diff --git a/packages/app/src/context/global-sync/bootstrap.test.ts b/packages/app/src/context/global-sync/bootstrap.test.ts index f715e71a1f..435902eb53 100644 --- a/packages/app/src/context/global-sync/bootstrap.test.ts +++ b/packages/app/src/context/global-sync/bootstrap.test.ts @@ -86,5 +86,4 @@ describe("query keys", () => { { id: "b", sandboxes: [] }, ]) }) - }) diff --git a/packages/app/src/context/global-sync/child-store.ts b/packages/app/src/context/global-sync/child-store.ts index 78f56bde34..02f408dea7 100644 --- a/packages/app/src/context/global-sync/child-store.ts +++ b/packages/app/src/context/global-sync/child-store.ts @@ -233,7 +233,10 @@ export function createChildStoreManager(input: { }, get mcp() { return Object.fromEntries( - (input.data.location.mcp.server.list({ directory }) ?? []).map((server) => [server.name, server.status]), + (input.data.location.mcp.server.list({ directory }) ?? []).map((server) => [ + server.name, + server.status, + ]), ) }, get mcp_resource() { diff --git a/packages/app/src/context/global-sync/utils.test.ts b/packages/app/src/context/global-sync/utils.test.ts index 405f6f4f3d..da051c3311 100644 --- a/packages/app/src/context/global-sync/utils.test.ts +++ b/packages/app/src/context/global-sync/utils.test.ts @@ -1,9 +1,5 @@ import { describe, expect, test } from "bun:test" -import type { - AgentListOutput, - ModelListOutput, - ProviderListOutput, -} from "@opencode-ai/client/promise" +import type { AgentListOutput, ModelListOutput, ProviderListOutput } from "@opencode-ai/client/promise" import { directoryKey, normalizeAgentList, normalizeProviderList } from "./utils" describe("normalizeAgentList", () => { diff --git a/packages/app/src/context/global-sync/utils.ts b/packages/app/src/context/global-sync/utils.ts index 16cf33f705..1c33c29757 100644 --- a/packages/app/src/context/global-sync/utils.ts +++ b/packages/app/src/context/global-sync/utils.ts @@ -1,8 +1,4 @@ -import type { - AgentListOutput, - ModelListOutput, - ProviderListOutput, -} from "@opencode-ai/client/promise" +import type { AgentListOutput, ModelListOutput, ProviderListOutput } from "@opencode-ai/client/promise" import type { Agent, Project, Provider, ProviderListResponse } from "@/types" import type { Project as CurrentProject } from "@opencode-ai/client/promise" import { NormalizedProviderListResponse } from "@opencode-ai/session-ui/context" diff --git a/packages/app/src/context/location.tsx b/packages/app/src/context/location.tsx index 12eaa0c7bf..4122e4161f 100644 --- a/packages/app/src/context/location.tsx +++ b/packages/app/src/context/location.tsx @@ -13,10 +13,7 @@ export type WorkspaceLocation = LocationContext & { const context = createSimpleContext({ name: "Location", - init: (props: { - directory: string | Accessor - workspaceID?: string | Accessor - }) => { + init: (props: { directory: string | Accessor; workspaceID?: string | Accessor }) => { const serverSDK = useServerSDK() const data = useData() const ref = createMemo(() => ({ @@ -44,9 +41,7 @@ const context = createSimpleContext({ }) }) - const location = createMemo(() => - serverSDK.ensureDirSdkContext(current()?.directory ?? ref().directory), - ) + const location = createMemo(() => serverSDK.ensureDirSdkContext(current()?.directory ?? ref().directory)) return createMemo(() => ({ ...location(), ref: ref(), diff --git a/packages/app/src/context/mcp.ts b/packages/app/src/context/mcp.ts index 85b2ff7a14..c4981b52b7 100644 --- a/packages/app/src/context/mcp.ts +++ b/packages/app/src/context/mcp.ts @@ -26,7 +26,8 @@ export function useMcpToggle(directory?: Accessor, onSuccess } else if (server.status.status === "needs_auth" && server.integrationID) { const integration = await serverSDK.api.integration.get({ integrationID: server.integrationID, location: ref }) const method = integration.data?.methods.find((item) => item.type === "oauth" && !item.form?.length) - if (!method || method.type !== "oauth") throw new Error(`MCP server ${name} requires an interactive authentication form`) + if (!method || method.type !== "oauth") + throw new Error(`MCP server ${name} requires an interactive authentication form`) const attempt = await serverSDK.api.integration.oauth.connect({ integrationID: server.integrationID, methodID: method.id, diff --git a/packages/app/src/context/server-sync.tsx b/packages/app/src/context/server-sync.tsx index 3c4141e817..8289c7ee5c 100644 --- a/packages/app/src/context/server-sync.tsx +++ b/packages/app/src/context/server-sync.tsx @@ -5,12 +5,7 @@ import { getOwner, onCleanup, untrack } from "solid-js" import { createStore } from "solid-js/store" import { useLanguage } from "@/context/language" import { type ServerSDK } from "./server-sdk" -import { - bootstrapDirectory, - bootstrapGlobal, - loadGlobalConfigQuery, - loadPathQuery, -} from "./global-sync/bootstrap" +import { bootstrapDirectory, bootstrapGlobal, loadGlobalConfigQuery, loadPathQuery } from "./global-sync/bootstrap" import { createChildStoreManager } from "./global-sync/child-store" import { applyDirectoryEvent, applyGlobalEvent } from "./global-sync/event-reducer" import type { ProjectMeta } from "./global-sync/types" @@ -247,11 +242,7 @@ export function createServerSyncContextInner(serverSDK: ServerSDK, data: Data) { const existing = children.children[key] if (!existing) return children.mark(key) - if ( - eventType === "config.updated" || - eventType === "agent.updated" - ) - queue.push(key) + if (eventType === "config.updated" || eventType === "agent.updated") queue.push(key) const [store, setStore] = existing if (eventType === "worktree.updated") void bootstrap.refetch() if (eventType !== "vcs.branch.updated") diff --git a/packages/app/src/pages/home/home-sessions-controller.tsx b/packages/app/src/pages/home/home-sessions-controller.tsx index e077a3d1c0..25ac215717 100644 --- a/packages/app/src/pages/home/home-sessions-controller.tsx +++ b/packages/app/src/pages/home/home-sessions-controller.tsx @@ -57,17 +57,16 @@ export function createHomeSessionsController(home: HomeController) { ? homeSessionIndexKey(ServerConnection.key(conn)) : (["home", "session-index", "unselected"] as const), enabled: !!ctx && ctx.sdk.connection.status() === "connected", - queryFn: - ctx - ? async ({ signal }) => { - const index = await loadHomeSessionIndex( - (input, options) => ctx.sdk.api.session.list(input, options), - signal, - ) - index.forEach(ctx.data.session.remember) - return Date.now() - } - : skipToken, + queryFn: ctx + ? async ({ signal }) => { + const index = await loadHomeSessionIndex( + (input, options) => ctx.sdk.api.session.list(input, options), + signal, + ) + index.forEach(ctx.data.session.remember) + return Date.now() + } + : skipToken, retry: false, staleTime: 30_000, refetchOnMount: true, diff --git a/packages/app/src/pages/layout/project-avatar-state.ts b/packages/app/src/pages/layout/project-avatar-state.ts index 65cb4cef28..c77e0dd6f5 100644 --- a/packages/app/src/pages/layout/project-avatar-state.ts +++ b/packages/app/src/pages/layout/project-avatar-state.ts @@ -15,9 +15,14 @@ export function useSessionTabAvatarState( const ctx = serverCtx() if (!ctx) return false const permission = ctx.permission - return !!sessionPermissionRequest(ctx.data.session.list(), ctx.data.session.permission.list, sessionId(), (item) => { - return !permission.autoResponds(item, directory()) - }) + return !!sessionPermissionRequest( + ctx.data.session.list(), + ctx.data.session.permission.list, + sessionId(), + (item) => { + return !permission.autoResponds(item, directory()) + }, + ) }) const hasQuestions = createMemo(() => { const data = serverCtx()?.data diff --git a/packages/app/src/pages/new-session/new-session-workspace-controller.ts b/packages/app/src/pages/new-session/new-session-workspace-controller.ts index 80cd92acd0..338e14dd30 100644 --- a/packages/app/src/pages/new-session/new-session-workspace-controller.ts +++ b/packages/app/src/pages/new-session/new-session-workspace-controller.ts @@ -88,7 +88,9 @@ export function createNewSessionWorkspaceController(input: { ) const projectRoot = createMemo(() => currentProject()?.worktree ?? sdk().directory) createEffect(() => { - void Promise.all([data.location.syncInfo({ directory: sdk().directory }), data.project.sync()]).catch(() => undefined) + void Promise.all([data.location.syncInfo({ directory: sdk().directory }), data.project.sync()]).catch( + () => undefined, + ) const project = currentProject() const directories = project ? [project.worktree, ...workspaceDirectories(project)] : [sdk().directory] directories.forEach((directory) => void data.location.vcs.sync({ directory }).catch(() => undefined)) diff --git a/packages/app/src/pages/session/composer/session-composer-controls.ts b/packages/app/src/pages/session/composer/session-composer-controls.ts index f2a4d15e9a..25f6da6a60 100644 --- a/packages/app/src/pages/session/composer/session-composer-controls.ts +++ b/packages/app/src/pages/session/composer/session-composer-controls.ts @@ -40,7 +40,9 @@ export function createPromptInputController(input: { model: { selection: input.model ?? local.model, paid: providers.paid().length > 0, - loading: (local.agent.visible() && data.location.agent.list({ directory: sdk().directory }) === undefined) || !providers.ready(), + loading: + (local.agent.visible() && data.location.agent.list({ directory: sdk().directory }) === undefined) || + !providers.ready(), }, session: { id: input.sessionID(), diff --git a/packages/app/src/pages/session/composer/session-composer-region.tsx b/packages/app/src/pages/session/composer/session-composer-region.tsx index 9d3369e490..5e6a8f459a 100644 --- a/packages/app/src/pages/session/composer/session-composer-region.tsx +++ b/packages/app/src/pages/session/composer/session-composer-region.tsx @@ -72,9 +72,7 @@ export function SessionComposerRegion(props: { )} -
+
{controller.handoffPrompt() || language.t("prompt.loading")}
diff --git a/packages/app/src/pages/session/composer/session-composer-state.ts b/packages/app/src/pages/session/composer/session-composer-state.ts index 03c8bf59c6..1e792d9561 100644 --- a/packages/app/src/pages/session/composer/session-composer-state.ts +++ b/packages/app/src/pages/session/composer/session-composer-state.ts @@ -53,9 +53,9 @@ export function createSessionComposerController() { if (!primary()) return [] const id = params.id if (!id) return [] - const assistant = data.session.message.list(id).findLast( - (message) => message.type === "assistant" && message.time.completed === undefined, - ) + const assistant = data.session.message + .list(id) + .findLast((message) => message.type === "assistant" && message.time.completed === undefined) if (assistant?.type !== "assistant") return [] return assistant.content.flatMap((part) => { if (part.type !== "tool" || part.state.status !== "running") return [] @@ -149,14 +149,12 @@ export function createSessionComposerController() { if (!primary()) return const sessionID = params.id if (!sessionID) return - await serverSDK.api.session - .background({ sessionID }) - .catch((error) => { - showToast({ - title: language.t("common.requestFailed"), - description: error instanceof Error ? error.message : String(error), - }) + await serverSDK.api.session.background({ sessionID }).catch((error) => { + showToast({ + title: language.t("common.requestFailed"), + description: error instanceof Error ? error.message : String(error), }) + }) } const [store, setStore] = createStore({ diff --git a/packages/app/src/pages/session/timeline/model.ts b/packages/app/src/pages/session/timeline/model.ts index 710dca4e5e..4a660f2607 100644 --- a/packages/app/src/pages/session/timeline/model.ts +++ b/packages/app/src/pages/session/timeline/model.ts @@ -44,7 +44,6 @@ export function createTimelineModel(input: { session: Pick - Effect.gen(function* () { - requests.length = 0 - hasHttpMiddleware = false - instruction = "Initial context" - const { db, bus, instructions } = yield* setup - yield* InstructionState.prepare(db, bus, instructions, sessionID) - const existing = SessionMessage.ID.create() - yield* bus.publish(SessionEvent.InboxEnqueued, { - sessionID, - inboxID: existing, - item: { type: "user", payload: { text: "Existing durable context" }, delivery: "steer" }, - }) - yield* bus.publish(SessionEvent.InboxDelivered, { - sessionID, - inboxID: existing, - }) - const settledAssistant = SessionMessage.ID.create() - yield* bus.publish(SessionEvent.Step.Started, { - sessionID, - assistantMessageID: settledAssistant, - agent: Agent.ID.make("build"), - model: { id: ID.make("generate-model"), providerID: Provider.ID.make("test") }, - }) - yield* bus.publish(SessionEvent.Text.Started, { - sessionID, - assistantMessageID: settledAssistant, - ordinal: 0, - }) - yield* bus.publish(SessionEvent.Text.Ended, { - sessionID, - assistantMessageID: settledAssistant, - ordinal: 0, - text: "Settled partial answer", - }) - const activeAssistant = SessionMessage.ID.create() - yield* bus.publish(SessionEvent.Step.Started, { - sessionID, - assistantMessageID: activeAssistant, - agent: Agent.ID.make("build"), - model: { id: ID.make("generate-model"), providerID: Provider.ID.make("test") }, - }) - yield* bus.publish(SessionEvent.Tool.Input.Started, { - sessionID, - assistantMessageID: activeAssistant, - id: "active-call", - name: "echo", - }) - yield* bus.publish(SessionEvent.Tool.Input.Ended, { - sessionID, - assistantMessageID: activeAssistant, - id: "active-call", - text: "{}", - }) - yield* bus.publish(SessionEvent.Tool.Called, { - sessionID, - assistantMessageID: activeAssistant, - id: "active-call", - input: {}, - executed: false, - }) - yield* bus.publish(SessionEvent.InboxEnqueued, { - sessionID, - inboxID: SessionMessage.ID.create(), - item: { type: "user", payload: { text: "Queued input must remain invisible" }, delivery: "queue" }, - }) - instruction = "Changed context" - const before = yield* durableState(db, sessionID) - const hooks = yield* PluginHooks.Service - yield* hooks.register("session", "context", (event) => - Effect.sync(() => { - event.system = [SystemPart.make("Hooked system"), ...event.system] - if (event.tools.lookup) event.tools.lookup.description = "Hooked lookup" - }), - ) +it.effect( + "generates from fresh settled Session context without durable mutation", + () => + Effect.gen(function* () { + requests.length = 0 + hasHttpMiddleware = false + instruction = "Initial context" + const { db, bus, instructions } = yield* setup + yield* InstructionState.prepare(db, bus, instructions, sessionID) + const existing = SessionMessage.ID.create() + yield* bus.publish(SessionEvent.InboxEnqueued, { + sessionID, + inboxID: existing, + item: { type: "user", payload: { text: "Existing durable context" }, delivery: "steer" }, + }) + yield* bus.publish(SessionEvent.InboxDelivered, { + sessionID, + inboxID: existing, + }) + const settledAssistant = SessionMessage.ID.create() + yield* bus.publish(SessionEvent.Step.Started, { + sessionID, + assistantMessageID: settledAssistant, + agent: Agent.ID.make("build"), + model: { id: ID.make("generate-model"), providerID: Provider.ID.make("test") }, + }) + yield* bus.publish(SessionEvent.Text.Started, { + sessionID, + assistantMessageID: settledAssistant, + ordinal: 0, + }) + yield* bus.publish(SessionEvent.Text.Ended, { + sessionID, + assistantMessageID: settledAssistant, + ordinal: 0, + text: "Settled partial answer", + }) + const activeAssistant = SessionMessage.ID.create() + yield* bus.publish(SessionEvent.Step.Started, { + sessionID, + assistantMessageID: activeAssistant, + agent: Agent.ID.make("build"), + model: { id: ID.make("generate-model"), providerID: Provider.ID.make("test") }, + }) + yield* bus.publish(SessionEvent.Tool.Input.Started, { + sessionID, + assistantMessageID: activeAssistant, + id: "active-call", + name: "echo", + }) + yield* bus.publish(SessionEvent.Tool.Input.Ended, { + sessionID, + assistantMessageID: activeAssistant, + id: "active-call", + text: "{}", + }) + yield* bus.publish(SessionEvent.Tool.Called, { + sessionID, + assistantMessageID: activeAssistant, + id: "active-call", + input: {}, + executed: false, + }) + yield* bus.publish(SessionEvent.InboxEnqueued, { + sessionID, + inboxID: SessionMessage.ID.create(), + item: { type: "user", payload: { text: "Queued input must remain invisible" }, delivery: "queue" }, + }) + instruction = "Changed context" + const before = yield* durableState(db, sessionID) + const hooks = yield* PluginHooks.Service + yield* hooks.register("session", "context", (event) => + Effect.sync(() => { + event.system = [SystemPart.make("Hooked system"), ...event.system] + if (event.tools.lookup) event.tools.lookup.description = "Hooked lookup" + }), + ) - const generate = yield* SessionGenerate.Service - const result = yield* generate.generate({ sessionID, prompt: "Summarize privately" }) + const generate = yield* SessionGenerate.Service + const result = yield* generate.generate({ sessionID, prompt: "Summarize privately" }) - expect(result).toBe("Transient answer") - expect(requests).toHaveLength(1) - expect(hasHttpMiddleware).toBe(true) - expect(requests[0]?.model).toBe(model) - expect(requests[0]?.system[0]?.text).toBe("Hooked system") - expect(requests[0]?.system.map((part) => part.text)).toContain("Initial context") - expect(requests[0]?.http?.headers).toMatchObject({ "X-Session-Id": sessionID }) - expect(requests[0]?.promptCacheKey).toBe(sessionID) - const instructionUpdates = requests[0]?.messages.flatMap((message) => - message.role === "system" - ? message.content.flatMap((content) => (content.type === "text" ? [content.text] : [])) - : [], - ) - expect(instructionUpdates).toHaveLength(1) - expect(instructionUpdates?.[0]).toContain("Changed context") - expect(instructionUpdates?.[0]).toContain("tools.captured.lookup(input: {}): Promise") - expect(userTexts(requests[0])).toEqual(["Existing durable context", "Summarize privately"]) - expect( - requests[0]?.messages.flatMap((message) => - message.role === "assistant" + expect(result).toBe("Transient answer") + expect(requests).toHaveLength(1) + expect(hasHttpMiddleware).toBe(true) + expect(requests[0]?.model).toBe(model) + expect(requests[0]?.system[0]?.text).toBe("Hooked system") + expect(requests[0]?.system.map((part) => part.text)).toContain("Initial context") + expect(requests[0]?.http?.headers).toMatchObject({ "X-Session-Id": sessionID }) + expect(requests[0]?.promptCacheKey).toBe(sessionID) + const instructionUpdates = requests[0]?.messages.flatMap((message) => + message.role === "system" ? message.content.flatMap((content) => (content.type === "text" ? [content.text] : [])) : [], - ), - ).toEqual(["Settled partial answer"]) - expect(requests[0]?.tools).toMatchObject([{ name: "lookup", description: "Hooked lookup" }]) - expect(requests[0]?.toolChoice).toBeUndefined() - expect(yield* durableState(db, sessionID)).toEqual(before) - }), + ) + expect(instructionUpdates).toHaveLength(1) + expect(instructionUpdates?.[0]).toContain("Changed context") + expect(instructionUpdates?.[0]).toContain("tools.captured.lookup(input: {}): Promise") + expect(userTexts(requests[0])).toEqual(["Existing durable context", "Summarize privately"]) + expect( + requests[0]?.messages.flatMap((message) => + message.role === "assistant" + ? message.content.flatMap((content) => (content.type === "text" ? [content.text] : [])) + : [], + ), + ).toEqual(["Settled partial answer"]) + expect(requests[0]?.tools).toMatchObject([{ name: "lookup", description: "Hooked lookup" }]) + expect(requests[0]?.toolChoice).toBeUndefined() + expect(yield* durableState(db, sessionID)).toEqual(before) + }), { timeout: 15_000 }, ) -it.effect("blocks unavailable initial instructions before generation", () => - Effect.gen(function* () { - requests.length = 0 - instruction = Instructions.unavailable - const { db } = yield* setup - const before = yield* durableState(db, sessionID) - const generate = yield* SessionGenerate.Service +it.effect( + "blocks unavailable initial instructions before generation", + () => + Effect.gen(function* () { + requests.length = 0 + instruction = Instructions.unavailable + const { db } = yield* setup + const before = yield* durableState(db, sessionID) + const generate = yield* SessionGenerate.Service - const error = yield* generate.generate({ sessionID, prompt: "Summarize privately" }).pipe(Effect.flip) + const error = yield* generate.generate({ sessionID, prompt: "Summarize privately" }).pipe(Effect.flip) - expect(error).toBeInstanceOf(Instructions.InitializationBlocked) - expect(requests).toEqual([]) - expect(yield* durableState(db, sessionID)).toEqual(before) - }), + expect(error).toBeInstanceOf(Instructions.InitializationBlocked) + expect(requests).toEqual([]) + expect(yield* durableState(db, sessionID)).toEqual(before) + }), { timeout: 15_000 }, ) diff --git a/packages/core/test/vcs-hg.test.ts b/packages/core/test/vcs-hg.test.ts index ff6e5b2911..06053ac51f 100644 --- a/packages/core/test/vcs-hg.test.ts +++ b/packages/core/test/vcs-hg.test.ts @@ -180,28 +180,30 @@ describeHg("Vcs mercurial", () => { ), ) - it.live("diffs a named branch against the default branch", () => - withHg((directory) => - Effect.gen(function* () { - yield* Effect.promise(async () => { - await fs.writeFile(path.join(directory, "file.txt"), "one\n") - await commitAll(directory, "initial") - }) - const vcs = yield* Vcs.Service - expect(yield* vcs.diff("branch")).toEqual([]) + it.live( + "diffs a named branch against the default branch", + () => + withHg((directory) => + Effect.gen(function* () { + yield* Effect.promise(async () => { + await fs.writeFile(path.join(directory, "file.txt"), "one\n") + await commitAll(directory, "initial") + }) + const vcs = yield* Vcs.Service + expect(yield* vcs.diff("branch")).toEqual([]) - yield* Effect.promise(async () => { - await hg(directory, "branch", "-q", "feature") - await fs.writeFile(path.join(directory, "file.txt"), "one\ntwo\n") - await commitAll(directory, "feature change") - }) - const diff = yield* vcs.diff("branch") - expect(diff.map((item) => ({ file: item.file, status: item.status }))).toEqual([ - { file: "file.txt", status: "modified" }, - ]) - expect(diff[0].patch).toContain("+two") - }), - ), + yield* Effect.promise(async () => { + await hg(directory, "branch", "-q", "feature") + await fs.writeFile(path.join(directory, "file.txt"), "one\ntwo\n") + await commitAll(directory, "feature change") + }) + const diff = yield* vcs.diff("branch") + expect(diff.map((item) => ({ file: item.file, status: item.status }))).toEqual([ + { file: "file.txt", status: "modified" }, + ]) + expect(diff[0].patch).toContain("+two") + }), + ), 15_000, ) })