@skyold/protocol-engine 0.1.0-beta.0 → 0.1.0-beta.2

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 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 are configured for public
29
- npm access, but no registry release has been made yet. Therefore:
28
+ The package and its normalized-contract dependency have a public
29
+ `0.1.0-beta.2` 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 availability, a versioned release, and provenance signing are
36
- not claimed until the release gate completes.
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, ignores additive fields
319
- in synchronous bodies and known stream frames, and never retains those unknown
320
- fields in normalized output. This lets a Provider add response metadata without
321
- breaking an otherwise compatible protocol implementation.
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
- still fail. An unknown stream event kind can alter lifecycle semantics, so it is
326
- not treated as a harmless additive field and fails unless the protocol Adapter
327
- explicitly supports it. Parser depth, body-size, URL, base64, index, usage, and
328
- terminal-consistency limits remain enforced.
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, and list result structure.
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 fields are rejected. Raw Provider
335
- extensions therefore cannot leak through an Adapter to Host egress, usage, or
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 | What it proves | What it does not prove |
375
- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
376
- | 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. |
377
- | 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. |
378
- | Architecture boundary | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts` | Engine dependency isolation, package surface, no raw-forward/direct Adapter bypass, Host neutrality. | Runtime semantic correctness by itself. |
379
- | 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. |
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. | Deployment, billing correctness, or a missing family. |
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 repository's opt-in certification Host drives this public Engine API and
392
- the built-in Adapter Registry directly; it does not enter through any product
393
- application route. A versioned JSON manifest supplies one or more cases for every
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
- 该包及其规范化协议依赖已经配置公开 npm 元数据,但尚未执行 registry 首次发布。因此,当前可以确认的是:
15
+ 该包及其规范化协议依赖已通过 `beta` dist-tag 公开发布为 `0.1.0-beta.2`。因此,当前可以确认的是:
16
16
 
17
17
  - 仓库内其他 package 可以独立嵌入并测试它;
18
18
  - 可以独立检查它的打包运行时边界;
19
19
  - 打包 tarball 已可由隔离的外部消费者安装与验证;
20
- - 在发布门禁完成前,不承诺公共 registry 可用性、版本化发布或来源签名。
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 校验并提取当前协议版本理解的字段,忽略同步 body 和已知 stream frame 中新增的附加字段,且不会把这些未知字段保留到规范化输出中。这样 Provider 新增响应元数据时,不会破坏原本兼容的协议实现。
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
- 兼容不代表把已知字段异常当作成功。已知字段缺失、类型错误、冲突、重复、不安全或语义无效仍然失败。未知 stream event kind 可能改变生命周期语义,不能视为无害附加字段;除非协议 Adapter 明确支持,否则必须失败。解析深度、body 大小、URLbase64index、usage 和终态一致性限制继续生效。
241
+ 随后,引擎会在向 Host 暴露结果前校验 Adapter 输出,包括同步/流式接受结果、终态结构与一致性、每个规范化流事件,以及异步启动、snapshot、列表和取消结果结构。规范化 eventmetadataterminalresult、usage、Responses 和异步任务对象仍是封闭 schema:未声明的派生事实会被拒绝。显式 Provider response envelope 不属于这个封闭投影;Host 可以只用规范化事实处理用量或结算,同时把权威 Provider 响应返回给同协议调用者。
237
242
 
238
- 随后,引擎会在向 Host 暴露结果前校验 Adapter 输出,包括同步/流式接受结果、终态结构与一致性、每个规范化流事件,以及异步启动、snapshot 和列表结果结构。规范化 event、metadata、terminal、result、usage、Responses 和异步任务对象都是封闭 schema:未声明字段会被拒绝。因此 Provider 原始扩展字段不能穿过 Adapter 泄漏到 Host egress、用量或结算流程。
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` | 独立构建,以及 Registry、binding、同步、流式、异步任务、校验和 Host 等价性测试。 | 具体 Adapter wire 格式、Host 持久化、真实网络。 |
257
- | 内置 conformance | `pnpm --filter @skyold/provider-adapters test -- src/conformance/runtime-conformance.test.ts` | 每个已注册内置协议族均通过公共 Engine API 和 fixture Transport 执行。 | Provider 凭证或当前远端行为。 |
258
- | 架构边界 | `pnpm --filter @tokenforge/acceptance test -- src/L1-kernel/import-boundary.test.ts` | 引擎依赖隔离、package surface、禁止 raw-forward/直接绕过 Adapter、Host 中立性。 | 单独证明运行时语义正确。 |
259
- | Host 集成 | 各嵌入应用的定向测试与仓库 `pnpm check` | 相应调用方的上下文绑定、流式、媒体持久化、用量与结算兼容。 | exact-SHA 部署或真实 Provider 可用性。 |
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` | 每个成功证据分片对应的当前、已认证 Provider 行为。 | 部署、计费正确性或缺失协议族。 |
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
- 仓库内的 opt-in 认证 Host 会直接驱动这套公共 Engine API 和内置 Adapter Registry,不经过任何产品应用的 route。版本化 JSON manifest 为 `engine.protocolFamilies` 返回的每个协议族提供一个或多个 case,其中包含已解析 target 与 capability 事实、规范化 task、密钥环境变量名、精确 Provider 域名,以及 suite 请求/时长上限。定价和 Provider 计费刻意不属于引擎契约或认证事实。
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 \
@@ -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
- return { apiVersion: PROTOCOL_ENGINE_API_VERSION };
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.0",
3
+ "version": "0.1.0-beta.2",
4
4
  "description": "Host-neutral execution kernel for normalized AI provider protocols.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -49,17 +49,18 @@
49
49
  "publishConfig": {
50
50
  "access": "public"
51
51
  },
52
+ "scripts": {
53
+ "build": "tsc -b",
54
+ "check": "pnpm build && pnpm test",
55
+ "prepack": "pnpm build",
56
+ "test": "vitest run"
57
+ },
52
58
  "dependencies": {
53
- "@skyold/model-protocol": "^0.1.0-beta.0"
59
+ "@skyold/model-protocol": "workspace:^"
54
60
  },
55
61
  "devDependencies": {
56
62
  "@types/node": "22.19.19",
57
63
  "typescript": "5.9.3",
58
64
  "vitest": "2.1.9"
59
- },
60
- "scripts": {
61
- "build": "tsc -b",
62
- "check": "pnpm build && pnpm test",
63
- "test": "vitest run"
64
65
  }
65
- }
66
+ }
package/LICENSE DELETED
@@ -1,201 +0,0 @@
1
- Apache License
2
- Version 2.0, January 2004
3
- http://www.apache.org/licenses/
4
-
5
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
-
7
- 1. Definitions.
8
-
9
- "License" shall mean the terms and conditions for use, reproduction,
10
- and distribution as defined by Sections 1 through 9 of this document.
11
-
12
- "Licensor" shall mean the copyright owner or entity authorized by
13
- the copyright owner that is granting the License.
14
-
15
- "Legal Entity" shall mean the union of the acting entity and all
16
- other entities that control, are controlled by, or are under common
17
- control with that entity. For the purposes of this definition,
18
- "control" means (i) the power, direct or indirect, to cause the
19
- direction or management of such entity, whether by contract or
20
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
- outstanding shares, or (iii) beneficial ownership of such entity.
22
-
23
- "You" (or "Your") shall mean an individual or Legal Entity
24
- exercising permissions granted by this License.
25
-
26
- "Source" form shall mean the preferred form for making modifications,
27
- including but not limited to software source code, documentation
28
- source, and configuration files.
29
-
30
- "Object" form shall mean any form resulting from mechanical
31
- transformation or translation of a Source form, including but
32
- not limited to compiled object code, generated documentation,
33
- and conversions to other media types.
34
-
35
- "Work" shall mean the work of authorship, whether in Source or
36
- Object form, made available under the License, as indicated by a
37
- copyright notice that is included in or attached to the work
38
- (an example is provided in the Appendix below).
39
-
40
- "Derivative Works" shall mean any work, whether in Source or Object
41
- form, that is based on (or derived from) the Work and for which the
42
- editorial revisions, annotations, elaborations, or other modifications
43
- represent, as a whole, an original work of authorship. For the purposes
44
- of this License, Derivative Works shall not include works that remain
45
- separable from, or merely link (or bind by name) to the interfaces of,
46
- the Work and Derivative Works thereof.
47
-
48
- "Contribution" shall mean any work of authorship, including
49
- the original version of the Work and any modifications or additions
50
- to that Work or Derivative Works thereof, that is intentionally
51
- submitted to Licensor for inclusion in the Work by the copyright owner
52
- or by an individual or Legal Entity authorized to submit on behalf of
53
- the copyright owner. For the purposes of this definition, "submitted"
54
- means any form of electronic, verbal, or written communication sent
55
- to the Licensor or its representatives, including but not limited to
56
- communication on electronic mailing lists, source code control systems,
57
- and issue tracking systems that are managed by, or on behalf of, the
58
- Licensor for the purpose of discussing and improving the Work, but
59
- excluding communication that is conspicuously marked or otherwise
60
- designated in writing by the copyright owner as "Not a Contribution."
61
-
62
- "Contributor" shall mean Licensor and any individual or Legal Entity
63
- on behalf of whom a Contribution has been received by Licensor and
64
- subsequently incorporated within the Work.
65
-
66
- 2. Grant of Copyright License. Subject to the terms and conditions of
67
- this License, each Contributor hereby grants to You a perpetual,
68
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
- copyright license to reproduce, prepare Derivative Works of,
70
- publicly display, publicly perform, sublicense, and distribute the
71
- Work and such Derivative Works in Source or Object form.
72
-
73
- 3. Grant of Patent License. Subject to the terms and conditions of
74
- this License, each Contributor hereby grants to You a perpetual,
75
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
- (except as stated in this section) patent license to make, have made,
77
- use, offer to sell, sell, import, and otherwise transfer the Work,
78
- where such license applies only to those patent claims licensable
79
- by such Contributor that are necessarily infringed by their
80
- Contribution(s) alone or by combination of their Contribution(s)
81
- with the Work to which such Contribution(s) was submitted. If You
82
- institute patent litigation against any entity (including a
83
- cross-claim or counterclaim in a lawsuit) alleging that the Work
84
- or a Contribution incorporated within the Work constitutes direct
85
- or contributory patent infringement, then any patent licenses
86
- granted to You under this License for that Work shall terminate
87
- as of the date such litigation is filed.
88
-
89
- 4. Redistribution. You may reproduce and distribute copies of the
90
- Work or Derivative Works thereof in any medium, with or without
91
- modifications, and in Source or Object form, provided that You
92
- meet the following conditions:
93
-
94
- (a) You must give any other recipients of the Work or
95
- Derivative Works a copy of this License; and
96
-
97
- (b) You must cause any modified files to carry prominent notices
98
- stating that You changed the files; and
99
-
100
- (c) You must retain, in the Source form of any Derivative Works
101
- that You distribute, all copyright, patent, trademark, and
102
- attribution notices from the Source form of the Work,
103
- excluding those notices that do not pertain to any part of
104
- the Derivative Works; and
105
-
106
- (d) If the Work includes a "NOTICE" text file as part of its
107
- distribution, then any Derivative Works that You distribute must
108
- include a readable copy of the attribution notices contained
109
- within such NOTICE file, excluding those notices that do not
110
- pertain to any part of the Derivative Works, in at least one
111
- of the following places: within a NOTICE text file distributed
112
- as part of the Derivative Works; within the Source form or
113
- documentation, if provided along with the Derivative Works; or,
114
- within a display generated by the Derivative Works, if and
115
- wherever such third-party notices normally appear. The contents
116
- of the NOTICE file are for informational purposes only and
117
- do not modify the License. You may add Your own attribution
118
- notices within Derivative Works that You distribute, alongside
119
- or as an addendum to the NOTICE text from the Work, provided
120
- that such additional attribution notices cannot be construed
121
- as modifying the License.
122
-
123
- You may add Your own copyright statement to Your modifications and
124
- may provide additional or different license terms and conditions
125
- for use, reproduction, or distribution of Your modifications, or
126
- for any such Derivative Works as a whole, provided Your use,
127
- reproduction, and distribution of the Work otherwise complies with
128
- the conditions stated in this License.
129
-
130
- 5. Submission of Contributions. Unless You explicitly state otherwise,
131
- any Contribution intentionally submitted for inclusion in the Work
132
- by You to the Licensor shall be under the terms and conditions of
133
- this License, without any additional terms or conditions.
134
- Notwithstanding the above, nothing herein shall supersede or modify
135
- the terms of any separate license agreement you may have executed
136
- with Licensor regarding such Contributions.
137
-
138
- 6. Trademarks. This License does not grant permission to use the trade
139
- names, trademarks, service marks, or product names of the Licensor,
140
- except as required for reasonable and customary use in describing the
141
- origin of the Work and reproducing the content of the NOTICE file.
142
-
143
- 7. Disclaimer of Warranty. Unless required by applicable law or
144
- agreed to in writing, Licensor provides the Work (and each
145
- Contributor provides its Contributions) on an "AS IS" BASIS,
146
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
- implied, including, without limitation, any warranties or conditions
148
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
- PARTICULAR PURPOSE. You are solely responsible for determining the
150
- appropriateness of using or redistributing the Work and assume any
151
- risks associated with Your exercise of permissions under this License.
152
-
153
- 8. Limitation of Liability. In no event and under no legal theory,
154
- whether in tort (including negligence), contract, or otherwise,
155
- unless required by applicable law (such as deliberate and grossly
156
- negligent acts) or agreed to in writing, shall any Contributor be
157
- liable to You for damages, including any direct, indirect, special,
158
- incidental, or consequential damages of any character arising as a
159
- result of this License or out of the use or inability to use the
160
- Work (including but not limited to damages for loss of goodwill,
161
- work stoppage, computer failure or malfunction, or any and all
162
- other commercial damages or losses), even if such Contributor
163
- has been advised of the possibility of such damages.
164
-
165
- 9. Accepting Warranty or Additional Liability. While redistributing
166
- the Work or Derivative Works thereof, You may choose to offer,
167
- and charge a fee for, acceptance of support, warranty, indemnity,
168
- or other liability obligations and/or rights consistent with this
169
- License. However, in accepting such obligations, You may act only
170
- on Your own behalf and on Your sole responsibility, not on behalf
171
- of any other Contributor, and only if You agree to indemnify,
172
- defend, and hold each Contributor harmless for any liability
173
- incurred by, or claims asserted against, such Contributor by reason
174
- of your accepting any such warranty or additional liability.
175
-
176
- END OF TERMS AND CONDITIONS
177
-
178
- APPENDIX: How to apply the Apache License to your work.
179
-
180
- To apply the Apache License to your work, attach the following
181
- boilerplate notice, with the fields enclosed by brackets "[]"
182
- replaced with your own identifying information. (Don't include
183
- the brackets!) The text should be enclosed in the appropriate
184
- comment syntax for the file format. We also recommend that a
185
- file or class name and description of purpose be included on the
186
- same "printed page" as the copyright notice for easier
187
- identification within third-party archives.
188
-
189
- Copyright 2026 TokenForge Contributors
190
-
191
- Licensed under the Apache License, Version 2.0 (the "License");
192
- you may not use this file except in compliance with the License.
193
- You may obtain a copy of the License at
194
-
195
- http://www.apache.org/licenses/LICENSE-2.0
196
-
197
- Unless required by applicable law or agreed to in writing, software
198
- distributed under the License is distributed on an "AS IS" BASIS,
199
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
- See the License for the specific language governing permissions and
201
- limitations under the License.