workspai 0.75.2 → 0.77.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.
Files changed (118) hide show
  1. package/README.md +2 -2
  2. package/contracts/agent-framework-capabilities.v1.json +2 -2
  3. package/contracts/cli-runtime-command-inventory.v1.snapshot.json +12 -0
  4. package/contracts/create-planner-capabilities.v1.json +48 -0
  5. package/contracts/extension-cli-compatibility.v1.json +4 -2
  6. package/contracts/published-contract-catalog.v1.json +10 -0
  7. package/contracts/runtime-command-surface.v1.json +5 -1
  8. package/contracts/workspace-intelligence/agent-framework-admission-candidate.v2.json +217 -0
  9. package/contracts/workspace-intelligence/agent-framework-conformance-report.v2.json +274 -0
  10. package/contracts/workspace-intelligence/workspace-model.v1.json +4 -0
  11. package/contracts/workspace-intelligence-architecture.v1.json +7 -3
  12. package/dist/analyze-TJM66CPF.js +1 -0
  13. package/dist/autopilot-release-C7Q4OTPY.js +1 -0
  14. package/dist/capabilities-command-TIBIKWPK.js +1 -0
  15. package/dist/{chunk-K52IQX3J.js → chunk-223SWPTL.js} +1 -1
  16. package/dist/chunk-3EMAC3MF.js +4 -0
  17. package/dist/{chunk-GDOO2YZW.js → chunk-3EPR3D5Q.js} +1 -1
  18. package/dist/chunk-3SQIV3F4.js +1 -0
  19. package/dist/{chunk-ZI54C2AO.js → chunk-475GQJ2D.js} +1 -1
  20. package/dist/{chunk-P36TACKN.js → chunk-47UR6ZH4.js} +1 -1
  21. package/dist/{chunk-VHMDWRDG.js → chunk-4BPZJR4Z.js} +4 -4
  22. package/dist/{chunk-VWBWDDU2.js → chunk-4CXV3PPV.js} +1 -1
  23. package/dist/chunk-4ZUBPFSO.js +6 -0
  24. package/dist/chunk-5KXOV7W4.js +1 -0
  25. package/dist/chunk-6BO4QHKA.js +1 -0
  26. package/dist/{chunk-OA2YJN22.js → chunk-DWDYLKVW.js} +1 -1
  27. package/dist/{chunk-KDVNRNDU.js → chunk-DWKCFHHL.js} +1 -1
  28. package/dist/{chunk-EP7SUMAX.js → chunk-E7WJ2QX7.js} +1 -1
  29. package/dist/{chunk-FNVWDXK2.js → chunk-GLBFY7AB.js} +5 -5
  30. package/dist/{chunk-ZGVODK5C.js → chunk-HRVHQBTA.js} +1 -1
  31. package/dist/{chunk-WHBKKJ7L.js → chunk-IAXRGH4Q.js} +1 -1
  32. package/dist/chunk-IDETNYCF.js +1 -0
  33. package/dist/chunk-IHWS3ALP.js +2 -0
  34. package/dist/{chunk-FZWLWFR4.js → chunk-J2MPBSQJ.js} +1 -1
  35. package/dist/{chunk-PFAGXI3G.js → chunk-JVZA35OL.js} +1 -1
  36. package/dist/chunk-LUPKDFQ3.js +7 -0
  37. package/dist/chunk-N7DOKE7V.js +2 -0
  38. package/dist/chunk-O3TVVQ6X.js +6 -0
  39. package/dist/chunk-P5LI5DL3.js +1 -0
  40. package/dist/{chunk-4JXJRIJM.js → chunk-QCX6BO2M.js} +1 -1
  41. package/dist/chunk-SC43A6LH.js +97 -0
  42. package/dist/chunk-SEZTSMRR.js +1 -0
  43. package/dist/{chunk-COOVCORC.js → chunk-SVO2GGTV.js} +1 -1
  44. package/dist/{chunk-5K4O3TAW.js → chunk-TGNPMOM6.js} +1 -1
  45. package/dist/chunk-UNEK3VVR.js +2684 -0
  46. package/dist/{chunk-IKBE3HOH.js → chunk-UP3UWAJV.js} +1 -1
  47. package/dist/{chunk-LFY7KTPD.js → chunk-UTIK3K3M.js} +1 -1
  48. package/dist/{chunk-WXUCAVFT.js → chunk-V6HVDVKJ.js} +8 -8
  49. package/dist/chunk-WC2K5QFI.js +10 -0
  50. package/dist/{chunk-DTNOF3MP.js → chunk-X5XFT5UZ.js} +1 -1
  51. package/dist/{chunk-ADHX7D3F.js → chunk-ZIWWE4BQ.js} +1 -1
  52. package/dist/{create-ZFSFWAW3.js → create-YPAZJYCE.js} +1 -1
  53. package/dist/{doctor-4KL4XUCS.js → doctor-W5PXJ5FL.js} +1 -1
  54. package/dist/{goal-lifecycle-EXQZD7IA.js → goal-lifecycle-5R55CA7H.js} +1 -1
  55. package/dist/{goal-pack-DKJU2DEP.js → goal-pack-IS6GGJPH.js} +1 -1
  56. package/dist/index.d.ts +12 -6
  57. package/dist/index.js +181 -171
  58. package/dist/{pipeline-4GVAOCEN.js → pipeline-LW3SW2WT.js} +1 -1
  59. package/dist/platform-capabilities-RWOWND5Q.js +1 -0
  60. package/dist/{project-agent-entry-7QKNEEXP.js → project-agent-entry-5TYKK5IV.js} +1 -1
  61. package/dist/{project-intelligence-lens-IHRYCLNU.js → project-intelligence-lens-TEOSTKNU.js} +1 -1
  62. package/dist/{project-test-coverage-3P5FH6E5.js → project-test-coverage-T4TIWGKN.js} +1 -1
  63. package/dist/proof-carrying-change-2TKRADCW.js +1 -0
  64. package/dist/{pythonRapidkitExec-UMFLDIYR.js → pythonRapidkitExec-S35OEIEF.js} +1 -1
  65. package/dist/{verified-goal-XI4RQESU.js → verified-goal-RN4TOV34.js} +1 -1
  66. package/dist/{workspace-EYXAKMVI.js → workspace-A33YMF2N.js} +1 -1
  67. package/dist/{workspace-agent-sync-SN55KT6N.js → workspace-agent-sync-VCA4G4DT.js} +1 -1
  68. package/dist/{workspace-context-PW6ESEHH.js → workspace-context-YBWLSKR6.js} +1 -1
  69. package/dist/{workspace-contract-FPLFL4G3.js → workspace-contract-3QUZDBMM.js} +1 -1
  70. package/dist/{workspace-explain-TVQ5IF5D.js → workspace-explain-NXYRAQFI.js} +1 -1
  71. package/dist/{workspace-foundation-DWIJPZRH.js → workspace-foundation-DZ52PECX.js} +1 -1
  72. package/dist/{workspace-graph-stream-RUTNWLQB.js → workspace-graph-stream-4VT46HZ7.js} +1 -1
  73. package/dist/{workspace-intelligence-4CA2CSPZ.js → workspace-intelligence-FKYQQDJ6.js} +1 -1
  74. package/dist/{workspace-intelligence-runner-VM5SXXAT.js → workspace-intelligence-runner-3K2JZH6Q.js} +1 -1
  75. package/dist/{workspace-knowledge-graph-JXN2ZOAF.js → workspace-knowledge-graph-CPBKNXQI.js} +1 -1
  76. package/dist/workspace-knowledge-graph-snapshot-67XDXUOT.js +1 -0
  77. package/dist/{workspace-mcp-serve-4WN4CSD7.js → workspace-mcp-serve-UXPKX7K6.js} +1 -1
  78. package/dist/{workspace-model-D4IECQML.js → workspace-model-POCPMNRT.js} +1 -1
  79. package/dist/{workspace-onboarding-6XWDKLM5.js → workspace-onboarding-KLG3GG7S.js} +1 -1
  80. package/dist/{workspace-registry-summary-Y3ET36NH.js → workspace-registry-summary-M4WMFDQ4.js} +1 -1
  81. package/dist/{workspace-repair-engine-HBUXRHDY.js → workspace-repair-engine-2VGN42QD.js} +1 -1
  82. package/dist/workspace-run-5YQR7GEZ.js +1 -0
  83. package/dist/{workspace-verify-OOAZ3NMZ.js → workspace-verify-PLAKTF3J.js} +1 -1
  84. package/dist/{workspace-watch-BNM5B6HU.js → workspace-watch-VNU4KVYN.js} +1 -1
  85. package/docs/README.md +4 -2
  86. package/docs/agent-framework-adapters.md +168 -39
  87. package/docs/ci-workflows.md +40 -22
  88. package/docs/commands-reference.md +19 -8
  89. package/docs/create-planner-capabilities.md +1 -0
  90. package/docs/creating-workspaces-and-projects.md +28 -9
  91. package/docs/doctor-command.md +9 -4
  92. package/docs/model-gateways.md +204 -0
  93. package/docs/native-kit-baselines.md +4 -2
  94. package/docs/workspace-run.md +3 -1
  95. package/package.json +5 -1
  96. package/scripts/enterprise-package-smoke.mjs +200 -5
  97. package/dist/analyze-6UEBC5OJ.js +0 -1
  98. package/dist/autopilot-release-H5TO6NYY.js +0 -1
  99. package/dist/capabilities-command-VK3LQLP2.js +0 -1
  100. package/dist/chunk-3N27P3KO.js +0 -7
  101. package/dist/chunk-3RNWBFRP.js +0 -4
  102. package/dist/chunk-66O62XNJ.js +0 -97
  103. package/dist/chunk-7NKM6EHE.js +0 -7
  104. package/dist/chunk-BR6QIYQS.js +0 -1
  105. package/dist/chunk-DFREJHG7.js +0 -1
  106. package/dist/chunk-E42IFQFR.js +0 -6
  107. package/dist/chunk-EWD6QTAM.js +0 -2
  108. package/dist/chunk-G5UCEXP6.js +0 -1
  109. package/dist/chunk-JKAAKE4Z.js +0 -6
  110. package/dist/chunk-LARREYZN.js +0 -1
  111. package/dist/chunk-RGNCL5OB.js +0 -1
  112. package/dist/chunk-SZHRPELL.js +0 -1
  113. package/dist/chunk-VXFIMEC6.js +0 -2
  114. package/dist/chunk-X7OI24LO.js +0 -418
  115. package/dist/platform-capabilities-DB4BZPTD.js +0 -1
  116. package/dist/proof-carrying-change-O37XUPIZ.js +0 -1
  117. package/dist/workspace-knowledge-graph-snapshot-KBNUXA64.js +0 -1
  118. package/dist/workspace-run-VN7KV4BU.js +0 -1
@@ -0,0 +1,204 @@
1
+ # AI Gateway
2
+
3
+ AI Gateway is a first-class Workspai project category for unified model access
4
+ and routing. It is not an Agent Framework.
5
+
6
+ ## Why Gateway is not Agent Framework
7
+
8
+ Agent Framework owns agent loops, tools, handoffs, state, and orchestration.
9
+ AI Gateway owns model and provider access, routing preferences, fallbacks,
10
+ privacy policy, and normalized transport behavior.
11
+
12
+ A model identifier is runtime configuration, not a project kit. Workspai owns
13
+ project generation, governance, context, lifecycle, verification, workspace
14
+ integration, and proof. OpenRouter owns provider and model routing. Generated
15
+ projects do not reimplement OpenRouter's routing algorithms.
16
+
17
+ Future agent-to-gateway composition should consume the generated Model Gateway
18
+ port. Do not copy these templates into an Agent Framework kit.
19
+
20
+ ## Current kits
21
+
22
+ These kits ship as source-ready. That is not Workspai release admission and
23
+ does not label them `stable` or `qualified`. SDK `1.x` stability is an upstream
24
+ classification, not a Workspai release gate.
25
+
26
+ | Kit | Runtime | Official Client SDK | Reviewed |
27
+ | --- | --- | --- | --- |
28
+ | `gateway.openrouter.typescript` | Node.js `>=20` | `@openrouter/sdk` `1.3.11` | 2026-09-21 |
29
+ | `gateway.openrouter.python` | Python `>=3.10` | `openrouter` `1.2.11` | 2026-09-21 |
30
+
31
+ Aliases: `openrouter.typescript`, `gateway.openrouter.ts`, `openrouter-typescript`,
32
+ and the matching Python aliases. Bare `openrouter` is not an alias.
33
+
34
+ Interactive Create shows **AI Gateway** with the hint **Unified model access
35
+ and routing** after at least one gateway kit is registered. Labels are:
36
+
37
+ - `AI Gateway · OpenRouter · TypeScript`
38
+ - `AI Gateway · OpenRouter · Python`
39
+
40
+ ```bash
41
+ npx workspai create project gateway.openrouter.typescript <name>
42
+ npx workspai create project gateway.openrouter.python <name>
43
+ ```
44
+
45
+ Create does not install dependencies, call a model, or write credentials.
46
+
47
+ Attach is not supported for AI Gateway in this release. Do not use
48
+ `agent framework attach` or any gateway-specific attach command. Adopt an
49
+ existing OpenRouter project only by creating a new gateway kit or by adding
50
+ the generated starter beside user-owned code. Repeated Create is not Attach.
51
+
52
+ ## Version upgrades
53
+
54
+ Exact SDK pins live only in
55
+ [`version-baselines.v1.json`](../src/model-gateways/version-baselines.v1.json).
56
+ `releaseChannel: stable` means the upstream SDK release is stable. It does not
57
+ mean the Workspai adapter passed Linux, macOS, and Windows qualification.
58
+
59
+ The supported upgrade path is read-only until a human reviews the proposal:
60
+
61
+ ```text
62
+ discover → propose → review → qualify → merge
63
+ ```
64
+
65
+ ```bash
66
+ npm --workspace workspai run propose:model-gateway:versions -- \
67
+ --baseline src/model-gateways/version-baselines.v1.json \
68
+ --report test-results/model-gateway-version-discovery.json
69
+ ```
70
+
71
+ The command does not commit, push, open a pull request, or write the baseline
72
+ unless `--write` is passed after the on-disk file matches the validated
73
+ in-memory document and every candidate agrees across registry and GitHub.
74
+ A normal SDK upgrade should not require editing generator source when the
75
+ upstream contract is compatible. Three-OS qualification must be green on the
76
+ same commit before a Workspai release can present these kits as qualified.
77
+
78
+ ## Intentionally unsupported languages
79
+
80
+ The official Go SDK (`github.com/OpenRouterTeam/go-sdk`) was evaluated on
81
+ 2026-09-21. The upstream README classifies the current `0.x` line as beta and
82
+ warns that breaking changes may ship in minor releases. proxy.golang.org
83
+ reported `v0.8.11`. Go is excluded from the stable Create surface until
84
+ upstream classifies a release line as stable.
85
+
86
+ The OpenRouter Agent SDK (`@openrouter/agent`) belongs to the agent-runtime
87
+ dimension. It is not used by AI Gateway kits and is not added as
88
+ `agent.openrouter.typescript` in this family.
89
+
90
+ ## Configuration
91
+
92
+ Required environment:
93
+
94
+ - `OPENROUTER_API_KEY` — server-side only
95
+ - `OPENROUTER_MODEL` — chosen by the operator; Workspai does not default a
96
+ billable model
97
+
98
+ Optional attribution maps to the official SDK fields:
99
+
100
+ - TypeScript: `httpReferer`, `appTitle` via `OPENROUTER_HTTP_REFERER` and
101
+ `OPENROUTER_APP_TITLE`
102
+ - Python: `http_referer`, `x_open_router_title` via `OPENROUTER_HTTP_REFERER`
103
+ and `OPENROUTER_X_OPEN_ROUTER_TITLE`
104
+
105
+ Routing and privacy policy live in source-controlled `gateway.policy.json`.
106
+ Field names match the pinned SDK for that language. Unknown fields are rejected
107
+ fail-closed in both languages. Supported controls:
108
+
109
+ | Official field | TypeScript policy | Python policy | Validation |
110
+ | --- | --- | --- | --- |
111
+ | model fallbacks | `models` | `models` | unique non-empty strings; must not repeat `OPENROUTER_MODEL` |
112
+ | `allowFallbacks` | `allowFallbacks` | `allow_fallbacks` | boolean when present |
113
+ | `order` / `only` / `ignore` | same camelCase | same snake_case | unique non-empty strings; `only`∩`ignore` empty; `order` must not include ignored providers |
114
+ | `sort` | string or `{ by, partition }` | string or `{ by, partition }` | `price`/`throughput`/`latency`/`exacto`; `partition` is `model` or `none` |
115
+ | `quantizations` | camelCase list | snake_case list | pinned SDK enum values |
116
+ | `requireParameters` | `requireParameters` | `require_parameters` | boolean when present |
117
+ | `zdr` | `zdr` | `zdr` | boolean; cannot combine with `dataCollection`/`data_collection` `allow` |
118
+ | `dataCollection` | `dataCollection` | `data_collection` | `allow` or `deny` |
119
+ | `enforceDistillableText` | `enforceDistillableText` | `enforce_distillable_text` | boolean when present; both pinned SDKs expose this field |
120
+ | `maxPrice` | `maxPrice` | `max_price` | object with `prompt`/`completion`/`request`/`image`/`audio` as non-negative numeric strings that remain finite after numeric interpretation |
121
+ | `preferredMinThroughput` | number or `{ p50, p75, p90, p99 }` | same | finite non-negative numbers |
122
+ | `preferredMaxLatency` | number or `{ p50, p75, p90, p99 }` | same | finite non-negative numbers |
123
+ | timeout | `timeoutMs` | `timeout_ms` | integer 1–600000; env overrides with a digit string |
124
+ | attribution | `httpReferer`, `appTitle` | `http_referer`, `x_open_router_title` | strings when present |
125
+
126
+ `exacto` is part of the pinned SDK `ProviderSort` enums, not a Workspai extension.
127
+ Malformed JSON and wrong types raise `GatewayConfigurationError` before SDK
128
+ construction. Contradictory policy, including ZDR combined with
129
+ `dataCollection: "allow"`, fails before a network request.
130
+
131
+ Omitted privacy or routing fields leave OpenRouter upstream defaults in
132
+ effect. The SDKs document `dataCollection` / `data_collection` default as
133
+ `allow` when unset. Application-side retries are set to strategy `none` so they
134
+ cannot duplicate billable requests. OpenRouter may still apply provider
135
+ fallbacks when `allowFallbacks` / `allow_fallbacks` is not `false`.
136
+
137
+ Capabilities such as tools, JSON schema, structured output, images, audio, and
138
+ embeddings vary by model and provider. These starters only claim chat
139
+ generation and streaming.
140
+
141
+ ## Security
142
+
143
+ - Never commit, log, or serialize API keys.
144
+ - Generated clients are server-side only. Do not expose `OPENROUTER_API_KEY`
145
+ through browser bundles or frontend environment prefixes such as `VITE_` or
146
+ `NEXT_PUBLIC_`.
147
+ - Diagnostics redact Authorization headers, API keys, URL secrets, and request
148
+ bodies.
149
+ - TLS verification is not disabled.
150
+ - Telemetry and prompt recording are not added.
151
+
152
+ ## Lifecycle
153
+
154
+ After local install, documented commands are:
155
+
156
+ TypeScript: `npm install`, `npm test`, `npm run typecheck`, `npm run build`,
157
+ `npm run smoke`, `npm start`.
158
+
159
+ Python: create a virtualenv, `python -m pip install -e .`, `python -m compileall .`,
160
+ `python -m unittest discover -s tests`, `python main.py --smoke`, `python main.py`.
161
+
162
+ Workspace Run uses those same manifests and the generated project's declared
163
+ polyglot unit commands. Stage commands, including `&&` chains and a multi-step
164
+ `start`, run as argv. They are not passed to a shell, so an absolute interpreter
165
+ path cannot be expanded by `/bin/sh`. `test` and `--smoke` inject a fake
166
+ transport. They do not require a live `OPENROUTER_API_KEY`.
167
+
168
+ Streaming uses the official Client SDK event stream. Opening and iterating the
169
+ stream are both normalized: configuration errors stay configuration errors,
170
+ timeouts and caller aborts stay recognizable, and other failures become
171
+ `GatewayUpstreamError` with redacted messages and preserved status / request id /
172
+ retry-after when the SDK exposes them. Cleanup is once-only and prefers, in
173
+ order: async iterator `return()` / generator `close()`, then SDK `close()`, then
174
+ SDK `cancel()`. A cleanup failure does not replace the primary error or convert
175
+ a successful completion into an error.
176
+
177
+ The TypeScript starter forwards `AbortSignal` through `chat.send` options,
178
+ including an already-aborted signal. Pinned `@openrouter/sdk` `1.3.11`
179
+ `EventStream` extends `ReadableStream` and does not expose a dedicated `close()`.
180
+ Cancellation is `ReadableStream` async-iterator `return()` → `cancel()`. If the
181
+ stream has already errored, `cancel()` may be a no-op; the gateway still drops
182
+ its own reference and remains usable for a later request. The Python starter
183
+ closes the SDK iterator first (`EventStream.generator.close()`), which is the
184
+ consumer `generator.close()` path. Python does not expose `AbortSignal`.
185
+ Application-side retries stay disabled. Timeouts use SDK `timeoutMs` /
186
+ `timeout_ms`. An empty successful completion returns an empty string.
187
+
188
+ Exact pins live in
189
+ [`version-baselines.v1.json`](../src/model-gateways/version-baselines.v1.json).
190
+ Do not copy versions into unrelated files.
191
+
192
+ ## Qualification boundary
193
+
194
+ Workspai source readiness is not release qualification. These kits are not
195
+ described as `stable`, `release-ready`, `admitted`, or `qualified` until Linux,
196
+ macOS, and Windows CI prove generated-project installation and lifecycle on the
197
+ same commit SHA, and a later admission change records that evidence. The
198
+ dedicated workflow is `.github/workflows/model-gateway-qualification.yml`.
199
+ Path-triggered runs and manual `full` mode each qualify both the TypeScript and
200
+ Python OpenRouter adapters on Linux, macOS, and Windows. Manual `fast` mode
201
+ stays on Linux. There is no unused kit axis. Discovery is
202
+ `.github/workflows/model-gateway-version-discovery.yml`. Its job summary shows
203
+ the sdk-core candidate diff when registry and GitHub agree. A green job that
204
+ skipped installation or lifecycle is not qualified.
@@ -12,9 +12,11 @@ native kits:
12
12
  This policy does not cover FastAPI or NestJS kits owned by the RapidKit Python
13
13
  engine. It also does not cover generators delegated to an upstream official
14
14
  CLI, such as Next.js, Astro, Angular, Vue, Svelte, Nuxt, or React Native.
15
- Governed Microsoft Agent Framework kits are versioned separately through the
15
+ Governed Microsoft Agent Framework and OpenAI Agents SDK kits are versioned
16
+ separately through the
16
17
  [Agent Framework Adapter Contract](./agent-framework-adapters.md); they are
17
- not native HTTP service generators.
18
+ not native HTTP service generators. OpenRouter AI Gateway kits are versioned
19
+ through [AI Gateway](./model-gateways.md) and are not Agent Framework kits.
18
20
 
19
21
  ## Current tested baseline
20
22
 
@@ -52,7 +52,9 @@ Microsoft Agent Framework projects keep their executable manifest under
52
52
  `agents/<instance>/`. Workspace Run treats that nested `pyproject.toml` or
53
53
  `.csproj` as its own unit (`python3 -m compileall .`,
54
54
  `python3 -m unittest discover -s tests`, `python3 main.py`, or the matching
55
- .NET commands). An empty legacy root manifest is ignored only when it still
55
+ .NET commands). A Python unit with `main.py` uses `compileall` even when
56
+ `pyproject.toml` declares `[build-system]`; `python -m build` remains only for
57
+ packaging projects that have no agent entrypoint. An empty legacy root manifest is ignored only when it still
56
58
  matches the exact older Workspai shell and a real `agents/primary` runtime
57
59
  exists. Any user edit restores the root as an independent unit.
58
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workspai",
3
- "version": "0.75.2",
3
+ "version": "0.77.0",
4
4
  "type": "module",
5
5
  "description": "Open-source workspace intelligence CLI for software systems: create, adopt, govern, verify, and align polyglot workspaces for humans, CI, IDEs, and AI agents.",
6
6
  "keywords": [
@@ -115,9 +115,13 @@
115
115
  "test:runtime-contract": "node scripts/runtime-acceptance-matrix.mjs --contract-only",
116
116
  "test:agent-framework:python": "tsx scripts/smoke-microsoft-agent-framework-adapter.ts --runtime python",
117
117
  "test:agent-framework:dotnet": "tsx scripts/smoke-microsoft-agent-framework-adapter.ts --runtime dotnet",
118
+ "test:agent-framework:openai:python": "tsx scripts/smoke-openai-agents-adapter.ts --runtime python",
119
+ "test:agent-framework:openai:typescript": "tsx scripts/smoke-openai-agents-adapter.ts --runtime typescript",
118
120
  "verify:agent-framework:matrix": "tsx scripts/verify-agent-framework-conformance.ts --reports test-results/agent-framework-conformance",
119
121
  "discover:agent-framework:versions": "tsx scripts/discover-agent-framework-versions.ts",
120
122
  "propose:agent-framework:versions": "tsx scripts/propose-agent-framework-version-update.ts",
123
+ "discover:model-gateway:versions": "tsx scripts/propose-model-gateway-version-update.ts",
124
+ "propose:model-gateway:versions": "tsx scripts/propose-model-gateway-version-update.ts",
121
125
  "promote:agent-framework:admission": "tsx scripts/promote-agent-framework-release-admission.ts",
122
126
  "prepush:test:runtime-contract": "node scripts/run-cached-prepush-gate.mjs runtime-contract",
123
127
  "test:runtime-matrix:full": "node scripts/runtime-acceptance-matrix.mjs --full",
@@ -9,6 +9,7 @@ import process from 'node:process';
9
9
  const repoRoot = process.cwd();
10
10
  const cliPath = path.join(repoRoot, 'dist', 'index.js');
11
11
  const isolatedHome = fs.mkdtempSync(path.join(os.tmpdir(), 'workspai-enterprise-home-'));
12
+ let releaseAdmittedAdapterIds = new Set();
12
13
 
13
14
  process.on('exit', () => {
14
15
  fs.rmSync(isolatedHome, { recursive: true, force: true });
@@ -348,12 +349,43 @@ function assertCliContracts() {
348
349
  }
349
350
 
350
351
  const frameworks = parseTrailingJson(runCli(['agent', 'framework', 'list', '--json']));
352
+ if (frameworks.schemaVersion !== 'workspai.agent-framework-list.v1') {
353
+ fail(`unexpected agent framework list schema: ${frameworks.schemaVersion}`);
354
+ }
355
+ const adapters = Array.isArray(frameworks.adapters) ? frameworks.adapters : [];
356
+ const admittedIds = adapters
357
+ .filter((adapter) => adapter.status === 'admitted')
358
+ .map((adapter) => adapter.id)
359
+ .sort();
360
+ const expectedAdapterIds = [
361
+ 'microsoft-agent-framework-dotnet',
362
+ 'microsoft-agent-framework-python',
363
+ 'openai-agents-python',
364
+ 'openai-agents-typescript',
365
+ ];
351
366
  if (
352
- frameworks.schemaVersion !== 'workspai.agent-framework-list.v1' ||
353
- frameworks.adapters?.length !== 2 ||
354
- frameworks.adapters.some((adapter) => adapter.status !== 'admitted')
367
+ admittedIds.length !== 0 &&
368
+ JSON.stringify(admittedIds) !== JSON.stringify(expectedAdapterIds)
355
369
  ) {
356
- fail('published CLI does not expose the two exact release-admitted framework adapters');
370
+ fail(
371
+ `published CLI does not expose the exact release-admitted framework adapters (admitted: ${
372
+ admittedIds.join(', ') || 'none'
373
+ })`
374
+ );
375
+ }
376
+ releaseAdmittedAdapterIds = new Set(admittedIds);
377
+ for (const openaiId of ['openai-agents-python', 'openai-agents-typescript']) {
378
+ const openai = adapters.find((adapter) => adapter.id === openaiId);
379
+ if (!openai) {
380
+ fail(`published CLI is missing implemented OpenAI adapter ${openaiId}`);
381
+ }
382
+ if (openai.stability !== 'stable') {
383
+ fail(
384
+ `${openaiId} must be labeled stable after the digest-changing promotion (observed: ${
385
+ openai.stability ?? 'missing'
386
+ })`
387
+ );
388
+ }
357
389
  }
358
390
 
359
391
  log(`verified CLI contract surfaces for v${version.version}`);
@@ -390,6 +422,7 @@ function smokeCreateAgentFrameworkKits() {
390
422
  {
391
423
  kit: 'agent.microsoft.python',
392
424
  name: 'python-agent',
425
+ expectCreate: releaseAdmittedAdapterIds.has('microsoft-agent-framework-python'),
393
426
  expectedFiles: [
394
427
  'README.md',
395
428
  '.workspai/project.json',
@@ -404,6 +437,7 @@ function smokeCreateAgentFrameworkKits() {
404
437
  {
405
438
  kit: 'agent.microsoft.dotnet',
406
439
  name: 'dotnet-agent',
440
+ expectCreate: releaseAdmittedAdapterIds.has('microsoft-agent-framework-dotnet'),
407
441
  expectedFiles: [
408
442
  'README.md',
409
443
  '.workspai/project.json',
@@ -417,6 +451,41 @@ function smokeCreateAgentFrameworkKits() {
417
451
  'agents/primary/README.md',
418
452
  ],
419
453
  },
454
+ {
455
+ kit: 'agent.openai.python',
456
+ name: 'openai-python-agent',
457
+ expectCreate: releaseAdmittedAdapterIds.has('openai-agents-python'),
458
+ expectedFiles: [
459
+ 'README.md',
460
+ '.workspai/project.json',
461
+ '.workspai/agent-frameworks/openai-agents-python/primary.json',
462
+ 'agents/primary/main.py',
463
+ 'agents/primary/workspai_context.py',
464
+ 'agents/primary/agent.py',
465
+ 'agents/primary/pyproject.toml',
466
+ 'agents/primary/tests/test_context.py',
467
+ 'agents/primary/.env.example',
468
+ 'agents/primary/README.md',
469
+ ],
470
+ },
471
+ {
472
+ kit: 'agent.openai.typescript',
473
+ name: 'openai-typescript-agent',
474
+ expectCreate: releaseAdmittedAdapterIds.has('openai-agents-typescript'),
475
+ expectedFiles: [
476
+ 'README.md',
477
+ '.workspai/project.json',
478
+ '.workspai/agent-frameworks/openai-agents-typescript/primary.json',
479
+ 'agents/primary/src/main.ts',
480
+ 'agents/primary/src/workspai-context.ts',
481
+ 'agents/primary/src/agent.ts',
482
+ 'agents/primary/package.json',
483
+ 'agents/primary/tsconfig.json',
484
+ 'agents/primary/tests/context.test.ts',
485
+ 'agents/primary/.env.example',
486
+ 'agents/primary/README.md',
487
+ ],
488
+ },
420
489
  ];
421
490
  try {
422
491
  for (const scenario of scenarios) {
@@ -430,6 +499,22 @@ function smokeCreateAgentFrameworkKits() {
430
499
  stdio: ['ignore', 'pipe', 'pipe'],
431
500
  }
432
501
  );
502
+ const combined = `${result.stdout}\n${result.stderr}`;
503
+ if (scenario.expectCreate === false) {
504
+ if (result.status === 0) {
505
+ fail(`${scenario.kit} create succeeded before the stable digest was release-admitted`);
506
+ }
507
+ if (
508
+ !/not release-admitted|adapter (?:manifest|implementation) changed after release admission/i.test(
509
+ combined
510
+ )
511
+ ) {
512
+ fail(
513
+ `${scenario.kit} failed closed without an admission blocker\n${result.stdout}\n${result.stderr}`
514
+ );
515
+ }
516
+ continue;
517
+ }
433
518
  if (result.status !== 0) {
434
519
  fail(
435
520
  `${scenario.kit} governed create failed with exit ${result.status}\n${result.stdout}\n${result.stderr}`
@@ -437,8 +522,16 @@ function smokeCreateAgentFrameworkKits() {
437
522
  }
438
523
  assertGeneratedProject(path.join(workspacePath, scenario.name), scenario.expectedFiles);
439
524
  }
525
+ const admittedCreates = scenarios.filter((scenario) => scenario.expectCreate);
440
526
  const ownershipRoot = path.join(workspacePath, '.workspai', 'agent-frameworks', 'ownership');
441
- if (!fs.existsSync(ownershipRoot)) fail('agent kit smoke did not record ownership receipts');
527
+ const recordedOwnership = fs.existsSync(ownershipRoot);
528
+ if (admittedCreates.length === 0) {
529
+ if (recordedOwnership) {
530
+ fail('agent kit smoke recorded ownership receipts without a release-admitted create');
531
+ }
532
+ } else if (!recordedOwnership) {
533
+ fail('agent kit smoke did not record ownership receipts');
534
+ }
442
535
  } finally {
443
536
  fs.rmSync(tempDir, { recursive: true, force: true });
444
537
  }
@@ -626,10 +719,112 @@ for (const relativePath of [
626
719
  assertFile(relativePath);
627
720
  }
628
721
 
722
+ function smokeCreateOpenRouterGatewayKits() {
723
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'workspai-gateway-kit-smoke-'));
724
+ const workspaceName = 'gateway-kit-workspace';
725
+ const workspacePath = path.join(tempDir, workspaceName);
726
+ const createWorkspace = spawnSync(
727
+ process.execPath,
728
+ [
729
+ cliPath,
730
+ 'create',
731
+ 'workspace',
732
+ workspaceName,
733
+ '--output',
734
+ tempDir,
735
+ '--profile',
736
+ 'minimal',
737
+ '--skip-python-engine',
738
+ '--skip-git',
739
+ '--yes',
740
+ ],
741
+ { cwd: tempDir, encoding: 'utf8', env: cliEnv(), stdio: ['ignore', 'pipe', 'pipe'] }
742
+ );
743
+ if (createWorkspace.status !== 0) {
744
+ fail(
745
+ `gateway kit workspace creation failed with exit ${createWorkspace.status}\n${createWorkspace.stdout}\n${createWorkspace.stderr}`
746
+ );
747
+ }
748
+
749
+ const scenarios = [
750
+ {
751
+ kit: 'gateway.openrouter.typescript',
752
+ name: 'openrouter-ts-gateway',
753
+ expectedFiles: [
754
+ 'README.md',
755
+ '.env.example',
756
+ '.gitignore',
757
+ 'gateway.policy.json',
758
+ 'package.json',
759
+ 'tsconfig.json',
760
+ 'src/port.ts',
761
+ 'src/config.ts',
762
+ 'src/openrouter-gateway.ts',
763
+ 'src/main.ts',
764
+ 'tests/gateway.test.ts',
765
+ '.workspai/project.json',
766
+ ],
767
+ },
768
+ {
769
+ kit: 'gateway.openrouter.python',
770
+ name: 'openrouter-py-gateway',
771
+ expectedFiles: [
772
+ 'README.md',
773
+ '.env.example',
774
+ '.gitignore',
775
+ 'gateway.policy.json',
776
+ 'pyproject.toml',
777
+ 'main.py',
778
+ 'src/model_gateway/port.py',
779
+ 'src/model_gateway/config.py',
780
+ 'src/model_gateway/gateway.py',
781
+ 'src/model_gateway/main.py',
782
+ 'tests/test_gateway.py',
783
+ '.workspai/project.json',
784
+ ],
785
+ },
786
+ ];
787
+ try {
788
+ for (const scenario of scenarios) {
789
+ const result = spawnSync(
790
+ process.execPath,
791
+ [cliPath, 'create', 'project', scenario.kit, scenario.name, '--skip-git', '--yes'],
792
+ {
793
+ cwd: workspacePath,
794
+ encoding: 'utf8',
795
+ env: cliEnv(),
796
+ stdio: ['ignore', 'pipe', 'pipe'],
797
+ }
798
+ );
799
+ if (result.status !== 0) {
800
+ fail(
801
+ `${scenario.kit} create failed with exit ${result.status}\n${result.stdout}\n${result.stderr}`
802
+ );
803
+ }
804
+ const combined = `${result.stdout}\n${result.stderr}`;
805
+ if (/openrouter\.ai|chat\.completions|Authorization: Bearer/i.test(combined)) {
806
+ fail(`${scenario.kit} create output looks like a live model call\n${combined}`);
807
+ }
808
+ assertGeneratedProject(path.join(workspacePath, scenario.name), scenario.expectedFiles);
809
+ const envExample = fs.readFileSync(
810
+ path.join(workspacePath, scenario.name, '.env.example'),
811
+ 'utf8'
812
+ );
813
+ if (/sk-or-[A-Za-z0-9]/.test(envExample) || /OPENROUTER_API_KEY=.+/.test(envExample)) {
814
+ fail(`${scenario.kit} .env.example must not contain a credential value`);
815
+ }
816
+ }
817
+ log(`verified ${scenarios.length} model gateway create scenarios`);
818
+ } finally {
819
+ fs.rmSync(tempDir, { recursive: true, force: true });
820
+ }
821
+ }
822
+
629
823
  assertPackContents();
630
824
  assertCliContracts();
631
825
  smokeCreateNpmBackedKits();
632
826
  smokeCreateAgentFrameworkKits();
827
+ smokeCreateOpenRouterGatewayKits();
633
828
  smokeCreateOfflineFallbackKits();
634
829
 
635
830
  log('enterprise package smoke passed');
@@ -1 +0,0 @@
1
- export{b as printAnalyzeReport,a as runAnalyze}from'./chunk-K52IQX3J.js';
@@ -1 +0,0 @@
1
- export{b as AUTOPILOT_RELEASE_ALIAS_FILENAME,a as AUTOPILOT_RELEASE_LAST_RUN_FILENAME,c as runAutopilotRelease}from'./chunk-EP7SUMAX.js';
@@ -1 +0,0 @@
1
- export{b as runDoctorCapabilities}from'./chunk-JKAAKE4Z.js';