From e38b6a188fafd499df347331f8c8279ff320c207 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 8 Oct 2026 16:34:47 -0700 Subject: [PATCH] refactor(provider-core): share attachment prompts, notifications, and event loggers (#17330) Co-authored-by: Claude Opus 5.5 --- .../scripts/record-grok-acp-replay-fixture.ts | 2 +- .../orchestration-v2/Adapters/AcpAdapterV2.ts | 9 +++--- .../Adapters/ClaudeAdapterV2.ts | 12 ++++--- .../Adapters/CodexAdapterV2.ts | 7 +++-- .../Adapters/CursorAdapterV2.ts | 7 +++-- .../Adapters/MuseAdapterV2.ts | 15 ++++++--- .../Adapters/OpenCode2AdapterV2.ts | 19 +++++++++--- .../Adapters/OpenCodeAdapterV2.ts | 13 +++++--- .../src/orchestration-v2/Orchestrator.ts | 2 +- .../testkit/ReplayRuntimeInstructions.ts | 2 +- .../provider/CodexDeveloperInstructions.ts | 2 +- .../src/provider/Drivers/AntigravityDriver.ts | 2 +- .../src/provider/Drivers/ClaudeDriver.ts | 2 +- .../src/provider/Drivers/CodexDriver.ts | 2 +- .../provider/Drivers/CodexManagedProvider.ts | 2 +- .../src/provider/Drivers/CursorDriver.ts | 2 +- .../server/src/provider/Drivers/GrokDriver.ts | 2 +- .../server/src/provider/Drivers/MuseDriver.ts | 2 +- .../src/provider/Drivers/OpenCodeDriver.ts | 2 +- apps/server/src/provider/EventNdjsonLogger.ts | 7 ++--- .../src/provider/ProviderEventLoggers.ts | 31 +++---------------- packages/provider-core/package.json | 20 ++++++++++++ .../src/server/ProviderEventLoggers.ts | 30 ++++++++++++++++++ .../src/server/attachmentPrompt.test.ts | 14 ++++++--- .../src/server/attachmentPrompt.ts | 10 ++---- .../src/server}/instanceIdentity.ts | 0 .../src/server/notification.test.ts | 2 +- .../provider-core/src/server/notification.ts | 0 .../src/server/runtimeInstructions.test.ts | 2 +- .../src/server/runtimeInstructions.ts | 0 30 files changed, 138 insertions(+), 84 deletions(-) create mode 100644 packages/provider-core/src/server/ProviderEventLoggers.ts rename apps/server/src/orchestration-v2/AttachmentPrompt.test.ts => packages/provider-core/src/server/attachmentPrompt.test.ts (92%) rename apps/server/src/orchestration-v2/AttachmentPrompt.ts => packages/provider-core/src/server/attachmentPrompt.ts (96%) rename {apps/server/src/provider/Drivers => packages/provider-core/src/server}/instanceIdentity.ts (100%) rename apps/server/src/orchestration-v2/Notification.test.ts => packages/provider-core/src/server/notification.test.ts (99%) rename apps/server/src/orchestration-v2/Notification.ts => packages/provider-core/src/server/notification.ts (100%) rename apps/server/src/provider/RuntimeInstructions.test.ts => packages/provider-core/src/server/runtimeInstructions.test.ts (96%) rename apps/server/src/provider/RuntimeInstructions.ts => packages/provider-core/src/server/runtimeInstructions.ts (100%) diff --git a/apps/server/scripts/record-grok-acp-replay-fixture.ts b/apps/server/scripts/record-grok-acp-replay-fixture.ts index a6b88d3024ff78f140d5d71ac3350a896e6fa10e..23d687eb91309e98a9efab764f43173465ff2f98 100644 --- a/apps/server/scripts/record-grok-acp-replay-fixture.ts +++ b/apps/server/scripts/record-grok-acp-replay-fixture.ts @@ -44,7 +44,7 @@ import { makeReplayServerConfig } from "../src/orchestration-v2/testkit/Provider import * as ProviderReplayHarness from "../src/orchestration-v2/testkit/ProviderReplayHarness.ts"; import { checkpointWorkspace } from "@t3tools/provider-testing/replayWorkspace"; import { makeGrokAcpRuntime } from "../src/provider/acp/GrokAcpSupport.ts"; -import { buildRuntimeInstructions } from "../src/provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; const wallClock = Clock.Clock.defaultValue(); const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); diff --git a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts index 2767e0e1b41d3a7f4617a517f832356d812d0e8b..90255fe75a78701fe5842be70694d55122a4fb43 100644 --- a/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/AcpAdapterV2.ts @@ -98,14 +98,14 @@ import { t3AcpPromptWithInstructions, type T3AcpInstructionState, } from "@t3tools/provider-core/server/orchestrationInstructions"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { type ProviderContinuationRequest } from "@t3tools/provider-core/server/continuationRequests"; import { type BackgroundWork, type BackgroundWorkReport, backgroundWorkNotification, -} from "../Notification.ts"; +} from "@t3tools/provider-core/server/notification"; import { makeProviderFailure, makeProviderRetryTurnItem, @@ -114,7 +114,7 @@ import { acpSelectionTransition } from "@t3tools/provider-core/server/selectionT import { isProviderNativeImageAttachment, providerMessageTextWithAttachmentPaths, -} from "../AttachmentPrompt.ts"; +} from "@t3tools/provider-core/server/attachmentPrompt"; import { makeSubagentChildThread, makeSubagentConversationArtifacts, @@ -6781,7 +6781,8 @@ export function makeAcpAdapterV2( const messageText = providerMessageTextWithAttachmentPaths({ text: turnInput.message.text, attachments: turnInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ attachmentsDir: serverConfig.attachmentsDir, attachment }), }); const text = t3AcpPromptWithInstructions({ prompt: messageText, diff --git a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts index 0b33327b18509bed426ee269b7660a1a74c8fe62..50fab3e191dba7f5e4255b5f0375c1add9faf867 100644 --- a/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts @@ -112,7 +112,7 @@ import { import type { ServerProviderShape } from "@t3tools/provider-core/server/snapshot"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { T3_CODE_ORCHESTRATION_INSTRUCTIONS } from "@t3tools/provider-core/server/orchestrationInstructions"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import { mcpToolPresentation, normalizeMcpText, @@ -124,14 +124,17 @@ import { makeProviderRetryTurnItem, } from "@t3tools/provider-core/server/failure"; import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; -import { providerMessageTextWithAttachmentPaths } from "../AttachmentPrompt.ts"; +import { providerMessageTextWithAttachmentPaths } from "@t3tools/provider-core/server/attachmentPrompt"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; import { ProviderAdapterDriverCreateError, type ProviderAdapterDriver, type ProviderAdapterDriverCreateInput, } from "@t3tools/provider-core/server/adapterDriver"; -import { type BackgroundWorkReport, backgroundWorkNotification } from "../Notification.ts"; +import { + type BackgroundWorkReport, + backgroundWorkNotification, +} from "@t3tools/provider-core/server/notification"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; import { makeSubagentChildThread, @@ -1344,7 +1347,8 @@ const makeClaudeUserMessageWithAttachments = Effect.fnUntraced(function* (input: const textWithAttachmentPaths = providerMessageTextWithAttachmentPaths({ text: input.text, attachments: input.attachments, - attachmentsDir: input.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ attachmentsDir: input.attachmentsDir, attachment }), }); const dispatch = diff --git a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts index 660e690b5887bb0be47b5c122f03995afa300319..5318971e89d4e7997209d65f8d4d4ec58d848d07 100644 --- a/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts @@ -126,7 +126,7 @@ import { type ProviderContinuationRequest, ProviderContinuationRequests, } from "@t3tools/provider-core/server/continuationRequests"; -import { backgroundWorkNotification } from "../Notification.ts"; +import { backgroundWorkNotification } from "@t3tools/provider-core/server/notification"; import { makeProviderFailure, makeProviderFailureTurnItem, @@ -136,7 +136,7 @@ import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/sel import { isProviderNativeImageAttachment, providerMessageTextWithAttachmentPaths, -} from "../AttachmentPrompt.ts"; +} from "@t3tools/provider-core/server/attachmentPrompt"; import { ProviderAdapterEnsureThreadError, ProviderAdapterForkThreadError, @@ -3150,7 +3150,8 @@ export function makeCodexAdapterV2(adapterOptions: CodexAdapterV2Options): Provi const text = providerMessageTextWithAttachmentPaths({ text: codexSkillMentionText(turnInput.message.text), attachments: turnInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ attachmentsDir: serverConfig.attachmentsDir, attachment }), }); if (text.length > 0) { inputItems.push({ diff --git a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts index 077cb6ead3373fe809a69c261339c27835eb5358..53a1d260cc838d199857a293e5ee75722bb13a5a 100644 --- a/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/CursorAdapterV2.ts @@ -55,14 +55,14 @@ import { } from "../../provider/Drivers/CursorSkills.ts"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { t3OrchestrationPromptForFirstRun } from "@t3tools/provider-core/server/orchestrationInstructions"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; import { isProviderNativeImageAttachment, providerMessageTextWithAttachmentPaths, -} from "../AttachmentPrompt.ts"; +} from "@t3tools/provider-core/server/attachmentPrompt"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; import { ProviderAdapterDriverCreateError, @@ -2133,7 +2133,8 @@ export function makeCursorAdapterV2( ? rawText : rewriteCursorSkillMentions(rawText, cursorSkillNames), attachments: turnInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ attachmentsDir: serverConfig.attachmentsDir, attachment }), }), runOrdinal: turnInput.runOrdinal, hasT3Mcp: cursorMcpServers(turnInput.threadId) !== undefined, diff --git a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts index b68f90c60a66720c69c8bd93c29491916e8550b4..30d7073463a76903bbe73a1953b70b97e6e93386 100644 --- a/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/MuseAdapterV2.ts @@ -38,7 +38,7 @@ import * as Stream from "effect/Stream"; import { resolveAttachmentPath } from "../../attachmentStore.ts"; import * as ServerConfig from "../../config.ts"; import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import { museModelCapabilities, resolveMuseReasoningEffort, @@ -71,7 +71,7 @@ import type { EventNdjsonLogger } from "../../provider/EventNdjsonLogger.ts"; import { providerMessageTextWithAttachmentPaths, isProviderNativeImageAttachment, -} from "../AttachmentPrompt.ts"; +} from "@t3tools/provider-core/server/attachmentPrompt"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { ProviderAdapterEnsureThreadError, @@ -96,7 +96,10 @@ import { type ProviderAdapterV2TurnInput, type ProviderAdapterV2TurnMessage, } from "@t3tools/provider-core/server/ProviderAdapter"; -import { backgroundWorkNotification, type BackgroundWorkReport } from "../Notification.ts"; +import { + backgroundWorkNotification, + type BackgroundWorkReport, +} from "@t3tools/provider-core/server/notification"; import type * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; @@ -1598,7 +1601,11 @@ export function makeMuseAdapterV2(options: MuseAdapterV2Options): ProviderAdapte const text = providerMessageTextWithAttachmentPaths({ text: message.text, attachments: message.attachments, - attachmentsDir: options.serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ + attachmentsDir: options.serverConfig.attachmentsDir, + attachment, + }), }); if (text) parts.push({ type: "text", text }); for (const attachment of message.attachments) diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.ts index 5806fc5fb68d2951d9d7c8e749035feeb2c43d9b..6bc194b811a40fac09725f48863907cb8cc49eb1 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCode2AdapterV2.ts @@ -70,6 +70,7 @@ import * as Schema from "effect/Schema"; import * as Semaphore from "effect/Semaphore"; import * as Stream from "effect/Stream"; +import { resolveAttachmentPath } from "../../attachmentStore.ts"; import * as ServerConfig from "../../config.ts"; import { paginate, type OpenCode2StreamEvent } from "../../provider/opencode2/OpenCode2Client.ts"; import * as OpenCode2Server from "../../provider/opencode2/OpenCode2Server.ts"; @@ -78,16 +79,19 @@ import { type OpenCodeRuntimeError, } from "../../provider/opencodeRuntime.ts"; import * as McpProviderSession from "@t3tools/provider-core/server/mcpSession"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import { t3OrchestrationSystemPrompt } from "@t3tools/provider-core/server/orchestrationInstructions"; import { SKILL_MENTION_PATTERN } from "@t3tools/shared/composerInlineTokens"; import * as KeyedLock from "@t3tools/shared/KeyedLock"; import { getModelSelectionStringOptionValue, modelSelectionsEqual } from "@t3tools/shared/model"; import { causeErrorTag } from "@t3tools/shared/observability"; -import { providerMessageTextWithAttachmentPaths } from "../AttachmentPrompt.ts"; +import { providerMessageTextWithAttachmentPaths } from "@t3tools/provider-core/server/attachmentPrompt"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; -import { backgroundWorkNotification, type BackgroundWorkReport } from "../Notification.ts"; +import { + backgroundWorkNotification, + type BackgroundWorkReport, +} from "@t3tools/provider-core/server/notification"; import * as ProviderContinuationRequests from "@t3tools/provider-core/server/continuationRequests"; import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import { @@ -3212,7 +3216,8 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr providerMessageTextWithAttachmentPaths({ text: turnInput.message.text, attachments: turnInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ attachmentsDir: serverConfig.attachmentsDir, attachment }), }).trim(); const removeMcp = (mcp: { readonly name: string; readonly directory: string }) => @@ -3893,7 +3898,11 @@ export const make = Effect.fn("OpenCode2Adapter.make")(function* (instanceId: Pr text: providerMessageTextWithAttachmentPaths({ text: steerInput.message.text, attachments: steerInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ + attachmentsDir: serverConfig.attachmentsDir, + attachment, + }), }).trim(), delivery: "steer", }) diff --git a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts index 8b3216fbaaa7de419695ccdeaf10b08da51dd942..c746bec2180e61fef52195da1d78bdc9a526d682 100644 --- a/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts +++ b/apps/server/src/orchestration-v2/Adapters/OpenCodeAdapterV2.ts @@ -66,12 +66,12 @@ import { } from "../../provider/NativeProtocolLogging.ts"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { t3OrchestrationSystemPrompt } from "@t3tools/provider-core/server/orchestrationInstructions"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import * as OpenCodeRuntime from "../../provider/opencodeRuntime.ts"; import * as IdAllocator from "@t3tools/provider-core/server/IdAllocator"; import { makeProviderFailure } from "@t3tools/provider-core/server/failure"; import { turnScopedSelectionTransition } from "@t3tools/provider-core/server/selectionTransition"; -import { providerMessageTextWithAttachmentPaths } from "../AttachmentPrompt.ts"; +import { providerMessageTextWithAttachmentPaths } from "@t3tools/provider-core/server/attachmentPrompt"; import * as ProviderAdapter from "@t3tools/provider-core/server/ProviderAdapter"; import { ProviderAdapterDriverCreateError, @@ -2853,7 +2853,8 @@ export function makeOpenCodeAdapterV2( const text = providerMessageTextWithAttachmentPaths({ text: turnInput.message.text, attachments: turnInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ attachmentsDir: serverConfig.attachmentsDir, attachment }), }).trim(); const files = OpenCodeRuntime.toOpenCodeFileParts({ attachments: turnInput.message.attachments, @@ -3356,7 +3357,11 @@ export function makeOpenCodeAdapterV2( const text = providerMessageTextWithAttachmentPaths({ text: steerInput.message.text, attachments: steerInput.message.attachments, - attachmentsDir: serverConfig.attachmentsDir, + resolveAttachmentPath: (attachment) => + resolveAttachmentPath({ + attachmentsDir: serverConfig.attachmentsDir, + attachment, + }), }).trim(); const files = OpenCodeRuntime.toOpenCodeFileParts({ attachments: steerInput.message.attachments, diff --git a/apps/server/src/orchestration-v2/Orchestrator.ts b/apps/server/src/orchestration-v2/Orchestrator.ts index a2e45ed21c5115be7158789917c90cacb68957ab..54127e20bc7a375a491d02d3d56a3a243ba4c27a 100644 --- a/apps/server/src/orchestration-v2/Orchestrator.ts +++ b/apps/server/src/orchestration-v2/Orchestrator.ts @@ -85,7 +85,7 @@ import { CheckpointServiceV2 } from "./CheckpointService.ts"; import { CommandPolicyV2, resolveMessageDispatchIntent } from "./CommandPolicy.ts"; import { CommandReceiptStoreV2 } from "./CommandReceiptStore.ts"; import { ContextHandoffServiceV2 } from "./ContextHandoffService.ts"; -import { notificationTurnItem } from "./Notification.ts"; +import { notificationTurnItem } from "@t3tools/provider-core/server/notification"; import { isRestartNoteSource } from "./RestartBackgroundNote.ts"; import { isUndeliveredMailboxSteer } from "./NotificationMailbox.ts"; import { EventSinkV2 } from "./EventSink.ts"; diff --git a/apps/server/src/orchestration-v2/testkit/ReplayRuntimeInstructions.ts b/apps/server/src/orchestration-v2/testkit/ReplayRuntimeInstructions.ts index 18f87d1f7a4f299ff4c28cd2efe119f098bc502e..31fa33f055d20dafc9c66a0970e3d2251fc6be2e 100644 --- a/apps/server/src/orchestration-v2/testkit/ReplayRuntimeInstructions.ts +++ b/apps/server/src/orchestration-v2/testkit/ReplayRuntimeInstructions.ts @@ -1,6 +1,6 @@ import type { ProviderDriverKind, ProviderReplayTranscript } from "@t3tools/contracts"; -import { buildRuntimeInstructions } from "../../provider/RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; /** Adds current runtime context to legacy prompt expectations, keeping outbound matching exact. */ export function materializeReplayTranscriptRuntimeInstructions( diff --git a/apps/server/src/provider/CodexDeveloperInstructions.ts b/apps/server/src/provider/CodexDeveloperInstructions.ts index e9cbe27997abb1e80c97a560830f02d3cf91de84..f80aefe98a707571b31c03c419d2d81c4a059202 100644 --- a/apps/server/src/provider/CodexDeveloperInstructions.ts +++ b/apps/server/src/provider/CodexDeveloperInstructions.ts @@ -1,6 +1,6 @@ import type { ProviderInteractionMode } from "@t3tools/contracts"; import type { V2TurnStartParams__AdditionalContextEntry } from "effect-codex-app-server/schema"; -import { buildRuntimeInstructions } from "./RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "@t3tools/provider-core/server/runtimeInstructions"; import { T3_CODE_BROWSER_TOOL_INSTRUCTIONS, diff --git a/apps/server/src/provider/Drivers/AntigravityDriver.ts b/apps/server/src/provider/Drivers/AntigravityDriver.ts index 238834f63a11f8e6428782f9e73c23a4b8f79c60..f354dc4b4aa74c17b6ff6a7026812ba0c52fe482 100644 --- a/apps/server/src/provider/Drivers/AntigravityDriver.ts +++ b/apps/server/src/provider/Drivers/AntigravityDriver.ts @@ -60,7 +60,7 @@ import { type ProviderInstance, } from "@t3tools/provider-core/server/driver"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { discoverAntigravitySkills, resolveAntigravityUserHome } from "./AntigravitySkills.ts"; const DRIVER = ProviderDriverKind.make("antigravity"); diff --git a/apps/server/src/provider/Drivers/ClaudeDriver.ts b/apps/server/src/provider/Drivers/ClaudeDriver.ts index ab9e6d2e8f2f353322f40391f383029c9ef938a3..aea45e37cb9b3d0095dae4f918925e5128ba0d23 100644 --- a/apps/server/src/provider/Drivers/ClaudeDriver.ts +++ b/apps/server/src/provider/Drivers/ClaudeDriver.ts @@ -50,7 +50,7 @@ import { type ProviderDriver, type ProviderInstance, } from "@t3tools/provider-core/server/driver"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { enrichProviderSnapshotWithVersionAdvisory, diff --git a/apps/server/src/provider/Drivers/CodexDriver.ts b/apps/server/src/provider/Drivers/CodexDriver.ts index 0d6da5a5dea66ec2d58b9dd7f827ff3ced14a4cd..b007785a6e823ea9dfe1be046bf882c2bdf721c9 100644 --- a/apps/server/src/provider/Drivers/CodexDriver.ts +++ b/apps/server/src/provider/Drivers/CodexDriver.ts @@ -51,7 +51,7 @@ import { resolveCodexLaunchArgs } from "../codexLaunchArgs.ts"; import { makeManagedServerProvider } from "@t3tools/provider-core/server/managedProvider"; import * as ModelManifest from "../ModelManifest.ts"; import type { ProviderDriver, ProviderInstance } from "@t3tools/provider-core/server/driver"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { enrichProviderSnapshotWithVersionAdvisory, diff --git a/apps/server/src/provider/Drivers/CodexManagedProvider.ts b/apps/server/src/provider/Drivers/CodexManagedProvider.ts index b7c12ff3391d8e0fed0a8c67a1c8f1ca0a77c89c..32d88e64487932d14ecdf29639de0cddb62ce819 100644 --- a/apps/server/src/provider/Drivers/CodexManagedProvider.ts +++ b/apps/server/src/provider/Drivers/CodexManagedProvider.ts @@ -21,7 +21,7 @@ import { type ProviderInstance, } from "@t3tools/provider-core/server/driver"; import { codexContinuationIdentity } from "./CodexHomeLayout.ts"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { HttpClient } from "effect/http"; const DRIVER = ProviderDriverKind.make("codex"); diff --git a/apps/server/src/provider/Drivers/CursorDriver.ts b/apps/server/src/provider/Drivers/CursorDriver.ts index 21bc7a0b7af83be866522d3b882f59811aac819d..cf4d82bcfdb1b73992e824fb8c20a789e13da30f 100644 --- a/apps/server/src/provider/Drivers/CursorDriver.ts +++ b/apps/server/src/provider/Drivers/CursorDriver.ts @@ -35,7 +35,7 @@ import { type ProviderDriver, type ProviderInstance, } from "@t3tools/provider-core/server/driver"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { makeManualOnlyProviderMaintenanceCapabilities } from "@t3tools/provider-core/server/maintenanceResolver"; import { diff --git a/apps/server/src/provider/Drivers/GrokDriver.ts b/apps/server/src/provider/Drivers/GrokDriver.ts index 5962c72482645686da4efea83f3daa8930383cdd..f7226d5f1e9fb1b85016fa41c41cdec1d9fef0e2 100644 --- a/apps/server/src/provider/Drivers/GrokDriver.ts +++ b/apps/server/src/provider/Drivers/GrokDriver.ts @@ -28,7 +28,7 @@ import { type ProviderDriver, type ProviderInstance, } from "@t3tools/provider-core/server/driver"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { discoverGrokSkills } from "./GrokSkills.ts"; import { diff --git a/apps/server/src/provider/Drivers/MuseDriver.ts b/apps/server/src/provider/Drivers/MuseDriver.ts index e22b22ab0dee2b618ebace5aaf038dbfd2ce6cf7..16bed1c3dab27181b34f674e2050c9fbf02733c5 100644 --- a/apps/server/src/provider/Drivers/MuseDriver.ts +++ b/apps/server/src/provider/Drivers/MuseDriver.ts @@ -35,7 +35,7 @@ import { makeProviderSnapshotSettingsSource, type ProviderSnapshotSettings, } from "@t3tools/provider-core/server/snapshotSettings"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; const DRIVER_KIND = ProviderDriverKind.make("muse"); const decodeMuseSettings = Schema.decodeSync(MuseSettings); diff --git a/apps/server/src/provider/Drivers/OpenCodeDriver.ts b/apps/server/src/provider/Drivers/OpenCodeDriver.ts index 76a9dcdc7b789f1d5115ed850f321c268fef0b62..83347d86a87a6540183f81003a3fe4576c4778e9 100644 --- a/apps/server/src/provider/Drivers/OpenCodeDriver.ts +++ b/apps/server/src/provider/Drivers/OpenCodeDriver.ts @@ -59,7 +59,7 @@ import { type ProviderDriver, type ProviderInstance, } from "@t3tools/provider-core/server/driver"; -import { withInstanceIdentity } from "./instanceIdentity.ts"; +import { withInstanceIdentity } from "@t3tools/provider-core/server/instanceIdentity"; import { mergeProviderInstanceEnvironment } from "@t3tools/provider-core/server/instanceEnvironment"; import { enrichProviderSnapshotWithVersionAdvisory, diff --git a/apps/server/src/provider/EventNdjsonLogger.ts b/apps/server/src/provider/EventNdjsonLogger.ts index 491119c3cdfdb82156040334064c44e8be6c5ec1..1ec5c46d9324e4e38373f7900ab482477eaf8fc4 100644 --- a/apps/server/src/provider/EventNdjsonLogger.ts +++ b/apps/server/src/provider/EventNdjsonLogger.ts @@ -9,6 +9,7 @@ import * as NodeFS from "node:fs"; import * as NodePath from "node:path"; import type { ThreadId } from "@t3tools/contracts"; +import type { EventNdjsonLogger } from "@t3tools/provider-core/server/ProviderEventLoggers"; import { RotatingFileSink } from "@t3tools/shared/logging"; import { errorTag } from "@t3tools/shared/observability"; import * as Clock from "effect/Clock"; @@ -63,11 +64,7 @@ const transientAcpUpdates = new Set(["agent_message_chunk", "agent_thought_chunk export type EventNdjsonStream = "native" | "canonical" | "orchestration"; -export interface EventNdjsonLogger { - readonly filePath: string; - readonly write: (event: unknown, threadId: ThreadId | null) => Effect.Effect; - readonly close: () => Effect.Effect; -} +export type { EventNdjsonLogger }; export interface EventNdjsonLogStore { readonly filePath: string; diff --git a/apps/server/src/provider/ProviderEventLoggers.ts b/apps/server/src/provider/ProviderEventLoggers.ts index 1cb96058d3b2afb517a9142bda66506bbd7c1798..b31acbef952bd932866031bba131ce6ebf16326d 100644 --- a/apps/server/src/provider/ProviderEventLoggers.ts +++ b/apps/server/src/provider/ProviderEventLoggers.ts @@ -24,39 +24,18 @@ * * @module provider/ProviderEventLoggers */ -import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as ServerConfig from "../config.ts"; import * as ResourceAttribution from "../resourceTelemetry/ResourceAttribution.ts"; -import * as EventNdjsonLogger from "./EventNdjsonLogger.ts"; - -/** - * Shared logger pair for native + canonical provider event streams. - * - * Service value is intentionally a struct of two optional loggers rather - * than two parallel tags. Construction site is one place - * (`layer`); consumers (drivers, `ProviderService`) read one tag and pluck the - * field they need. - */ -export class ProviderEventLoggers extends Context.Service< +import { + NoOpProviderEventLoggers, ProviderEventLoggers, - { - readonly native: EventNdjsonLogger.EventNdjsonLogger | undefined; - readonly canonical: EventNdjsonLogger.EventNdjsonLogger | undefined; - } ->()("t3/provider/ProviderEventLoggers") {} +} from "@t3tools/provider-core/server/ProviderEventLoggers"; +import * as EventNdjsonLogger from "./EventNdjsonLogger.ts"; -/** - * Constant value used by tests / boot layers that want to opt out of native - * + canonical logging entirely. Keeps the tag non-optional in the type - * system while letting the runtime treat absence as a no-op. - */ -export const NoOpProviderEventLoggers: ProviderEventLoggers["Service"] = { - native: undefined, - canonical: undefined, -}; +export { NoOpProviderEventLoggers, ProviderEventLoggers }; /** * Builds both stream views over one shared store. Setup failures are logged diff --git a/packages/provider-core/package.json b/packages/provider-core/package.json index 073faf381440156c27ee3811cb92a0730897e6ff..fa21d1f2efeec0a8e3ad17749ee2556b435f1f05 100644 --- a/packages/provider-core/package.json +++ b/packages/provider-core/package.json @@ -11,6 +11,10 @@ "types": "./src/server/adapterDriver.ts", "import": "./src/server/adapterDriver.ts" }, + "./server/attachmentPrompt": { + "types": "./src/server/attachmentPrompt.ts", + "import": "./src/server/attachmentPrompt.ts" + }, "./server/auth": { "types": "./src/server/auth.ts", "import": "./src/server/auth.ts" @@ -47,6 +51,10 @@ "types": "./src/server/instanceEnvironment.ts", "import": "./src/server/instanceEnvironment.ts" }, + "./server/instanceIdentity": { + "types": "./src/server/instanceIdentity.ts", + "import": "./src/server/instanceIdentity.ts" + }, "./server/maintenance": { "types": "./src/server/maintenance.ts", "import": "./src/server/maintenance.ts" @@ -67,6 +75,10 @@ "types": "./src/server/mcpToolPresentation.ts", "import": "./src/server/mcpToolPresentation.ts" }, + "./server/notification": { + "types": "./src/server/notification.ts", + "import": "./src/server/notification.ts" + }, "./server/orchestrationInstructions": { "types": "./src/server/orchestrationInstructions.ts", "import": "./src/server/orchestrationInstructions.ts" @@ -83,6 +95,10 @@ "types": "./src/server/ProviderAdapter.ts", "import": "./src/server/ProviderAdapter.ts" }, + "./server/ProviderEventLoggers": { + "types": "./src/server/ProviderEventLoggers.ts", + "import": "./src/server/ProviderEventLoggers.ts" + }, "./server/ProviderHost": { "types": "./src/server/ProviderHost.ts", "import": "./src/server/ProviderHost.ts" @@ -91,6 +107,10 @@ "types": "./src/server/randomUuid.ts", "import": "./src/server/randomUuid.ts" }, + "./server/runtimeInstructions": { + "types": "./src/server/runtimeInstructions.ts", + "import": "./src/server/runtimeInstructions.ts" + }, "./server/selectionTransition": { "types": "./src/server/selectionTransition.ts", "import": "./src/server/selectionTransition.ts" diff --git a/packages/provider-core/src/server/ProviderEventLoggers.ts b/packages/provider-core/src/server/ProviderEventLoggers.ts new file mode 100644 index 0000000000000000000000000000000000000000..6341bd96af1b8d1b02655862caafa249c8603086 --- /dev/null +++ b/packages/provider-core/src/server/ProviderEventLoggers.ts @@ -0,0 +1,30 @@ +/** + * Provider event logging as the server offers it to drivers: one stream for + * the provider's native protocol frames and one for canonical orchestration + * events. Either logger is absent when diagnostics logging could not start. + * + * @module provider-core/server/ProviderEventLoggers + */ +import type { ThreadId } from "@t3tools/contracts"; +import * as Context from "effect/Context"; +import type * as Effect from "effect/Effect"; + +export interface EventNdjsonLogger { + readonly filePath: string; + readonly write: (event: unknown, threadId: ThreadId | null) => Effect.Effect; + readonly close: () => Effect.Effect; +} + +export class ProviderEventLoggers extends Context.Service< + ProviderEventLoggers, + { + readonly native: EventNdjsonLogger | undefined; + readonly canonical: EventNdjsonLogger | undefined; + } +>()("@t3tools/provider-core/server/ProviderEventLoggers") {} + +/** Logging turned off: for tests and boot layers that skip diagnostics. */ +export const NoOpProviderEventLoggers: ProviderEventLoggers["Service"] = { + native: undefined, + canonical: undefined, +}; diff --git a/apps/server/src/orchestration-v2/AttachmentPrompt.test.ts b/packages/provider-core/src/server/attachmentPrompt.test.ts similarity index 92% rename from apps/server/src/orchestration-v2/AttachmentPrompt.test.ts rename to packages/provider-core/src/server/attachmentPrompt.test.ts index 03c35bd4ef9b7625feccdd1e0dfa64b4bb007c74..e31c8417f202e7280e8f9834ccbaacab912c6e0a 100644 --- a/apps/server/src/orchestration-v2/AttachmentPrompt.test.ts +++ b/packages/provider-core/src/server/attachmentPrompt.test.ts @@ -9,7 +9,7 @@ import { assert, describe, it } from "@effect/vitest"; import { isProviderNativeImageAttachment, providerMessageTextWithAttachmentPaths, -} from "./AttachmentPrompt.ts"; +} from "./attachmentPrompt.ts"; const document = ChatFileAttachment.make({ id: ChatAttachmentId.make("file-document"), @@ -26,13 +26,17 @@ const image = ChatImageAttachment.make({ sizeBytes: 456, }); +// Stands in for the host's resolver, which maps stored attachments to their files. +const resolveAttachmentPath = (attachment: { readonly id: string; readonly name: string }) => + `/attachments/${attachment.id}${attachment.name.slice(attachment.name.lastIndexOf("."))}`; + describe("provider attachment prompts", () => { it("appends resolvable file paths for documents and images", () => { assert.equal( providerMessageTextWithAttachmentPaths({ text: "Review these.", attachments: [document, image], - attachmentsDir: "/attachments", + resolveAttachmentPath, }), 'Review these.\n\n[Attached file "spec.pdf" is saved at: /attachments/file-document.pdf]\n' + '\n[Attached image "diagram.png" is saved at: /attachments/file-image.png]', @@ -53,7 +57,7 @@ describe("provider attachment prompts", () => { const text = providerMessageTextWithAttachmentPaths({ text: "Fix this.", attachments: [captured], - attachmentsDir: "/attachments", + resolveAttachmentPath, }); assert.include( @@ -103,7 +107,7 @@ describe("provider attachment prompts", () => { const text = providerMessageTextWithAttachmentPaths({ text: "Describe this.", attachments: [captured], - attachmentsDir: "/attachments", + resolveAttachmentPath, }); assert.include( @@ -133,7 +137,7 @@ describe("provider attachment prompts", () => { const text = providerMessageTextWithAttachmentPaths({ text: "Fix this.", attachments, - attachmentsDir: "/attachments", + resolveAttachmentPath, }); assert.isAtMost(text.length, PROVIDER_SEND_TURN_MAX_INPUT_CHARS); diff --git a/apps/server/src/orchestration-v2/AttachmentPrompt.ts b/packages/provider-core/src/server/attachmentPrompt.ts similarity index 96% rename from apps/server/src/orchestration-v2/AttachmentPrompt.ts rename to packages/provider-core/src/server/attachmentPrompt.ts index 7189ccf763f9827e62463c7a83f5b3323e63c727..1b09911f50d87dc0729233b60b7d0c6fa9bacedd 100644 --- a/apps/server/src/orchestration-v2/AttachmentPrompt.ts +++ b/packages/provider-core/src/server/attachmentPrompt.ts @@ -8,8 +8,6 @@ import { } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; -import { resolveAttachmentPath } from "../attachmentStore.ts"; - const encodePromptJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); interface SnapShotPromptAccessibilityNode { @@ -161,7 +159,8 @@ export function isProviderNativeImageAttachment(attachment: ChatAttachment): boo export function providerMessageTextWithAttachmentPaths(input: { readonly text: string; readonly attachments: ReadonlyArray; - readonly attachmentsDir: string; + /** Absolute path of a stored attachment, such as `ProviderHost.resolveAttachmentPath`. */ + readonly resolveAttachmentPath: (attachment: ChatAttachment) => string | null; }): string { let text = input.text; const appendContext = (context: string | undefined) => { @@ -171,10 +170,7 @@ export function providerMessageTextWithAttachmentPaths(input: { }; for (const attachment of input.attachments) { - const path = resolveAttachmentPath({ - attachmentsDir: input.attachmentsDir, - attachment, - }); + const path = input.resolveAttachmentPath(attachment); appendContext( path === null ? undefined diff --git a/apps/server/src/provider/Drivers/instanceIdentity.ts b/packages/provider-core/src/server/instanceIdentity.ts similarity index 100% rename from apps/server/src/provider/Drivers/instanceIdentity.ts rename to packages/provider-core/src/server/instanceIdentity.ts diff --git a/apps/server/src/orchestration-v2/Notification.test.ts b/packages/provider-core/src/server/notification.test.ts similarity index 99% rename from apps/server/src/orchestration-v2/Notification.test.ts rename to packages/provider-core/src/server/notification.test.ts index 197587a786e90c82a591877032d8f510f0c58edf..a67cab6b7e29218bcf0942f7352553407c1e5143 100644 --- a/apps/server/src/orchestration-v2/Notification.test.ts +++ b/packages/provider-core/src/server/notification.test.ts @@ -10,7 +10,7 @@ import { } from "@t3tools/contracts"; import * as DateTime from "effect/DateTime"; -import { backgroundWorkNotification, notificationTurnItem } from "./Notification.ts"; +import { backgroundWorkNotification, notificationTurnItem } from "./notification.ts"; const childThreadId = ThreadId.make("thread:child"); diff --git a/apps/server/src/orchestration-v2/Notification.ts b/packages/provider-core/src/server/notification.ts similarity index 100% rename from apps/server/src/orchestration-v2/Notification.ts rename to packages/provider-core/src/server/notification.ts diff --git a/apps/server/src/provider/RuntimeInstructions.test.ts b/packages/provider-core/src/server/runtimeInstructions.test.ts similarity index 96% rename from apps/server/src/provider/RuntimeInstructions.test.ts rename to packages/provider-core/src/server/runtimeInstructions.test.ts index 10f8413b5a137834ebd21dba4bb7d7959be2fe2d..6ad3b027e0477a0d2de3c4ff91773364428c7b52 100644 --- a/apps/server/src/provider/RuntimeInstructions.test.ts +++ b/packages/provider-core/src/server/runtimeInstructions.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vite-plus/test"; -import { buildRuntimeInstructions } from "./RuntimeInstructions.ts"; +import { buildRuntimeInstructions } from "./runtimeInstructions.ts"; describe("buildRuntimeInstructions", () => { it("requires explicit registration of every PR and stack layer", () => { diff --git a/apps/server/src/provider/RuntimeInstructions.ts b/packages/provider-core/src/server/runtimeInstructions.ts similarity index 100% rename from apps/server/src/provider/RuntimeInstructions.ts rename to packages/provider-core/src/server/runtimeInstructions.ts -- 2.54.0