@ontrails/core 1.0.0-beta.42 → 1.0.0-beta.45
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/CHANGELOG.md +36 -0
- package/README.md +3 -3
- package/package.json +6 -1
- package/src/{activation-source-projection.ts → activation-source-derivation.ts} +9 -9
- package/src/blob-ref.ts +3 -3
- package/src/derive.ts +27 -27
- package/src/detours.ts +1 -1
- package/src/{error-projection.ts → error-rendering.ts} +39 -3
- package/src/errors.ts +1 -1
- package/src/execute.ts +3 -3
- package/src/fire.ts +1 -1
- package/src/index.ts +27 -23
- package/src/{layer-projection.ts → layer-field-rendering.ts} +30 -30
- package/src/layer.ts +6 -6
- package/src/observe.ts +6 -6
- package/src/resource.ts +1 -1
- package/src/schedule-runtime.ts +1 -1
- package/src/serialization.ts +1 -1
- package/src/structured-examples.ts +27 -27
- package/src/surface-derivation.ts +1 -1
- package/src/surface-overlay.ts +5 -5
- package/src/surface-versioning.ts +3 -3
- package/src/tracing.ts +3 -3
- package/src/trail.ts +9 -9
- package/src/trails/derive-trail.ts +1 -1
- package/src/transport-error-map.ts +19 -30
- package/src/types.ts +2 -2
- package/src/validate-established-topo.ts +6 -6
- package/src/validate-topo.ts +1 -1
- package/src/validation.ts +2 -2
- package/src/version-marker.ts +17 -17
package/src/layer.ts
CHANGED
|
@@ -12,13 +12,13 @@ import type { Implementation } from './types.js';
|
|
|
12
12
|
*
|
|
13
13
|
* Layers attach at trail, surface, or topo scope and may declare an object
|
|
14
14
|
* `input` schema describing the configuration they need from the surrounding
|
|
15
|
-
* surface. Surface packages (CLI, MCP, HTTP)
|
|
15
|
+
* surface. Surface packages (CLI, MCP, HTTP) render this schema onto their
|
|
16
16
|
* native idioms — flags, tool parameters, query strings — alongside the
|
|
17
17
|
* trail's own input schema.
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
20
|
-
* The `input` schema is metadata for surface
|
|
21
|
-
* schema so every surface can
|
|
20
|
+
* The `input` schema is metadata for surface rendering. It must be an object
|
|
21
|
+
* schema so every surface can render named fields consistently. It is
|
|
22
22
|
* optional; layers without an `input` schema behave as plain wrappers. The
|
|
23
23
|
* layer's `wrap` function is the runtime contract.
|
|
24
24
|
*/
|
|
@@ -31,13 +31,13 @@ export interface Layer {
|
|
|
31
31
|
/**
|
|
32
32
|
* Authored configuration the layer needs from the surrounding surface.
|
|
33
33
|
*
|
|
34
|
-
* Surface packages
|
|
34
|
+
* Surface packages render this schema onto their native idioms (CLI flags,
|
|
35
35
|
* MCP tool parameters, HTTP query strings) so a layer's input fields appear
|
|
36
36
|
* alongside the trail's own input fields. Optional — layers that wrap purely
|
|
37
37
|
* by behavior, with no surface-visible inputs, may omit it.
|
|
38
38
|
*
|
|
39
|
-
* @see TRL-473 for CLI flag
|
|
40
|
-
* @see TRL-474 for MCP and HTTP
|
|
39
|
+
* @see TRL-473 for CLI flag rendering.
|
|
40
|
+
* @see TRL-474 for MCP and HTTP rendering.
|
|
41
41
|
*/
|
|
42
42
|
readonly input?: LayerInputSchema | undefined;
|
|
43
43
|
|
package/src/observe.ts
CHANGED
|
@@ -210,10 +210,10 @@ const stringifyDefaultConsoleRecord = (record: LogRecord): string => {
|
|
|
210
210
|
};
|
|
211
211
|
|
|
212
212
|
/**
|
|
213
|
-
* In-core mirror of `@ontrails/
|
|
213
|
+
* In-core mirror of `@ontrails/observability`'s `createConsoleSink` shape, kept
|
|
214
214
|
* minimal and private to avoid a reverse dependency from `@ontrails/core`
|
|
215
|
-
* onto `@ontrails/
|
|
216
|
-
* `packages/
|
|
215
|
+
* onto `@ontrails/observability`. It mirrors the console level mapping in
|
|
216
|
+
* `packages/observability/src/sinks.ts:50` and emits each record as a single-line
|
|
217
217
|
* JSON object written to the matching `console.{debug|info|warn|error}` method.
|
|
218
218
|
*
|
|
219
219
|
* @remarks
|
|
@@ -231,7 +231,7 @@ const createDefaultConsoleSink = (): LogSink => ({
|
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
233
233
|
const payload = stringifyDefaultConsoleRecord(record);
|
|
234
|
-
// oxlint-disable-next-line trails-local/no-console-in-packages -- ADR 0041 mandates a default console logger in core; this is the single sanctioned console boundary, mirroring `@ontrails/
|
|
234
|
+
// oxlint-disable-next-line trails-local/no-console-in-packages -- ADR 0041 mandates a default console logger in core; this is the single sanctioned console boundary, mirroring `@ontrails/observability`'s `createConsoleSink`.
|
|
235
235
|
console[method](payload);
|
|
236
236
|
},
|
|
237
237
|
});
|
|
@@ -251,8 +251,8 @@ export const normalizeObserve = (
|
|
|
251
251
|
if (observe === undefined) {
|
|
252
252
|
// ADR 0041 promises a non-null `ctx.logger` with zero configuration.
|
|
253
253
|
// Returning the default config here lets the existing topo → adapter
|
|
254
|
-
// path
|
|
255
|
-
// resolution point or a reverse dependency on `@ontrails/
|
|
254
|
+
// path renders this log sink into `ctx.logger` without a second
|
|
255
|
+
// resolution point or a reverse dependency on `@ontrails/observability`.
|
|
256
256
|
return DEFAULT_OBSERVE_CONFIG;
|
|
257
257
|
}
|
|
258
258
|
|
package/src/resource.ts
CHANGED
|
@@ -52,7 +52,7 @@ export interface ResourceSpec<T, C = unknown> {
|
|
|
52
52
|
readonly description?: string | undefined;
|
|
53
53
|
/** Arbitrary meta for tooling and filtering. */
|
|
54
54
|
readonly meta?: Readonly<Record<string, unknown>> | undefined;
|
|
55
|
-
/** Signals
|
|
55
|
+
/** Signals derived or owned by this resource. */
|
|
56
56
|
readonly signals?: readonly AnySignal[] | undefined;
|
|
57
57
|
/** Reserved for future resource-specific design; trail versioning is trail-only. */
|
|
58
58
|
readonly version?: never;
|
package/src/schedule-runtime.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ActivationEntry } from './activation-source.js';
|
|
2
|
-
import { activationSourceKey } from './activation-source-
|
|
2
|
+
import { activationSourceKey } from './activation-source-derivation.js';
|
|
3
3
|
import {
|
|
4
4
|
buildActivationProvenanceTraceAttrs,
|
|
5
5
|
withActivationProvenance,
|
package/src/serialization.ts
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
redactErrorContext,
|
|
27
27
|
redactErrorStack,
|
|
28
28
|
redactErrorString,
|
|
29
|
-
} from './error-
|
|
29
|
+
} from './error-rendering.js';
|
|
30
30
|
import { Result } from './result.js';
|
|
31
31
|
|
|
32
32
|
// ---------------------------------------------------------------------------
|
|
@@ -40,7 +40,7 @@ export interface StructuredSignalExample {
|
|
|
40
40
|
// `Date`, `RegExp`, `Map`, and `Set` are objects (`typeof === 'object'`),
|
|
41
41
|
// so they would pass the structural walk and reach `JSON.stringify`, which
|
|
42
42
|
// silently coerces them: a `Date` becomes its ISO string, a `RegExp` and
|
|
43
|
-
// any `Map`/`Set` become `{}`. Either way the
|
|
43
|
+
// any `Map`/`Set` become `{}`. Either way the derived shape diverges
|
|
44
44
|
// from the example author's declared input. Treat them as non-serializable
|
|
45
45
|
// leaves so the example is dropped rather than misrepresented to MCP
|
|
46
46
|
// clients.
|
|
@@ -107,7 +107,7 @@ const signalIdFromAssertion = (
|
|
|
107
107
|
: undefined;
|
|
108
108
|
};
|
|
109
109
|
|
|
110
|
-
const
|
|
110
|
+
const deriveSignalAssertion = (
|
|
111
111
|
assertion: TrailExampleSignalAssertion
|
|
112
112
|
): StructuredTrailExampleSignalAssertion | undefined => {
|
|
113
113
|
const signalId = signalIdFromAssertion(assertion);
|
|
@@ -115,43 +115,43 @@ const projectSignalAssertion = (
|
|
|
115
115
|
return undefined;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
const
|
|
118
|
+
const derived: Record<string, unknown> = { signalId };
|
|
119
119
|
if (assertion.payload !== undefined) {
|
|
120
120
|
const payload = toJsonSerializable(assertion.payload);
|
|
121
121
|
if (payload === undefined) {
|
|
122
122
|
return undefined;
|
|
123
123
|
}
|
|
124
|
-
|
|
124
|
+
derived['payload'] = payload;
|
|
125
125
|
}
|
|
126
126
|
if (assertion.payloadMatch !== undefined) {
|
|
127
127
|
const payloadMatch = toJsonSerializable(assertion.payloadMatch);
|
|
128
128
|
if (payloadMatch === undefined) {
|
|
129
129
|
return undefined;
|
|
130
130
|
}
|
|
131
|
-
|
|
131
|
+
derived['payloadMatch'] = payloadMatch;
|
|
132
132
|
}
|
|
133
133
|
if (assertion.times !== undefined) {
|
|
134
|
-
|
|
134
|
+
derived['times'] = assertion.times;
|
|
135
135
|
}
|
|
136
136
|
return Object.freeze(
|
|
137
|
-
|
|
137
|
+
derived
|
|
138
138
|
) as unknown as StructuredTrailExampleSignalAssertion;
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
-
const
|
|
141
|
+
const deriveSignalAssertions = (
|
|
142
142
|
assertions: readonly TrailExampleSignalAssertion[] | undefined
|
|
143
143
|
): readonly StructuredTrailExampleSignalAssertion[] | undefined => {
|
|
144
144
|
if (assertions === undefined) {
|
|
145
145
|
return undefined;
|
|
146
146
|
}
|
|
147
|
-
const
|
|
148
|
-
if (
|
|
147
|
+
const derived = assertions.map(deriveSignalAssertion);
|
|
148
|
+
if (derived.some((assertion) => assertion === undefined)) {
|
|
149
149
|
return undefined;
|
|
150
150
|
}
|
|
151
|
-
return Object.freeze(
|
|
151
|
+
return Object.freeze(derived as StructuredTrailExampleSignalAssertion[]);
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
-
const
|
|
154
|
+
const deriveExample = (
|
|
155
155
|
example: TrailExample<unknown, unknown>,
|
|
156
156
|
provenance: StructuredTrailExampleProvenance
|
|
157
157
|
): StructuredTrailExample | undefined => {
|
|
@@ -160,7 +160,7 @@ const projectExample = (
|
|
|
160
160
|
return undefined;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
const
|
|
163
|
+
const derived: Record<string, unknown> = {
|
|
164
164
|
input,
|
|
165
165
|
kind: example.error === undefined ? 'success' : 'error',
|
|
166
166
|
name: example.name,
|
|
@@ -168,37 +168,37 @@ const projectExample = (
|
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
if (example.description !== undefined) {
|
|
171
|
-
|
|
171
|
+
derived['description'] = example.description;
|
|
172
172
|
}
|
|
173
173
|
if (example.expected !== undefined) {
|
|
174
174
|
const expected = toJsonSerializable(example.expected);
|
|
175
175
|
if (expected === undefined) {
|
|
176
176
|
return undefined;
|
|
177
177
|
}
|
|
178
|
-
|
|
178
|
+
derived['expected'] = expected;
|
|
179
179
|
}
|
|
180
180
|
if (example.expectedMatch !== undefined) {
|
|
181
181
|
const expectedMatch = toJsonSerializable(example.expectedMatch);
|
|
182
182
|
if (expectedMatch === undefined) {
|
|
183
183
|
return undefined;
|
|
184
184
|
}
|
|
185
|
-
|
|
185
|
+
derived['expectedMatch'] = expectedMatch;
|
|
186
186
|
}
|
|
187
187
|
if (example.error !== undefined) {
|
|
188
|
-
|
|
188
|
+
derived['error'] = example.error;
|
|
189
189
|
}
|
|
190
190
|
if (example.signals !== undefined) {
|
|
191
|
-
const signals =
|
|
191
|
+
const signals = deriveSignalAssertions(example.signals);
|
|
192
192
|
if (signals === undefined) {
|
|
193
193
|
return undefined;
|
|
194
194
|
}
|
|
195
|
-
|
|
195
|
+
derived['signals'] = signals;
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
-
return Object.freeze(
|
|
198
|
+
return Object.freeze(derived) as unknown as StructuredTrailExample;
|
|
199
199
|
};
|
|
200
200
|
|
|
201
|
-
const
|
|
201
|
+
const deriveSignalExample = (
|
|
202
202
|
payload: unknown
|
|
203
203
|
): StructuredSignalExample | undefined => {
|
|
204
204
|
const serializablePayload = toJsonSerializable(payload);
|
|
@@ -222,13 +222,13 @@ export const deriveStructuredTrailExamples = (
|
|
|
222
222
|
}
|
|
223
223
|
|
|
224
224
|
const provenance = options?.provenance ?? { source: 'trail.examples' };
|
|
225
|
-
const
|
|
226
|
-
.map((example) =>
|
|
225
|
+
const derived = examples
|
|
226
|
+
.map((example) => deriveExample(example, provenance))
|
|
227
227
|
.filter(
|
|
228
228
|
(example): example is StructuredTrailExample => example !== undefined
|
|
229
229
|
);
|
|
230
230
|
|
|
231
|
-
return
|
|
231
|
+
return derived.length > 0 ? Object.freeze(derived) : undefined;
|
|
232
232
|
};
|
|
233
233
|
|
|
234
234
|
export const deriveStructuredSignalExamples = (
|
|
@@ -238,11 +238,11 @@ export const deriveStructuredSignalExamples = (
|
|
|
238
238
|
return undefined;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
const
|
|
242
|
-
.map(
|
|
241
|
+
const derived = examples
|
|
242
|
+
.map(deriveSignalExample)
|
|
243
243
|
.filter(
|
|
244
244
|
(example): example is StructuredSignalExample => example !== undefined
|
|
245
245
|
);
|
|
246
246
|
|
|
247
|
-
return
|
|
247
|
+
return derived.length > 0 ? Object.freeze(derived) : undefined;
|
|
248
248
|
};
|
|
@@ -21,7 +21,7 @@ export interface SurfaceSelectionOptions {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export interface SurfaceValidationOptions {
|
|
24
|
-
/** Set to `false` to skip established-topo validation during
|
|
24
|
+
/** Set to `false` to skip established-topo validation during derivation. */
|
|
25
25
|
readonly validate?: boolean | undefined;
|
|
26
26
|
}
|
|
27
27
|
|
package/src/surface-overlay.ts
CHANGED
|
@@ -33,7 +33,7 @@ export const SURFACES_OVERLAY_NAMESPACE = 'surfaces' as const;
|
|
|
33
33
|
/**
|
|
34
34
|
* Who authored an overlay envelope.
|
|
35
35
|
*
|
|
36
|
-
* `'adapter-derived'` marks facts an adapter
|
|
36
|
+
* `'adapter-derived'` marks facts an adapter renders from the topo;
|
|
37
37
|
* `'app-authored'` marks bindings the app wrote by hand. Surfaces obey
|
|
38
38
|
* app-authored overlays only — adapters contribute facts, never bindings.
|
|
39
39
|
*
|
|
@@ -242,7 +242,7 @@ export interface SurfaceOverlay {
|
|
|
242
242
|
/** The bindings schema, enforced again on the compile path. */
|
|
243
243
|
readonly schema: z.ZodType;
|
|
244
244
|
/**
|
|
245
|
-
*
|
|
245
|
+
* Derive the overlay facts. Ignores the topo — bindings are authored —
|
|
246
246
|
* so the parameter is optional; the signature stays structurally
|
|
247
247
|
* assignable to the adapter-kit `Overlay` contract's `(topo) => unknown`.
|
|
248
248
|
*/
|
|
@@ -313,7 +313,7 @@ export interface OverlayEnvelopeLike {
|
|
|
313
313
|
readonly provenance?: OverlayProvenance | undefined;
|
|
314
314
|
/** The envelope's fact schema. */
|
|
315
315
|
readonly schema: z.ZodType;
|
|
316
|
-
/**
|
|
316
|
+
/** Derive the envelope's facts. */
|
|
317
317
|
derive(topo?: Topo): unknown;
|
|
318
318
|
/**
|
|
319
319
|
* Static surface bindings, when the envelope carries them directly.
|
|
@@ -575,11 +575,11 @@ const assertMcpSynonymBindingName = (name: string): void => {
|
|
|
575
575
|
|
|
576
576
|
/**
|
|
577
577
|
* Derive the deterministic default description for an MCP grouped entry
|
|
578
|
-
*
|
|
578
|
+
* rendered from the `surfaces` overlay.
|
|
579
579
|
*
|
|
580
580
|
* Both the MCP surface (the runtime tool description) and Topography (the
|
|
581
581
|
* lock's trailhead entry description) read this helper, so the authored
|
|
582
|
-
* binding
|
|
582
|
+
* binding renders one description everywhere.
|
|
583
583
|
*
|
|
584
584
|
* @example
|
|
585
585
|
* ```ts
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from './trail.js';
|
|
6
6
|
import { deriveTrailVersionMarkers } from './version-marker.js';
|
|
7
7
|
|
|
8
|
-
export interface
|
|
8
|
+
export interface SurfaceTrailVersionRendering {
|
|
9
9
|
readonly current: boolean;
|
|
10
10
|
readonly deprecated: boolean;
|
|
11
11
|
readonly marker?: string | undefined;
|
|
@@ -13,9 +13,9 @@ export interface SurfaceTrailVersionProjection {
|
|
|
13
13
|
readonly version: number;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export const
|
|
16
|
+
export const deriveSurfaceTrailVersionRenderings = (
|
|
17
17
|
trail: AnyTrail
|
|
18
|
-
): readonly
|
|
18
|
+
): readonly SurfaceTrailVersionRendering[] | undefined => {
|
|
19
19
|
if (trail.version === undefined) {
|
|
20
20
|
return undefined;
|
|
21
21
|
}
|
package/src/tracing.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This module is the home for the trace record type, the sink interface,
|
|
5
5
|
* the sink registry, and the helpers `executeTrail` uses to create root
|
|
6
6
|
* trace records and child spans. Core keeps this minimal contract public so
|
|
7
|
-
* `@ontrails/
|
|
7
|
+
* `@ontrails/observability`, adapters, and tests share the
|
|
8
8
|
* same intrinsic execution record shape.
|
|
9
9
|
*
|
|
10
10
|
* Tracing is intrinsic: every `executeTrail` call automatically produces a
|
|
@@ -58,8 +58,8 @@ export interface TraceRecord {
|
|
|
58
58
|
/**
|
|
59
59
|
* Minimal shape a tracing sink must satisfy.
|
|
60
60
|
*
|
|
61
|
-
* Kept intentionally tiny so adapters in `@ontrails/
|
|
62
|
-
* `@ontrails/
|
|
61
|
+
* Kept intentionally tiny so adapters in `@ontrails/observability`,
|
|
62
|
+
* `@ontrails/observability`, and user code can all satisfy it without
|
|
63
63
|
* additional dependencies.
|
|
64
64
|
*/
|
|
65
65
|
export interface TraceSink {
|
package/src/trail.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type { AnyEntity } from './entity.js';
|
|
|
15
15
|
import type {
|
|
16
16
|
FieldOverride,
|
|
17
17
|
CliCommandPathInput,
|
|
18
|
-
|
|
18
|
+
TrailCliRendering,
|
|
19
19
|
} from './derive.js';
|
|
20
20
|
import type { Layer } from './layer.js';
|
|
21
21
|
import type { Result } from './result.js';
|
|
@@ -504,13 +504,13 @@ export interface TrailSpec<
|
|
|
504
504
|
* `topo → surface → trail → implementation` (outermost-first).
|
|
505
505
|
*
|
|
506
506
|
* Layers are typed and inspectable. Omit `input` for surface-invisible
|
|
507
|
-
* wrappers that do not
|
|
507
|
+
* wrappers that do not render any fields.
|
|
508
508
|
*/
|
|
509
509
|
readonly layers?: readonly Layer[] | undefined;
|
|
510
510
|
/** Per-field overrides for deriveFields() (labels, hints, options) */
|
|
511
511
|
readonly fields?: Readonly<Record<string, FieldOverride>> | undefined;
|
|
512
|
-
/** CLI
|
|
513
|
-
readonly cli?: CliCommandPathInput |
|
|
512
|
+
/** CLI rendering metadata for canonical command path overrides and aliases. */
|
|
513
|
+
readonly cli?: CliCommandPathInput | TrailCliRendering | undefined;
|
|
514
514
|
/** Entities this trail operates on. */
|
|
515
515
|
readonly entities?: readonly AnyEntity[] | undefined;
|
|
516
516
|
/** IDs or trail objects of downstream trails this trail may invoke via ctx.compose() */
|
|
@@ -548,13 +548,13 @@ export interface TrailSpec<
|
|
|
548
548
|
| undefined;
|
|
549
549
|
/** Auth requirement: scopes object, 'public', or omitted (undeclared) */
|
|
550
550
|
readonly permit?: PermitRequirement | undefined;
|
|
551
|
-
/** Primary input fields and their order. CLI
|
|
551
|
+
/** Primary input fields and their order. CLI derives as positional args. */
|
|
552
552
|
readonly args?: readonly string[] | false | undefined;
|
|
553
553
|
/** Current trail version number. Omit for current-only unversioned trails. */
|
|
554
554
|
readonly version?: number | undefined;
|
|
555
555
|
/** Explicit historical trail versions. Current stays top-level. */
|
|
556
556
|
readonly versions?: TrailVersions<I, O> | undefined;
|
|
557
|
-
/** Version markers are
|
|
557
|
+
/** Version markers are derived into the resolved graph, not authored. */
|
|
558
558
|
readonly marker?: never;
|
|
559
559
|
}
|
|
560
560
|
|
|
@@ -976,12 +976,12 @@ const normalizeVersionEntry = <CurrentInput, CurrentOutput>(
|
|
|
976
976
|
|
|
977
977
|
if (hasOwn(raw, 'kind')) {
|
|
978
978
|
throw new ValidationError(
|
|
979
|
-
`Trail "${trailId}" version ${version} must not author kind; it is
|
|
979
|
+
`Trail "${trailId}" version ${version} must not author kind; it is derived`
|
|
980
980
|
);
|
|
981
981
|
}
|
|
982
982
|
if (hasOwn(raw, 'marker')) {
|
|
983
983
|
throw new ValidationError(
|
|
984
|
-
`Trail "${trailId}" version ${version} must not author marker; it is
|
|
984
|
+
`Trail "${trailId}" version ${version} must not author marker; it is derived`
|
|
985
985
|
);
|
|
986
986
|
}
|
|
987
987
|
|
|
@@ -1286,7 +1286,7 @@ export function trail<
|
|
|
1286
1286
|
|
|
1287
1287
|
if (hasOwn(rawSpec, 'marker')) {
|
|
1288
1288
|
throw new ValidationError(
|
|
1289
|
-
`Trail "${resolved.id}" must not author marker; it is
|
|
1289
|
+
`Trail "${resolved.id}" must not author marker; it is derived`
|
|
1290
1290
|
);
|
|
1291
1291
|
}
|
|
1292
1292
|
|
|
@@ -760,7 +760,7 @@ const synthesizeDefaultImplementation = <
|
|
|
760
760
|
};
|
|
761
761
|
|
|
762
762
|
/**
|
|
763
|
-
* Mechanically
|
|
763
|
+
* Mechanically derive one CRUD-shaped trail from a entity declaration.
|
|
764
764
|
*
|
|
765
765
|
* When `spec.implementation` is omitted and the call declares a single resource, the
|
|
766
766
|
* helper derives a default implementation that dispatches to the resource accessor
|
|
@@ -9,14 +9,12 @@ import {
|
|
|
9
9
|
codesByCategory,
|
|
10
10
|
errorClasses,
|
|
11
11
|
exitCodeMap,
|
|
12
|
-
isTrailsError,
|
|
13
12
|
jsonRpcCodeMap,
|
|
14
13
|
statusCodeMap,
|
|
15
14
|
} from './errors.js';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from './error-projection.js';
|
|
15
|
+
import { renderPublicError } from './error-rendering.js';
|
|
16
|
+
|
|
17
|
+
const CLI_INTERNAL_ERROR_PUBLIC_MESSAGE = 'Internal error';
|
|
20
18
|
|
|
21
19
|
export const surfaceNames = ['cli', 'http', 'jsonRpc', 'mcp'] as const;
|
|
22
20
|
|
|
@@ -48,7 +46,7 @@ export type SurfaceErrorMappings<T> = Record<ErrorCategory, T>;
|
|
|
48
46
|
export type SurfaceErrorCode =
|
|
49
47
|
(typeof codesByCategory)[ErrorCategory][(typeof surfaceCodeKeys)[SurfaceName]];
|
|
50
48
|
|
|
51
|
-
export interface
|
|
49
|
+
export interface SurfaceErrorRendering {
|
|
52
50
|
readonly category: ErrorCategory;
|
|
53
51
|
readonly code: SurfaceErrorCode;
|
|
54
52
|
readonly message: string;
|
|
@@ -57,7 +55,7 @@ export interface SurfaceErrorProjection {
|
|
|
57
55
|
readonly surface: SurfaceName;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
export interface
|
|
58
|
+
export interface ErrorClassSurfaceRendering {
|
|
61
59
|
readonly category: ErrorCategory;
|
|
62
60
|
readonly code: SurfaceErrorCode;
|
|
63
61
|
readonly name: string;
|
|
@@ -102,10 +100,10 @@ export const mapSurfaceError = (
|
|
|
102
100
|
): SurfaceErrorCode =>
|
|
103
101
|
codesByCategory[error.category][surfaceCodeKeys[surface]];
|
|
104
102
|
|
|
105
|
-
export const
|
|
103
|
+
export const renderSurfaceError = (
|
|
106
104
|
surface: SurfaceName,
|
|
107
105
|
error: TrailsError
|
|
108
|
-
):
|
|
106
|
+
): SurfaceErrorRendering => ({
|
|
109
107
|
category: error.category,
|
|
110
108
|
code: mapSurfaceError(surface, error),
|
|
111
109
|
message: error.message,
|
|
@@ -114,27 +112,18 @@ export const projectSurfaceError = (
|
|
|
114
112
|
surface,
|
|
115
113
|
});
|
|
116
114
|
|
|
117
|
-
export const
|
|
115
|
+
export const renderPublicSurfaceError = (
|
|
118
116
|
surface: SurfaceName,
|
|
119
117
|
error: Error
|
|
120
|
-
):
|
|
121
|
-
|
|
122
|
-
const projection = projectSurfaceError(surface, error);
|
|
123
|
-
return {
|
|
124
|
-
...projection,
|
|
125
|
-
message:
|
|
126
|
-
projection.category === 'internal'
|
|
127
|
-
? INTERNAL_ERROR_PUBLIC_MESSAGE
|
|
128
|
-
: redactErrorString(projection.message),
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
|
|
118
|
+
): SurfaceErrorRendering => {
|
|
119
|
+
const rendering = renderPublicError(error);
|
|
132
120
|
return {
|
|
133
|
-
|
|
134
|
-
code: codesByCategory.
|
|
135
|
-
message:
|
|
136
|
-
|
|
137
|
-
|
|
121
|
+
...rendering,
|
|
122
|
+
code: codesByCategory[rendering.category][surfaceCodeKeys[surface]],
|
|
123
|
+
message:
|
|
124
|
+
surface === 'cli' && rendering.category === 'internal'
|
|
125
|
+
? CLI_INTERNAL_ERROR_PUBLIC_MESSAGE
|
|
126
|
+
: rendering.message,
|
|
138
127
|
surface,
|
|
139
128
|
};
|
|
140
129
|
};
|
|
@@ -151,15 +140,15 @@ const fixedErrorClassByName: ReadonlyMap<string, FixedErrorClassRegistryEntry> =
|
|
|
151
140
|
);
|
|
152
141
|
|
|
153
142
|
/**
|
|
154
|
-
*
|
|
143
|
+
* Render a known error class name onto a surface without constructing it.
|
|
155
144
|
*
|
|
156
145
|
* Dynamic-category errors such as `RetryExhaustedError` return `undefined`
|
|
157
146
|
* because their surface code depends on the wrapped runtime error.
|
|
158
147
|
*/
|
|
159
|
-
export const
|
|
148
|
+
export const renderErrorClassSurface = (
|
|
160
149
|
surface: SurfaceName,
|
|
161
150
|
errorName: string
|
|
162
|
-
):
|
|
151
|
+
): ErrorClassSurfaceRendering | undefined => {
|
|
163
152
|
const entry = fixedErrorClassByName.get(errorName);
|
|
164
153
|
if (entry === undefined) {
|
|
165
154
|
return undefined;
|
package/src/types.ts
CHANGED
|
@@ -215,13 +215,13 @@ export const SURFACE_LAYER_NAMES_KEY = '__trails_surface_layer_names' as const;
|
|
|
215
215
|
/**
|
|
216
216
|
* Context extension key carrying per-layer runtime input.
|
|
217
217
|
*
|
|
218
|
-
* Surfaces (CLI, MCP, HTTP)
|
|
218
|
+
* Surfaces (CLI, MCP, HTTP) render each typed layer's `input` schema onto
|
|
219
219
|
* their native idioms (flags, tool params, query strings). At execute time
|
|
220
220
|
* the parsed values are partitioned per layer and stored under this key as
|
|
221
221
|
* `Record<layerName, unknown>`. Layers that need runtime input read their
|
|
222
222
|
* own slot via `ctx.extensions?.[LAYER_INPUTS_KEY]?.[layer.name]`.
|
|
223
223
|
*
|
|
224
|
-
* @see TRL-473 for the CLI
|
|
224
|
+
* @see TRL-473 for the CLI derivation contract.
|
|
225
225
|
*/
|
|
226
226
|
export const LAYER_INPUTS_KEY = '__trails_layer_inputs' as const;
|
|
227
227
|
|
|
@@ -5,7 +5,7 @@ import { validateDraftFreeTopo } from './draft.js';
|
|
|
5
5
|
import type { TopoDiagnostic } from './validate-topo.js';
|
|
6
6
|
import { validateTopo } from './validate-topo.js';
|
|
7
7
|
|
|
8
|
-
const
|
|
8
|
+
const DERIVATION_BLOCKING_RULES = new Set([
|
|
9
9
|
'compose-cycle',
|
|
10
10
|
'compose-exists',
|
|
11
11
|
'no-self-compose',
|
|
@@ -20,12 +20,12 @@ const PROJECTION_BLOCKING_RULES = new Set([
|
|
|
20
20
|
'signal-origin-exists',
|
|
21
21
|
]);
|
|
22
22
|
|
|
23
|
-
const
|
|
24
|
-
|
|
23
|
+
const isDerivationBlockingIssue = (issue: TopoDiagnostic): boolean =>
|
|
24
|
+
DERIVATION_BLOCKING_RULES.has(issue.rule) ||
|
|
25
25
|
(issue.rule === 'activation-source-input-compatible' &&
|
|
26
26
|
issue.sourceKind === 'queue');
|
|
27
27
|
|
|
28
|
-
const
|
|
28
|
+
const keepDerivationBlockingIssues = (
|
|
29
29
|
result: ReturnType<typeof validateTopo>
|
|
30
30
|
) => {
|
|
31
31
|
if (result.isOk()) {
|
|
@@ -35,7 +35,7 @@ const keepProjectionBlockingIssues = (
|
|
|
35
35
|
const issues = (
|
|
36
36
|
result.error.context as { issues?: readonly TopoDiagnostic[] } | undefined
|
|
37
37
|
)?.issues;
|
|
38
|
-
const remainingIssues = issues?.filter(
|
|
38
|
+
const remainingIssues = issues?.filter(isDerivationBlockingIssue);
|
|
39
39
|
|
|
40
40
|
if (remainingIssues === undefined || remainingIssues.length === 0) {
|
|
41
41
|
return Result.ok();
|
|
@@ -59,7 +59,7 @@ const keepProjectionBlockingIssues = (
|
|
|
59
59
|
* valid, and they must also reject any remaining draft state.
|
|
60
60
|
*/
|
|
61
61
|
export const validateEstablishedTopo = (topo: Topo) => {
|
|
62
|
-
const structural =
|
|
62
|
+
const structural = keepDerivationBlockingIssues(validateTopo(topo));
|
|
63
63
|
if (structural.isErr()) {
|
|
64
64
|
return structural;
|
|
65
65
|
}
|
package/src/validate-topo.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { getActivationSourceInputCompatibilityIssues } from './activation-source
|
|
|
11
11
|
import {
|
|
12
12
|
activationSourceDeclarationSignature,
|
|
13
13
|
activationSourceKey,
|
|
14
|
-
} from './activation-source-
|
|
14
|
+
} from './activation-source-derivation.js';
|
|
15
15
|
import type { AnyEntity } from './entity.js';
|
|
16
16
|
import { getEntityReferences } from './entity.js';
|
|
17
17
|
import { ValidationError } from './errors.js';
|
package/src/validation.ts
CHANGED
|
@@ -84,9 +84,9 @@ const getSchemaJsonSchemaOverride = (
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* Whether a schema has a deterministic JSON-schema override
|
|
87
|
+
* Whether a schema has a deterministic JSON-schema override derivation (for
|
|
88
88
|
* example `blobRefSchema`, a `z.custom(...)` carrying the descriptor metadata).
|
|
89
|
-
* Such schemas
|
|
89
|
+
* Such schemas derive to a canonical descriptor regardless of their underlying
|
|
90
90
|
* Zod internals, so marker derivation can treat them as supported.
|
|
91
91
|
*/
|
|
92
92
|
export const schemaHasJsonSchemaOverride = (schema: z.ZodType): boolean =>
|