authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-26 09:04:10-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
log7ad5d03948a27e64bb11f6862d2f23333a0a2bd1
tree91c75ffa9bda352366e9794f2b6be5c5df1eb4ea
parent6cf03d987c28af7fdc7867d63f791720a0464a27

fix bad decoding


2 files changed, 17 insertions(+), 30 deletions(-)

.plans/16c-pr89-remediation-checklist.md+2-2
...@@ -360,8 +360,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6`...@@ -360,8 +360,8 @@ Counts: active `51` (`valid=33`, `partially-valid=18`), closed-invalid `6`
360 - Threads: PRRT_kwDORLtfbc5wiaR-360 - Threads: PRRT_kwDORLtfbc5wiaR-
361 - Audit note: Decode and SQL errors still collapsed into one persistence error kind.361 - Audit note: Decode and SQL errors still collapsed into one persistence error kind.
362362
363- [ ] `C049` `JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback.363- [x] `C049` `JSON.stringify(cause)` returns `undefined` for `undefined`, functions, or symbols, violating the `string` return type. Consider coercing the result to a string (e.g., `String(JSON.stringify(cause))`) or adding a fallback.
364 - Status: `TODO`364 - Status: `DONE`
365 - Verdict: `valid`365 - Verdict: `valid`
366 - Severity: `Low`366 - Severity: `Low`
367 - Area: `Other`367 - Area: `Other`
apps/server/src/provider/Layers/ProviderService.ts+15-28
...@@ -22,7 +22,7 @@ import {...@@ -22,7 +22,7 @@ import {
22 type ProviderRuntimeEvent,22 type ProviderRuntimeEvent,
23 type ProviderSession,23 type ProviderSession,
24} from "@t3tools/contracts";24} from "@t3tools/contracts";
25import { Effect, Layer, Option, PubSub, Queue, Ref, Schema, Stream } from "effect";25import { Effect, Layer, Option, PubSub, Queue, Ref, Schema, SchemaIssue, Stream } from "effect";
2626
27import { ProviderSessionNotFoundError, ProviderValidationError } from "../Errors.ts";27import { ProviderSessionNotFoundError, ProviderValidationError } from "../Errors.ts";
28import { ProviderAdapterRegistry } from "../Services/ProviderAdapterRegistry.ts";28import { ProviderAdapterRegistry } from "../Services/ProviderAdapterRegistry.ts";
...@@ -54,30 +54,21 @@ function toValidationError(...@@ -54,30 +54,21 @@ function toValidationError(
54 });54 });
55}55}
5656
57function decodeIssueMessage(cause: unknown): string {57const decodeInputOrValidationError = <S extends Schema.Top>(input: {
58 if (cause instanceof Error && cause.message.length > 0) {
59 return cause.message;
60 }
61 try {
62 return JSON.stringify(cause);
63 } catch {
64 return "Invalid provider input.";
65 }
66}
67
68function decodeInputOrValidationError<S extends Schema.Top>(input: {
69 readonly operation: string;58 readonly operation: string;
70 readonly schema: S;59 readonly schema: S;
71 readonly payload: unknown;60 readonly payload: unknown;
72}): Effect.Effect<Schema.Schema.Type<S>, ProviderValidationError> {61}) =>
73 try {62 Schema.decodeUnknownEffect(input.schema)(input.payload).pipe(
74 return Effect.succeed(63 Effect.mapError(
75 Schema.decodeUnknownSync(input.schema as never)(input.payload) as Schema.Schema.Type<S>,64 (schemaError) =>
76 );65 new ProviderValidationError({
77 } catch (cause) {66 operation: input.operation,
78 return Effect.fail(toValidationError(input.operation, decodeIssueMessage(cause), cause));67 issue: SchemaIssue.makeFormatterDefault()(schemaError.issue),
79 }68 cause: schemaError,
80}69 }),
70 ),
71 );
8172
82function toRuntimeStatus(session: ProviderSession): "starting" | "running" | "stopped" | "error" {73function toRuntimeStatus(session: ProviderSession): "starting" | "running" | "stopped" | "error" {
83 switch (session.status) {74 switch (session.status) {
...@@ -265,9 +256,7 @@ const makeProviderService = (options?: ProviderServiceLiveOptions) =>...@@ -265,9 +256,7 @@ const makeProviderService = (options?: ProviderServiceLiveOptions) =>
265 provider: existing.provider,256 provider: existing.provider,
266 threadId: input.binding.threadId,257 threadId: input.binding.threadId,
267 providerThreadId: existingProviderThreadId,258 providerThreadId: existingProviderThreadId,
268 ...(existing.resumeCursor !== undefined259 ...(existing.resumeCursor !== undefined ? { resumeCursor: existing.resumeCursor } : {}),
269 ? { resumeCursor: existing.resumeCursor }
270 : {}),
271 });260 });
272 if (existing.sessionId !== input.staleSessionId) {261 if (existing.sessionId !== input.staleSessionId) {
273 yield* setAlias(input.staleSessionId, existing.sessionId);262 yield* setAlias(input.staleSessionId, existing.sessionId);
...@@ -310,9 +299,7 @@ const makeProviderService = (options?: ProviderServiceLiveOptions) =>...@@ -310,9 +299,7 @@ const makeProviderService = (options?: ProviderServiceLiveOptions) =>
310 provider: resumed.provider,299 provider: resumed.provider,
311 threadId: input.binding.threadId,300 threadId: input.binding.threadId,
312 providerThreadId: resumedProviderThreadId,301 providerThreadId: resumedProviderThreadId,
313 ...(resumed.resumeCursor !== undefined302 ...(resumed.resumeCursor !== undefined ? { resumeCursor: resumed.resumeCursor } : {}),
314 ? { resumeCursor: resumed.resumeCursor }
315 : {}),
316 });303 });
317304
318 if (resumed.sessionId !== input.staleSessionId) {305 if (resumed.sessionId !== input.staleSessionId) {