@skyold/protocol-engine 0.1.0-beta.0 → 0.1.0-beta.1
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 +53 -26
- package/README.zh-CN.md +20 -13
- package/dist/contracts.d.ts +4 -1
- package/dist/engine.js +48 -5
- package/dist/errors.d.ts +4 -0
- package/dist/errors.js +4 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -25,15 +25,16 @@ its own TypeScript build, its own test command, and one runtime dependency:
|
|
|
25
25
|
concrete Provider Adapters, storage, HTTP frameworks, credential stores, or
|
|
26
26
|
network clients.
|
|
27
27
|
|
|
28
|
-
The package and its normalized-contract dependency
|
|
29
|
-
|
|
28
|
+
The package and its normalized-contract dependency have a public
|
|
29
|
+
`0.1.0-beta.1` release under the `beta` dist-tag. Therefore:
|
|
30
30
|
|
|
31
31
|
- it is independently embeddable and testable by packages in this repository;
|
|
32
32
|
- its packed runtime surface is independently inspectable;
|
|
33
33
|
- packed tarballs can be installed and exercised by an isolated external
|
|
34
34
|
consumer;
|
|
35
|
-
- public registry
|
|
36
|
-
|
|
35
|
+
- public registry installation has been smoke-tested from an isolated consumer;
|
|
36
|
+
- exact-SHA CI for the release-preparation tree, downstream adoption,
|
|
37
|
+
provenance signing, and real-Provider acceptance remain separate gates.
|
|
37
38
|
|
|
38
39
|
The local V1 core implementation is complete against its frozen execution
|
|
39
40
|
boundary. Overall rollout certification is still incomplete: fixture-backed
|
|
@@ -302,6 +303,17 @@ selects the protocol Adapter, and returns one versioned execution start:
|
|
|
302
303
|
- `accepted-job` for durable asynchronous Provider acceptance;
|
|
303
304
|
- `rejected` for not-accepted or acceptance-unknown asynchronous starts.
|
|
304
305
|
|
|
306
|
+
When a Provider response was received, `providerResponse` carries its status,
|
|
307
|
+
headers, and body bytes as the authoritative result. Successfully parsed and
|
|
308
|
+
semantically validated non-failure stream events carry their exact SSE frame in
|
|
309
|
+
`providerWire`; normalized terminal and event fields are derived observations of
|
|
310
|
+
that same result. Provider errors, malformed or unknown frames, cancellation,
|
|
311
|
+
and bytes after a terminal never use raw passthrough and produce only bounded
|
|
312
|
+
safe egress. Caller-facing direct egress projects headers through
|
|
313
|
+
`projectProviderResponseHeadersForCallerV1`: connection, credential, cookie,
|
|
314
|
+
representation-length/encoding, caching, CORS, rate-limit, and security-policy
|
|
315
|
+
fields remain Host-owned and are not copied from upstream.
|
|
316
|
+
|
|
305
317
|
Use `engine.jobs.get`, `engine.jobs.list`, and `engine.jobs.cancel` for later
|
|
306
318
|
async-job operations. Persistence, polling, callbacks, materialization, usage,
|
|
307
319
|
and settlement remain Host responsibilities.
|
|
@@ -315,25 +327,36 @@ support `get` without pretending to support `list` or `cancel`.
|
|
|
315
327
|
### Provider response compatibility
|
|
316
328
|
|
|
317
329
|
Provider wire responses are an open-world boundary. An Adapter validates and
|
|
318
|
-
extracts the fields understood by its protocol version
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
330
|
+
extracts the fields understood by its protocol version as derived normalized
|
|
331
|
+
facts. For same-protocol egress, status, raw headers, and body bytes remain the
|
|
332
|
+
authoritative response. A recognized non-failure SSE event retains its exact
|
|
333
|
+
validated frame, including additive fields. Unknown event kinds, malformed
|
|
334
|
+
frames, Provider failures, cancellation, opaque passthrough, and post-terminal
|
|
335
|
+
bytes fail closed or use bounded normalized egress; they are never returned as
|
|
336
|
+
raw caller-visible wire. Headers cross a separate caller-safe projection so
|
|
337
|
+
transport or security metadata for one HTTP hop is not mistaken for another.
|
|
322
338
|
|
|
323
339
|
Compatibility does not turn malformed known facts into success. Missing,
|
|
324
340
|
mistyped, conflicting, duplicated, unsafe, or semantically invalid known fields
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
341
|
+
make the derived normalization fail. Parser depth, body-size, URL, base64, index,
|
|
342
|
+
usage, and terminal-consistency limits continue to protect normalization and
|
|
343
|
+
execution; transport or framing failures can of course prevent a complete
|
|
344
|
+
response from being received.
|
|
329
345
|
|
|
330
346
|
The Engine then validates Adapter outputs before exposing them to a Host. This
|
|
331
347
|
includes sync/stream acceptance, terminal structure and consistency, every
|
|
332
|
-
normalized stream event, plus async start, snapshot,
|
|
348
|
+
normalized stream event, plus async start, snapshot, list, and cancellation
|
|
349
|
+
result structure.
|
|
333
350
|
Normalized event, metadata, terminal, result, usage, Responses, and async-job
|
|
334
|
-
objects are closed schemas: undeclared
|
|
335
|
-
|
|
336
|
-
settlement
|
|
351
|
+
objects are closed schemas: undeclared derived facts are rejected. The explicit
|
|
352
|
+
Provider response envelope is not part of that closed projection. Hosts may use
|
|
353
|
+
only normalized facts for usage or settlement while returning the authoritative
|
|
354
|
+
Provider response to a same-protocol caller.
|
|
355
|
+
|
|
356
|
+
When an async follow-up receives a complete non-success Provider response, the
|
|
357
|
+
Engine error exposes that validated response in `providerResponse`. Hosts must
|
|
358
|
+
never serialize the whole Engine error or its diagnostic `cause`; they may
|
|
359
|
+
explicitly apply same-protocol egress policy to the response envelope.
|
|
337
360
|
|
|
338
361
|
Terminal results must match the requested Task kind, and duplicate result,
|
|
339
362
|
terminal, and Responses usage facts must agree when present. A custom Adapter
|
|
@@ -371,13 +394,13 @@ No test suite can mathematically guarantee all future Engine functionality.
|
|
|
371
394
|
TokenForge uses separate proof layers so a narrow unit test is not mistaken for
|
|
372
395
|
complete proxy evidence.
|
|
373
396
|
|
|
374
|
-
| Layer | Command
|
|
375
|
-
| --------------------------- |
|
|
376
|
-
| Engine package | `pnpm --dir packages/protocol-engine check`
|
|
377
|
-
| Built-in conformance | `pnpm --filter @skyold/provider-adapters test -- src/conformance/runtime-conformance.test.ts`
|
|
378
|
-
| Architecture boundary | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts` | Engine dependency isolation, package surface,
|
|
379
|
-
| Host integration | Targeted suites for each embedding application and repository `pnpm check`
|
|
380
|
-
| Real Provider certification | `pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run --env-file .env --ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS` | Current authenticated Provider behavior for every successful evidence shard.
|
|
397
|
+
| Layer | Command | What it proves | What it does not prove |
|
|
398
|
+
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
399
|
+
| Engine package | `pnpm --dir packages/protocol-engine check` | Independent build plus core Registry, binding, sync, stream, async-job, validation and Host-equivalence tests. | Concrete Adapter wire formats, Host persistence, live network. |
|
|
400
|
+
| Built-in conformance | `pnpm --filter @skyold/provider-adapters test -- src/conformance/runtime-conformance.test.ts` | Every registered built-in family executes through public Engine API with fixture Transports. | Provider credentials or current remote behavior. |
|
|
401
|
+
| Architecture boundary | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts` (TokenForge source monorepo) | Engine dependency isolation, package surface, explicit Provider response ownership through Adapters, Host neutrality. | Runtime semantic correctness by itself. |
|
|
402
|
+
| Host integration | Targeted suites for each embedding application and repository `pnpm check` | Caller-specific context binding, streams, media persistence, usage and settlement compatibility. | Exact deployed SHA or live Provider availability. |
|
|
403
|
+
| Real Provider certification | `pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run --env-file .env --ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS` (TokenForge source monorepo) | Current authenticated Provider behavior for every successful evidence shard. | Deployment, billing correctness, or a missing family. |
|
|
381
404
|
|
|
382
405
|
For a local Engine change, the minimum independent gate is the Engine package
|
|
383
406
|
command. A protocol or Adapter change must also pass built-in conformance. A
|
|
@@ -388,9 +411,11 @@ gates.
|
|
|
388
411
|
|
|
389
412
|
## Real-Provider certification
|
|
390
413
|
|
|
391
|
-
The
|
|
392
|
-
the built-in Adapter Registry directly; it does not
|
|
393
|
-
|
|
414
|
+
The TokenForge source monorepo ships the opt-in certification Host that drives
|
|
415
|
+
this public Engine API and the built-in Adapter Registry directly; it does not
|
|
416
|
+
enter through any product application route. The certification commands in this
|
|
417
|
+
section run in that monorepo, not in this repository. A versioned JSON manifest
|
|
418
|
+
supplies one or more cases for every
|
|
394
419
|
protocol family returned by `engine.protocolFamilies`, with resolved target and
|
|
395
420
|
capability facts, normalized tasks, secret environment-variable names, exact
|
|
396
421
|
Provider domains, and suite request/time ceilings. Pricing and Provider billing
|
|
@@ -434,6 +459,7 @@ Host can perform a one-request, read-only diagnostic through `engine.jobs.list`
|
|
|
434
459
|
without creating another task:
|
|
435
460
|
|
|
436
461
|
```bash
|
|
462
|
+
# Run in the TokenForge source monorepo (apps/acceptance), not in this repository.
|
|
437
463
|
pnpm --filter @tokenforge/acceptance diagnose:protocol-engine-real-async \
|
|
438
464
|
--manifest /protected/path/async-shard.json \
|
|
439
465
|
--env-file /protected/path/provider.env \
|
|
@@ -451,6 +477,7 @@ exhausted after the Provider had accepted the task, recover that exact submitted
|
|
|
451
477
|
task without creating another one:
|
|
452
478
|
|
|
453
479
|
```bash
|
|
480
|
+
# Run in the TokenForge source monorepo (apps/acceptance), not in this repository.
|
|
454
481
|
pnpm --filter @tokenforge/acceptance recover:protocol-engine-real-shard \
|
|
455
482
|
--manifest /protected/path/async-shard.json \
|
|
456
483
|
--failure-evidence /protected/path/polling-exhausted.json \
|
package/README.zh-CN.md
CHANGED
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
|
|
13
13
|
引擎是一个独立的 workspace package,提供唯一的包根 ESM API、独立的 TypeScript 构建和测试命令,且只有一个运行时依赖:`@skyold/model-protocol`。它不依赖任何 TokenForge Host、具体 Provider Adapter、存储、HTTP 框架、凭证仓库或网络客户端。
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
该包及其规范化协议依赖已通过 `beta` dist-tag 公开发布为 `0.1.0-beta.1`。因此,当前可以确认的是:
|
|
16
16
|
|
|
17
17
|
- 仓库内其他 package 可以独立嵌入并测试它;
|
|
18
18
|
- 可以独立检查它的打包运行时边界;
|
|
19
19
|
- 打包 tarball 已可由隔离的外部消费者安装与验证;
|
|
20
|
-
-
|
|
20
|
+
- 已从隔离消费者通过公共 registry 安装并完成运行 smoke;
|
|
21
|
+
- 发布准备树的 exact-SHA CI、下游采用、来源签名和真实 Provider 验收仍是独立门禁。
|
|
21
22
|
|
|
22
23
|
本地 V1 核心已经完成并冻结在既定执行边界内。整体上线认证尚未完成:九种内置协议族的 fixture conformance 已全部通过,当前已有七种协议的真实 Provider 成功证据;`openai-embeddings/v1` 和 `openai-images/v1` 仍待真实成功认证。公开发布、exact-SHA CI 和部署属于独立交付门禁,不是把 Host 业务逻辑加入引擎的理由。
|
|
23
24
|
|
|
@@ -225,17 +226,21 @@ try {
|
|
|
225
226
|
- `accepted-job`:Provider 已持久接受异步任务;
|
|
226
227
|
- `rejected`:异步启动未被接受,或接受状态未知。
|
|
227
228
|
|
|
229
|
+
只要已经收到 Provider 响应,`providerResponse` 就以权威结果的身份携带状态码、响应头和 body 原始字节。成功解析并通过语义校验的非失败流事件会通过 `providerWire` 携带其精确 SSE frame;规范化 terminal 和 event 是对同一结果的派生观察。Provider error、malformed/unknown frame、取消、opaque passthrough 和终态后的字节一律不得 raw passthrough,只能产生有界的安全下行。面向调用方的 direct egress 还会通过 `projectProviderResponseHeadersForCallerV1` 投影响应头:连接级、凭证、Cookie、表示长度/编码、缓存、CORS、限流和安全策略字段都由 Host 持有,不能从上游直接复制。
|
|
230
|
+
|
|
228
231
|
后续异步任务操作使用 `engine.jobs.get`、`engine.jobs.list` 和 `engine.jobs.cancel`。持久化、轮询、回调、物化、用量和结算仍是 Host 职责。
|
|
229
232
|
|
|
230
233
|
异步 Adapter factory 通过 `jobOperations` 声明支持的后续操作。引擎会在绑定 Transport 前,以 `PROTOCOL_ENGINE_CAPABILITY_UNSUPPORTED` 拒绝未声明的操作。因此协议可以保留真实生命周期差异:例如,一个协议可以支持 `get`,而不需要伪装成支持 `list` 或 `cancel`。
|
|
231
234
|
|
|
232
235
|
### Provider response 兼容原则
|
|
233
236
|
|
|
234
|
-
Provider wire response 是开放世界边界。Adapter
|
|
237
|
+
Provider wire response 是开放世界边界。Adapter 会把当前协议版本理解的字段提取为派生的规范化事实;对于同协议下行,状态码、原始响应头和 body 原始字节仍是权威结果。已识别的非失败 SSE 事件会保留其通过校验的精确 frame,包括附加字段。未知事件类型、malformed frame、Provider failure、取消、opaque passthrough 和终态后字节必须 fail closed 或使用有界的规范化下行,绝不能作为 raw wire 返回调用方。响应头另行经过 caller-safe 投影,避免把上一段 HTTP 连接或安全元数据误用于下一段连接。
|
|
238
|
+
|
|
239
|
+
兼容不代表把已知字段异常当作成功。已知字段缺失、类型错误、冲突、重复、不安全或语义无效会导致派生归一化失败。解析深度、body 大小、URL、base64、index、usage 和终态一致性限制继续保护归一化与执行;Transport 或 framing 失败则可能使完整响应根本无法被接收。
|
|
235
240
|
|
|
236
|
-
|
|
241
|
+
随后,引擎会在向 Host 暴露结果前校验 Adapter 输出,包括同步/流式接受结果、终态结构与一致性、每个规范化流事件,以及异步启动、snapshot、列表和取消结果结构。规范化 event、metadata、terminal、result、usage、Responses 和异步任务对象仍是封闭 schema:未声明的派生事实会被拒绝。显式 Provider response envelope 不属于这个封闭投影;Host 可以只用规范化事实处理用量或结算,同时把权威 Provider 响应返回给同协议调用者。
|
|
237
242
|
|
|
238
|
-
|
|
243
|
+
异步后续操作收到完整的非成功 Provider 响应时,引擎错误会在 `providerResponse` 中暴露经过校验的响应。Host 绝不能序列化整个引擎错误或诊断用 `cause`;只能显式地对该响应 envelope 应用同协议 egress 策略。
|
|
239
244
|
|
|
240
245
|
终态 result 必须与请求的 Task kind 一致;当 result、terminal 和 Responses 中重复出现 usage 事实时,它们必须一致。自定义 Adapter 不能依赖 TypeScript 类型断言绕过 V1 运行时契约,也不能让 Host egress 和结算观察到不同事实。
|
|
241
246
|
|
|
@@ -251,19 +256,19 @@ Provider wire response 是开放世界边界。Adapter 校验并提取当前协
|
|
|
251
256
|
|
|
252
257
|
任何测试套件都无法在数学意义上保证未来全部引擎功能。TokenForge 把证明拆分为不同层级,避免把一个狭窄单元测试误认为完整 proxy 证据。
|
|
253
258
|
|
|
254
|
-
| 层级 | 命令
|
|
255
|
-
| ------------------ |
|
|
256
|
-
| 引擎 package | `pnpm --dir packages/protocol-engine check`
|
|
257
|
-
| 内置 conformance | `pnpm --filter @skyold/provider-adapters test -- src/conformance/runtime-conformance.test.ts`
|
|
258
|
-
| 架构边界 | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts
|
|
259
|
-
| Host 集成 | 各嵌入应用的定向测试与仓库 `pnpm check`
|
|
260
|
-
| 真实 Provider 认证 | `pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run --env-file .env --ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS
|
|
259
|
+
| 层级 | 命令 | 能证明 | 不能证明 |
|
|
260
|
+
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | ----------------------------------------------- |
|
|
261
|
+
| 引擎 package | `pnpm --dir packages/protocol-engine check` | 独立构建,以及 Registry、binding、同步、流式、异步任务、校验和 Host 等价性测试。 | 具体 Adapter wire 格式、Host 持久化、真实网络。 |
|
|
262
|
+
| 内置 conformance | `pnpm --filter @skyold/provider-adapters test -- src/conformance/runtime-conformance.test.ts` | 每个已注册内置协议族均通过公共 Engine API 和 fixture Transport 执行。 | Provider 凭证或当前远端行为。 |
|
|
263
|
+
| 架构边界 | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts`(在 TokenForge 源 monorepo 中执行) | 引擎依赖隔离、package surface、经 Adapter 显式承载 Provider 响应、Host 中立性。 | 单独证明运行时语义正确。 |
|
|
264
|
+
| Host 集成 | 各嵌入应用的定向测试与仓库 `pnpm check` | 相应调用方的上下文绑定、流式、媒体持久化、用量与结算兼容。 | exact-SHA 部署或真实 Provider 可用性。 |
|
|
265
|
+
| 真实 Provider 认证 | `pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run --env-file .env --ack-real-provider-calls I_AUTHORIZE_REAL_PROVIDER_CALLS`(在 TokenForge 源 monorepo 中执行) | 每个成功证据分片对应的当前、已认证 Provider 行为。 | 部署、计费正确性或缺失协议族。 |
|
|
261
266
|
|
|
262
267
|
本地修改引擎时,最低独立门禁是引擎 package 命令。协议或 Adapter 变更还必须通过内置 conformance。Host 集成变更还必须通过受影响 Host 的测试。只有 Registry 清单中所有协议族都取得真实 Provider 成功证据,且实际交付的 exact SHA 通过 CI 和部署门禁,整个目标才算完成。
|
|
263
268
|
|
|
264
269
|
## 真实 Provider 认证
|
|
265
270
|
|
|
266
|
-
|
|
271
|
+
TokenForge 源 monorepo 内的 opt-in 认证 Host 会直接驱动这套公共 Engine API 和内置 Adapter Registry,不经过任何产品应用的 route。本节的认证命令都在该 monorepo 中执行,不在本仓库执行。版本化 JSON manifest 为 `engine.protocolFamilies` 返回的每个协议族提供一个或多个 case,其中包含已解析 target 与 capability 事实、规范化 task、密钥环境变量名、精确 Provider 域名,以及 suite 请求/时长上限。定价和 Provider 计费刻意不属于引擎契约或认证事实。
|
|
267
272
|
|
|
268
273
|
默认只做离线校验:
|
|
269
274
|
|
|
@@ -286,6 +291,7 @@ pnpm certify:protocol-engine-real --manifest /protected/path/suite.json --run \
|
|
|
286
291
|
对于已被接受且支持 list 的异步 target,Acceptance Host 可以通过 `engine.jobs.list` 执行一次只读诊断,不会创建新任务:
|
|
287
292
|
|
|
288
293
|
```bash
|
|
294
|
+
# 在 TokenForge 源 monorepo(apps/acceptance)中执行,不在本仓库执行。
|
|
289
295
|
pnpm --filter @tokenforge/acceptance diagnose:protocol-engine-real-async \
|
|
290
296
|
--manifest /protected/path/async-shard.json \
|
|
291
297
|
--env-file /protected/path/provider.env \
|
|
@@ -298,6 +304,7 @@ pnpm --filter @tokenforge/acceptance diagnose:protocol-engine-real-async \
|
|
|
298
304
|
当支持 list 的异步分片已经被 Provider 接受、但认证仅因有界轮询耗尽而停止时,可以恢复这一笔已提交任务,而不创建第二笔任务:
|
|
299
305
|
|
|
300
306
|
```bash
|
|
307
|
+
# 在 TokenForge 源 monorepo(apps/acceptance)中执行,不在本仓库执行。
|
|
301
308
|
pnpm --filter @tokenforge/acceptance recover:protocol-engine-real-shard \
|
|
302
309
|
--manifest /protected/path/async-shard.json \
|
|
303
310
|
--failure-evidence /protected/path/polling-exhausted.json \
|
package/dist/contracts.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdapterTerminalOutcome, NormalizedStreamEventV1, PreparedMediaExecutionInputV2, ProtocolFamily, ProviderAsyncJobListInputV2, ProviderAsyncJobSnapshotV2, ProviderAsyncJobAdapterFactory, ProviderAcceptanceFacts, ProviderAdapterFactory, ProviderDispatchTargetV2, ProviderResponseMetadata, ProviderTransport, TaskEnvelopeV2 } from '@skyold/model-protocol';
|
|
1
|
+
import type { AdapterTerminalOutcome, NormalizedStreamEventV1, PreparedMediaExecutionInputV2, ProtocolFamily, ProviderAsyncJobListInputV2, ProviderAsyncJobSnapshotV2, ProviderAsyncJobAdapterFactory, ProviderAcceptanceFacts, ProviderAdapterFactory, ProviderDispatchTargetV2, ProviderResponseMetadata, ProviderWireResponseV1, ProviderTransport, TaskEnvelopeV2 } from '@skyold/model-protocol';
|
|
2
2
|
export declare const PROTOCOL_ENGINE_API_VERSION: "tokenforge-protocol-engine/v1";
|
|
3
3
|
export type ProtocolEngineApiVersion = typeof PROTOCOL_ENGINE_API_VERSION;
|
|
4
4
|
export type ProtocolEngineDispatchTargetV1 = ProviderDispatchTargetV2;
|
|
@@ -22,6 +22,7 @@ interface ProtocolEngineExecutionBaseV1 {
|
|
|
22
22
|
readonly apiVersion: ProtocolEngineApiVersion;
|
|
23
23
|
readonly acceptance: ProviderAcceptanceFacts;
|
|
24
24
|
readonly responseMetadata: ProviderResponseMetadata;
|
|
25
|
+
readonly providerResponse?: ProviderWireResponseV1;
|
|
25
26
|
}
|
|
26
27
|
export interface ProtocolEngineResponseV1 extends ProtocolEngineExecutionBaseV1 {
|
|
27
28
|
readonly kind: 'response';
|
|
@@ -70,11 +71,13 @@ export interface ProtocolEngineAsyncJobObservationV1 {
|
|
|
70
71
|
}
|
|
71
72
|
export interface ProtocolEngineAsyncJobListResultV1 {
|
|
72
73
|
readonly apiVersion: ProtocolEngineApiVersion;
|
|
74
|
+
readonly providerResponse?: ProviderWireResponseV1;
|
|
73
75
|
readonly items: readonly ProviderAsyncJobSnapshotV2[];
|
|
74
76
|
readonly total: number;
|
|
75
77
|
}
|
|
76
78
|
export interface ProtocolEngineAsyncJobCancellationV1 {
|
|
77
79
|
readonly apiVersion: ProtocolEngineApiVersion;
|
|
80
|
+
readonly providerResponse?: ProviderWireResponseV1;
|
|
78
81
|
}
|
|
79
82
|
export interface ProtocolEngineAsyncJobsV1 {
|
|
80
83
|
get(input: ProtocolEngineAsyncJobInputV1): Promise<ProtocolEngineAsyncJobObservationV1>;
|
package/dist/engine.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { providerCapabilityFromTarget, validateProviderDispatchInputV2, validateProviderAsyncJobList, validateProviderAsyncJobSnapshot, validateProviderAsyncJobStart, validateProviderExecutionStart, } from '@skyold/model-protocol';
|
|
1
|
+
import { isProviderWireResponseErrorV1, providerCapabilityFromTarget, validateProviderDispatchInputV2, validateProviderAsyncJobList, validateProviderAsyncJobSnapshot, validateProviderAsyncJobStart, validateProviderExecutionStart, validateProviderWireResponseV1, } from '@skyold/model-protocol';
|
|
2
2
|
import { PROTOCOL_ENGINE_API_VERSION, } from './contracts.js';
|
|
3
3
|
import { PROTOCOL_ENGINE_ERROR_CODES_V1, ProtocolEngineErrorV1, isProtocolEngineErrorV1, } from './errors.js';
|
|
4
4
|
export function createProtocolEngineV1(input) {
|
|
@@ -28,14 +28,23 @@ export function createProtocolEngineV1(input) {
|
|
|
28
28
|
const result = validateAdapterOutput(() => validateProviderAsyncJobList(adapterResult));
|
|
29
29
|
return {
|
|
30
30
|
apiVersion: PROTOCOL_ENGINE_API_VERSION,
|
|
31
|
+
...(result.providerResponse === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { providerResponse: result.providerResponse }),
|
|
31
34
|
items: result.items,
|
|
32
35
|
total: result.total,
|
|
33
36
|
};
|
|
34
37
|
},
|
|
35
38
|
async cancel(jobInput) {
|
|
36
39
|
const adapter = await bindAsyncJobAdapter(input.registry, jobInput.binding, jobInput.signal, protocolFamilies, 'cancel');
|
|
37
|
-
await callAdapter(() => adapter.cancel(jobInput.providerJobId, jobInput.signal), jobInput.signal);
|
|
38
|
-
|
|
40
|
+
const adapterResult = await callAdapter(() => adapter.cancel(jobInput.providerJobId, jobInput.signal), jobInput.signal);
|
|
41
|
+
const providerResponse = adapterResult === undefined
|
|
42
|
+
? undefined
|
|
43
|
+
: validateAdapterOutput(() => validateProviderWireResponseV1(adapterResult));
|
|
44
|
+
return {
|
|
45
|
+
apiVersion: PROTOCOL_ENGINE_API_VERSION,
|
|
46
|
+
...(providerResponse === undefined ? {} : { providerResponse }),
|
|
47
|
+
};
|
|
39
48
|
},
|
|
40
49
|
},
|
|
41
50
|
async execute(executionInput) {
|
|
@@ -76,6 +85,9 @@ export function createProtocolEngineV1(input) {
|
|
|
76
85
|
kind: 'response',
|
|
77
86
|
acceptance: started.acceptance,
|
|
78
87
|
responseMetadata: started.responseMetadata,
|
|
88
|
+
...(started.providerResponse === undefined
|
|
89
|
+
? {}
|
|
90
|
+
: { providerResponse: started.providerResponse }),
|
|
79
91
|
terminal: started.terminal,
|
|
80
92
|
};
|
|
81
93
|
return result;
|
|
@@ -180,6 +192,9 @@ async function executeAsync(registry, executionInput, binding, capability) {
|
|
|
180
192
|
kind: 'accepted-job',
|
|
181
193
|
acceptance: started.acceptance,
|
|
182
194
|
responseMetadata: started.responseMetadata,
|
|
195
|
+
...(started.providerResponse === undefined
|
|
196
|
+
? {}
|
|
197
|
+
: { providerResponse: started.providerResponse }),
|
|
183
198
|
providerJobId: started.providerJobId,
|
|
184
199
|
};
|
|
185
200
|
}
|
|
@@ -188,6 +203,9 @@ async function executeAsync(registry, executionInput, binding, capability) {
|
|
|
188
203
|
kind: 'rejected',
|
|
189
204
|
acceptance: started.acceptance,
|
|
190
205
|
responseMetadata: started.responseMetadata,
|
|
206
|
+
...(started.providerResponse === undefined
|
|
207
|
+
? {}
|
|
208
|
+
: { providerResponse: started.providerResponse }),
|
|
191
209
|
failureCode: started.failureCode,
|
|
192
210
|
retryable: started.retryable,
|
|
193
211
|
};
|
|
@@ -298,20 +316,44 @@ function abortAdapter(abort, reason) {
|
|
|
298
316
|
}
|
|
299
317
|
function wrapOperationError(code, cause, signal) {
|
|
300
318
|
if (isProtocolEngineErrorV1(cause))
|
|
301
|
-
return cause;
|
|
319
|
+
return validateProtocolEngineError(cause);
|
|
302
320
|
if (signal.aborted || (cause instanceof Error && cause.name === 'AbortError')) {
|
|
303
321
|
return wrapProtocolEngineError(PROTOCOL_ENGINE_ERROR_CODES_V1.ABORTED, cause);
|
|
304
322
|
}
|
|
323
|
+
if (isProviderWireResponseErrorV1(cause)) {
|
|
324
|
+
try {
|
|
325
|
+
const providerResponse = validateProviderWireResponseV1(cause.providerResponse);
|
|
326
|
+
return new ProtocolEngineErrorV1(code, { cause, providerResponse });
|
|
327
|
+
}
|
|
328
|
+
catch (validationCause) {
|
|
329
|
+
return wrapProtocolEngineError(PROTOCOL_ENGINE_ERROR_CODES_V1.ADAPTER_OUTPUT_INVALID, validationCause, adapterOutputCompatibilityMessage(validationCause));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
305
332
|
return wrapProtocolEngineError(code, cause);
|
|
306
333
|
}
|
|
307
334
|
function wrapProtocolEngineError(code, cause, compatibilityMessage) {
|
|
308
335
|
if (isProtocolEngineErrorV1(cause))
|
|
309
|
-
return cause;
|
|
336
|
+
return validateProtocolEngineError(cause);
|
|
310
337
|
return new ProtocolEngineErrorV1(code, {
|
|
311
338
|
cause,
|
|
312
339
|
...(compatibilityMessage === undefined ? {} : { compatibilityMessage }),
|
|
313
340
|
});
|
|
314
341
|
}
|
|
342
|
+
function validateProtocolEngineError(cause) {
|
|
343
|
+
if (cause.providerResponse === undefined)
|
|
344
|
+
return cause;
|
|
345
|
+
try {
|
|
346
|
+
validateProviderWireResponseV1(cause.providerResponse);
|
|
347
|
+
return cause;
|
|
348
|
+
}
|
|
349
|
+
catch (validationCause) {
|
|
350
|
+
const compatibilityMessage = adapterOutputCompatibilityMessage(validationCause);
|
|
351
|
+
return new ProtocolEngineErrorV1(PROTOCOL_ENGINE_ERROR_CODES_V1.ADAPTER_OUTPUT_INVALID, {
|
|
352
|
+
cause: validationCause,
|
|
353
|
+
...(compatibilityMessage === undefined ? {} : { compatibilityMessage }),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
315
357
|
function protocolEngineError(code, compatibilityMessage) {
|
|
316
358
|
return new ProtocolEngineErrorV1(code, {
|
|
317
359
|
...(compatibilityMessage === undefined ? {} : { compatibilityMessage }),
|
|
@@ -339,6 +381,7 @@ const ADAPTER_OUTPUT_COMPATIBILITY_MESSAGES = new Set([
|
|
|
339
381
|
'ADAPTER_ASYNC_START_INVALID',
|
|
340
382
|
'ADAPTER_ASYNC_JOB_SNAPSHOT_INVALID',
|
|
341
383
|
'ADAPTER_ASYNC_JOB_LIST_INVALID',
|
|
384
|
+
'ADAPTER_PROVIDER_RESPONSE_INVALID',
|
|
342
385
|
]);
|
|
343
386
|
function inputCompatibilityMessage(cause) {
|
|
344
387
|
return cause instanceof Error && INPUT_COMPATIBILITY_MESSAGES.has(cause.message)
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ProtocolEngineApiVersion } from './contracts.js';
|
|
2
|
+
import type { ProviderWireResponseV1 } from '@skyold/model-protocol';
|
|
2
3
|
export declare const PROTOCOL_ENGINE_ERROR_CODES_V1: Readonly<{
|
|
3
4
|
readonly REGISTRY_INVALID: "PROTOCOL_ENGINE_REGISTRY_INVALID";
|
|
4
5
|
readonly PROTOCOL_FAMILY_UNAVAILABLE: "PROTOCOL_ENGINE_PROTOCOL_FAMILY_UNAVAILABLE";
|
|
@@ -17,6 +18,8 @@ export type ProtocolEngineErrorPhaseV1 = 'configuration' | 'input' | 'transport'
|
|
|
17
18
|
export interface ProtocolEngineErrorOptionsV1 {
|
|
18
19
|
/** Internal diagnostic cause. Hosts must never serialize it to clients. */
|
|
19
20
|
readonly cause?: unknown;
|
|
21
|
+
/** Complete Provider response available for explicit same-protocol Host egress. */
|
|
22
|
+
readonly providerResponse?: ProviderWireResponseV1;
|
|
20
23
|
/**
|
|
21
24
|
* Temporary compatibility message for Hosts that still classify historical
|
|
22
25
|
* fixed-runtime failures by Error.message. New integrations must use code.
|
|
@@ -33,6 +36,7 @@ export declare class ProtocolEngineErrorV1 extends Error {
|
|
|
33
36
|
readonly apiVersion: ProtocolEngineApiVersion;
|
|
34
37
|
readonly code: ProtocolEngineErrorCodeV1;
|
|
35
38
|
readonly phase: ProtocolEngineErrorPhaseV1;
|
|
39
|
+
readonly providerResponse?: ProviderWireResponseV1;
|
|
36
40
|
constructor(code: ProtocolEngineErrorCodeV1, options?: ProtocolEngineErrorOptionsV1);
|
|
37
41
|
}
|
|
38
42
|
export declare function isProtocolEngineErrorV1(error: unknown): error is ProtocolEngineErrorV1;
|
package/dist/errors.js
CHANGED
|
@@ -22,11 +22,15 @@ export class ProtocolEngineErrorV1 extends Error {
|
|
|
22
22
|
apiVersion = PROTOCOL_ENGINE_API_VERSION;
|
|
23
23
|
code;
|
|
24
24
|
phase;
|
|
25
|
+
providerResponse;
|
|
25
26
|
constructor(code, options = {}) {
|
|
26
27
|
super(options.compatibilityMessage ?? code, { cause: options.cause });
|
|
27
28
|
this.name = 'ProtocolEngineErrorV1';
|
|
28
29
|
this.code = code;
|
|
29
30
|
this.phase = phaseForCode(code);
|
|
31
|
+
if (options.providerResponse !== undefined) {
|
|
32
|
+
this.providerResponse = options.providerResponse;
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
export function isProtocolEngineErrorV1(error) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skyold/protocol-engine",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.1",
|
|
4
4
|
"description": "Host-neutral execution kernel for normalized AI provider protocols.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@skyold/model-protocol": "^0.1.0-beta.
|
|
53
|
+
"@skyold/model-protocol": "^0.1.0-beta.1"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@types/node": "22.19.19",
|