@xemahq/kernel-contracts 0.41.0 → 0.43.0
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/dist/agent-composition/lib/model-resolution-matrix.d.ts +5 -2
- package/dist/agent-composition/lib/model-resolution-matrix.d.ts.map +1 -1
- package/dist/agent-composition/lib/model-resolution-matrix.js +1 -0
- package/dist/agent-composition/lib/model-resolution-matrix.js.map +1 -1
- package/dist/contribution/lib/contribution-kind.d.ts +2 -1
- package/dist/contribution/lib/contribution-kind.d.ts.map +1 -1
- package/dist/contribution/lib/contribution-kind.js +1 -0
- package/dist/contribution/lib/contribution-kind.js.map +1 -1
- package/dist/ingestion-source/index.d.ts +2 -0
- package/dist/ingestion-source/index.d.ts.map +1 -0
- package/dist/ingestion-source/index.js +18 -0
- package/dist/ingestion-source/index.js.map +1 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.d.ts +62 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.d.ts.map +1 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.js +78 -0
- package/dist/ingestion-source/lib/ingestion-source-contribution.js.map +1 -0
- package/dist/llm-gateway/lib/errors.d.ts +5 -0
- package/dist/llm-gateway/lib/errors.d.ts.map +1 -1
- package/dist/llm-gateway/lib/errors.js +21 -1
- package/dist/llm-gateway/lib/errors.js.map +1 -1
- package/dist/output/index.d.ts +1 -0
- package/dist/output/index.d.ts.map +1 -1
- package/dist/output/index.js +1 -0
- package/dist/output/index.js.map +1 -1
- package/dist/output/lib/output-version-dto.d.ts +2 -0
- package/dist/output/lib/output-version-dto.d.ts.map +1 -1
- package/dist/output/lib/point-of-attention.d.ts +19 -0
- package/dist/output/lib/point-of-attention.d.ts.map +1 -0
- package/dist/output/lib/point-of-attention.js +24 -0
- package/dist/output/lib/point-of-attention.js.map +1 -0
- package/dist/worker-runtime/index.d.ts +1 -0
- package/dist/worker-runtime/index.d.ts.map +1 -1
- package/dist/worker-runtime/index.js +1 -0
- package/dist/worker-runtime/index.js.map +1 -1
- package/dist/worker-runtime/lib/enums.d.ts +5 -0
- package/dist/worker-runtime/lib/enums.d.ts.map +1 -1
- package/dist/worker-runtime/lib/enums.js +7 -1
- package/dist/worker-runtime/lib/enums.js.map +1 -1
- package/dist/worker-runtime/lib/runtime-layout-profile.d.ts +4 -1
- package/dist/worker-runtime/lib/runtime-layout-profile.d.ts.map +1 -1
- package/dist/worker-runtime/lib/runtime-layout-profile.js +12 -0
- package/dist/worker-runtime/lib/runtime-layout-profile.js.map +1 -1
- package/dist/worker-runtime/lib/workspace-sandbox.d.ts +7 -0
- package/dist/worker-runtime/lib/workspace-sandbox.d.ts.map +1 -0
- package/dist/worker-runtime/lib/workspace-sandbox.js +36 -0
- package/dist/worker-runtime/lib/workspace-sandbox.js.map +1 -0
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.d.ts.map +1 -1
- package/dist/workflow/index.js +1 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/lib/enums.d.ts +1 -0
- package/dist/workflow/lib/enums.d.ts.map +1 -1
- package/dist/workflow/lib/enums.js +1 -0
- package/dist/workflow/lib/enums.js.map +1 -1
- package/dist/workflow/lib/model-ref.d.ts +17 -0
- package/dist/workflow/lib/model-ref.d.ts.map +1 -1
- package/dist/workflow/lib/model-ref.js +19 -1
- package/dist/workflow/lib/model-ref.js.map +1 -1
- package/dist/workflow/lib/workflow-control.d.ts +121 -0
- package/dist/workflow/lib/workflow-control.d.ts.map +1 -0
- package/dist/workflow/lib/workflow-control.js +101 -0
- package/dist/workflow/lib/workflow-control.js.map +1 -0
- package/package.json +1 -1
- package/src/agent-composition/lib/model-resolution-matrix.ts +16 -1
- package/src/contribution/lib/contribution-kind.ts +18 -0
- package/src/ingestion-source/index.ts +20 -0
- package/src/ingestion-source/lib/ingestion-source-contribution.ts +238 -0
- package/src/llm-gateway/lib/errors.ts +46 -0
- package/src/output/index.ts +1 -0
- package/src/output/lib/output-version-dto.ts +9 -0
- package/src/output/lib/point-of-attention.ts +69 -0
- package/src/worker-runtime/index.ts +1 -0
- package/src/worker-runtime/lib/enums.ts +29 -0
- package/src/worker-runtime/lib/runtime-layout-profile.ts +61 -1
- package/src/worker-runtime/lib/workspace-sandbox.ts +129 -0
- package/src/workflow/index.ts +1 -0
- package/src/workflow/lib/enums.ts +8 -0
- package/src/workflow/lib/model-ref.ts +45 -0
- package/src/workflow/lib/workflow-control.ts +234 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Maximum length of the short identifier / label / tag strings on an ingestion
|
|
5
|
+
* source manifest. Bounded so list/describe responses stay small and persisted
|
|
6
|
+
* rows have a predictable upper size.
|
|
7
|
+
*/
|
|
8
|
+
const INGESTION_SOURCE_STRING_MAX = 200;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Maximum length of the free-text summary. A summary is a one-liner, not a
|
|
12
|
+
* document.
|
|
13
|
+
*/
|
|
14
|
+
const INGESTION_SOURCE_SUMMARY_MAX = 1000;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maximum length of a fetch URL / cadence expression / record selector — each
|
|
18
|
+
* is a small path/pattern, never a document.
|
|
19
|
+
*/
|
|
20
|
+
const INGESTION_SOURCE_URL_MAX = 2000;
|
|
21
|
+
const INGESTION_SOURCE_EXPRESSION_MAX = 200;
|
|
22
|
+
const INGESTION_SOURCE_SELECTOR_MAX = 500;
|
|
23
|
+
|
|
24
|
+
/** Hard cap on the number of static request headers a source may declare. */
|
|
25
|
+
const INGESTION_SOURCE_HEADERS_MAX = 50;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* HTTP method used to pull from an ingestion source. Closed set — a source
|
|
29
|
+
* either GETs a resource or POSTs a query body.
|
|
30
|
+
*/
|
|
31
|
+
export enum IngestionFetchMethod {
|
|
32
|
+
GET = 'GET',
|
|
33
|
+
POST = 'POST',
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const INGESTION_FETCH_METHOD_VALUES: readonly IngestionFetchMethod[] = [
|
|
37
|
+
IngestionFetchMethod.GET,
|
|
38
|
+
IngestionFetchMethod.POST,
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
export const IngestionFetchMethodSchema = z.enum(
|
|
42
|
+
INGESTION_FETCH_METHOD_VALUES as readonly [
|
|
43
|
+
IngestionFetchMethod,
|
|
44
|
+
...IngestionFetchMethod[],
|
|
45
|
+
],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* How the pull cadence is expressed. Closed set: a fixed interval or a cron
|
|
50
|
+
* expression. The `expression` field carries the concrete value (a duration
|
|
51
|
+
* string for `interval`, a cron string for `cron`).
|
|
52
|
+
*/
|
|
53
|
+
export enum IngestionCadenceKind {
|
|
54
|
+
INTERVAL = 'interval',
|
|
55
|
+
CRON = 'cron',
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const INGESTION_CADENCE_KIND_VALUES: readonly IngestionCadenceKind[] = [
|
|
59
|
+
IngestionCadenceKind.INTERVAL,
|
|
60
|
+
IngestionCadenceKind.CRON,
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
export const IngestionCadenceKindSchema = z.enum(
|
|
64
|
+
INGESTION_CADENCE_KIND_VALUES as readonly [
|
|
65
|
+
IngestionCadenceKind,
|
|
66
|
+
...IngestionCadenceKind[],
|
|
67
|
+
],
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* How the platform decides a pulled resource CHANGED since the last pull.
|
|
72
|
+
* Closed set — the source declares which signal is authoritative so the puller
|
|
73
|
+
* never guesses.
|
|
74
|
+
*/
|
|
75
|
+
export enum IngestionChangeDetectionStrategy {
|
|
76
|
+
/** Compare the HTTP `ETag` response header. */
|
|
77
|
+
ETAG = 'etag',
|
|
78
|
+
/** Compare the HTTP `Last-Modified` response header. */
|
|
79
|
+
LAST_MODIFIED = 'last-modified',
|
|
80
|
+
/** Hash the fetched body and compare content hashes. */
|
|
81
|
+
CONTENT_HASH = 'content-hash',
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const INGESTION_CHANGE_DETECTION_STRATEGY_VALUES: readonly IngestionChangeDetectionStrategy[] =
|
|
85
|
+
[
|
|
86
|
+
IngestionChangeDetectionStrategy.ETAG,
|
|
87
|
+
IngestionChangeDetectionStrategy.LAST_MODIFIED,
|
|
88
|
+
IngestionChangeDetectionStrategy.CONTENT_HASH,
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
export const IngestionChangeDetectionStrategySchema = z.enum(
|
|
92
|
+
INGESTION_CHANGE_DETECTION_STRATEGY_VALUES as readonly [
|
|
93
|
+
IngestionChangeDetectionStrategy,
|
|
94
|
+
...IngestionChangeDetectionStrategy[],
|
|
95
|
+
],
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* `IngestionFetchSpec` — WHERE the data comes from and HOW to request it. A
|
|
100
|
+
* plain HTTP pull descriptor: URL, method, and optional static headers. No
|
|
101
|
+
* credential is carried inline — a private source references a broker-resolved
|
|
102
|
+
* credential through the platform, never a secret in the manifest.
|
|
103
|
+
*/
|
|
104
|
+
export interface IngestionFetchSpec {
|
|
105
|
+
/** Absolute URL to pull from. ≤ 2000 chars. */
|
|
106
|
+
readonly url: string;
|
|
107
|
+
/** HTTP method. Absent ⇒ `GET`. */
|
|
108
|
+
readonly method?: IngestionFetchMethod;
|
|
109
|
+
/** Static request headers (non-secret). Hard-capped at 50 entries. */
|
|
110
|
+
readonly headers?: Readonly<Record<string, string>>;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export const IngestionFetchSpecSchema: z.ZodType<IngestionFetchSpec> = z.object({
|
|
114
|
+
url: z.string().min(1).max(INGESTION_SOURCE_URL_MAX),
|
|
115
|
+
method: IngestionFetchMethodSchema.optional(),
|
|
116
|
+
headers: z
|
|
117
|
+
.record(
|
|
118
|
+
z.string().min(1).max(INGESTION_SOURCE_STRING_MAX),
|
|
119
|
+
z.string().max(INGESTION_SOURCE_STRING_MAX),
|
|
120
|
+
)
|
|
121
|
+
.refine((h) => Object.keys(h).length <= INGESTION_SOURCE_HEADERS_MAX, {
|
|
122
|
+
message: `at most ${INGESTION_SOURCE_HEADERS_MAX} headers`,
|
|
123
|
+
})
|
|
124
|
+
.optional(),
|
|
125
|
+
}) as z.ZodType<IngestionFetchSpec>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* `IngestionCadenceSpec` — HOW OFTEN the source is pulled. `kind` selects the
|
|
129
|
+
* grammar; `expression` carries the concrete value.
|
|
130
|
+
*/
|
|
131
|
+
export interface IngestionCadenceSpec {
|
|
132
|
+
readonly kind: IngestionCadenceKind;
|
|
133
|
+
/** Interval duration (for `interval`) or cron expression (for `cron`). */
|
|
134
|
+
readonly expression: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export const IngestionCadenceSpecSchema: z.ZodType<IngestionCadenceSpec> =
|
|
138
|
+
z.object({
|
|
139
|
+
kind: IngestionCadenceKindSchema,
|
|
140
|
+
expression: z.string().min(1).max(INGESTION_SOURCE_EXPRESSION_MAX),
|
|
141
|
+
}) as z.ZodType<IngestionCadenceSpec>;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* `IngestionChangeDetectionSpec` — HOW the puller decides a resource changed.
|
|
145
|
+
*/
|
|
146
|
+
export interface IngestionChangeDetectionSpec {
|
|
147
|
+
readonly strategy: IngestionChangeDetectionStrategy;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export const IngestionChangeDetectionSpecSchema: z.ZodType<IngestionChangeDetectionSpec> =
|
|
151
|
+
z.object({
|
|
152
|
+
strategy: IngestionChangeDetectionStrategySchema,
|
|
153
|
+
}) as z.ZodType<IngestionChangeDetectionSpec>;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* `IngestionCatalogSpec` — WHERE pulled records land and how they are tagged
|
|
157
|
+
* for provenance. `targetIndex` is the local catalog/index the records are
|
|
158
|
+
* written into; `provenanceTag` stamps each record so a downstream reader can
|
|
159
|
+
* trace it back to this source.
|
|
160
|
+
*/
|
|
161
|
+
export interface IngestionCatalogSpec {
|
|
162
|
+
/** The local catalog/index pulled records are written into. ≤ 200 chars. */
|
|
163
|
+
readonly targetIndex: string;
|
|
164
|
+
/** Provenance tag stamped on every pulled record. ≤ 200 chars. */
|
|
165
|
+
readonly provenanceTag: string;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export const IngestionCatalogSpecSchema: z.ZodType<IngestionCatalogSpec> =
|
|
169
|
+
z.object({
|
|
170
|
+
targetIndex: z.string().min(1).max(INGESTION_SOURCE_STRING_MAX),
|
|
171
|
+
provenanceTag: z.string().min(1).max(INGESTION_SOURCE_STRING_MAX),
|
|
172
|
+
}) as z.ZodType<IngestionCatalogSpec>;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Manifest shape carried by every `ContributionKind.IngestionSource` envelope.
|
|
176
|
+
* One contribution = one PULL-BASED external data source.
|
|
177
|
+
*
|
|
178
|
+
* Mirrors `CapabilityContributionManifest` / `SearchTypeContributionManifest`
|
|
179
|
+
* precisely: a value-identical subset of the wire body the owning ingestion
|
|
180
|
+
* service accepts, validated fail-fast at biome-discovery time. Provenance
|
|
181
|
+
* (`biome.id`, `biome.version`) is NOT a field here — the parser stamps it from
|
|
182
|
+
* the discovering biome's `xema-biome.json`. Authors do not declare provenance
|
|
183
|
+
* inline.
|
|
184
|
+
*
|
|
185
|
+
* The descriptor is pure DATA: it declares WHERE the data comes from
|
|
186
|
+
* (`fetch`), HOW OFTEN to pull it (`cadence`), HOW to detect change
|
|
187
|
+
* (`changeDetection`), WHERE records land (`catalog`), and OPTIONALLY how to
|
|
188
|
+
* extract records from the fetched body (`recordSelector`). It never carries
|
|
189
|
+
* transform/mint code and never carries a secret.
|
|
190
|
+
*
|
|
191
|
+
* Validation is fail-fast: a contribution that violates any constraint is
|
|
192
|
+
* rejected at boot. No silent coercion.
|
|
193
|
+
*/
|
|
194
|
+
export interface IngestionSourceContributionManifest {
|
|
195
|
+
/** Stable identifier for this source within its owning biome. ≤ 200 chars. */
|
|
196
|
+
readonly sourceKey: string;
|
|
197
|
+
/** Human-readable source title. ≤ 200 chars. */
|
|
198
|
+
readonly title: string;
|
|
199
|
+
/** Optional one-line summary. ≤ 1000 chars. */
|
|
200
|
+
readonly summary?: string;
|
|
201
|
+
/** WHERE the data comes from and HOW to request it. */
|
|
202
|
+
readonly fetch: IngestionFetchSpec;
|
|
203
|
+
/** HOW OFTEN the source is pulled. */
|
|
204
|
+
readonly cadence: IngestionCadenceSpec;
|
|
205
|
+
/** HOW the puller decides a resource changed since the last pull. */
|
|
206
|
+
readonly changeDetection: IngestionChangeDetectionSpec;
|
|
207
|
+
/** WHERE pulled records land and how they are tagged for provenance. */
|
|
208
|
+
readonly catalog: IngestionCatalogSpec;
|
|
209
|
+
/**
|
|
210
|
+
* Optional JSONPath-ish selector applied to the fetched body to extract the
|
|
211
|
+
* record array. Absent ⇒ the whole body is treated as a single record.
|
|
212
|
+
* ≤ 500 chars.
|
|
213
|
+
*/
|
|
214
|
+
readonly recordSelector?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Zod validator for `IngestionSourceContributionManifest`. The biome-discovery
|
|
219
|
+
* parser AND the owning ingestion service's contribution-sync handler BOTH
|
|
220
|
+
* route every candidate envelope through this schema, so a malformed
|
|
221
|
+
* contribution fails at the boundary instead of at the wire (mirrors
|
|
222
|
+
* `CapabilityContributionManifestSchema`).
|
|
223
|
+
*/
|
|
224
|
+
export const IngestionSourceContributionManifestSchema: z.ZodType<IngestionSourceContributionManifest> =
|
|
225
|
+
z.object({
|
|
226
|
+
sourceKey: z.string().min(1).max(INGESTION_SOURCE_STRING_MAX),
|
|
227
|
+
title: z.string().min(1).max(INGESTION_SOURCE_STRING_MAX),
|
|
228
|
+
summary: z.string().min(1).max(INGESTION_SOURCE_SUMMARY_MAX).optional(),
|
|
229
|
+
fetch: IngestionFetchSpecSchema,
|
|
230
|
+
cadence: IngestionCadenceSpecSchema,
|
|
231
|
+
changeDetection: IngestionChangeDetectionSpecSchema,
|
|
232
|
+
catalog: IngestionCatalogSpecSchema,
|
|
233
|
+
recordSelector: z
|
|
234
|
+
.string()
|
|
235
|
+
.min(1)
|
|
236
|
+
.max(INGESTION_SOURCE_SELECTOR_MAX)
|
|
237
|
+
.optional(),
|
|
238
|
+
}) as z.ZodType<IngestionSourceContributionManifest>;
|
|
@@ -77,6 +77,52 @@ export interface LlmGatewayErrorFrame {
|
|
|
77
77
|
error: LlmGatewayErrorPayload;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Message signatures that identify a context-window overflow when NO typed
|
|
82
|
+
* {@link LlmGatewayErrorCode} survived to the consumer.
|
|
83
|
+
*
|
|
84
|
+
* The authoritative provider-message → code classification lives in
|
|
85
|
+
* llm-gateway-api's `upstream-error-classifier.ts` (an independent submodule
|
|
86
|
+
* that cannot import this kernel package). This list is the CONSUMER-side
|
|
87
|
+
* counterpart: when an agentic runtime (e.g. OpenCode) swallows the gateway's
|
|
88
|
+
* typed code and re-surfaces only a human-readable string, this is the single
|
|
89
|
+
* place a consumer recognizes the overflow. Keep the two lists in parity when
|
|
90
|
+
* a new provider phrasing appears.
|
|
91
|
+
*/
|
|
92
|
+
export const CONTEXT_OVERFLOW_MESSAGE_PATTERNS: readonly RegExp[] = [
|
|
93
|
+
/maximum context length is \d+ tokens/i,
|
|
94
|
+
/input length and max_tokens exceed context limit/i,
|
|
95
|
+
/context_length_exceeded/i,
|
|
96
|
+
/context window/i,
|
|
97
|
+
/prompt is too long/i,
|
|
98
|
+
/reduce the length of (?:the )?messages/i,
|
|
99
|
+
/exceed(?:s|ed)?\b[^.]{0,40}\bcontext/i,
|
|
100
|
+
];
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Deterministically decide whether an error signal is a context-window
|
|
104
|
+
* overflow — the single well-named predicate every consumer uses instead of
|
|
105
|
+
* an ad-hoc string match. Prefers the TYPED code
|
|
106
|
+
* ({@link LlmGatewayErrorCode.CONTEXT_LENGTH_EXCEEDED}); falls back to the
|
|
107
|
+
* centralized {@link CONTEXT_OVERFLOW_MESSAGE_PATTERNS} ONLY when the code did
|
|
108
|
+
* not survive to the consumer (e.g. a runtime that forwards a raw message).
|
|
109
|
+
*
|
|
110
|
+
* Passing NEITHER a code nor a message returns `false` — never a silent true.
|
|
111
|
+
*/
|
|
112
|
+
export function isContextOverflowError(input: {
|
|
113
|
+
code?: string | null;
|
|
114
|
+
message?: string | null;
|
|
115
|
+
}): boolean {
|
|
116
|
+
if (input.code === LlmGatewayErrorCode.CONTEXT_LENGTH_EXCEEDED) {
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
const message = input.message;
|
|
120
|
+
if (typeof message !== 'string' || message.length === 0) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
return CONTEXT_OVERFLOW_MESSAGE_PATTERNS.some((p) => p.test(message));
|
|
124
|
+
}
|
|
125
|
+
|
|
80
126
|
/**
|
|
81
127
|
* Type guard for {@link LlmGatewayErrorFrame}.
|
|
82
128
|
*/
|
package/src/output/index.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
14
14
|
|
|
15
15
|
export * from './lib/output-kind';
|
|
16
|
+
export * from './lib/point-of-attention';
|
|
16
17
|
export * from './lib/output-render-hint';
|
|
17
18
|
export * from './lib/output-deliverable-expectation';
|
|
18
19
|
export * from './lib/output-version-dto';
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { PointOfAttention } from './point-of-attention';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* One row in the version history of an output. The Outputs panel
|
|
3
5
|
* renders a dropdown when `versions.length > 1`; clicking a non-current
|
|
@@ -18,4 +20,11 @@ export interface OutputVersionDto {
|
|
|
18
20
|
readonly producerAgentSlug: string | null;
|
|
19
21
|
/** True when this row is the latest version. Exactly one row has this set. */
|
|
20
22
|
readonly isCurrent: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Generic producer-authored highlights for THIS version — the salient
|
|
25
|
+
* things a reader should notice (an assumption, a gap, a risk). Optional
|
|
26
|
+
* extensibility escape hatch so the fixed version schema stays open without
|
|
27
|
+
* a per-domain field. Absent/empty ⇒ nothing flagged.
|
|
28
|
+
*/
|
|
29
|
+
readonly pointsOfAttention?: readonly PointOfAttention[];
|
|
21
30
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bounded maxes for the short label / longer detail strings on a point of
|
|
5
|
+
* attention. Kept small so a version row carrying a handful of them stays a
|
|
6
|
+
* cheap, list-safe payload.
|
|
7
|
+
*/
|
|
8
|
+
const POINT_OF_ATTENTION_LABEL_MAX = 200;
|
|
9
|
+
const POINT_OF_ATTENTION_DETAIL_MAX = 2000;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Severity of a {@link PointOfAttention}. Closed set — a consumer renders each
|
|
13
|
+
* value with a fixed badge/colour, so the domain must never be a free-form
|
|
14
|
+
* string.
|
|
15
|
+
*/
|
|
16
|
+
export enum PointOfAttentionSeverity {
|
|
17
|
+
/** Neutral highlight — worth surfacing, no action implied. */
|
|
18
|
+
INFO = 'info',
|
|
19
|
+
/** Something the reader should scrutinise before relying on the artifact. */
|
|
20
|
+
WARNING = 'warning',
|
|
21
|
+
/** A blocking concern — the artifact should not be trusted/acted on as-is. */
|
|
22
|
+
CRITICAL = 'critical',
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const POINT_OF_ATTENTION_SEVERITY_VALUES: readonly PointOfAttentionSeverity[] =
|
|
26
|
+
[
|
|
27
|
+
PointOfAttentionSeverity.INFO,
|
|
28
|
+
PointOfAttentionSeverity.WARNING,
|
|
29
|
+
PointOfAttentionSeverity.CRITICAL,
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Zod schema for {@link PointOfAttentionSeverity}. Built from the enum so the
|
|
34
|
+
* literal list lives in exactly one place.
|
|
35
|
+
*/
|
|
36
|
+
export const PointOfAttentionSeveritySchema = z.enum(
|
|
37
|
+
POINT_OF_ATTENTION_SEVERITY_VALUES as readonly [
|
|
38
|
+
PointOfAttentionSeverity,
|
|
39
|
+
...PointOfAttentionSeverity[],
|
|
40
|
+
],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A GENERIC "point of attention" / highlight attached to a per-version
|
|
45
|
+
* artifact. This is the deliberate extensibility escape hatch that keeps a
|
|
46
|
+
* fixed artifact schema open: a producer can flag the salient things a reader
|
|
47
|
+
* should notice (an assumption made, a gap, a risk) WITHOUT the kernel growing
|
|
48
|
+
* a domain-specific field for each. Domain-agnostic — carries only a label, a
|
|
49
|
+
* detail, and a {@link PointOfAttentionSeverity}; the meaning is the
|
|
50
|
+
* producer's, never the kernel's.
|
|
51
|
+
*/
|
|
52
|
+
export interface PointOfAttention {
|
|
53
|
+
/** Short headline for the point (≤ 200 chars). */
|
|
54
|
+
readonly label: string;
|
|
55
|
+
/** Longer human-readable explanation (≤ 2000 chars). */
|
|
56
|
+
readonly detail: string;
|
|
57
|
+
/** How much scrutiny this point warrants. */
|
|
58
|
+
readonly severity: PointOfAttentionSeverity;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Zod validator for {@link PointOfAttention}. Fail-fast: a malformed point is
|
|
63
|
+
* rejected at the boundary rather than silently coerced.
|
|
64
|
+
*/
|
|
65
|
+
export const PointOfAttentionSchema: z.ZodType<PointOfAttention> = z.object({
|
|
66
|
+
label: z.string().min(1).max(POINT_OF_ATTENTION_LABEL_MAX),
|
|
67
|
+
detail: z.string().min(1).max(POINT_OF_ATTENTION_DETAIL_MAX),
|
|
68
|
+
severity: PointOfAttentionSeveritySchema,
|
|
69
|
+
}) as z.ZodType<PointOfAttention>;
|
|
@@ -26,6 +26,35 @@ export enum WorkerRuntimeKind {
|
|
|
26
26
|
Hermes = 'hermes',
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* How a runtime consumes a per-skill "slash command" launcher file, if at
|
|
31
|
+
* all. Closed set — a runtime that grows a new command surface adds a member
|
|
32
|
+
* here and the composer/proxy switch exhaustively on it.
|
|
33
|
+
*
|
|
34
|
+
* The platform composes ONE per-skill launch command (markdown body with a
|
|
35
|
+
* `$ARGUMENTS` placeholder) so typing `/<skill>` loads + applies that skill.
|
|
36
|
+
* WHETHER and in WHICH FORMAT a runtime reads that file differs:
|
|
37
|
+
*
|
|
38
|
+
* - `Markdown` — the runtime reads markdown command files from its
|
|
39
|
+
* `commandsDir` (OpenCode: `.opencode/{command,commands}/**.md`; Claude
|
|
40
|
+
* Code: `.claude/commands/*.md`). Xema emits the launcher for these.
|
|
41
|
+
* - `Toml` — the runtime's custom commands are TOML, NOT markdown (Qwen).
|
|
42
|
+
* Xema does NOT synthesise TOML skill launchers — skills launch via the
|
|
43
|
+
* runtime's own `/skills <name>` surface / model-invoked skill tool — so
|
|
44
|
+
* the platform emits NOTHING for a TOML runtime rather than a file the
|
|
45
|
+
* runtime silently ignores.
|
|
46
|
+
* - `None` — the runtime has no file-based slash-command surface at all
|
|
47
|
+
* (Hermes: `commandsDir` is `null`). Emit nothing.
|
|
48
|
+
*
|
|
49
|
+
* Only `Markdown` runtimes receive a materialised per-skill command file;
|
|
50
|
+
* `Toml`/`None` receive none (never a stray, silently-ignored file).
|
|
51
|
+
*/
|
|
52
|
+
export enum RuntimeCommandFormat {
|
|
53
|
+
Markdown = 'markdown',
|
|
54
|
+
Toml = 'toml',
|
|
55
|
+
None = 'none',
|
|
56
|
+
}
|
|
57
|
+
|
|
29
58
|
/**
|
|
30
59
|
* Lifecycle hook surfaces that a runtime may support. The set is closed —
|
|
31
60
|
* adding a new hook is a kernel change.
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// is where the platform renders the system overlay per WS7.
|
|
21
21
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
22
22
|
|
|
23
|
-
import { WorkerRuntimeKind } from './enums';
|
|
23
|
+
import { RuntimeCommandFormat, WorkerRuntimeKind } from './enums';
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* The workspace slots whose on-disk location differs per orchestrator. The
|
|
@@ -48,6 +48,43 @@ export interface RuntimeLayoutProfile {
|
|
|
48
48
|
* fail-fast contract as `agentBundlesDir`.
|
|
49
49
|
*/
|
|
50
50
|
readonly commandsDir: string | null;
|
|
51
|
+
/**
|
|
52
|
+
* How this runtime consumes a per-skill `/`-launch command — the capability
|
|
53
|
+
* the composer/proxy switch on to decide WHETHER (and in which format) to
|
|
54
|
+
* materialise a launcher file. `Markdown` runtimes get a launcher written at
|
|
55
|
+
* `commandsDir`; `Toml`/`None` runtimes get NOTHING (skills still mount as
|
|
56
|
+
* bundles and launch via the runtime's own `/skills` surface / skill tool),
|
|
57
|
+
* so no stray file lands where the runtime would silently ignore it.
|
|
58
|
+
*
|
|
59
|
+
* INVARIANT: `commandFormat === Markdown` ⟺ `commandsDir !== null`. A runtime
|
|
60
|
+
* with no command surface (`commandsDir: null`) MUST declare `None`.
|
|
61
|
+
*/
|
|
62
|
+
readonly commandFormat: RuntimeCommandFormat;
|
|
63
|
+
/**
|
|
64
|
+
* Absolute path to the runtime's PRIMARY config file — the file the
|
|
65
|
+
* orchestrator's bundle composer renders and the supervisor materialises on
|
|
66
|
+
* the PVC. It carries the LLM-gateway credential (OpenCode reads
|
|
67
|
+
* `.opencode/opencode.jsonc`; Qwen `.qwen/settings.json` → `env.OPENAI_API_KEY`;
|
|
68
|
+
* Claude `.claude/settings.json` → `env.ANTHROPIC_AUTH_TOKEN`; Hermes
|
|
69
|
+
* `.hermes/config.json` → `provider.api_key`) and/or merged MCP bearer headers.
|
|
70
|
+
*
|
|
71
|
+
* This is the single source of truth for the config-file location: both the
|
|
72
|
+
* proxy's on-disk layout and the workspace-sandbox protected-path contract
|
|
73
|
+
* (`deriveWorkspaceSandboxProtection`) source it here rather than each
|
|
74
|
+
* re-deriving a per-runtime basename literal.
|
|
75
|
+
*/
|
|
76
|
+
readonly runtimeConfigFile: string;
|
|
77
|
+
/**
|
|
78
|
+
* Absolute path to a DEDICATED MCP-server file the runtime reads separately
|
|
79
|
+
* from its primary config (Claude Code: `.mcp.json` at the workspace root),
|
|
80
|
+
* or `null` when the runtime merges MCP into {@link runtimeConfigFile}
|
|
81
|
+
* (OpenCode/Qwen/Hermes: `InConfig`). When non-null the file carries per-server
|
|
82
|
+
* bearer headers and is therefore part of the secret read-deny set.
|
|
83
|
+
*
|
|
84
|
+
* INVARIANT: `mcpConfigFile !== null` ⟺ the runtime places MCP in a separate
|
|
85
|
+
* file. A runtime that folds MCP into its primary config MUST declare `null`.
|
|
86
|
+
*/
|
|
87
|
+
readonly mcpConfigFile: string | null;
|
|
51
88
|
}
|
|
52
89
|
|
|
53
90
|
/**
|
|
@@ -63,6 +100,11 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
63
100
|
contextFile: '/workspace/AGENTS.md',
|
|
64
101
|
agentBundlesDir: '/workspace/.opencode/agents',
|
|
65
102
|
commandsDir: '/workspace/.opencode/command',
|
|
103
|
+
// OpenCode reads markdown commands from `{command,commands}/**.md`.
|
|
104
|
+
commandFormat: RuntimeCommandFormat.Markdown,
|
|
105
|
+
runtimeConfigFile: '/workspace/.opencode/opencode.jsonc',
|
|
106
|
+
// OpenCode pre-merges MCP into opencode.jsonc (`InConfig`) — no separate file.
|
|
107
|
+
mcpConfigFile: null,
|
|
66
108
|
},
|
|
67
109
|
[WorkerRuntimeKind.Qwen]: {
|
|
68
110
|
kind: WorkerRuntimeKind.Qwen,
|
|
@@ -70,6 +112,13 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
70
112
|
contextFile: '/workspace/AGENTS.md',
|
|
71
113
|
agentBundlesDir: '/workspace/.qwen/agents',
|
|
72
114
|
commandsDir: '/workspace/.qwen/commands',
|
|
115
|
+
// Qwen custom commands are TOML, not markdown; skills launch via Qwen's
|
|
116
|
+
// own `/skills <name>` surface. A synthesised markdown `.md` under
|
|
117
|
+
// `.qwen/commands` would be silently ignored — so Xema emits NOTHING.
|
|
118
|
+
commandFormat: RuntimeCommandFormat.Toml,
|
|
119
|
+
runtimeConfigFile: '/workspace/.qwen/settings.json',
|
|
120
|
+
// Qwen reads its `mcpServers` map INSIDE `.qwen/settings.json` (`InConfig`).
|
|
121
|
+
mcpConfigFile: null,
|
|
73
122
|
},
|
|
74
123
|
[WorkerRuntimeKind.ClaudeCode]: {
|
|
75
124
|
kind: WorkerRuntimeKind.ClaudeCode,
|
|
@@ -77,6 +126,12 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
77
126
|
contextFile: '/workspace/CLAUDE.md',
|
|
78
127
|
agentBundlesDir: '/workspace/.claude/agents',
|
|
79
128
|
commandsDir: '/workspace/.claude/commands',
|
|
129
|
+
// Claude Code custom slash-commands ARE markdown, under `.claude/commands`.
|
|
130
|
+
commandFormat: RuntimeCommandFormat.Markdown,
|
|
131
|
+
runtimeConfigFile: '/workspace/.claude/settings.json',
|
|
132
|
+
// Claude Code reads project-scoped MCP from a DEDICATED `.mcp.json` at the
|
|
133
|
+
// workspace root (SeparateFile) — NOT from `.claude/settings.json`.
|
|
134
|
+
mcpConfigFile: '/workspace/.mcp.json',
|
|
80
135
|
},
|
|
81
136
|
[WorkerRuntimeKind.Hermes]: {
|
|
82
137
|
kind: WorkerRuntimeKind.Hermes,
|
|
@@ -89,6 +144,11 @@ export const RUNTIME_LAYOUT_PROFILES: Readonly<
|
|
|
89
144
|
// is a portable `.md` surface, so the composer must not mount them as files.
|
|
90
145
|
agentBundlesDir: null,
|
|
91
146
|
commandsDir: null,
|
|
147
|
+
// No file-based slash-command surface at all — emit nothing.
|
|
148
|
+
commandFormat: RuntimeCommandFormat.None,
|
|
149
|
+
runtimeConfigFile: '/workspace/.hermes/config.json',
|
|
150
|
+
// Hermes reads its `mcpServers` map INSIDE `.hermes/config.json` (`InConfig`).
|
|
151
|
+
mcpConfigFile: null,
|
|
92
152
|
},
|
|
93
153
|
};
|
|
94
154
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
2
|
+
// ── Workspace-sandbox protection contract ──
|
|
3
|
+
//
|
|
4
|
+
// A FIRST-CLASS, per-runtime security invariant for the agent-workspace sandbox.
|
|
5
|
+
// The agent runs a code CLI (OpenCode/Qwen/Claude/Hermes) inside the workspace
|
|
6
|
+
// pod with its OWN file tools (read/edit/write). Those tools MUST NOT be able to:
|
|
7
|
+
//
|
|
8
|
+
// (a) READ the mounted platform secrets — the runtime config file carries the
|
|
9
|
+
// LLM-gateway credential (a signed JWT) and/or merged MCP bearer headers,
|
|
10
|
+
// and a dedicated `.mcp.json` (Claude) carries per-server bearer headers.
|
|
11
|
+
// (b) MUTATE the agent's OWN system-instructions / agent-definition bundles —
|
|
12
|
+
// the auto-loaded context file (`AGENTS.md`/`CLAUDE.md`), the file-based
|
|
13
|
+
// sub-agent definitions, the Xema-private system overlay + injected
|
|
14
|
+
// instruction sections, and the `context.json` capability-authority spec
|
|
15
|
+
// (rewriting it would let the agent self-grant tools).
|
|
16
|
+
//
|
|
17
|
+
// This module DECLARES that protected-path set ONCE, derived from the kernel
|
|
18
|
+
// `RuntimeLayoutProfile` (the SSOT for every per-runtime path). Each runtime's
|
|
19
|
+
// config builder translates the neutral glob set into its NATIVE permission
|
|
20
|
+
// vocabulary (OpenCode `permission.read`/`permission.edit` globs; Claude Code
|
|
21
|
+
// `permissions.deny: ["Read(...)","Edit(...)"]`). Runtimes with no per-file
|
|
22
|
+
// permission surface (Qwen/Hermes) cannot consume the deny set and fall back to
|
|
23
|
+
// the pod's uid + per-org mount confinement — see the residual note in each
|
|
24
|
+
// builder. The contract is declared here regardless so the invariant is
|
|
25
|
+
// single-sourced, not scattered across builders.
|
|
26
|
+
//
|
|
27
|
+
// Globs are runtime-syntax-NEUTRAL absolute paths anchored at `/workspace`
|
|
28
|
+
// (`*` matches any character; `<dir>/**` covers a whole slot). Callers adapt
|
|
29
|
+
// them to their runtime's matcher.
|
|
30
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
31
|
+
|
|
32
|
+
import {
|
|
33
|
+
WorkspaceMount,
|
|
34
|
+
} from '../../agent-workspace/lib/workspace-layout';
|
|
35
|
+
|
|
36
|
+
import type { RuntimeLayoutProfile } from './runtime-layout-profile';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The resolved protected-path set for one runtime's workspace sandbox.
|
|
40
|
+
*
|
|
41
|
+
* - `secretReadDenyGlobs` — the agent's READ tool must DENY these (secrets). The
|
|
42
|
+
* agent may still read its own instructions/code; only credential-bearing
|
|
43
|
+
* files are denied.
|
|
44
|
+
* - `protectedEditDenyGlobs` — the agent's EDIT/WRITE tool must DENY these
|
|
45
|
+
* (own system-instructions, agent-definitions, authority spec, and — defense
|
|
46
|
+
* in depth — its own credential config). The agent may still edit workspace
|
|
47
|
+
* code, deliverables, repos, inputs, uploads, and tmp.
|
|
48
|
+
*
|
|
49
|
+
* Every entry is an absolute `/workspace/...` glob. Both sets are de-duplicated
|
|
50
|
+
* and stable-ordered.
|
|
51
|
+
*/
|
|
52
|
+
export interface WorkspaceSandboxProtection {
|
|
53
|
+
readonly secretReadDenyGlobs: readonly string[];
|
|
54
|
+
readonly protectedEditDenyGlobs: readonly string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Constant, runtime-AGNOSTIC Xema-private system-instruction + authority slots.
|
|
59
|
+
* These are excluded from the per-runtime `RuntimeLayoutProfile` on purpose (the
|
|
60
|
+
* profile only carries the slots whose ON-DISK path differs per runtime), so
|
|
61
|
+
* they are sourced from the `WorkspaceMount` SSOT here — never re-literalled.
|
|
62
|
+
*
|
|
63
|
+
* All are EDIT-denied (never mutate the injected system overlay / instructions /
|
|
64
|
+
* capability-authority). They are NOT read-denied: the runtime legitimately
|
|
65
|
+
* loads the overlay + instructions as context, and the plugin reads
|
|
66
|
+
* `context.json`.
|
|
67
|
+
*/
|
|
68
|
+
const XEMA_PRIVATE_INSTRUCTION_EDIT_GLOBS: readonly string[] = [
|
|
69
|
+
// /workspace/.xema/system-overlay.md — the injected system prompt overlay.
|
|
70
|
+
WorkspaceMount.SystemOverlay,
|
|
71
|
+
// /workspace/.xema/instructions/** — injected rule/policy instruction sections.
|
|
72
|
+
`${WorkspaceMount.Instructions}/**`,
|
|
73
|
+
// /workspace/context.json — carries authority.allowedXemaTools; rewriting it
|
|
74
|
+
// would let the agent escalate its own tool grants.
|
|
75
|
+
WorkspaceMount.ContextJson,
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
function dedupeStable(globs: readonly string[]): string[] {
|
|
79
|
+
return [...new Set(globs)];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Derive the workspace-sandbox protected-path set for a runtime, from its
|
|
84
|
+
* kernel `RuntimeLayoutProfile`. Pure — no I/O, no defaults that mask input.
|
|
85
|
+
*
|
|
86
|
+
* The set is CLOSED and fail-closed: a config file always exists (every runtime
|
|
87
|
+
* declares `runtimeConfigFile`), and every credential-bearing / instruction path
|
|
88
|
+
* the profile exposes is protected. A runtime with no file-based sub-agent
|
|
89
|
+
* surface (`agentBundlesDir === null`, e.g. Hermes) contributes no agent-def
|
|
90
|
+
* glob — there is no such directory to protect.
|
|
91
|
+
*/
|
|
92
|
+
export function deriveWorkspaceSandboxProtection(
|
|
93
|
+
profile: RuntimeLayoutProfile,
|
|
94
|
+
): WorkspaceSandboxProtection {
|
|
95
|
+
const configFile = profile.runtimeConfigFile;
|
|
96
|
+
const mcpFile = profile.mcpConfigFile;
|
|
97
|
+
|
|
98
|
+
// ── (a) Secret READ deny ─────────────────────────────────────────────────
|
|
99
|
+
// The credential-bearing config file + (if separate) the bearer-header MCP
|
|
100
|
+
// file. Nothing else is read-denied — the agent must still read its own
|
|
101
|
+
// instructions + workspace code.
|
|
102
|
+
const secretReadDenyGlobs: string[] = [configFile];
|
|
103
|
+
if (mcpFile !== null) {
|
|
104
|
+
secretReadDenyGlobs.push(mcpFile);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── (b) Own-instruction / agent-def EDIT deny ────────────────────────────
|
|
108
|
+
const protectedEditDenyGlobs: string[] = [
|
|
109
|
+
// The auto-loaded context file (AGENTS.md / CLAUDE.md) IS the agent's own
|
|
110
|
+
// system instructions.
|
|
111
|
+
profile.contextFile,
|
|
112
|
+
// Defense in depth: never let the agent rewrite its own credential config
|
|
113
|
+
// (e.g. to strip its own deny rules or exfiltrate the token).
|
|
114
|
+
configFile,
|
|
115
|
+
...XEMA_PRIVATE_INSTRUCTION_EDIT_GLOBS,
|
|
116
|
+
];
|
|
117
|
+
if (mcpFile !== null) {
|
|
118
|
+
protectedEditDenyGlobs.push(mcpFile);
|
|
119
|
+
}
|
|
120
|
+
if (profile.agentBundlesDir !== null) {
|
|
121
|
+
// The whole file-based sub-agent-definition slot.
|
|
122
|
+
protectedEditDenyGlobs.push(`${profile.agentBundlesDir}/**`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
secretReadDenyGlobs: dedupeStable(secretReadDenyGlobs),
|
|
127
|
+
protectedEditDenyGlobs: dedupeStable(protectedEditDenyGlobs),
|
|
128
|
+
};
|
|
129
|
+
}
|
package/src/workflow/index.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
9
9
|
|
|
10
10
|
export * from './lib/enums';
|
|
11
|
+
export * from './lib/workflow-control';
|
|
11
12
|
export * from './lib/temporal-namespace';
|
|
12
13
|
export * from './lib/platform-task-queue';
|
|
13
14
|
export * from './lib/errors';
|