-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlease-effect.ts
More file actions
148 lines (137 loc) · 4.72 KB
/
lease-effect.ts
File metadata and controls
148 lines (137 loc) · 4.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Effect-shaped wrappers over the pure {@link ./lease.ts} validation helpers.
//
// The legacy `validateLeaseOp`, `assertLeaseSubset`, and
// `validateLeaseConstraints` throw on failure (typed via the `ARCPError`
// hierarchy). This file exposes thin `Effect`-valued twins that translate
// those throws into typed {@link TaggedSdkError} channel failures. No new
// behavior — pure surface adaptation so call sites inside `Effect.gen` can
// compose lease checks without `Effect.try` boilerplate.
import {
taggedFromARCP,
type TaggedBudgetExhausted,
type TaggedInvalidRequest,
type TaggedLeaseExpired,
type TaggedLeaseSubsetViolation,
type TaggedPermissionDenied,
type TaggedSdkError,
} from "@agentruntimecontrolprotocol/core";
import { ARCPError as ARCPErrorClass } from "@agentruntimecontrolprotocol/core/errors";
import type { Lease, LeaseConstraints } from "@agentruntimecontrolprotocol/core/messages";
import { Effect } from "effect";
import {
assertLeaseConstraintsSubset,
assertLeaseSubset,
validateLeaseConstraints,
validateLeaseOp,
type ValidateLeaseOpInput,
} from "./lease.js";
/**
* §9.3 lease validation failures. Mirrors the legacy throw set of
* {@link validateLeaseOp}: capability mismatch, lease expiration, and
* budget exhaustion.
*/
export type ValidateLeaseOpFailure =
| TaggedBudgetExhausted
| TaggedLeaseExpired
| TaggedPermissionDenied;
/**
* Effect twin of {@link validateLeaseOp}. Succeeds with `void`; on a legacy
* throw, lifts the {@link ARCPError} subclass through {@link taggedFromARCP}
* onto the typed-error channel.
*
* The error channel is narrowed to {@link ValidateLeaseOpFailure} — any
* other `ARCPError` subclass leaking from `validateLeaseOp` is an
* implementation bug and surfaces as an Effect defect.
*
* @example
* ```ts
* yield* validateLeaseOpEffect({ lease, capability: "fs.read", target: "/a" })
* ```
*/
export function validateLeaseOpEffect(
input: ValidateLeaseOpInput,
): Effect.Effect<void, ValidateLeaseOpFailure> {
return Effect.try({
try: () => {
validateLeaseOp(input);
},
catch: narrowValidateLeaseOpFailure,
});
}
/**
* Effect twin of {@link assertLeaseSubset}. Succeeds with `void`; on a legacy
* throw, surfaces a {@link TaggedLeaseSubsetViolation} on the typed channel.
*/
export function assertLeaseSubsetEffect(
child: Lease,
parent: Lease,
parentBudgetRemaining?: ReadonlyMap<string, number>,
): Effect.Effect<void, TaggedLeaseSubsetViolation> {
return Effect.try({
try: () => {
assertLeaseSubset(child, parent, parentBudgetRemaining);
},
catch: narrowSubsetViolation,
});
}
/**
* Effect twin of {@link assertLeaseConstraintsSubset}.
*/
export function assertLeaseConstraintsSubsetEffect(
child: LeaseConstraints | undefined,
parent: LeaseConstraints | undefined,
): Effect.Effect<void, TaggedLeaseSubsetViolation> {
return Effect.try({
try: () => {
assertLeaseConstraintsSubset(child, parent);
},
catch: narrowSubsetViolation,
});
}
/**
* Effect twin of {@link validateLeaseConstraints}. Returns the parsed
* millisecond expiry (or `null` when no `expires_at` was supplied);
* malformed/past values surface as {@link TaggedInvalidRequest}.
*/
export function validateLeaseConstraintsEffect(
constraints: LeaseConstraints | undefined,
now?: number,
): Effect.Effect<number | null, TaggedInvalidRequest> {
return Effect.try({
try: () => validateLeaseConstraints(constraints, now),
catch: narrowInvalidRequest,
});
}
/**
* Narrow `cause` to a known `ARCPError` and translate it through
* {@link taggedFromARCP}; non-`ARCPError` defects are re-thrown so they
* surface as Effect defects (preserving the legacy "unexpected throw"
* channel rather than masquerading as a typed failure).
*/
function liftAsTagged(cause: unknown): TaggedSdkError {
if (cause instanceof ARCPErrorClass) return taggedFromARCP(cause);
throw cause as Error;
}
function narrowValidateLeaseOpFailure(cause: unknown): ValidateLeaseOpFailure {
const lifted = liftAsTagged(cause);
if (
lifted.code === "BUDGET_EXHAUSTED" ||
lifted.code === "LEASE_EXPIRED" ||
lifted.code === "PERMISSION_DENIED"
) {
return lifted;
}
throw new Error(`Unexpected lease op failure code: ${lifted.code}`);
}
function narrowSubsetViolation(cause: unknown): TaggedLeaseSubsetViolation {
const lifted = liftAsTagged(cause);
if (lifted.code === "LEASE_SUBSET_VIOLATION") return lifted;
throw new Error(`Unexpected lease subset failure code: ${lifted.code}`);
}
function narrowInvalidRequest(cause: unknown): TaggedInvalidRequest {
const lifted = liftAsTagged(cause);
if (lifted.code === "INVALID_REQUEST") return lifted;
throw new Error(
`Unexpected validate-constraints failure code: ${lifted.code}`,
);
}