opencode-effect-enforcer 0.2.5 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -140
- package/docs/effect-4.0.0-rc.116-changelog.md +2654 -0
- package/docs/effect-4.0.0-rc.116.md +102 -0
- package/guidance/effect-first-development.md +28 -311
- package/guidance/progressive-disclosure-guidance.md +5 -19
- package/package.json +2 -2
- package/patterns/avoid-direct-tag-checks.md +1 -1
- package/patterns/avoid-process-env.md +4 -4
- package/patterns/context-tag-extends.md +4 -4
- package/patterns/prefer-arr-sort.md +1 -1
- package/patterns/prefer-redacted-config.md +10 -10
- package/patterns/prefer-schema-class.md +1 -1
- package/patterns/require-effect-concurrency.md +1 -1
- package/skills/effect-ai-chat/SKILL.md +2 -2
- package/skills/effect-ai-language-model/SKILL.md +36 -4
- package/skills/effect-ai-prompt/SKILL.md +1 -1
- package/skills/effect-ai-provider/SKILL.md +23 -12
- package/skills/effect-ai-tool/SKILL.md +13 -0
- package/skills/effect-atom-rpc/SKILL.md +7 -1
- package/skills/effect-atom-state/SKILL.md +8 -2
- package/skills/effect-cache/SKILL.md +10 -1
- package/skills/effect-cli/SKILL.md +105 -94
- package/skills/effect-command-executor/SKILL.md +7 -1
- package/skills/effect-config/SKILL.md +67 -44
- package/skills/effect-domain-modeling/SKILL.md +3 -3
- package/skills/effect-error-handling/SKILL.md +2 -2
- package/skills/effect-fiber/SKILL.md +2 -2
- package/skills/effect-filesystem/SKILL.md +34 -4
- package/skills/effect-http-api/SKILL.md +17 -2
- package/skills/effect-http-client/SKILL.md +11 -2
- package/skills/effect-http-server/SKILL.md +28 -11
- package/skills/effect-layer-design/SKILL.md +6 -2
- package/skills/effect-mcp-server/SKILL.md +21 -4
- package/skills/effect-observability/SKILL.md +2 -2
- package/skills/effect-optics/SKILL.md +1 -1
- package/skills/effect-parallelization/SKILL.md +2 -2
- package/skills/effect-pattern-matching/SKILL.md +1 -1
- package/skills/effect-platform-abstraction/SKILL.md +3 -3
- package/skills/effect-rpc-api/SKILL.md +3 -3
- package/skills/effect-rpc-client/SKILL.md +14 -13
- package/skills/effect-rpc-cluster/SKILL.md +15 -12
- package/skills/effect-rpc-server/SKILL.md +11 -13
- package/skills/effect-scheduling/SKILL.md +7 -0
- package/skills/effect-schema-composition/SKILL.md +12 -4
- package/skills/effect-schema-v4/SKILL.md +75 -20
- package/skills/effect-scope/SKILL.md +4 -4
- package/skills/effect-socket/SKILL.md +161 -658
- package/skills/effect-sql/SKILL.md +50 -12
- package/skills/effect-stream/SKILL.md +19 -24
- package/skills/effect-testing/SKILL.md +35 -22
- package/skills/effect-workflow/SKILL.md +13 -2
- package/src/guidance.ts +0 -1
|
@@ -19,9 +19,9 @@ suggestSkills:
|
|
|
19
19
|
processEnv :: String -> IO (Maybe String) -- side effect, untyped, untestable
|
|
20
20
|
|
|
21
21
|
-- Instead
|
|
22
|
-
Config.
|
|
22
|
+
Config.String :: String -> Config String -- typed, composable, testable
|
|
23
23
|
Config.withDefault :: a -> Config a -> Config a
|
|
24
|
-
Config.
|
|
24
|
+
Config.Redacted :: String -> Config Redacted -- for sensitive values
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
```haskell
|
|
@@ -30,10 +30,10 @@ bad :: Effect String
|
|
|
30
30
|
bad = Effect.sync \_ -> process.env.API_KEY -- raw side effect
|
|
31
31
|
|
|
32
32
|
good :: Effect String ConfigError
|
|
33
|
-
good = Config.
|
|
33
|
+
good = Config.Redacted "API_KEY" -- typed, redacted
|
|
34
34
|
|
|
35
35
|
better :: Effect String ConfigError
|
|
36
|
-
better = Config.
|
|
36
|
+
better = Config.Int("PORT")
|
|
37
37
|
& Config.withDefault "3000"
|
|
38
38
|
& Config.map Number.parse -- with transformation
|
|
39
39
|
```
|
|
@@ -26,13 +26,13 @@ suggestSkills:
|
|
|
26
26
|
|
|
27
27
|
# Use `Context.Service` for All Service Definitions
|
|
28
28
|
|
|
29
|
-
`Context.Service`
|
|
29
|
+
Use `Context.Service` for service definitions. Replace these legacy spellings:
|
|
30
30
|
|
|
31
31
|
| Legacy API | Era | Replacement |
|
|
32
32
|
| --------------------------- | --------- | -------------------------- |
|
|
33
33
|
| `Context.Tag` / `GenericTag` | pre-v4 | `Context.Service` |
|
|
34
34
|
| `Effect.Service` | early v4 | `Context.Service` |
|
|
35
|
-
| `ServiceMap.Service` / `.*` |
|
|
35
|
+
| `ServiceMap.Service` / `.*` | prerelease | `Context.Service` / `Context.*` |
|
|
36
36
|
|
|
37
37
|
```haskell
|
|
38
38
|
-- Anti-pattern: *Tag suffix + Context.Tag — removed in v4
|
|
@@ -43,10 +43,10 @@ data ParallelClientService = ...
|
|
|
43
43
|
-- Anti-pattern: Effect.Service — also removed in v4
|
|
44
44
|
class ParallelClient extends Effect.Service<ParallelClient>()(...)
|
|
45
45
|
|
|
46
|
-
-- Anti-pattern: ServiceMap.*
|
|
46
|
+
-- Anti-pattern: ServiceMap.*
|
|
47
47
|
class MyService extends ServiceMap.Service<MyService>()("@app/MyService", { ... })
|
|
48
48
|
|
|
49
|
-
-- Fix: Context.Service
|
|
49
|
+
-- Fix: Context.Service
|
|
50
50
|
class ParallelClient extends Context.Service<ParallelClient>()(
|
|
51
51
|
"@parallel/ParallelClient"
|
|
52
52
|
)
|
|
@@ -49,4 +49,4 @@ descending = Arr.sort (Order.reverse byName)
|
|
|
49
49
|
|
|
50
50
|
Native `.sort()` mutates the array in place and uses an untyped comparator. `Arr.sort` from `effect/Array` returns a new sorted array using a composable, typed `Order`.
|
|
51
51
|
|
|
52
|
-
References: EF-
|
|
52
|
+
References: EF-5 in effect-first-development.md
|
|
@@ -3,13 +3,13 @@ action: context
|
|
|
3
3
|
tool: (edit|write)
|
|
4
4
|
event: after
|
|
5
5
|
name: prefer-redacted-config
|
|
6
|
-
description: Use Config.
|
|
6
|
+
description: Use Config.Redacted or Schema.Redacted for secret-like configuration values
|
|
7
7
|
glob: '**/*.{ts,tsx}'
|
|
8
8
|
detector: ast
|
|
9
9
|
rule:
|
|
10
10
|
any:
|
|
11
|
-
- pattern: Config.
|
|
12
|
-
- pattern: Config.
|
|
11
|
+
- pattern: Config.String($KEY)
|
|
12
|
+
- pattern: Config.NonEmptyString($KEY)
|
|
13
13
|
- all:
|
|
14
14
|
- kind: pair
|
|
15
15
|
- has:
|
|
@@ -33,18 +33,18 @@ suggestSkills:
|
|
|
33
33
|
|
|
34
34
|
```haskell
|
|
35
35
|
-- Transformation
|
|
36
|
-
Config.
|
|
37
|
-
Config.
|
|
36
|
+
Config.String secretKey :: String -- easy to log accidentally
|
|
37
|
+
Config.Redacted secretKey :: Redacted String -- hidden from logs/toString
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
// Bad
|
|
42
|
-
const apiKey = Config.
|
|
43
|
-
const token = Config.
|
|
42
|
+
const apiKey = Config.String('API_KEY');
|
|
43
|
+
const token = Config.NonEmptyString('GITHUB_TOKEN');
|
|
44
44
|
|
|
45
45
|
// Good
|
|
46
|
-
const apiKey = Config.
|
|
47
|
-
const token = Config.
|
|
46
|
+
const apiKey = Config.Redacted('API_KEY');
|
|
47
|
+
const token = Config.Redacted('GITHUB_TOKEN');
|
|
48
48
|
```
|
|
49
49
|
|
|
50
50
|
For structured config schemas, wrap secret-like string fields in `Schema.Redacted`:
|
|
@@ -67,4 +67,4 @@ const AppConfig = Config.schema(
|
|
|
67
67
|
);
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
Secrets should remain redacted from the moment they enter the program. Use `Config.
|
|
70
|
+
Secrets should remain redacted from the moment they enter the program. Use `Config.Redacted` for primitive config values and `Schema.Redacted(Schema.String)` for schema-based config fields.
|
|
@@ -51,4 +51,4 @@ extend = class Admin extends User.extend<Admin>("Admin")({
|
|
|
51
51
|
|
|
52
52
|
`Schema.Struct` produces an anonymous schema without a constructor or `instanceof` support. `Schema.Class` provides a named type, constructor, extensibility, and optional annotation support when docs or introspection benefit from it. Prefer `Schema.Class` for decoded domain/API shapes, union members, and values that need identity. `Schema.Struct` remains appropriate for local structural composition, configuration internals, and schemas where class identity adds no value, so review this informational finding in context.
|
|
53
53
|
|
|
54
|
-
References: EF-3
|
|
54
|
+
References: EF-3 in effect-first-development.md
|
|
@@ -79,7 +79,7 @@ Effect.forEach xs f opts -- concurrency intent explicit
|
|
|
79
79
|
Effect.forEach(items, processItem);
|
|
80
80
|
Effect.all(tasks);
|
|
81
81
|
Effect.validate(inputs, validateInput, { discard: true });
|
|
82
|
-
Effect.all(tasks, { concurrency: 'inherit' }); //
|
|
82
|
+
Effect.all(tasks, { concurrency: 'inherit' }); // unsupported ambient policy
|
|
83
83
|
|
|
84
84
|
// Good
|
|
85
85
|
Effect.forEach(items, processItem, { concurrency: 1 });
|
|
@@ -231,7 +231,7 @@ const program = Effect.gen(function* () {
|
|
|
231
231
|
timeToLive: '1 hour' // optional TTL
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
-
// chat is a `Chat.Persisted` — same API as Chat.
|
|
234
|
+
// chat is a `Chat.Persisted` — same API as Chat.Chat but auto-saves
|
|
235
235
|
const response = yield* chat
|
|
236
236
|
.generateText({
|
|
237
237
|
prompt: 'Hello!'
|
|
@@ -245,7 +245,7 @@ const program = Effect.gen(function* () {
|
|
|
245
245
|
});
|
|
246
246
|
```
|
|
247
247
|
|
|
248
|
-
The `Persisted` interface extends `Chat.
|
|
248
|
+
The `Persisted` interface extends `Chat.Chat` with:
|
|
249
249
|
|
|
250
250
|
- `id: string` — the chat identifier in the store
|
|
251
251
|
- `save: Effect<void, AiError | PersistenceError>` — manual save trigger
|
|
@@ -94,7 +94,13 @@ const manualTools = LanguageModel.generateText({
|
|
|
94
94
|
});
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
With `disableToolCallResolution: true`, tool-call `params` retain the schema's
|
|
98
|
+
**encoded** representation. Response generics use the mode `"encoded"`, for
|
|
99
|
+
example `GenerateTextResponse<Tools, "encoded">` and
|
|
100
|
+
`Response.StreamPart<Tools, "encoded">`. Automatic resolution uses `"opaque"`
|
|
101
|
+
parameters (`unknown`), because invalid calls can also appear in responses.
|
|
102
|
+
Only successfully decoded handler inputs have the decoded parameter type.
|
|
103
|
+
For `Schema.NumberFromString`, manual calls contain `{ count: '3' }`.
|
|
98
104
|
|
|
99
105
|
Pass those encoded params directly to `toolkit.handle(name, params, toolCallId)` when resolving manually. `Toolkit.handle` now accepts `Tool.ParametersEncoded<T>` and performs the decode before the handler receives `Tool.Parameters<T>`.
|
|
100
106
|
|
|
@@ -104,7 +110,7 @@ Pass those encoded params directly to `toolkit.handle(name, params, toolCallId)`
|
|
|
104
110
|
const response = yield* LanguageModel.generateText({ prompt: '...' });
|
|
105
111
|
|
|
106
112
|
response.text; // string - concatenated text content
|
|
107
|
-
response.toolCalls; //
|
|
113
|
+
response.toolCalls; // opaque params normally; encoded params when resolution is disabled
|
|
108
114
|
response.toolResults; // Array<ToolResultParts> - tool outputs
|
|
109
115
|
response.finishReason; // "stop" | "length" | "content-filter" | "tool-calls" | "error" | "pause" | "unknown" | "other"
|
|
110
116
|
response.usage; // Usage object with nested structure, e.g. response.usage.outputTokens.total
|
|
@@ -297,6 +303,32 @@ const robust = LanguageModel.generateText({
|
|
|
297
303
|
);
|
|
298
304
|
```
|
|
299
305
|
|
|
306
|
+
## Batched classification, rating, and probability
|
|
307
|
+
|
|
308
|
+
Use `DecisionModel` for structured judgments over one schema-encoded input.
|
|
309
|
+
Define named decisions once and send them in one call; input encoding services
|
|
310
|
+
remain explicit and provider/validation failures use `AiError`. Classification
|
|
311
|
+
requires at least two labels, rating requires at least two distinct ordered
|
|
312
|
+
levels, and a definition requires at least one decision.
|
|
313
|
+
|
|
314
|
+
<!-- typecheck -->
|
|
315
|
+
```ts
|
|
316
|
+
import * as Schema from 'effect/Schema';
|
|
317
|
+
import { Decision, DecisionModel } from 'effect/unstable/ai';
|
|
318
|
+
|
|
319
|
+
class Ticket extends Schema.Class<Ticket>('Ticket')({ body: Schema.String }) {}
|
|
320
|
+
const triage = Decision.make({
|
|
321
|
+
input: Ticket,
|
|
322
|
+
decisions: {
|
|
323
|
+
team: Decision.classify({
|
|
324
|
+
instructions: 'Choose the team responsible for this request',
|
|
325
|
+
criteria: { billing: 'Payments and invoices', support: 'Product support' }
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
const result = DecisionModel.decide(triage, { input: new Ticket({ body: 'Invoice question' }) });
|
|
330
|
+
```
|
|
331
|
+
|
|
300
332
|
## Type Extraction Utilities
|
|
301
333
|
|
|
302
334
|
```typescript
|
|
@@ -308,8 +340,8 @@ type MyError = LanguageModel.ExtractError<typeof options>;
|
|
|
308
340
|
// Extract service requirements from options
|
|
309
341
|
type MyRequirements = LanguageModel.ExtractServices<typeof options>;
|
|
310
342
|
|
|
311
|
-
//
|
|
312
|
-
type
|
|
343
|
+
// "encoded" for literal disableToolCallResolution: true; otherwise "opaque"
|
|
344
|
+
type ParametersMode = LanguageModel.ExtractToolParametersMode<typeof options>;
|
|
313
345
|
|
|
314
346
|
// Inferred based on:
|
|
315
347
|
// - toolkit: Toolkit.WithHandler<Tools> → Tool.HandlerError<Tools> ∈ E
|
|
@@ -516,7 +516,7 @@ const program = Effect.gen(function* () {
|
|
|
516
516
|
|
|
517
517
|
## Provider-Specific Options
|
|
518
518
|
|
|
519
|
-
### OpenAI Responses explicit cache breakpoints
|
|
519
|
+
### OpenAI Responses explicit cache breakpoints
|
|
520
520
|
|
|
521
521
|
With `@effect/ai-openai` loaded, system-message and text-part options accept
|
|
522
522
|
`openai.promptCacheBreakpoint`. This requires GPT-5.6 or later; earlier models
|
|
@@ -100,7 +100,7 @@ import { FetchHttpClient } from 'effect/unstable/http';
|
|
|
100
100
|
|
|
101
101
|
// Client layer (reusable across models)
|
|
102
102
|
const AnthropicClientLayer = AnthropicClient.layerConfig({
|
|
103
|
-
apiKey: Config.
|
|
103
|
+
apiKey: Config.Redacted('ANTHROPIC_API_KEY')
|
|
104
104
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
105
105
|
|
|
106
106
|
// Option A: model() — returns Model.Model (preferred)
|
|
@@ -130,7 +130,7 @@ import { Config, Layer } from 'effect';
|
|
|
130
130
|
import { FetchHttpClient } from 'effect/unstable/http';
|
|
131
131
|
|
|
132
132
|
const OpenAiClientLayer = OpenAiClient.layerConfig({
|
|
133
|
-
apiKey: Config.
|
|
133
|
+
apiKey: Config.Redacted('OPENAI_API_KEY')
|
|
134
134
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
135
135
|
|
|
136
136
|
// model() constructor (preferred)
|
|
@@ -186,7 +186,7 @@ import { Config, Layer } from 'effect';
|
|
|
186
186
|
import { FetchHttpClient, HttpClient, HttpClientRequest } from 'effect/unstable/http';
|
|
187
187
|
|
|
188
188
|
const CompatibleClientLayer = OpenAiClient.layerConfig({
|
|
189
|
-
apiKey: Config.
|
|
189
|
+
apiKey: Config.Redacted('OPENAI_COMPAT_API_KEY'),
|
|
190
190
|
apiUrl: Config.succeed('https://my-provider.example.com/v1')
|
|
191
191
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
192
192
|
|
|
@@ -211,7 +211,7 @@ import { Config, Layer } from 'effect';
|
|
|
211
211
|
import { FetchHttpClient } from 'effect/unstable/http';
|
|
212
212
|
|
|
213
213
|
const OpenRouterClientLayer = OpenRouterClient.layerConfig({
|
|
214
|
-
apiKey: Config.
|
|
214
|
+
apiKey: Config.Redacted('OPENROUTER_API_KEY')
|
|
215
215
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
216
216
|
|
|
217
217
|
// model() constructor — use provider-prefixed model IDs
|
|
@@ -420,7 +420,7 @@ export class AiWriter extends Context.Service<
|
|
|
420
420
|
|
|
421
421
|
## Custom Error Wrapping
|
|
422
422
|
|
|
423
|
-
|
|
423
|
+
`AiError.AuthenticationError` accepts an optional `description` and
|
|
424
424
|
appends it after the kind-based remediation message. Anthropic, OpenAI,
|
|
425
425
|
OpenAI-compatible, and OpenRouter adapters propagate provider error text from
|
|
426
426
|
401/403 responses. Preserve this reason rather than replacing it with a generic
|
|
@@ -496,11 +496,11 @@ import { FetchHttpClient } from 'effect/unstable/http';
|
|
|
496
496
|
// ---------------------------------------------------------------------------
|
|
497
497
|
|
|
498
498
|
const AnthropicClientLayer = AnthropicClient.layerConfig({
|
|
499
|
-
apiKey: Config.
|
|
499
|
+
apiKey: Config.Redacted('ANTHROPIC_API_KEY')
|
|
500
500
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
501
501
|
|
|
502
502
|
const OpenAiClientLayer = OpenAiClient.layerConfig({
|
|
503
|
-
apiKey: Config.
|
|
503
|
+
apiKey: Config.Redacted('OPENAI_API_KEY')
|
|
504
504
|
}).pipe(Layer.provide(FetchHttpClient.layer));
|
|
505
505
|
|
|
506
506
|
// ---------------------------------------------------------------------------
|
|
@@ -619,15 +619,15 @@ Effect.runPromise(program.pipe(Effect.provide(AiWriter.layer)));
|
|
|
619
619
|
// WRONG: Hardcoded API keys
|
|
620
620
|
AnthropicClient.layerConfig({ apiKey: 'sk-...' });
|
|
621
621
|
|
|
622
|
-
// RIGHT: Config.
|
|
623
|
-
AnthropicClient.layerConfig({ apiKey: Config.
|
|
622
|
+
// RIGHT: Config.Redacted for secrets
|
|
623
|
+
AnthropicClient.layerConfig({ apiKey: Config.Redacted('ANTHROPIC_API_KEY') });
|
|
624
624
|
|
|
625
625
|
// WRONG: Missing FetchHttpClient layer
|
|
626
|
-
AnthropicClient.layerConfig({ apiKey: Config.
|
|
626
|
+
AnthropicClient.layerConfig({ apiKey: Config.Redacted('KEY') });
|
|
627
627
|
// Will fail at runtime — providers require an HttpClient
|
|
628
628
|
|
|
629
629
|
// RIGHT: Always provide an HTTP client layer
|
|
630
|
-
AnthropicClient.layerConfig({ apiKey: Config.
|
|
630
|
+
AnthropicClient.layerConfig({ apiKey: Config.Redacted('KEY') }).pipe(
|
|
631
631
|
Layer.provide(FetchHttpClient.layer)
|
|
632
632
|
);
|
|
633
633
|
|
|
@@ -653,7 +653,7 @@ import { BedrockClient } from '@effect/ai-amazon-bedrock'; // Does NOT exist
|
|
|
653
653
|
|
|
654
654
|
## Quality Checklist
|
|
655
655
|
|
|
656
|
-
- [ ] Use `Config.
|
|
656
|
+
- [ ] Use `Config.Redacted` for API keys (never hardcode)
|
|
657
657
|
- [ ] Provide `FetchHttpClient.layer` to all client layers
|
|
658
658
|
- [ ] Use `.model()` constructor for `ExecutionPlan` and `Effect.provide`
|
|
659
659
|
- [ ] Use `ExecutionPlan` for multi-provider fallback with retry
|
|
@@ -674,6 +674,17 @@ import { BedrockClient } from '@effect/ai-amazon-bedrock'; // Does NOT exist
|
|
|
674
674
|
|
|
675
675
|
## References
|
|
676
676
|
|
|
677
|
+
Provider-neutral structured decisions use `Decision` / `DecisionModel` from
|
|
678
|
+
`effect/unstable/ai`. `OpenRouterDecisionModel` targets OpenRouter's alpha Decisions
|
|
679
|
+
API; `@effect/ai-typesafe` supplies a provider for TypeSafe System One. Custom
|
|
680
|
+
OpenRouter client implementations include `createDecisions`.
|
|
681
|
+
|
|
682
|
+
Use each branded service's same-name type (`LanguageModel.LanguageModel`,
|
|
683
|
+
`EmbeddingModel.EmbeddingModel`, `Chat.Chat`) and its exported TypeId for custom
|
|
684
|
+
implementations. Prefer provided constructors; do not hard-code marker strings.
|
|
685
|
+
OpenAI web-search sources are a discriminated union: narrow `type` before reading
|
|
686
|
+
`url` or `name`. Provider-executed tool failures remain failure results.
|
|
687
|
+
|
|
677
688
|
- `packages/ai/anthropic/src/AnthropicLanguageModel.ts`
|
|
678
689
|
- `packages/ai/openai/src/OpenAiLanguageModel.ts`
|
|
679
690
|
- `packages/ai/openrouter/src/OpenRouterLanguageModel.ts`
|
|
@@ -208,6 +208,19 @@ type Error = Tool.Failure<typeof FindUser>;
|
|
|
208
208
|
|
|
209
209
|
**Key Pattern: failureMode**
|
|
210
210
|
|
|
211
|
+
Parameter validation follows the tool's `failureMode`. Failure results include
|
|
212
|
+
`Tool.ExecutionFailure` for framework failures (AI errors, denial, interruption)
|
|
213
|
+
as well as declared user errors. Select result codecs using `isFailure` and
|
|
214
|
+
`Tool.failureResultSchema(tool)`; preserve `encodedResult` when storing or
|
|
215
|
+
replaying response parts. A failure with `Schema.NumberFromString` is stored as
|
|
216
|
+
a string, even when the success schema stores a number. Validation errors do not
|
|
217
|
+
expose a `toolParams` field.
|
|
218
|
+
|
|
219
|
+
`Toolkit.handle` accepts parse options for parameter decoding. Returned failures
|
|
220
|
+
carry `failureOrigin`, also available through the `Toolkit.FailureOrigin` cause
|
|
221
|
+
annotation and `Tool.FailureOrigin` type. Keep validation, declared handler, and
|
|
222
|
+
internal failures distinct when presenting or reporting them.
|
|
223
|
+
|
|
211
224
|
- `"error"` (default): Failures go to Effect error channel
|
|
212
225
|
- `"return"`: Failures returned as tool result (captured, not thrown)
|
|
213
226
|
|
|
@@ -20,7 +20,7 @@ Use this skill when building React (or Atom-based) frontends that consume an exi
|
|
|
20
20
|
For the underlying RPC definitions, see the `effect-rpc-cluster` skill.
|
|
21
21
|
For Atom fundamentals (`Atom.make`, `family`, `keepAlive`, `AsyncResult`, hydration), see the `effect-atom-state` skill.
|
|
22
22
|
|
|
23
|
-
The underlying RPC protocols require `codecFor
|
|
23
|
+
The underlying RPC protocols require `codecFor`. Built-in protocol
|
|
24
24
|
layers supply it; forward it when implementing a custom transport. You can pair
|
|
25
25
|
`RpcSerialization.layerSchemaBinary` on client/server without changing query or
|
|
26
26
|
mutation call sites; see `effect-rpc-client` for frame limits and compatibility.
|
|
@@ -29,6 +29,12 @@ type is `string`. This is distinct from the client's `{ discard: true }` option.
|
|
|
29
29
|
|
|
30
30
|
## Effect Source Reference
|
|
31
31
|
|
|
32
|
+
Query and mutation errors include client middleware errors. AtomHttpApi stream
|
|
33
|
+
successes retain transport, decoding, and SSE failures in the stream error channel;
|
|
34
|
+
handle them when consuming the stream. An explicit zero `timeToLive` disables
|
|
35
|
+
default idle retention, so unmount/remount may dispose and refetch. Omission uses
|
|
36
|
+
the registry default. HttpApi calls accept per-call `sseOptions`.
|
|
37
|
+
|
|
32
38
|
- `packages/effect/src/unstable/reactivity/AtomRpc.ts` — the whole API (~270 lines)
|
|
33
39
|
- `packages/effect/test/reactivity/AtomRpc.test.ts` — minimal usage + serialization test
|
|
34
40
|
- `packages/effect/src/unstable/reactivity/AtomHttpApi.ts` — the cousin pattern for `HttpApi` (same idea, same options)
|
|
@@ -7,7 +7,7 @@ description: Implement reactive state management with Effect Atom for React appl
|
|
|
7
7
|
|
|
8
8
|
Effect Atom is a reactive state management library for Effect that seamlessly integrates with React.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
`@effect/atom-react` supports React `>=19.0.0 <20.0.0` (the peer range
|
|
11
11
|
was relaxed). This does not add React 18 support. Keep the adapter aligned with
|
|
12
12
|
the Effect release; core atoms still live in `effect/unstable/reactivity`, and
|
|
13
13
|
React bindings live in `@effect/atom-react` (`packages/atom/react` upstream).
|
|
@@ -27,6 +27,12 @@ Reference this for:
|
|
|
27
27
|
|
|
28
28
|
### Atoms as References
|
|
29
29
|
|
|
30
|
+
`Reactivity.Reactivity` is the branded service interface. Custom implementations
|
|
31
|
+
include its exported `[TypeId]: TypeId`; prefer the supplied constructor/layer.
|
|
32
|
+
Registry dehydration skips unencodable values while preserving other atoms.
|
|
33
|
+
`Atom.withFallback` forwards writes to the primary atom. Keep encoding contracts
|
|
34
|
+
explicit for state that must survive SSR dehydration.
|
|
35
|
+
|
|
30
36
|
Atoms work **by reference** - they are stable containers for reactive state:
|
|
31
37
|
|
|
32
38
|
```typescript
|
|
@@ -301,7 +307,7 @@ export const notifications = Atom.make(
|
|
|
301
307
|
Stream.fromEventListener(window, 'notification').pipe(
|
|
302
308
|
Stream.map(parseNotification),
|
|
303
309
|
Stream.filter(isValid),
|
|
304
|
-
Stream.scan([], (acc, n) => [...acc, n].slice(-10))
|
|
310
|
+
Stream.scan(() => [], (acc, n) => [...acc, n].slice(-10))
|
|
305
311
|
)
|
|
306
312
|
);
|
|
307
313
|
```
|
|
@@ -7,6 +7,15 @@ You are an Effect TypeScript expert specializing in in-memory caching with `Cach
|
|
|
7
7
|
|
|
8
8
|
## Effect Source Reference
|
|
9
9
|
|
|
10
|
+
`Effect.cachedWithTTL` accepts either a fixed Duration input or an Exit-dependent
|
|
11
|
+
TTL function, so success and failure retention can differ. Allocate the cache
|
|
12
|
+
once in its owning layer. A zero TTL is explicit, not omission.
|
|
13
|
+
For `LayerMap.Service({ preload: true, ... })`, preserve acquisition errors on
|
|
14
|
+
the yielded service and `get` / `contextEffect` / `contextEffectOption`: an entry
|
|
15
|
+
can fail when reacquired after successful preloading. Invalidating an active
|
|
16
|
+
RcMap/LayerMap entry releases it after its last borrower closes; a replacement
|
|
17
|
+
entry remains independently owned.
|
|
18
|
+
|
|
10
19
|
The Effect v4 source is available at `~/.local/share/opencode/repos/github.com/Effect-TS/effect@main/`.
|
|
11
20
|
Browse and read files there directly to look up APIs, types, and implementations.
|
|
12
21
|
|
|
@@ -363,7 +372,7 @@ The full method surface (`get`, `getOption`, `getSuccess`, `set`, `has`, `invali
|
|
|
363
372
|
|
|
364
373
|
## 9. Services in Lookups (requireServicesAt)
|
|
365
374
|
|
|
366
|
-
### Retain an existing keyed resource
|
|
375
|
+
### Retain an existing keyed resource
|
|
367
376
|
|
|
368
377
|
`RcMap.getOption(map, key)` atomically retains a cached entry for the caller's
|
|
369
378
|
`Scope` before awaiting it. It returns `Option.none()` if the entry is missing or
|