| author | |
| committer | |
| log | bcd640bf4d81df20ac5686ff0ca86d861f909be2 |
| tree | 2cbd09cd447d00850caeff753a9cc0c35ced17a0 |
| parent | b49a2e677c6fba6d705ad16dbd913f56df4035d6 |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Julius Marminge <julius0216@outlook.com>14 files changed, 90 insertions(+), 14 deletions(-)
apps/server/src/provider/Drivers/CursorDriver.ts+2| ... | @@ -101,6 +101,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { | ... | @@ -101,6 +101,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { |
| 101 | defaultConfig: (): CursorSettings => decodeCursorSettings({}), | 101 | defaultConfig: (): CursorSettings => decodeCursorSettings({}), |
| 102 | create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => | 102 | create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => |
| 103 | Effect.gen(function* () { | 103 | Effect.gen(function* () { |
| 104 | const crypto = yield* Crypto.Crypto; | ||
| 104 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 105 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 105 | const fileSystem = yield* FileSystem.FileSystem; | 106 | const fileSystem = yield* FileSystem.FileSystem; |
| 106 | const path = yield* Path.Path; | 107 | const path = yield* Path.Path; |
| ... | @@ -133,6 +134,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { | ... | @@ -133,6 +134,7 @@ export const CursorDriver: ProviderDriver<CursorSettings, CursorDriverEnv> = { |
| 133 | 134 | ||
| 134 | const checkProvider = checkCursorProviderStatus(effectiveConfig, processEnv).pipe( | 135 | const checkProvider = checkCursorProviderStatus(effectiveConfig, processEnv).pipe( |
| 135 | Effect.map(stampIdentity), | 136 | Effect.map(stampIdentity), |
| 137 | Effect.provideService(Crypto.Crypto, crypto), | ||
| 136 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 138 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 137 | Effect.provideService(FileSystem.FileSystem, fileSystem), | 139 | Effect.provideService(FileSystem.FileSystem, fileSystem), |
| 138 | Effect.provideService(Path.Path, path), | 140 | Effect.provideService(Path.Path, path), |
apps/server/src/provider/Drivers/GrokDriver.ts+2| ... | @@ -84,6 +84,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { | ... | @@ -84,6 +84,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { |
| 84 | defaultConfig: (): GrokSettings => decodeGrokSettings({}), | 84 | defaultConfig: (): GrokSettings => decodeGrokSettings({}), |
| 85 | create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => | 85 | create: ({ instanceId, displayName, accentColor, environment, enabled, config }) => |
| 86 | Effect.gen(function* () { | 86 | Effect.gen(function* () { |
| 87 | const crypto = yield* Crypto.Crypto; | ||
| 87 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 88 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 88 | const httpClient = yield* HttpClient.HttpClient; | 89 | const httpClient = yield* HttpClient.HttpClient; |
| 89 | const serverSettings = yield* ServerSettingsService; | 90 | const serverSettings = yield* ServerSettingsService; |
| ... | @@ -114,6 +115,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { | ... | @@ -114,6 +115,7 @@ export const GrokDriver: ProviderDriver<GrokSettings, GrokDriverEnv> = { |
| 114 | 115 | ||
| 115 | const checkProvider = checkGrokProviderStatus(effectiveConfig, processEnv).pipe( | 116 | const checkProvider = checkGrokProviderStatus(effectiveConfig, processEnv).pipe( |
| 116 | Effect.map(stampIdentity), | 117 | Effect.map(stampIdentity), |
| 118 | Effect.provideService(Crypto.Crypto, crypto), | ||
| 117 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), | 119 | Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 118 | ); | 120 | ); |
| 119 | 121 |
apps/server/src/provider/Layers/CursorAdapter.test.ts+5-2| ... | @@ -228,7 +228,7 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { | ... | @@ -228,7 +228,7 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { |
| 228 | assert.isDefined(delta); | 228 | assert.isDefined(delta); |
| 229 | if (delta?.type === "content.delta") { | 229 | if (delta?.type === "content.delta") { |
| 230 | assert.equal(delta.payload.delta, "hello from mock"); | 230 | assert.equal(delta.payload.delta, "hello from mock"); |
| 231 | assert.match(String(delta.itemId), /^assistant:mock-session-1:segment:0$/); | 231 | assert.match(String(delta.itemId), /^assistant:mock-session-1:runtime:[^:]+:segment:0$/); |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | const assistantCompleted = runtimeEvents.find( | 234 | const assistantCompleted = runtimeEvents.find( |
| ... | @@ -687,7 +687,10 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { | ... | @@ -687,7 +687,10 @@ cursorAdapterTestLayer("CursorAdapterLive", (it) => { |
| 687 | if (contentDelta?.type === "content.delta") { | 687 | if (contentDelta?.type === "content.delta") { |
| 688 | assert.equal(String(contentDelta.turnId), String(turn.turnId)); | 688 | assert.equal(String(contentDelta.turnId), String(turn.turnId)); |
| 689 | assert.equal(contentDelta.payload.delta, "hello from mock"); | 689 | assert.equal(contentDelta.payload.delta, "hello from mock"); |
| 690 | assert.equal(String(contentDelta.itemId), "assistant:mock-session-1:segment:0"); | 690 | assert.match( |
| 691 | String(contentDelta.itemId), | ||
| 692 | /^assistant:mock-session-1:runtime:[^:]+:segment:0$/, | ||
| 693 | ); | ||
| 691 | } | 694 | } |
| 692 | }); | 695 | }); |
| 693 | 696 |
apps/server/src/provider/Layers/CursorAdapter.ts+1| ... | @@ -558,6 +558,7 @@ export function makeCursorAdapter( | ... | @@ -558,6 +558,7 @@ export function makeCursorAdapter( |
| 558 | : {}), | 558 | : {}), |
| 559 | ...acpNativeLoggers, | 559 | ...acpNativeLoggers, |
| 560 | }).pipe( | 560 | }).pipe( |
| 561 | Effect.provideService(Crypto.Crypto, crypto), | ||
| 561 | Effect.provideService(Scope.Scope, sessionScope), | 562 | Effect.provideService(Scope.Scope, sessionScope), |
| 562 | Effect.mapError( | 563 | Effect.mapError( |
| 563 | (cause) => | 564 | (cause) => |
apps/server/src/provider/Layers/CursorProvider.test.ts+2-1| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import * as NodeOS from "node:os"; | 1 | import * as NodeOS from "node:os"; |
| 2 | 2 | ||
| 3 | import * as NodeServices from "@effect/platform-node/NodeServices"; | 3 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 4 | import type * as Crypto from "effect/Crypto"; | ||
| 4 | import * as Effect from "effect/Effect"; | 5 | import * as Effect from "effect/Effect"; |
| 5 | import * as FileSystem from "effect/FileSystem"; | 6 | import * as FileSystem from "effect/FileSystem"; |
| 6 | import * as Path from "effect/Path"; | 7 | import * as Path from "effect/Path"; |
| ... | @@ -28,7 +29,7 @@ const runNode = <A, E>( | ... | @@ -28,7 +29,7 @@ const runNode = <A, E>( |
| 28 | effect: Effect.Effect< | 29 | effect: Effect.Effect< |
| 29 | A, | 30 | A, |
| 30 | E, | 31 | E, |
| 31 | ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | 32 | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem | Path.Path |
| 32 | >, | 33 | >, |
| 33 | ): Promise<A> => Effect.runPromise(effect.pipe(Effect.provide(NodeServices.layer))); | 34 | ): Promise<A> => Effect.runPromise(effect.pipe(Effect.provide(NodeServices.layer))); |
| 34 | 35 |
apps/server/src/provider/Layers/CursorProvider.ts+2-1| ... | @@ -11,6 +11,7 @@ import type { | ... | @@ -11,6 +11,7 @@ import type { |
| 11 | import { ProviderDriverKind } from "@t3tools/contracts"; | 11 | import { ProviderDriverKind } from "@t3tools/contracts"; |
| 12 | import type * as EffectAcpSchema from "effect-acp/schema"; | 12 | import type * as EffectAcpSchema from "effect-acp/schema"; |
| 13 | import { causeErrorTag } from "@t3tools/shared/observability"; | 13 | import { causeErrorTag } from "@t3tools/shared/observability"; |
| 14 | import * as Crypto from "effect/Crypto"; | ||
| 14 | import * as DateTime from "effect/DateTime"; | 15 | import * as DateTime from "effect/DateTime"; |
| 15 | import * as Effect from "effect/Effect"; | 16 | import * as Effect from "effect/Effect"; |
| 16 | import * as Exit from "effect/Exit"; | 17 | import * as Exit from "effect/Exit"; |
| ... | @@ -992,7 +993,7 @@ export const checkCursorProviderStatus = Effect.fn("checkCursorProviderStatus")( | ... | @@ -992,7 +993,7 @@ export const checkCursorProviderStatus = Effect.fn("checkCursorProviderStatus")( |
| 992 | ): Effect.fn.Return< | 993 | ): Effect.fn.Return< |
| 993 | ServerProviderDraft, | 994 | ServerProviderDraft, |
| 994 | never, | 995 | never, |
| 995 | ChildProcessSpawner.ChildProcessSpawner | FileSystem.FileSystem | Path.Path | 996 | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | FileSystem.FileSystem | Path.Path |
| 996 | > { | 997 | > { |
| 997 | const checkedAt = DateTime.formatIso(yield* DateTime.now); | 998 | const checkedAt = DateTime.formatIso(yield* DateTime.now); |
| 998 | const fallbackModels = getCursorFallbackModels(cursorSettings); | 999 | const fallbackModels = getCursorFallbackModels(cursorSettings); |
apps/server/src/provider/Layers/GrokAdapter.ts+1| ... | @@ -596,6 +596,7 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte | ... | @@ -596,6 +596,7 @@ export function makeGrokAdapter(grokSettings: GrokSettings, options?: GrokAdapte |
| 596 | : {}), | 596 | : {}), |
| 597 | ...acpNativeLoggers, | 597 | ...acpNativeLoggers, |
| 598 | }).pipe( | 598 | }).pipe( |
| 599 | Effect.provideService(Crypto.Crypto, crypto), | ||
| 599 | Effect.provideService(Scope.Scope, sessionScope), | 600 | Effect.provideService(Scope.Scope, sessionScope), |
| 600 | Effect.mapError( | 601 | Effect.mapError( |
| 601 | (cause) => | 602 | (cause) => |
apps/server/src/provider/Layers/GrokProvider.ts+6-1| ... | @@ -7,6 +7,7 @@ import { | ... | @@ -7,6 +7,7 @@ import { |
| 7 | } from "@t3tools/contracts"; | 7 | } from "@t3tools/contracts"; |
| 8 | import type * as EffectAcpSchema from "effect-acp/schema"; | 8 | import type * as EffectAcpSchema from "effect-acp/schema"; |
| 9 | import { causeErrorTag } from "@t3tools/shared/observability"; | 9 | import { causeErrorTag } from "@t3tools/shared/observability"; |
| 10 | import * as Crypto from "effect/Crypto"; | ||
| 10 | import * as DateTime from "effect/DateTime"; | 11 | import * as DateTime from "effect/DateTime"; |
| 11 | import * as Effect from "effect/Effect"; | 12 | import * as Effect from "effect/Effect"; |
| 12 | import * as Exit from "effect/Exit"; | 13 | import * as Exit from "effect/Exit"; |
| ... | @@ -167,7 +168,11 @@ const runGrokVersionCommand = ( | ... | @@ -167,7 +168,11 @@ const runGrokVersionCommand = ( |
| 167 | export const checkGrokProviderStatus = Effect.fn("checkGrokProviderStatus")(function* ( | 168 | export const checkGrokProviderStatus = Effect.fn("checkGrokProviderStatus")(function* ( |
| 168 | grokSettings: GrokSettings, | 169 | grokSettings: GrokSettings, |
| 169 | environment: NodeJS.ProcessEnv = process.env, | 170 | environment: NodeJS.ProcessEnv = process.env, |
| 170 | ): Effect.fn.Return<ServerProviderDraft, never, ChildProcessSpawner.ChildProcessSpawner> { | 171 | ): Effect.fn.Return< |
| 172 | ServerProviderDraft, | ||
| 173 | never, | ||
| 174 | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | ||
| 175 | > { | ||
| 171 | const checkedAt = DateTime.formatIso(yield* DateTime.now); | 176 | const checkedAt = DateTime.formatIso(yield* DateTime.now); |
| 172 | const fallbackModels = grokModelsFromSettings(grokSettings.customModels); | 177 | const fallbackModels = grokModelsFromSettings(grokSettings.customModels); |
| 173 | 178 |
apps/server/src/provider/acp/AcpJsonRpcConnection.test.ts+37| ... | @@ -116,6 +116,43 @@ describe("AcpSessionRuntime", () => { | ... | @@ -116,6 +116,43 @@ describe("AcpSessionRuntime", () => { |
| 116 | ), | 116 | ), |
| 117 | ); | 117 | ); |
| 118 | 118 | ||
| 119 | it.effect("keeps assistant item IDs unique when a provider session restarts", () => { | ||
| 120 | const collectFirstAssistantItemId = Effect.gen(function* () { | ||
| 121 | const runtime = yield* AcpSessionRuntime.AcpSessionRuntime; | ||
| 122 | const started = yield* runtime.start(); | ||
| 123 | expect(started.sessionId).toBe("mock-session-1"); | ||
| 124 | |||
| 125 | yield* runtime.prompt({ | ||
| 126 | prompt: [{ type: "text", text: "hi" }], | ||
| 127 | }); | ||
| 128 | |||
| 129 | const events = Array.from(yield* Stream.runCollect(Stream.take(runtime.getEvents(), 4))); | ||
| 130 | const assistantStart = events.find((event) => event._tag === "AssistantItemStarted"); | ||
| 131 | expect(assistantStart?._tag).toBe("AssistantItemStarted"); | ||
| 132 | return assistantStart?._tag === "AssistantItemStarted" ? assistantStart.itemId : ""; | ||
| 133 | }).pipe( | ||
| 134 | Effect.provide( | ||
| 135 | AcpSessionRuntime.layer({ | ||
| 136 | spawn: { | ||
| 137 | command: mockAgentCommand, | ||
| 138 | args: mockAgentArgs, | ||
| 139 | }, | ||
| 140 | cwd: process.cwd(), | ||
| 141 | clientInfo: { name: "t3-test", version: "0.0.0" }, | ||
| 142 | authMethodId: "test", | ||
| 143 | }), | ||
| 144 | ), | ||
| 145 | Effect.scoped, | ||
| 146 | ); | ||
| 147 | |||
| 148 | return Effect.gen(function* () { | ||
| 149 | const beforeRestart = yield* collectFirstAssistantItemId; | ||
| 150 | const afterRestart = yield* collectFirstAssistantItemId; | ||
| 151 | |||
| 152 | expect(afterRestart).not.toBe(beforeRestart); | ||
| 153 | }).pipe(Effect.provide(NodeServices.layer)); | ||
| 154 | }); | ||
| 155 | |||
| 119 | it.effect("drops session updates emitted for a child ACP session", () => | 156 | it.effect("drops session updates emitted for a child ACP session", () => |
| 120 | Effect.gen(function* () { | 157 | Effect.gen(function* () { |
| 121 | const runtime = yield* AcpSessionRuntime.AcpSessionRuntime; | 158 | const runtime = yield* AcpSessionRuntime.AcpSessionRuntime; |
apps/server/src/provider/acp/AcpSessionRuntime.ts+22-5| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import * as Cause from "effect/Cause"; | 1 | import * as Cause from "effect/Cause"; |
| 2 | import * as Clock from "effect/Clock"; | 2 | import * as Clock from "effect/Clock"; |
| 3 | import * as Context from "effect/Context"; | 3 | import * as Context from "effect/Context"; |
| 4 | import * as Crypto from "effect/Crypto"; | ||
| 4 | import * as Deferred from "effect/Deferred"; | 5 | import * as Deferred from "effect/Deferred"; |
| 5 | import * as Duration from "effect/Duration"; | 6 | import * as Duration from "effect/Duration"; |
| 6 | import * as Effect from "effect/Effect"; | 7 | import * as Effect from "effect/Effect"; |
| ... | @@ -270,14 +271,24 @@ export const make = ( | ... | @@ -270,14 +271,24 @@ export const make = ( |
| 270 | ): Effect.Effect< | 271 | ): Effect.Effect< |
| 271 | AcpSessionRuntime["Service"], | 272 | AcpSessionRuntime["Service"], |
| 272 | EffectAcpErrors.AcpError, | 273 | EffectAcpErrors.AcpError, |
| 273 | ChildProcessSpawner.ChildProcessSpawner | Scope.Scope | 274 | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto | Scope.Scope |
| 274 | > => | 275 | > => |
| 275 | Effect.gen(function* () { | 276 | Effect.gen(function* () { |
| 277 | const crypto = yield* Crypto.Crypto; | ||
| 276 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 278 | const spawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 277 | const runtimeScope = yield* Scope.Scope; | 279 | const runtimeScope = yield* Scope.Scope; |
| 278 | const eventQueue = yield* Queue.unbounded<AcpSessionRuntimeEvent>(); | 280 | const eventQueue = yield* Queue.unbounded<AcpSessionRuntimeEvent>(); |
| 279 | const modeStateRef = yield* Ref.make<AcpSessionModeState | undefined>(undefined); | 281 | const modeStateRef = yield* Ref.make<AcpSessionModeState | undefined>(undefined); |
| 280 | const toolCallsRef = yield* Ref.make(new Map<string, AcpToolCallState>()); | 282 | const toolCallsRef = yield* Ref.make(new Map<string, AcpToolCallState>()); |
| 283 | const assistantItemRuntimeId = yield* crypto.randomUUIDv4.pipe( | ||
| 284 | Effect.mapError( | ||
| 285 | (cause) => | ||
| 286 | new EffectAcpErrors.AcpTransportError({ | ||
| 287 | detail: "Failed to generate an ACP assistant item runtime identifier.", | ||
| 288 | cause, | ||
| 289 | }), | ||
| 290 | ), | ||
| 291 | ); | ||
| 281 | const assistantSegmentRef = yield* Ref.make<AcpAssistantSegmentState>({ nextSegmentIndex: 0 }); | 292 | const assistantSegmentRef = yield* Ref.make<AcpAssistantSegmentState>({ nextSegmentIndex: 0 }); |
| 282 | const configOptionsRef = yield* Ref.make(sessionConfigOptionsFromSetup(undefined)); | 293 | const configOptionsRef = yield* Ref.make(sessionConfigOptionsFromSetup(undefined)); |
| 283 | const startStateRef = yield* Ref.make<AcpStartState>({ _tag: "NotStarted" }); | 294 | const startStateRef = yield* Ref.make<AcpStartState>({ _tag: "NotStarted" }); |
| ... | @@ -387,6 +398,7 @@ export const make = ( | ... | @@ -387,6 +398,7 @@ export const make = ( |
| 387 | modeStateRef, | 398 | modeStateRef, |
| 388 | toolCallsRef, | 399 | toolCallsRef, |
| 389 | assistantSegmentRef, | 400 | assistantSegmentRef, |
| 401 | assistantItemRuntimeId, | ||
| 390 | params: notification, | 402 | params: notification, |
| 391 | }); | 403 | }); |
| 392 | }), | 404 | }), |
| ... | @@ -802,7 +814,7 @@ export const layer = ( | ... | @@ -802,7 +814,7 @@ export const layer = ( |
| 802 | ): Layer.Layer< | 814 | ): Layer.Layer< |
| 803 | AcpSessionRuntime, | 815 | AcpSessionRuntime, |
| 804 | EffectAcpErrors.AcpError, | 816 | EffectAcpErrors.AcpError, |
| 805 | ChildProcessSpawner.ChildProcessSpawner | 817 | ChildProcessSpawner.ChildProcessSpawner | Crypto.Crypto |
| 806 | > => Layer.effect(AcpSessionRuntime, make(options)); | 818 | > => Layer.effect(AcpSessionRuntime, make(options)); |
| 807 | 819 | ||
| 808 | function sessionConfigOptionsFromSetup( | 820 | function sessionConfigOptionsFromSetup( |
| ... | @@ -834,12 +846,14 @@ const handleSessionUpdate = ({ | ... | @@ -834,12 +846,14 @@ const handleSessionUpdate = ({ |
| 834 | modeStateRef, | 846 | modeStateRef, |
| 835 | toolCallsRef, | 847 | toolCallsRef, |
| 836 | assistantSegmentRef, | 848 | assistantSegmentRef, |
| 849 | assistantItemRuntimeId, | ||
| 837 | params, | 850 | params, |
| 838 | }: { | 851 | }: { |
| 839 | readonly queue: Queue.Queue<AcpSessionRuntimeEvent>; | 852 | readonly queue: Queue.Queue<AcpSessionRuntimeEvent>; |
| 840 | readonly modeStateRef: Ref.Ref<AcpSessionModeState | undefined>; | 853 | readonly modeStateRef: Ref.Ref<AcpSessionModeState | undefined>; |
| 841 | readonly toolCallsRef: Ref.Ref<Map<string, AcpToolCallState>>; | 854 | readonly toolCallsRef: Ref.Ref<Map<string, AcpToolCallState>>; |
| 842 | readonly assistantSegmentRef: Ref.Ref<AcpAssistantSegmentState>; | 855 | readonly assistantSegmentRef: Ref.Ref<AcpAssistantSegmentState>; |
| 856 | readonly assistantItemRuntimeId: string; | ||
| 843 | readonly params: EffectAcpSchema.SessionNotification; | 857 | readonly params: EffectAcpSchema.SessionNotification; |
| 844 | }): Effect.Effect<void> => | 858 | }): Effect.Effect<void> => |
| 845 | Effect.gen(function* () { | 859 | Effect.gen(function* () { |
| ... | @@ -887,6 +901,7 @@ const handleSessionUpdate = ({ | ... | @@ -887,6 +901,7 @@ const handleSessionUpdate = ({ |
| 887 | queue, | 901 | queue, |
| 888 | assistantSegmentRef, | 902 | assistantSegmentRef, |
| 889 | sessionId: params.sessionId, | 903 | sessionId: params.sessionId, |
| 904 | assistantItemRuntimeId, | ||
| 890 | }); | 905 | }); |
| 891 | yield* Queue.offer(queue, { | 906 | yield* Queue.offer(queue, { |
| 892 | ...event, | 907 | ...event, |
| ... | @@ -924,17 +939,19 @@ function shouldEmitToolCallUpdate( | ... | @@ -924,17 +939,19 @@ function shouldEmitToolCallUpdate( |
| 924 | return previous === undefined || previous.title !== next.title || previous.detail !== next.detail; | 939 | return previous === undefined || previous.title !== next.title || previous.detail !== next.detail; |
| 925 | } | 940 | } |
| 926 | 941 | ||
| 927 | const assistantItemId = (sessionId: string, segmentIndex: number) => | 942 | const assistantItemId = (sessionId: string, runtimeId: string, segmentIndex: number) => |
| 928 | `assistant:${sessionId}:segment:${segmentIndex}`; | 943 | `assistant:${sessionId}:runtime:${runtimeId}:segment:${segmentIndex}`; |
| 929 | 944 | ||
| 930 | const ensureActiveAssistantSegment = ({ | 945 | const ensureActiveAssistantSegment = ({ |
| 931 | queue, | 946 | queue, |
| 932 | assistantSegmentRef, | 947 | assistantSegmentRef, |
| 933 | sessionId, | 948 | sessionId, |
| 949 | assistantItemRuntimeId, | ||
| 934 | }: { | 950 | }: { |
| 935 | readonly queue: Queue.Queue<AcpSessionRuntimeEvent>; | 951 | readonly queue: Queue.Queue<AcpSessionRuntimeEvent>; |
| 936 | readonly assistantSegmentRef: Ref.Ref<AcpAssistantSegmentState>; | 952 | readonly assistantSegmentRef: Ref.Ref<AcpAssistantSegmentState>; |
| 937 | readonly sessionId: string; | 953 | readonly sessionId: string; |
| 954 | readonly assistantItemRuntimeId: string; | ||
| 938 | }) => | 955 | }) => |
| 939 | Ref.modify<AcpAssistantSegmentState, EnsureActiveAssistantSegmentResult>( | 956 | Ref.modify<AcpAssistantSegmentState, EnsureActiveAssistantSegmentResult>( |
| 940 | assistantSegmentRef, | 957 | assistantSegmentRef, |
| ... | @@ -942,7 +959,7 @@ const ensureActiveAssistantSegment = ({ | ... | @@ -942,7 +959,7 @@ const ensureActiveAssistantSegment = ({ |
| 942 | if (current.activeItemId) { | 959 | if (current.activeItemId) { |
| 943 | return [{ itemId: current.activeItemId }, current] as const; | 960 | return [{ itemId: current.activeItemId }, current] as const; |
| 944 | } | 961 | } |
| 945 | const itemId = assistantItemId(sessionId, current.nextSegmentIndex); | 962 | const itemId = assistantItemId(sessionId, assistantItemRuntimeId, current.nextSegmentIndex); |
| 946 | return [ | 963 | return [ |
| 947 | { | 964 | { |
| 948 | itemId, | 965 | itemId, |
apps/server/src/provider/acp/CursorAcpSupport.ts+2-1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | import { type CursorSettings, type ProviderOptionSelection } from "@t3tools/contracts"; | 1 | import { type CursorSettings, type ProviderOptionSelection } from "@t3tools/contracts"; |
| 2 | import * as Crypto from "effect/Crypto"; | ||
| 2 | import * as Effect from "effect/Effect"; | 3 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; | 4 | import * as Layer from "effect/Layer"; |
| 4 | import * as Scope from "effect/Scope"; | 5 | import * as Scope from "effect/Scope"; |
| ... | @@ -50,7 +51,7 @@ export const makeCursorAcpRuntime = ( | ... | @@ -50,7 +51,7 @@ export const makeCursorAcpRuntime = ( |
| 50 | ): Effect.Effect< | 51 | ): Effect.Effect< |
| 51 | AcpSessionRuntime.AcpSessionRuntime["Service"], | 52 | AcpSessionRuntime.AcpSessionRuntime["Service"], |
| 52 | EffectAcpErrors.AcpError, | 53 | EffectAcpErrors.AcpError, |
| 53 | Scope.Scope | 54 | Crypto.Crypto | Scope.Scope |
| 54 | > => | 55 | > => |
| 55 | Effect.gen(function* () { | 56 | Effect.gen(function* () { |
| 56 | const acpContext = yield* Layer.build( | 57 | const acpContext = yield* Layer.build( |
apps/server/src/provider/acp/GrokAcpSupport.ts+2-1| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | import { type GrokSettings, ProviderDriverKind } from "@t3tools/contracts"; | 1 | import { type GrokSettings, ProviderDriverKind } from "@t3tools/contracts"; |
| 2 | import * as Crypto from "effect/Crypto"; | ||
| 2 | import * as Effect from "effect/Effect"; | 3 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; | 4 | import * as Layer from "effect/Layer"; |
| 4 | import * as Scope from "effect/Scope"; | 5 | import * as Scope from "effect/Scope"; |
| ... | @@ -55,7 +56,7 @@ export const makeGrokAcpRuntime = ( | ... | @@ -55,7 +56,7 @@ export const makeGrokAcpRuntime = ( |
| 55 | ): Effect.Effect< | 56 | ): Effect.Effect< |
| 56 | AcpSessionRuntime.AcpSessionRuntime["Service"], | 57 | AcpSessionRuntime.AcpSessionRuntime["Service"], |
| 57 | EffectAcpErrors.AcpError, | 58 | EffectAcpErrors.AcpError, |
| 58 | Scope.Scope | 59 | Crypto.Crypto | Scope.Scope |
| 59 | > => | 60 | > => |
| 60 | Effect.gen(function* () { | 61 | Effect.gen(function* () { |
| 61 | const acpContext = yield* Layer.build( | 62 | const acpContext = yield* Layer.build( |
apps/server/src/textGeneration/CursorTextGeneration.ts+3-1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | import * as Crypto from "effect/Crypto"; | ||
| 1 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 2 | import * as Option from "effect/Option"; | 3 | import * as Option from "effect/Option"; |
| 3 | import * as Ref from "effect/Ref"; | 4 | import * as Ref from "effect/Ref"; |
| ... | @@ -38,6 +39,7 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu | ... | @@ -38,6 +39,7 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu |
| 38 | cursorSettings: CursorSettings, | 39 | cursorSettings: CursorSettings, |
| 39 | environment?: NodeJS.ProcessEnv, | 40 | environment?: NodeJS.ProcessEnv, |
| 40 | ) { | 41 | ) { |
| 42 | const crypto = yield* Crypto.Crypto; | ||
| 41 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 43 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 42 | const resolvedEnvironment = environment ?? process.env; | 44 | const resolvedEnvironment = environment ?? process.env; |
| 43 | 45 | ||
| ... | @@ -66,7 +68,7 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu | ... | @@ -66,7 +68,7 @@ export const makeCursorTextGeneration = Effect.fn("makeCursorTextGeneration")(fu |
| 66 | childProcessSpawner: commandSpawner, | 68 | childProcessSpawner: commandSpawner, |
| 67 | cwd, | 69 | cwd, |
| 68 | clientInfo: { name: "t3-code-git-text", version: "0.0.0" }, | 70 | clientInfo: { name: "t3-code-git-text", version: "0.0.0" }, |
| 69 | }); | 71 | }).pipe(Effect.provideService(Crypto.Crypto, crypto)); |
| 70 | 72 | ||
| 71 | yield* runtime.handleSessionUpdate((notification) => { | 73 | yield* runtime.handleSessionUpdate((notification) => { |
| 72 | const update = notification.update; | 74 | const update = notification.update; |
apps/server/src/textGeneration/GrokTextGeneration.ts+3-1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | import * as Crypto from "effect/Crypto"; | ||
| 1 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 2 | import * as Option from "effect/Option"; | 3 | import * as Option from "effect/Option"; |
| 3 | import * as Ref from "effect/Ref"; | 4 | import * as Ref from "effect/Ref"; |
| ... | @@ -37,6 +38,7 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi | ... | @@ -37,6 +38,7 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi |
| 37 | grokSettings: GrokSettings, | 38 | grokSettings: GrokSettings, |
| 38 | environment: NodeJS.ProcessEnv = process.env, | 39 | environment: NodeJS.ProcessEnv = process.env, |
| 39 | ) { | 40 | ) { |
| 41 | const crypto = yield* Crypto.Crypto; | ||
| 40 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; | 42 | const commandSpawner = yield* ChildProcessSpawner.ChildProcessSpawner; |
| 41 | 43 | ||
| 42 | const runGrokJson = <S extends Schema.Top>({ | 44 | const runGrokJson = <S extends Schema.Top>({ |
| ... | @@ -65,7 +67,7 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi | ... | @@ -65,7 +67,7 @@ export const makeGrokTextGeneration = Effect.fn("makeGrokTextGeneration")(functi |
| 65 | childProcessSpawner: commandSpawner, | 67 | childProcessSpawner: commandSpawner, |
| 66 | cwd, | 68 | cwd, |
| 67 | clientInfo: { name: "t3-code-git-text", version: "0.0.0" }, | 69 | clientInfo: { name: "t3-code-git-text", version: "0.0.0" }, |
| 68 | }); | 70 | }).pipe(Effect.provideService(Crypto.Crypto, crypto)); |
| 69 | 71 | ||
| 70 | yield* runtime.handleSessionUpdate((notification) => { | 72 | yield* runtime.handleSessionUpdate((notification) => { |
| 71 | const update = notification.update; | 73 | const update = notification.update; |