diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5c17cff8..2212ee24b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ one canonical owner, listed below; update that owner when changing its contract. | API callers, credentials and route inventory | [API index](docs/api/README.md) | | Public wire types and qualified behavior | [Agents API contracts](contracts/agents-api/README.md), [pinned upstream](contracts/agents-api/upstream.json), and linked operation contracts | | Runtime messages, receipts and failure ownership | [Core–Runtime protocol](docs/runtime-protocol.md) and `internal/agentdaemon/proto` | -| Harness interfaces and onboarding | [Harness onboarding](contracts/agents-api/harness-onboarding.md) and `agent/harness.go` | +| Harness interfaces and onboarding | [Harness onboarding](contracts/agents-api/harness-onboarding.md) and `apps/parsar-daemon/internal/agent/harness.go` | | Provider interfaces and onboarding | [Sandbox Provider guide](docs/sandbox-provider.md) and `sandbox/sandbox_provider.go` | | Claude private bridge and Runtime artifact | [Claude SDK adapter](packages/claude-sdk-adapter/README.md) | | Operator installation and configuration | [Installation](docs/getting-started/install.md), [configuration](docs/configuration.md), [operations](docs/getting-started/operations.md) | @@ -2779,25 +2779,18 @@ replaced; do not carry obsolete compatibility code forward to satisfy this secti identity; retries cannot replace it. Keep this administrator query separate from runtime observations and do not touch activity or wake sandboxes. The versioned contract is `contracts/agents-api/execution-configuration.md`. -- Model communication uses `internal/modeltransport` in the Runtime. Core sends one - frozen confidential `model_provider` bundle, independent of engine and placement; - it must not manufacture native provider options or retain a historical-options - dispatch path. The adapter declares native protocols. Native matches connect - directly; mismatches use one private loopback endpoint owned by the existing - Session execution resource. Its credential is distinct from the upstream key. - Keep endpoint cleanup on final native teardown, including preparation failure, - rather than on individual Turn completion. No second Session manager is added. - CLIProxyAPI's pinned translator is an embedded conversion dependency, not a - gateway service. Requests, JSON responses and incremental SSE events share the - same conversion implementation for hosted and self-hosted execution. The - dependency owns protocol fields, tools, reasoning and usage conversion. Do not - add local field maps, parameter restoration or parallel compatibility rules. - Keep SDK format selection, HTTP/SSE framing, limits, cancellation and resource - cleanup here. Pin dependency versions and qualify upgrades with contract and - native engine tests; document upstream limitations instead of silently promising - lossless conversion. Preserve model identity, reject incomplete streams and - never redirect upstream credentials. See `contracts/agents-api/model-protocol-conversion.md` for coverage, - limits and dependency qualification. Go 1.26.8 is the pinned build toolchain. +- Model communication uses native direct connections only. Core sends one frozen + confidential `model_provider` bundle, independent of engine and placement; + adapters apply it through their native provider configuration. The shared + `internal/harnessconfig` descriptor declares one ordered `protocols` list, + with the first entry as the default. Core admission and Runtime enforce it. + There is no built-in model API proxy, passthrough gateway or cross-protocol + conversion, including inside individual Harnesses. Unsupported combinations + fail explicitly; saved configurations and immutable Session snapshots are + never rewritten, aliased or migrated to another protocol. Provider validation, + credential encryption, capability checks and native lifecycle ownership remain + mandatory. See `contracts/agents-api/model-execution.md` for the current + protocol matrix. Go 1.26.8 is the pinned build toolchain. - Provider input validation uses the adapter-owned rules in `internal/harnessconfig`. Keep one internal registry for protocol and token-limit validation; Core owns credential environment and endpoint admission policy. These rules are not a @@ -3379,9 +3372,11 @@ or containment of descendants that deliberately leave the group. ### Harness qualification and onboarding -`apps/parsar-daemon/internal/agent/harness.go` is the single source entry point +`apps/parsar-daemon/internal/agent/harness.go` owns the lifecycle entry point for Harness authors. Keep required lifecycle declarations, separate optional -interfaces and the existing registration methods there. Operation result types, +interfaces and the existing registration methods there. Model configuration has +one shared contract in `internal/harnessconfig/harness.go`, referenced by that +lifecycle entry point and consumed by Core without importing daemon internals. Operation result types, errors and Registry lookup/storage implementation may remain in focused files. Use the existing `proto.SupportedAgentKind` and `AgentKindCapabilities` schema; do not introduce a second capability descriptor or a combined optional interface. @@ -3596,15 +3591,29 @@ Keep the adapter artifact independent of the Core binary and product sources. ### Harness model configuration -The configuration preparation contract in `apps/parsar-daemon/internal/agent/harness.go` -is mandatory for every Executor factory. Shared wire types live in -`internal/agentdaemon/proto`; native model fields and their application are documented -in [Harness onboarding](contracts/agents-api/harness-onboarding.md#native-model-configuration). -The standalone Core build includes the shared `internal/modeltransport` route -qualification code alongside the adapter declarations; Runtime still owns proxy -startup and request forwarding. -Public configuration sources and inheritance belong to +`internal/harnessconfig/harness.go` is the sole authoring entry point for the +model configuration contract: its declaration and pure preparation cover the +current model, provider and native parameters. `RegisterKind` requires an explicit +adapter-owned declaration; direct, preparation and Executor registrations all run +that declaration's preparation before calling native code. Registry wrappers +transfer the same declaration, never reconstruct it from a second support table. +Core admission and support descriptions consume it too. JSON decoding, persistence +and native rendering may remain in focused implementation files. +Shared wire types remain in `internal/agentdaemon/proto`; current fields and +application are documented in +[Harness onboarding](contracts/agents-api/harness-onboarding.md#native-model-configuration). +Adapters configure direct native connections. Claude's private bridge receives +compiled native options and performs structural checks, not a second copy of +the Go declaration's enum, budget or combination rules. +Implemented public configuration sources and inheritance belong to [model execution](contracts/agents-api/model-execution.md). +[Unified model configuration design](contracts/agents-api/model-configuration-design.md) +is the canonical planned field/ownership contract. Extend Web defaults and the +public API by extending the existing shared contract, types and resolver; +source and authority may differ. Implement planned fields and qualify their +native application before exposing them as accepted HTTP input. This design +does not change current native configuration or require speculative Runtime +scaffolding or a second configuration registration interface. ### Harness selection and Agent defaults diff --git a/apps/docs/content/docs/admin-api.mdx b/apps/docs/content/docs/admin-api.mdx index b31aabdb9..48ff95c8d 100644 --- a/apps/docs/content/docs/admin-api.mdx +++ b/apps/docs/content/docs/admin-api.mdx @@ -8,7 +8,7 @@ resource inspection, Project and key management, credential issuance, audit, usa and sandbox operations. It never calls `/v1` or `/api/v1`, and has no Agent execution, copy or arbitrary asset editing operation. -Core request failures use the [Core administration error envelope](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core-errors.md), +Core request failures use the [Core administration error envelope](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core-errors.md), including optional typed safe details. Console sign-in endpoints retain their separate error shape described below. @@ -49,9 +49,9 @@ label, but could set any value. Never use it for authorization or as proof of origin. Web calls Core only through the typed Core clients in `packages/agents-client`: -[AdminClient](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/agents-client/src/admin-client.ts) (`/core/v1`), +[AdminClient](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/packages/agents-client/src/admin-client.ts) (`/core/v1`), `SandboxAdminClient` (`/core/v1/sandbox`) and `CoreMetricsClient` -(`/core/v1/metrics`). See [the complete administrator reference](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) +(`/core/v1/metrics`). See [the complete administrator reference](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) for methods, fields, filters, pagination and response shapes. Routes below are relative to `/core/v1`: @@ -62,8 +62,8 @@ Routes below are relative to `/core/v1`: | Resource lists/details/deletion | `/projects/{id}/agents`, `/sessions`, `/environment-templates`, `/skills`, `/files`, `/vaults`, including the documented nested reads | | Asset ownership | `GET /projects/{id}/resource-owners` with batched resource IDs | | Key operation history | `GET /projects/{id}/write-operations` with key/resource/time filters | -| Executor credentials | `GET/POST /projects/{id}/environments/{environment_id}/executor-credentials`, `DELETE …/executor-credentials/{key_id}` ([contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-executor-credentials.md)) | -| Deployment model configuration | `GET /harnesses`, `GET/PUT/DELETE /harnesses/{harness}/model-configuration`; the key is write-only ([contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/model-execution.md#deployment-defaults)) | +| Executor credentials | `GET/POST /projects/{id}/environments/{environment_id}/executor-credentials`, `DELETE …/executor-credentials/{key_id}` ([contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md)) | +| Deployment model configuration | `GET /harnesses`, `GET/PUT/DELETE /harnesses/{harness}/model-configuration`; the key is write-only ([contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/model-execution.md#deployment-defaults)) | | Usage and health | `GET /summary`, `/sandbox/runtime-observations`, `/metrics` | | Administrator audit | `GET /audit-log`; deployment-wide entries have `project_id: null` | @@ -76,9 +76,9 @@ it is operational attribution, not per-key billing. ## Sandbox administration -The [deployment configuration contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md), -[nodes and sandbox backends reference](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md) -and [generated OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core.openapi.yaml) +The [deployment configuration contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/sandbox-deployment.md), +[nodes and sandbox backends reference](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md) +and [generated OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core.openapi.yaml) define deployment and node operations: - `GET/POST/PUT /core/v1/sandbox/deployment` and @@ -119,7 +119,7 @@ is a durable serving pin, not proof of current connectivity. Target unknown, fai or update-required state does not by itself invalidate confirmed old-generation service; consume Core's connection/provider facts separately. -Administrators may [archive an individual hosted Session](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md#administrative-session-archive) +Administrators may [archive an individual hosted Session](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md#administrative-session-archive) at the current generation without reset. History and persisted Files/Artifacts survive; unpersisted workspace is lost and the original Session cannot resume. Cancel reset stops further archives, not cleanup already requested. After zero @@ -160,7 +160,7 @@ generated schema; do not interpret every empty or failed read as an absent resou `GET /core/v1/metrics?range=1h|6h|24h|7d` returns Core process, execution queue/slots, PostgreSQL and background-job measurements. It requires the Core key, rejects arbitrary query filters and never grants -Agent execution access. See the [exact measurement contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core-metrics.md) +Agent execution access. See the [exact measurement contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core-metrics.md) for complete buckets, null values, units and process-local retention. Frontend implementation is maintained separately; this backend change does not modify Agent metrics or the public Agent API. @@ -169,7 +169,7 @@ Agent metrics or the public Agent API. The Core key reads a node and its host history through `GET /core/v1/sandbox/nodes/{node_id}?range=1h|6h|24h`. See the -[node host history contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/node-host-history.md) +[node host history contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/node-host-history.md) for nullable observations, freshness and aggregation. The node list is unchanged. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/api/web-management.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/api/web-management.md) diff --git a/apps/docs/content/docs/agents-and-tools.mdx b/apps/docs/content/docs/agents-and-tools.mdx index 787ae1ae4..a8e622256 100644 --- a/apps/docs/content/docs/agents-and-tools.mdx +++ b/apps/docs/content/docs/agents-and-tools.mdx @@ -4,7 +4,7 @@ description: "Native harness tools, shared declarations and the limits of execut --- Assessed against Core `206474a4c10901442b1faa094281bfb5559082b5` on 2026-09-22. -The contract remains [OpenAI Python 3.13.0 at `d7c41ef`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json), Beta +The contract remains [OpenAI Python 3.13.0 at `d7c41ef`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream.json), Beta `agents=v1`. This matrix records the bounded execution/tools milestone. It does not establish complete protocol compatibility; the remaining limits below stay open. @@ -22,20 +22,20 @@ and Environment Plugin MCP have separate inventories and qualification. | Operation | Implemented behavior and real qualification | Unsupported or unverified boundary | | --- | --- | --- | -| Initial message input, `sessions.create` | String input and ordered user-message arrays share atomic admission. Codex/Claude text and inline image execution: M1/M2; ordinary MiniMax text: M1/P1. [Input contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md), [initial parser](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/session_initial_input.go). | Whitespace-only text is admitted verbatim for Codex and rejected at admission for Claude SDK and MiniMax Code ([text content](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md#text-content)). Empty parts beside text (SES-08) and local size-limit parity need upstream evidence. Images are only qualified for inline PNG/JPEG on `none` and Core-managed Docker `openai_hosted`; MiniMax, self-hosted images and remote URLs remain gaps. | -| Prepared and active messages, `sessions.events.create` | Ordered `input_text`/`input_image` arrays retain original content and distinct public user Items. HTTP 202 confirms persistence; native receipts establish application. Initial/prepared/active Docker PNG/JPEG: M2; active PNG on `none`: M1. [Shared admission](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/inputs.go). | Codex flattens native messages with blank-line separators. Claude can fold or queue native turns; it does not promise Codex's same-native-turn behavior. Public durability does not prove native consumption. Claude SDK and MiniMax Code reject messages without an image or non-whitespace text at admission (`unsupported_or_invalid_configuration`); Codex delivers them unchanged. | -| Structured output, `agent.text.format` | Save/inherit/freeze `{type:json_schema,schema:...}`. Claude SDK object-root, single Agent, medium verbosity, ordinary functions returning text: S1 (`none`) and S2 (Docker, including prepared/active input and Files/Artifacts). Native final text is retained unchanged; its stream follows the official message sequence with the whole text in one `output_text.delta` ([item serialization](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/history-events-usage.md#item-serialization-2026-09-23)). [Contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/structured-output.md), [profile](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/engine/claude.go). | An explicit non-object root type is a protocol error for every harness ([validation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/official-semantics-alignment.md#agent-configuration-validation--september-23)). Codex/MiniMax, schemas without an object root, schema numbers changed by binary64, self-hosted, Skills/Plugins, MCP, Subagent and discovery combinations reject execution. No output repair, coercion or extra model loop. Arbitrary schema dialects are unverified. | -| Function configuration, saved/inline Agents | Required name/description/schema; `defer_loading` defaults false. Protocol errors, repeated names and explicit non-object root types reject with the official fields ([validation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/official-semantics-alignment.md#agent-configuration-validation--september-23)). Saved references resolve into an immutable Session snapshot. Codex/Claude real calls: F1/F2/M2/S1/S2. [Parser](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/function_configuration.go), [saved tools](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/saved_tools.go). | MiniMax public functions reject. Claude requires an explicit object root. Local nonblank/512-byte name and 64-definition Session bounds are compatibility gaps. Saving configuration alone does not qualify execution. | -| Function-result admission, `events.create` | Required `turn_id`, `call_id`, `success`; optional nullable `error` and `output`. Output is string or ordered text/image content. Scoped atomic batches retain field presence, original content and retry identity in storage; public result Items and events always carry `output` and `error`, null when not submitted ([item serialization](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/history-events-usage.md#item-serialization-2026-09-23)). Same result retries are accepted; changed results and results after cancellation return 409 `conflict_error`, including after terminal state. In the caller's Session, an unknown call or a call of another Turn returns 400 `invalid_request_error` without changing the pending action; missing and foreign Sessions stay 404 ([error rows](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/official-semantics-alignment.md#session-input-conflicts-and-result-targets--september-23)). F1/F2/M2 plus [controlled SDK/raw checks](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/tests/official_function_inputs.py). [Parser](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/function_inputs.go), [Store](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/function_inputs.go). | Admission is separate from application and public Item publication. Invalid or unqualified content cannot consume a pending call. Core messages omit the call and executor IDs that official messages name; hosted defaults and publication timing remain unverified. | -| Function text results and native application | Codex waits for a matching live root dynamic-tool completion; Claude waits for a matching live root native tool result. Text/error results, retry/conflict, cancellation and cold continuation: F1/F2. [Receipt contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/function-result-images.md). | Transport writes alone do not confirm application. Confirmation is not provider consumption, crash recovery or exactly-once external effects. No automatic result replay. | +| Initial message input, `sessions.create` | String input and ordered user-message arrays share atomic admission. Codex/Claude text and inline image execution: M1/M2; ordinary MiniMax text: M1/P1. [Input contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/message-input.md), [initial parser](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/api/session_initial_input.go). | Whitespace-only text is admitted verbatim for Codex and rejected at admission for Claude SDK and MiniMax Code ([text content](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/message-input.md#text-content)). Empty parts beside text (SES-08) and local size-limit parity need upstream evidence. Images are only qualified for inline PNG/JPEG on `none` and Core-managed Docker `openai_hosted`; MiniMax, self-hosted images and remote URLs remain gaps. | +| Prepared and active messages, `sessions.events.create` | Ordered `input_text`/`input_image` arrays retain original content and distinct public user Items. HTTP 202 confirms persistence; native receipts establish application. Initial/prepared/active Docker PNG/JPEG: M2; active PNG on `none`: M1. [Shared admission](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/api/inputs.go). | Codex flattens native messages with blank-line separators. Claude can fold or queue native turns; it does not promise Codex's same-native-turn behavior. Public durability does not prove native consumption. Claude SDK and MiniMax Code reject messages without an image or non-whitespace text at admission (`unsupported_or_invalid_configuration`); Codex delivers them unchanged. | +| Structured output, `agent.text.format` | Save/inherit/freeze `{type:json_schema,schema:...}`. Claude SDK object-root, single Agent, medium verbosity, ordinary functions returning text: S1 (`none`) and S2 (Docker, including prepared/active input and Files/Artifacts). Native final text is retained unchanged; its stream follows the official message sequence with the whole text in one `output_text.delta` ([item serialization](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/history-events-usage.md#item-serialization-2026-09-23)). [Contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/structured-output.md), [profile](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/engine/claude.go). | An explicit non-object root type is a protocol error for every harness ([validation](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/official-semantics-alignment.md#agent-configuration-validation--september-23)). Codex/MiniMax, schemas without an object root, schema numbers changed by binary64, self-hosted, Skills/Plugins, MCP, Subagent and discovery combinations reject execution. No output repair, coercion or extra model loop. Arbitrary schema dialects are unverified. | +| Function configuration, saved/inline Agents | Required name/description/schema; `defer_loading` defaults false. Protocol errors, repeated names and explicit non-object root types reject with the official fields ([validation](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/official-semantics-alignment.md#agent-configuration-validation--september-23)). Saved references resolve into an immutable Session snapshot. Codex/Claude real calls: F1/F2/M2/S1/S2. [Parser](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/api/function_configuration.go), [saved tools](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/api/saved_tools.go). | MiniMax public functions reject. Claude requires an explicit object root. Local nonblank/512-byte name and 64-definition Session bounds are compatibility gaps. Saving configuration alone does not qualify execution. | +| Function-result admission, `events.create` | Required `turn_id`, `call_id`, `success`; optional nullable `error` and `output`. Output is string or ordered text/image content. Scoped atomic batches retain field presence, original content and retry identity in storage; public result Items and events always carry `output` and `error`, null when not submitted ([item serialization](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/history-events-usage.md#item-serialization-2026-09-23)). Same result retries are accepted; changed results and results after cancellation return 409 `conflict_error`, including after terminal state. In the caller's Session, an unknown call or a call of another Turn returns 400 `invalid_request_error` without changing the pending action; missing and foreign Sessions stay 404 ([error rows](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/official-semantics-alignment.md#session-input-conflicts-and-result-targets--september-23)). F1/F2/M2 plus [controlled SDK/raw checks](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/tests/official_function_inputs.py). [Parser](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/api/function_inputs.go), [Store](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/store/function_inputs.go). | Admission is separate from application and public Item publication. Invalid or unqualified content cannot consume a pending call. Core messages omit the call and executor IDs that official messages name; hosted defaults and publication timing remain unverified. | +| Function text results and native application | Codex waits for a matching live root dynamic-tool completion; Claude waits for a matching live root native tool result. Text/error results, retry/conflict, cancellation and cold continuation: F1/F2. [Receipt contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/function-result-images.md). | Transport writes alone do not confirm application. Confirmation is not provider consumption, crash recovery or exactly-once external effects. No automatic result replay. | | Function image results | Successful ordered inline PNG/JPEG with text, large PNG and image-only JPEG: Codex/Claude `none` F1/F2; Docker M2. Public Items retain submitted bytes. Codex receipt regression: F2. | Claude rejects failed images and remote references before persistence; native resizing may change its image bytes. MiniMax functions and self-hosted image workflows are unqualified. Other Codex image/error/reference combinations cannot be inferred from successful-inline evidence. | -| Deferred function discovery | Type-only `tool_search` plus mixed eager/deferred functions: Claude SDK 0.3.269/native 2.1.269, Kimi K3, single Agent, medium, `none`, text results; text and PNG input D1. Native provider observations establish lazy schema loading. [Contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/tool-search.md). | A repeated `tool_search` is a protocol error. Codex/MiniMax discovery, search-only/missing-search, workspace, MCP, structured-output and Subagent combinations remain gaps. Opaque native policy changes lack a reliable pre-input deferral signal. Saved tools include `tool_search`; the pinned Session response union excludes it. Exact hosted projection is unverified. | -| Explicit disabled search/PTC | Saved and inline `web_search.mode=disabled` and `programmatic_tool_calling.enabled=false`; shared native controls on initial and cold execution. All three harnesses on `none`: P1, including native inventory/control evidence. [Contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/tool-policy.md), [parser](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/api/disabled_tools.go). | Unsupported explicit enablement rejects at Session admission; saved Agents keep every pinned search mode as resource data (TV-05), and Sessions from such Agents reject unless they replace the tools. A repeated `web_search` is a protocol error. Omission retains approved native behavior, which does not establish official default-on PTC parity. Enabled search and default/error parity remain gaps; unrelated native utilities are not implicitly removed. | -| Agent service-origin MCP | Implemented Codex/Claude HTTP `none` profiles, anonymous/static bearer, scoped Vault selection and native `mcp_call` Items. [Configuration and qualification limits](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#http-mcp-execution), [profiles](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/engine/profile.go). | This closure batch does not requalify MCP/provider combinations. MiniMax, self-hosted/hosted service-origin MCP, OAuth, nonempty inline headers/metadata and other transports/origins reject. Claude requires a static connected inventory; original MCP-envelope fidelity and continuing server health are unverified. | -| Environment Plugin MCP/native tools | Separate [Docker Plugin transport matrix](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md#environment-origin-mcp-plugins) and [V1 deployment evidence](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md). Native tools stay within the existing colocated Runtime. | Plugin MCP is not Agent service-origin MCP or a public function action. No new optional cross-product is qualified here. Native utility inventories need not be identical. | -| Required action: `function_call` | Session GET/list and Session SSE expose persisted `{arguments,call_id,name,turn_id,type}`. Actions remain pending until native application or cancellation/terminal settlement. [Projection](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/function_state.go), [confirmation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/function_results.go); real handling F1/F2/M2/S1/S2/D1; explicit client disconnect/query/reconnect: F3. | Function-call history is not pending-state authority. Client reconnect does not replay events or redo external application effects. | -| Required action: `environment_connection` | Offline waiting input exposes `{environment_id,type}` before Turn creation. Connect the exact Session Environment through our scoped daemon enrollment. Three-harness Docker/E2B execution: E1; explicit initial pending-input/query/connection/native completion: E2; expiry: [controlled initial-input test](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/tests/official_self_hosted_initial.py). | Idle offline Sessions without pending input request nothing. Registration alone is not connectivity or native readiness. Stock `exec-server`/Noise transport is outside the approved V1 route. Exact upstream registration/action-removal timing remains unverified. | -| Stream disconnect and execution loss | SSE is live-only. Reconnect, buffer events, retrieve Session/Turn/Items and deduplicate Item IDs. Worker recovery fails previously claimed work without replay; queued work can remain. [Recovery contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#public-execution-admission), [connection reconciliation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/environment_connection_recovery.go). | Client disconnect and daemon/Core loss are different cases. F3 qualifies client-stream loss and continued pending function handling; F2 qualifies native process loss with an unapplied saved result. Neither promises replay or recovery of unknown external effects. | +| Deferred function discovery | Type-only `tool_search` plus mixed eager/deferred functions: Claude SDK 0.3.269/native 2.1.269, Kimi K3, single Agent, medium, `none`, text results; text and PNG input D1. Native provider observations establish lazy schema loading. [Contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/tool-search.md). | A repeated `tool_search` is a protocol error. Codex/MiniMax discovery, search-only/missing-search, workspace, MCP, structured-output and Subagent combinations remain gaps. Opaque native policy changes lack a reliable pre-input deferral signal. Saved tools include `tool_search`; the pinned Session response union excludes it. Exact hosted projection is unverified. | +| Explicit disabled search/PTC | Saved and inline `web_search.mode=disabled` and `programmatic_tool_calling.enabled=false`; shared native controls on initial and cold execution. All three harnesses on `none`: P1, including native inventory/control evidence. [Contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/tool-policy.md), [parser](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/api/disabled_tools.go). | Unsupported explicit enablement rejects at Session admission; saved Agents keep every pinned search mode as resource data (TV-05), and Sessions from such Agents reject unless they replace the tools. A repeated `web_search` is a protocol error. Omission retains approved native behavior, which does not establish official default-on PTC parity. Enabled search and default/error parity remain gaps; unrelated native utilities are not implicitly removed. | +| Agent service-origin MCP | Implemented Codex/Claude HTTP `none` profiles, anonymous/static bearer, scoped Vault selection and native `mcp_call` Items. [Configuration and qualification limits](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/README.md#http-mcp-execution), [profiles](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/engine/profile.go). | This closure batch does not requalify MCP/provider combinations. MiniMax, self-hosted/hosted service-origin MCP, OAuth, nonempty inline headers/metadata and other transports/origins reject. Claude requires a static connected inventory; original MCP-envelope fidelity and continuing server health are unverified. | +| Environment Plugin MCP/native tools | Separate [Docker Plugin transport matrix](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-templates.md#environment-origin-mcp-plugins) and [V1 deployment evidence](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/user-managed-runtime-v1.md). Native tools stay within the existing colocated Runtime. | Plugin MCP is not Agent service-origin MCP or a public function action. No new optional cross-product is qualified here. Native utility inventories need not be identical. | +| Required action: `function_call` | Session GET/list and Session SSE expose persisted `{arguments,call_id,name,turn_id,type}`. Actions remain pending until native application or cancellation/terminal settlement. [Projection](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/store/function_state.go), [confirmation](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/store/function_results.go); real handling F1/F2/M2/S1/S2/D1; explicit client disconnect/query/reconnect: F3. | Function-call history is not pending-state authority. Client reconnect does not replay events or redo external application effects. | +| Required action: `environment_connection` | Offline waiting input exposes `{environment_id,type}` before Turn creation. Connect the exact Session Environment through our scoped daemon enrollment. Three-harness Docker/E2B execution: E1; explicit initial pending-input/query/connection/native completion: E2; expiry: [controlled initial-input test](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/tests/official_self_hosted_initial.py). | Idle offline Sessions without pending input request nothing. Registration alone is not connectivity or native readiness. Stock `exec-server`/Noise transport is outside the approved V1 route. Exact upstream registration/action-removal timing remains unverified. | +| Stream disconnect and execution loss | SSE is live-only. Reconnect, buffer events, retrieve Session/Turn/Items and deduplicate Item IDs. Worker recovery fails previously claimed work without replay; queued work can remain. [Recovery contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md#public-execution-admission), [connection reconciliation](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/store/environment_connection_recovery.go). | Client disconnect and daemon/Core loss are different cases. F3 qualifies client-stream loss and continued pending function handling; F2 qualifies native process loss with an unapplied saved result. Neither promises replay or recovery of unknown external effects. | ## Required-action recovery contract @@ -52,7 +52,7 @@ application already performed the function, keep and submit that saved result; do not run an external effect again merely because an action remains visible. Core's acknowledgement boundary is native application. For an environment action, connect its exact Environment using the existing enrollment authorization. -[Connection observations](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/environment_connections.go) +[Connection observations](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/store/environment_connections.go) fence stale generations; transport connection can clear pre-Turn activity before native preparation. Neither registration nor an action disappearing proves that a model ran successfully. Query the matching Turn and Items for its outcome. @@ -61,8 +61,8 @@ Two timing questions remain open. Repeated `requires_action` notification order and acknowledgement timing are local implementation choices, not proven upstream semantics. Also, an admitted result that is cancelled before native observation can remain internally saved without a public output Item or `item.added`. -[Item publication](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/store/item_projection.go) and -[the existing coverage record](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#public-function-configuration) preserve +[Item publication](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/store/item_projection.go) and +[the existing coverage record](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md#public-function-configuration) preserve this gap. Successful retry cannot manufacture the missing observation or establish that the model consumed the result. @@ -82,12 +82,12 @@ not a claim that every historical workflow was rerun at the current baseline. | M2 | `20260922/workspace-images/validation-summary.json`; Codex `public-run-_lr5uiy_`, Claude `public-run-sb65p9e9`; candidate `2c295116d66ed9aafc808ec49a8cb6dcb5c674c2`, merge `1adb2489bc3415039440224e9a7905c53e68a557` ([#17](https://github.com/MiniMax-AI/parsar-core/pull/17)). Kimi K3, Codex 0.153.4, Claude SDK 0.3.269/native 2.1.269; Docker seven-Turn image/workspace workflow. Codex's receipt conclusion is superseded by F2. | | F1 | `20260922/function-result-images/validation-summary.json`, `validated/function-image-public-2567456666/public.json`; candidate `d3cdb225bc7628b968b76c7e1b400101894ce8cd`, merge `fd23f169e1ede1b2f1c39a1d9dcce71886e3c006` ([#16](https://github.com/MiniMax-AI/parsar-core/pull/16)). Claude SDK 0.3.269/native 2.1.269 with Kimi K3 on `none`; success PNG/JPEG, failed text, retries, cancellation, history continuation. Earlier Codex transport-only evidence does not qualify current receipts. | | F2 | `20260922/function-result-receipts/validation-summary.json`, `candidate-public-owner-sdk.log`; hosted `public-run-4fn70foy` (104.13s), `none` `function-image-public-945075091` (83.918s). Candidate `6dceb98e1c56469fcc70cd82025ce77fc870ed3d`, merge `206474a4c10901442b1faa094281bfb5559082b5` ([#20](https://github.com/MiniMax-AI/parsar-core/pull/20)); real Kimi, Codex 0.153.4. Qualifies native receipts, not completed provider consumption. | -| F3 | `20260922/execution-tools-closure/pending-actions/validation-summary.json`; Codex `codex/public-run-8gexf5a8` (65.58s), Claude `claude_sdk/public-run-5f2tr7e_` (74.33s), production source `206474a`. The same [public verifier](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/tests/official_pending_actions_native.py) checks disconnected-client pending queries, success/error/cancel, original results, target isolation, retry/conflict and no implicit call replay with real Kimi on Docker. No Core restart or native-loss injection is claimed by these runs. | +| F3 | `20260922/execution-tools-closure/pending-actions/validation-summary.json`; Codex `codex/public-run-8gexf5a8` (65.58s), Claude `claude_sdk/public-run-5f2tr7e_` (74.33s), production source `206474a`. The same [public verifier](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/tests/official_pending_actions_native.py) checks disconnected-client pending queries, success/error/cancel, original results, target isolation, retry/conflict and no implicit call replay with real Kimi on Docker. No Core restart or native-loss injection is claimed by these runs. | | S1 | `20260922/structured-output/acceptance-summary.json`, `structured-public-1905281777/public.json`; real candidate `7f533d46c472f3cdf7c16ce9471c225a2ba7eded`, merge `8716e699dbc34904499c94b6b0b03857bbebfaad` ([#11](https://github.com/MiniMax-AI/parsar-core/pull/11)). Claude/Kimi `none` schema/function execution and cold continuation. Schema-specific active steering was not separately qualified here. | | S2 | `20260922/hosted-structured-output/validation-summary.json`, `validated/public-inline.log`, `public-run-qvq3csf1` (186.09s); candidate `f6d2c3f2a1dc2ff5f177213bd2b20d78496011d0`, merge `aa85d8ecc60e0a8b7f2494b73caa81216ee197e2` ([#18](https://github.com/MiniMax-AI/parsar-core/pull/18)). Claude SDK 0.3.269/native 2.1.269, real Kimi; Docker saved/inline schema, prepared/active input, native files and four completed structured answers. | | D1 | `20260922/deferred-tools/tool-search-public-2039155387/public.json`, `public-image-isolated-tests.log` (86.71s); merge `178507ae7a63d4068e1e82bef9cd256ba398ae00` ([#13](https://github.com/MiniMax-AI/parsar-core/pull/13)). Claude SDK 0.3.269/native 2.1.269/Kimi K3 `none`; text results with initial/active PNGs, cancellation and cold continuation. `claude-native-1790052750` separately records provider schema inventories and native feasibility. | | P1 | `20260922/tool-policy/acceptance.json`, `server-evidence/tool-policy-{codex-2495445746,claude_sdk-2539346823,mcode-3747575401}/public.json`; candidate `eeb432c7495265ae3a9309e32f5b4323611c3245`, merge `4b8754a6eda6696d9b18eb8d583953ac16c6800f` ([#14](https://github.com/MiniMax-AI/parsar-core/pull/14)). Codex/Claude Kimi K3, MiniMax M2.7; four `none` Sessions per harness, native disable controls and cold continuation. | -| E1 | `20260921/self-hosted-onboarding/{source-candidate.json,source-verified.json,live,live-e2b}`; candidate `8f0cd2530d7b58cb7fb3ea124a1fc43dacecca36`. [Exact Docker/E2B run paths and limits](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md#evidence-and-verification-boundaries): Codex Kimi K3 Responses, Claude Kimi K3 Anthropic-compatible API, MiniMax M2.7, immutable Linux amd64 Runtime builds. Native execution, restart/history, cancellation, Files/Artifacts and isolation; shared credential lifecycle evidence is not a separate live rotation run for every E2B profile. | +| E1 | `20260921/self-hosted-onboarding/{source-candidate.json,source-verified.json,live,live-e2b}`; candidate `8f0cd2530d7b58cb7fb3ea124a1fc43dacecca36`. [Exact Docker/E2B run paths and limits](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/user-managed-runtime-v1.md#evidence-and-verification-boundaries): Codex Kimi K3 Responses, Claude Kimi K3 Anthropic-compatible API, MiniMax M2.7, immutable Linux amd64 Runtime builds. Native execution, restart/history, cancellation, Files/Artifacts and isolation; shared credential lifecycle evidence is not a separate live rotation run for every E2B profile. | | E2 | `20260922/execution-tools-closure/environment-actions/result.json` contains Codex/Claude Kimi passes (13 checks each) and the retained MiniMax direct-network failure; `mcode-relay-retry/result.json` separately passes MiniMax M2.7 (13 checks) after correcting provider routing. Source `206474a`; same shared SDK/raw workflow, isolated Core/PostgreSQL and user-managed Docker. Covers initial creation SSE, client disconnect, exact pending Environment/no Turn or Items, scoped enrollment, one actual completed model Turn and creation retries preserving history. This is connection-action qualification, not another full lifecycle or E2B regression. | Controlled tests establish parser, projection, atomicity and race behavior; native @@ -121,4 +121,4 @@ remain separate limitations. They do not authorize silently relabeling an unprov principal workflow as deferred. OAuth, full Usage, release publication, new tool kinds and new execution loops are outside this closure batch. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/execution-tools.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/execution-tools.md) diff --git a/apps/docs/content/docs/bootstrap-projects-keys.mdx b/apps/docs/content/docs/bootstrap-projects-keys.mdx index 7b20dd5de..58c6a1fa6 100644 --- a/apps/docs/content/docs/bootstrap-projects-keys.mdx +++ b/apps/docs/content/docs/bootstrap-projects-keys.mdx @@ -68,7 +68,7 @@ and display only. Keep deployment, application, node and provider credentials ou ## Projects and application keys Installation creates no Project or key. Create them on **Projects and keys** or -through the [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md); see +through the [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md); see [Projects and API keys](/troubleshooting#projects-and-api-keys). ## Verification and diagnosis @@ -89,4 +89,4 @@ management request points to the console's Core key or Core connection. A resour deletion conflict must remain visible; it does not authorize an execution call. Node and daemon `/api/v1` routes and native Runtime interfaces retain their own authentication. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/web/core-connection.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/web/core-connection.md) diff --git a/apps/docs/content/docs/concepts.mdx b/apps/docs/content/docs/concepts.mdx index 762bedd3e..03466d730 100644 --- a/apps/docs/content/docs/concepts.mdx +++ b/apps/docs/content/docs/concepts.mdx @@ -86,12 +86,12 @@ not protect Runtime data from tools running as the same user. Do not add product users, RBAC, cross-Project shared assets, administrator execution or compatibility with old private protocols. Implementers follow the -[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md) and the +[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md) and the [Core–Runtime protocol](/runtime-protocol). Native failure classification is adapter-owned and uses finite, structured native values. Optional Runtime error metadata is normalized once; it never replaces Core's terminal authority, cancellation receipts, Usage or native identity. See -[native failure classification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/native-error-classification.md). +[native failure classification](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/native-error-classification.md). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/design-principles.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/design-principles.md) diff --git a/apps/docs/content/docs/configure.mdx b/apps/docs/content/docs/configure.mdx index 4ce140ad1..1fbea1802 100644 --- a/apps/docs/content/docs/configure.mdx +++ b/apps/docs/content/docs/configure.mdx @@ -117,7 +117,7 @@ output or in Core's settings snapshot. Model providers are not process settings; The schema is -[`deploy/install/config.schema.json`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/deploy/install/config.schema.json); each +[`deploy/install/config.schema.json`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/deploy/install/config.schema.json); each installation keeps a copy in `generated/config.schema.json` for editors. Core serves the non-secret settings snapshot, with the path of `config.json` and the apply command, at `GET /core/v1/installation`. @@ -146,7 +146,7 @@ Which harnesses are enabled, and the default one, are process settings Exactly one provider serves the deployment: Docker, microsandbox or E2B. The deployment specification holds the per-sandbox resources and, for Docker and microsandbox, the Runtime release. Web's setup proposes the Standard size from -[`standard-sizes.json`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/src/features/sandbox/standard-sizes.json) and +[`standard-sizes.json`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/web/src/features/sandbox/standard-sizes.json) and derives Small and Large from it; the installer applies the bundle's copy of the same file. The database owns the saved values. @@ -188,8 +188,8 @@ Current Runtime generation coexistence and ownership-scoped garbage collection remain supported. They do not upgrade the installed node program or convert an old installation. History stays, and existing Sessions never move between providers. See the [nodes guide](/hosted-providers#change-the-sandbox-backend-or-size), -[operator reference](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-reset) -and [deployment contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md). +[operator reference](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-reset) +and [deployment contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/sandbox-deployment.md). ### Node capacity @@ -218,7 +218,7 @@ Web or with `PUT /core/v1/harnesses/{harness}/model-configuration`. Which Sessions use the default, and in what order it applies, is in [Which model provider a Session uses](/user-guide#which-model-provider-a-session-uses). -Full rules: [model execution](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/model-execution.md#deployment-defaults). +Full rules: [model execution](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/model-execution.md#deployment-defaults). The operator file `AGENTS_API_EXECUTION_OPTIONS_FILE` is retired; see [installation version policy](/troubleshooting#installation-version-policy). @@ -238,7 +238,7 @@ A Web-only installation has only `secrets/core.key`, a copy of its Core's key. Core reads only its environment. The installer renders `generated/core.env` from `config.json`; if you run Core yourself (see -[Maintainers and advanced deployments](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/maintainers.md)), set these variables. Compose +[Maintainers and advanced deployments](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/maintainers.md)), set these variables. Compose loads the file with `env_file` and systemd with `EnvironmentFile`, so Compose must be 2.26.0 or newer. @@ -253,7 +253,7 @@ loads the file with `env_file` and systemd with `EnvironmentFile`, so Compose mu | `OAC_INSTALLATION_ID` | `state.json`; pinned to the database | | `OAC_SETTINGS_FILE` | `generated/settings.json`, the snapshot Core serves at `GET /core/v1/installation`; Core does not act on it | | `OAC_EXECUTION_CONCURRENCY`, `OAC_DEFAULT_HARNESS`, `OAC_HARNESSES`, `OAC_WRITE_AUDIT_RETENTION`, `OAC_OAUTH_TRUSTED_ORIGINS` | The matching `core.*` settings | -| `OAC_HISTORY_SETTINGS_FILE` | `generated/runtime-history.json` when `core.runtime_history` is set; see the [history contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-history-api.md) | +| `OAC_HISTORY_SETTINGS_FILE` | `generated/runtime-history.json` when `core.runtime_history` is set; see the [history contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-history-api.md) | | `OAC_LOG_LEVEL`, `OAC_LOG_FORMAT`, `OAC_LOG_ADD_SOURCE` | `log.*`; Web reads the same three | | `OAC_E2B_STATE_DIR`, `OAC_E2B_PROVIDER_BIN` | The E2B receipt directory and, for native Core, the bundled helper. The E2B key and template live in the database | @@ -283,4 +283,4 @@ directory. Core checks the catalog's source revision, Runtime protocol and archi checksums before serving it. This setting supplies installation artifacts only; it does not change Runtime preparation, permissions or execution. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/configuration.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/configuration.md) diff --git a/apps/docs/content/docs/console.mdx b/apps/docs/content/docs/console.mdx index f98204339..76b682aa4 100644 --- a/apps/docs/content/docs/console.mdx +++ b/apps/docs/content/docs/console.mdx @@ -27,7 +27,7 @@ management client, and it sends nothing to `/v1`. | Platform | Projects and keys, Nodes, System | Project and key lifecycle; sandbox deployment and nodes; System: the installation's public address, API base URL, ID and source commit (read-only), each harness's default model provider (write-only key) beside its read-only startup state, the sandbox configuration every Project shares: provider, sandbox size, Runtime or E2B template build, idle suspension and reset, and Core's config.json startup settings with where to change them | Missing data is shown as missing (—), never as zero. How each figure is read and -bounded is recorded in [management interface coverage](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/web/protocol-coverage.md). +bounded is recorded in [management interface coverage](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/web/protocol-coverage.md). ## Management scope @@ -53,15 +53,15 @@ only the console server sends it to Core. - [Connection and authentication](/bootstrap-projects-keys) - [Architecture and ownership](/execution-model) -- [Management interface coverage](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/web/protocol-coverage.md) -- [Frontend handoff and acceptance](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/web/roadmap.md) -- [React application](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) +- [Management interface coverage](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/web/protocol-coverage.md) +- [Frontend handoff and acceptance](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/web/roadmap.md) +- [React application](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/web/README.md) -The [administrator API contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) defines -management routes and resource behavior. The [public API contracts](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md) +The [administrator API contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) defines +management routes and resource behavior. The [public API contracts](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md) define the separate application interface. See the [design principles](/concepts) -for ownership and [contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md) for required checks. +for ownership and [contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md) for required checks. -OpenAgentCore Web is available under the [MIT License](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/LICENSE). +OpenAgentCore Web is available under the [MIT License](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/LICENSE). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/web/README.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/web/README.md) diff --git a/apps/docs/content/docs/development.mdx b/apps/docs/content/docs/development.mdx index 405bdb408..085df5f99 100644 --- a/apps/docs/content/docs/development.mdx +++ b/apps/docs/content/docs/development.mdx @@ -6,7 +6,7 @@ description: "Set up a checkout and choose the right extension boundary." This guide is for contributors changing Core, Runtime, adapters, the Web console or the documentation site. For using a deployment, start with the [getting started guide](/). Read the -[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md) before changing code. +[contributor rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md) before changing code. ## Set up a checkout @@ -18,8 +18,8 @@ git worktree add ../openagentcore-change -b codex/my-change main cd ../openagentcore-change ``` -Install Go at the version in [go.mod](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/go.mod), Node 22, pnpm at the version -in [package.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/package.json), and Python 3.9 or newer. The complete gate +Install Go at the version in [go.mod](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/go.mod), Node 22, pnpm at the version +in [package.json](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/package.json), and Python 3.9 or newer. The complete gate runs on Linux and needs a dedicated PostgreSQL database, OpenSSL development libraries for the microsandbox helper, and a Playwright browser. Provider and Runtime builds have additional prerequisites in their component guides. @@ -65,17 +65,17 @@ under `~/.oac/build/daemon/` by default. `OAC_DEV_HOME` selects another build ro `OAC_DEV_CORE_BUILD_DIR` selects an absolute Core output directory. Building does not configure a database, start a deployment or qualify native execution. -Use the [service guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#build-standalone-binaries) +Use the [service guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/README.md#build-standalone-binaries) to run the Core migrator and server with a separate development database. The [configuration appendix](/configure#appendix-core-environment-without-the-installer) owns standalone process settings. For a complete operator installation, use the [installation guide](/install); building Core alone is a separate contributor workflow. -The [Web package guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) describes console development and +The [Web package guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/web/README.md) describes console development and its management-only integration. `pnpm dev:web` starts the frontend development server; it does not install Core, issue keys or start native execution. The -[docs app guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) describes the independent documentation +[docs app guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/docs/README.md) describes the independent documentation site; `pnpm dev:docs` starts its development server. ## Repository map @@ -83,7 +83,7 @@ site; `pnpm dev:docs` starts its development server. | Location | Responsibility | Read next | | --- | --- | --- | | `services/agents-api/internal/api` | Public, administrator and machine HTTP boundaries | [API index](/public-api) | -| `services/agents-api/internal/store` and `internal/db` | Core persistence, transactions, queries and migrations | [Service guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#database-ownership) | +| `services/agents-api/internal/store` and `internal/db` | Core persistence, transactions, queries and migrations | [Service guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/README.md#database-ownership) | | `services/agents-api/internal/execution` | Durable Turn dispatch and scheduling | [Runtime protocol](/runtime-protocol) | | `services/agents-api/internal/engine` | Pure qualification of harness operations and placements | [Harness onboarding](/harness-onboarding) | | `internal/agentdaemon/proto` and `gateway` | Shared wire types, validators and authenticated Runtime connections | [Runtime protocol](/runtime-protocol) | @@ -91,10 +91,10 @@ site; `pnpm dev:docs` starts its development server. | `apps/parsar-daemon/internal/agent` | Native harness adapters | [Native references](/harness-onboarding#minimal-runnable-example-and-native-references) | | `services/agents-api/internal/sandbox` | Provider interfaces and managed compute lifecycle | [Provider onboarding](/sandbox-provider) | | `services/core-console` | Console login and the server-side management proxy | [Web architecture](/execution-model) | -| `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/README.md) | -| `deploy/install` and `scripts` | Distribution, installation and validation tools | [Maintainers](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/maintainers.md) | -| `contracts/agents-api` | Pinned schema, local semantic contracts and qualification evidence | [Coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md) | -| `apps/docs` | Generated guide and API-reference website | [Generation workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) | +| `apps/web` and `packages/agents-client` | Console UI and typed clients | [Web guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/web/README.md) | +| `deploy/install` and `scripts` | Distribution, installation and validation tools | [Maintainers](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/maintainers.md) | +| `contracts/agents-api` | Pinned schema, local semantic contracts and qualification evidence | [Coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md) | +| `apps/docs` | Generated guide and API-reference website | [Generation workflow](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/docs/README.md) | Core owns durable execution facts. Runtime owns local execution and cleanup. Adapters translate native operations. Providers own outer compute. These boundaries @@ -108,11 +108,11 @@ apply to user-owned machines and Core-managed environments; read the Start with [Harness onboarding](/harness-onboarding). Choose a native SDK or machine-readable protocol, declare the operations and placements you intend to support, and implement the shared -[`Executor` and `Turn` interfaces](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go). +[`Executor` and `Turn` interfaces](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/harness.go). The guide covers factory registration, service profiles, capability declarations, input receipts, event translation, cancellation, history and cleanup. -Use the [synthetic onboarding fixture](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go) +Use the [synthetic onboarding fixture](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/testdata/onboarding/main.go) to see the full registration path. With the dedicated test database configured: ```sh @@ -125,7 +125,7 @@ client, Core, Runtime and a real model. Verify two ordinary Turns, cancellation, continuation after restart, unknown outcomes and the advertised optional operations. Record exact native versions and limitations in the linked operation contracts. -The [Claude bridge guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/claude-sdk-adapter/README.md) explains the +The [Claude bridge guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/packages/claude-sdk-adapter/README.md) explains the separate TypeScript SDK process. Codex and MiniMax implementation references are linked from the onboarding guide. Reuse common process and workspace helpers; keep native event interpretation inside the adapter. @@ -149,7 +149,7 @@ native acceptance remains a separate requirement. ### Add a Sandbox Provider Read [Provider onboarding](/sandbox-provider). Implement -[`SandboxProvider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/sandbox_provider.go) +[`SandboxProvider`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/sandbox/sandbox_provider.go) for the outer compute lifecycle. Register construction and vendor configuration at that boundary; use optional interfaces for observation and checkpoints. Runtime capability preparation and native execution keep their existing owners. @@ -158,7 +158,7 @@ capability preparation and native execution keep their existing owners. make check-sandbox-provider-contract ``` -Run the [shared contract suite](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/contracttest/provider.go) +Run the [shared contract suite](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/sandbox/contracttest/provider.go) through the adapter's native boundary, then qualify actual resource creation, bootstrap, observation, uncertain results and cleanup. Mocked compute cannot prove that resources were reclaimed or that the outer environment provides isolation. @@ -166,7 +166,7 @@ that resources were reclaimed or that the outer environment provides isolation. ## Validate a change Run checks for the affected boundary while developing. The repository -[required checks](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#required-checks) define completion, including +[required checks](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#required-checks) define completion, including `make check` and any changed native component's real acceptance. | Change | Focused validation | @@ -192,13 +192,13 @@ with `AGENTS_FIXTURE_PORT` and `AGENTS_WEB_PORT` when running parallel validatio Keep databases, ports and containers separate between validation workers. Compilation, fixture success and live model/provider acceptance establish different facts; report skipped or unavailable checks explicitly. Follow the -[independent blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#workflow-and-quality) +[independent blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#workflow-and-quality) after validation. ## Change documentation Edit the source that owns the subject, using the -[documentation ownership map](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#documentation-ownership). +[documentation ownership map](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#documentation-ownership). User guides explain a workflow and link to detailed contracts. Contributor rules explain ownership and required checks. Semantic contracts document exact behavior, implementation gaps and evidence. Avoid copying the same rule into all three. @@ -206,9 +206,9 @@ implementation gaps and evidence. Avoid copying the same rule into all three. Update authored sources before regenerating the docs site. Adding a site page also requires a source entry and navigation entry; source/output hashes verify freshness. API references render the namespace-specific generated schemas. Follow -[the docs app workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/docs/README.md) for link, type, build and browser +[the docs app workflow](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/docs/README.md) for link, type, build and browser checks. The release bundle has a separate explicit documentation list in `scripts/core-distribution-manifest.py`; changing a bundled path or heading must also pass its relative-link and anchor checks. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/development.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/development.md) diff --git a/apps/docs/content/docs/environments-and-files.mdx b/apps/docs/content/docs/environments-and-files.mdx index 665efeddf..3ef88fdc0 100644 --- a/apps/docs/content/docs/environments-and-files.mdx +++ b/apps/docs/content/docs/environments-and-files.mdx @@ -3,19 +3,19 @@ title: "Environments and files" description: "Execution placement, Environment resources and workspace ownership." --- -This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json), with partial +This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream.json), with partial coverage. Core-managed Docker runs the three qualified native harnesses. V1 user-managed `self_hosted` enrollment uses the same colocated Runtime for Codex, Claude SDK and MiniMax. `/workspace` names the packaged workspace; a self-hosted Session may instead select its exact canonical physical directory. The -[recorded deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) retains its historical +[recorded deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/user-managed-runtime-v1.md) retains its historical source, paths and tested capability scope. For hosted compute, the deployment selects E2B, Docker or microsandbox through -[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md). +[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md). For the separate caller-managed E2B path, the user owns allocation, renewal and -cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md). -[Templates](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md) remain a hosted-only resource path; -[Files](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace. +cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/deploy/e2b/README.md). +[Templates](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-templates.md) remain a hosted-only resource path; +[Files](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace. The internal Store now owns a durable Environment association for newly created `self_hosted` and `openai_hosted` snapshots, atomically with Session creation. @@ -33,7 +33,7 @@ readiness. Rotation/revocation, Session deletion and ownership loss deny further access without promising immediate cessation of native effects. Native history remains local to the bound Runtime and cannot be replaced on retry. There is no registry/Noise relay or transient service-side harness credential. -See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#user-managed-runtime-enrollment). +See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/README.md#user-managed-runtime-enrollment). ## Basic public Docker-hosted profile @@ -41,8 +41,8 @@ An explicitly configured default managed Docker provider enables `type=openai_ho for the qualified Codex, Claude Code and MiniMax Code profiles. Each uses the same Runtime lifecycle and workspace interfaces with its native adapter. The outer Environment provides managed isolation; the daemon adds no inner sandbox. -See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#public-engine-profiles) for setup and limits. -The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/codex/README.md#standalone-operator-configuration) +See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md#public-engine-profiles) for setup and limits. +The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/deploy/codex/README.md#standalone-operator-configuration) selects the qualified immutable Runtime image; advertised capabilities alone do not enable admission. An idle or initial-text creation commits Session, Environment and retry identity before the existing leased Worker provisions its allocation. @@ -63,7 +63,7 @@ rejects explicitly, including null and empty lists, and never invokes apt, sudo or elevated execution. Package responses include the official required `system: []` field as empty metadata; it does not enable installation. Unsupported hostname forms and installation combinations reject explicitly; see -the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md). Empty/null installation +the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-templates.md). Empty/null installation defaults produce safe empty metadata, not a live workspace inventory. Service-origin hosted MCP remains unsupported; Environment Plugin MCP has its own qualified transport matrix. @@ -83,11 +83,11 @@ semantics; this profile does not establish complete Environment compatibility. The application creates, renews and destroys its E2B sandbox through the official SDK. It deploys the shared Runtime, then enrolls that Runtime into a `self_hosted` Session. Core neither keeps an E2B allocation nor issues Provider renew/kill calls. -The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md) owns packaging and +The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/deploy/e2b/README.md) owns packaging and user-side lifecycle instructions. Expiry or lost workspace/history must not trigger -transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) +transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/user-managed-runtime-v1.md) records its own real deployment evidence. -The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md#e2b-v1-qualification) records a historical +The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md#e2b-v1-qualification) records a historical Core-managed route; it does not establish this caller-managed enrollment result. Current deployment-managed E2B is a separate hosted configuration in the manager guide. @@ -125,7 +125,7 @@ connection alone is not readiness. Retries preserve identity and the five-minute database deadline. HTTP disconnect retains the reservation. Local expired/cancelled outcomes return 409, lost ownership 503, and deletion 404; exact hosted error status/body and pending-input crash recovery are unverified. See the -[canonical wait rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#environment-ownership-and-placement). +[canonical wait rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#environment-ownership-and-placement). Cancellation-only batches use the existing locked admission and native delivery. An idle cancellation creates no Turn, and retry identity preserves the original @@ -200,7 +200,7 @@ Managed Providers remain Linux-only. The daemon does not sandbox tools, files or network access; tools use the starting account's permissions. Native installation and current validation limits are in the [native guide](/self-hosted-native). Historical deployment acceptance does not qualify later platform or capability changes. See the -[canonical preparation rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#shared-runtime-capability-preparation). +[canonical preparation rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#shared-runtime-capability-preparation). ## Contract inventory @@ -255,7 +255,7 @@ from Session and Turn status. ## Ownership and placement decision -The canonical [architecture rules](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#environment-ownership-and-placement) +The canonical [architecture rules](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#environment-ownership-and-placement) keep Environment lifecycle common while leaving process placement and native transport to adapters. Logical ownership does not require a machine per object. @@ -279,4 +279,4 @@ native history independently of disposable compute, or prove native restoration; never treat an Environment ID as a filesystem or history backup. Do not silently move an existing Session away from its bound device. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environments.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environments.md) diff --git a/apps/docs/content/docs/examples.mdx b/apps/docs/content/docs/examples.mdx index 5e7436ebe..4f91bf44b 100644 --- a/apps/docs/content/docs/examples.mdx +++ b/apps/docs/content/docs/examples.mdx @@ -16,7 +16,7 @@ A small single-user product built on Core. You save model providers, Skills and servers, combine them into Agents, then start Sessions in a managed sandbox, with no workspace, or on your own machine. -Source: [`example/parsar`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/README.md). +Source: [`example/parsar`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/README.md). ### Run it @@ -30,7 +30,7 @@ pnpm install --frozen-lockfile pnpm --filter @oac/parsar-example dev ``` -Open <http://127.0.0.1:18180>. The example [README](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/README.md) covers +Open <http://127.0.0.1:18180>. The example [README](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/README.md) covers storage, a production build, tests and connecting your own machine. ### What to look at @@ -39,15 +39,15 @@ Each feature maps to one part of the API. Read the code next to the guide sectio | Feature | API used | Guide | Code | | --- | --- | --- | --- | -| Server-side key and headers | Bearer key, `OpenAI-Beta`, route allowlist | [Before you start](/sessions#before-you-start) | [`server.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/server.mjs) | -| Agents with a harness and model | `x_agents_core.harness`, `model_provider` | [Core extensions](/sessions#core-extensions-x_agents_core) | [`server/sessions.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/server/sessions.mjs) | -| Sessions on three runtimes | `openai_hosted`, `none`, `self_hosted` with `workspace_directory` | [Create a Session](/sessions#create-a-session) | [`server/sessions.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/server/sessions.mjs) | -| Crash-safe Session creation | `Idempotency-Key`, recovery from a lost response | [Idempotency](/sessions#idempotency) | [`server/sessions.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/server/sessions.mjs) | -| Live replies | Event stream, text deltas, reconnect and reconcile | [Stream events](/sessions#stream-events) | [`src/lib/live-session.ts`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/src/lib/live-session.ts) | -| Follow-ups and cancel | Input events with idempotency keys | [Send input](/sessions#send-input) | [`src/Composer.tsx`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/src/Composer.tsx) | -| History | Paging Turns and Items | [Pagination](/sessions#pagination) | [`src/lib/api.ts`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/src/lib/api.ts) | -| Skills and versions | `/skills`, default version | [Skills](/sessions#skills) | [`src/Skills.tsx`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/src/Skills.tsx) | -| Connect your own machine | Environment status, executor credential | [Self-hosted execution](/self-hosted-execution) | [`src/ConnectMachine.tsx`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/parsar/src/ConnectMachine.tsx) | +| Server-side key and headers | Bearer key, `OpenAI-Beta`, route allowlist | [Before you start](/sessions#before-you-start) | [`server.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/server.mjs) | +| Agents with a harness and model | `x_agents_core.harness`, `model_provider` | [Core extensions](/sessions#core-extensions-x_agents_core) | [`server/sessions.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/server/sessions.mjs) | +| Sessions on three runtimes | `openai_hosted`, `none`, `self_hosted` with `workspace_directory` | [Create a Session](/sessions#create-a-session) | [`server/sessions.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/server/sessions.mjs) | +| Crash-safe Session creation | `Idempotency-Key`, recovery from a lost response | [Idempotency](/sessions#idempotency) | [`server/sessions.mjs`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/server/sessions.mjs) | +| Live replies | Event stream, text deltas, reconnect and reconcile | [Stream events](/sessions#stream-events) | [`src/lib/live-session.ts`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/src/lib/live-session.ts) | +| Follow-ups and cancel | Input events with idempotency keys | [Send input](/sessions#send-input) | [`src/Composer.tsx`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/src/Composer.tsx) | +| History | Paging Turns and Items | [Pagination](/sessions#pagination) | [`src/lib/api.ts`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/src/lib/api.ts) | +| Skills and versions | `/skills`, default version | [Skills](/sessions#skills) | [`src/Skills.tsx`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/src/Skills.tsx) | +| Connect your own machine | Environment status, executor credential | [Self-hosted execution](/self-hosted-execution) | [`src/ConnectMachine.tsx`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/parsar/src/ConnectMachine.tsx) | ### Not covered @@ -56,10 +56,10 @@ own machine. ## Add an example -Put it in its own directory under [`example/`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/example/README.md) with a README +Put it in its own directory under [`example/`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/example/README.md) with a README covering how to run it and which API features it shows, then add a row to the table above. Keep examples on the public API with a Project API key: never the Core key or private routes. Repository rules for examples are in -[CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#repository-boundary). +[CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#repository-boundary). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/examples.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/examples.md) diff --git a/apps/docs/content/docs/execution-model.mdx b/apps/docs/content/docs/execution-model.mdx index 76ccabb32..729b921d5 100644 --- a/apps/docs/content/docs/execution-model.mdx +++ b/apps/docs/content/docs/execution-model.mdx @@ -10,7 +10,7 @@ Agents API independently with their own Project keys. The management backend, `AdminClient` and the React console built on them are implemented. The [design principles](/concepts) define identity and authority. -The [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) defines exact +The [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) defines exact routes, payloads, pagination and audit records. ## Request boundaries @@ -97,6 +97,6 @@ comes from the deployment's allocations; the hosted Runtime total counts hosted observation records across projects and reported lifecycle states. These sources have different coverage and refresh independently, so the console does not infer resource retention or cleanup from their difference. -Native execution ownership remains governed by [CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md). +Native execution ownership remains governed by [CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/web/architecture.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/web/architecture.md) diff --git a/apps/docs/content/docs/harness-onboarding.mdx b/apps/docs/content/docs/harness-onboarding.mdx index 984b0b0fc..217f32339 100644 --- a/apps/docs/content/docs/harness-onboarding.mdx +++ b/apps/docs/content/docs/harness-onboarding.mdx @@ -3,17 +3,18 @@ title: "Add a Harness" description: "Implement and validate a native Harness adapter." --- -Start with [`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go). -It defines the required lifecycle, separate optional interfaces and existing -registration methods in one place. Implement one adapter for a native SDK or machine-readable protocol. Core and +Start with the shared [model configuration contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/harnessconfig/harness.go) +and the [execution lifecycle](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/harness.go). +The first owns configuration declarations and preparation; the second defines +the required lifecycle, separate optional interfaces and registration methods. Implement one adapter for a native SDK or machine-readable protocol. Core and Runtime use the common contract; adding a harness should not add engine-name branches to public handlers, storage, scheduling or environment providers. -The [harness contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md) defines observable behavior. Start with the +The [harness contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/harnesses.md) defines observable behavior. Start with the [developer guide](/development) for a working checkout; the -[contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#harness-qualification-and-onboarding) +[contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#harness-qualification-and-onboarding) owns architecture and required checks. The pinned public Agents API in -[upstream.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json) is separate from the internal adapter protocol. +[upstream.json](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream.json) is separate from the internal adapter protocol. ## Ownership @@ -57,9 +58,21 @@ model communication configuration, not Turn scheduling or native process ownersh ## Native model configuration -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go) owns the -preparation, validation, reserved-field, effective-connection and cleanup contract. -The shared wire object is `proto.HarnessConfig`. Adapter declarations in +[`internal/harnessconfig/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/harnessconfig/harness.go) owns +the shared configuration declaration and pure preparation contract. Each adapter +supplies one `Configuration` to Core's composition and Runtime's `RegisterKind`. +All three Runtime entry paths validate through that declaration before native +side effects: direct factory, preparation and Executor. Registry wrappers retain +the declaration alongside the factory. Lifecycle and cleanup ownership remain in +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/harness.go). +The shared wire object is `proto.HarnessConfig`. + +A supplied `model` must be a nonempty string, and an explicit `model_provider` +requires it. The native-owned connection path may omit both; explicit null is +invalid. An explicitly empty adapter declaration accepts no provider or nonempty +native parameters. It does not advertise provider support. Unknown protocol +formats and duplicate protocol declarations fail at registration. + Adapter declarations in `internal/harnessconfig/` own these native fields: | Harness | Accepted native fields | Application | @@ -75,16 +88,19 @@ These are native settings, not a shared reasoning vocabulary; model availability and provider support remain the selected harness's responsibility. Native protocol and parameter declarations also feed Core administration's small -configuration-support descriptor. `modeltransport` selects and validates routes -from those declarations. Its conversion qualification is distinct from adapter -acceptance and remote-model support. Follow the route and per-input requirements -in `agent/harness.go`; do not add a second model capability registry or infer -capabilities from model names. Nonempty `harness_config` currently requires a native -protocol route because the translator has no loss report for those parameters. +configuration-support descriptor. Its ordered `protocols` list is the sole source +for accepted protocols and the default (the first entry). Core and Runtime reject +unsupported combinations through the same declaration. The current protocol +matrix belongs to [model execution](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/model-execution.md#saved-defaults-and-precedence). +Adapters connect directly through native configuration; they must not introduce +a model API proxy or protocol converter. Follow the per-input capability +requirements in the execution lifecycle contract; do not add a second model capability registry +or infer capabilities from model names. Native protocol acceptance and remote +model support remain separate facts. ## Required adapter interfaces -[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/harness.go) is the +[`agent/harness.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/harness.go) is the canonical interface entry point. Its required lifecycle is `ExecutorFactory`, `Executor`, `Turn` and `TurnSettlement`. Optional Turn and workspace interfaces remain separate; their result types and error values stay in the corresponding @@ -120,7 +136,7 @@ semantics in each case. ## Events, inputs and optional capabilities -Use [`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto) for neutral +Use [`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto) for neutral requests, events and receipts. Each Turn emits only its own events with its Run ID, in order, and one terminal outcome. Native IDs and usage must be observed rather than invented. Capture the originating Turn before asynchronous native callbacks; @@ -160,7 +176,8 @@ still belongs to the service profile and is not granted by Runtime registration. 1. Pin the upstream source/package version and document the native entry point. 2. Implement the adapter using its SDK or native protocol. Reuse shared process, credential/configuration and local workspace helpers where applicable. -3. Register its `SupportedAgentKind` and direct-call factory with `RegisterKind`, +3. Register its `SupportedAgentKind`, shared `Configuration` and direct-call + factory with `RegisterKind`, then register the shared `ExecutorFactory` with `RegisterExecutor`. A direct-call factory should delegate to the same Executor implementation. Register separate read-only preparations only when required by qualified workspace operations. Runtime @@ -174,7 +191,7 @@ still belongs to the service profile and is not granted by Runtime registration. The static registration surface requires a build. Dynamic plugins are outside this contract. A small adapter does not remove the need for native qualification. The runnable test-only example is -[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go). +[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/testdata/onboarding/main.go). It registers a text-only synthetic harness and is never shipped as a real engine. ## Required behavior versus optional operations @@ -193,14 +210,14 @@ explicitly and record the gap. Never advertise a capability to bypass selection. Structured-output adapters consume `ExecutionControls.OutputFormat` and publish confirmed native output through the existing Message contract. Register public qualification separately from the Runtime capability; see the -[structured-output boundary](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/structured-output.md). No Core engine-name branch is required. +[structured-output boundary](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/structured-output.md). No Core engine-name branch is required. Initial requests, `Executor.StartTurn` and steering consume the same ordered `proto.MessageInput`. Text-only adapters use `TextOnly()` to reject images without discarding content. Image adapters translate each part natively and acknowledge an active batch only after all its messages are applied. Register `MessageImages` and qualify `MessageImagePlacements` separately; see the -[message-input contract and real acceptance](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md). +[message-input contract and real acceptance](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/message-input.md). Hosted workspace execution additionally requires verified preparation, workspace reads/output export, network behavior and credential/history isolation. Reuse the @@ -232,7 +249,7 @@ it does not expand to match another harness's feature list. development, then `make check` and applicable real regressions. API changes require `make openapi`; query changes require `make sqlc-generate`. - Review: follow the repository - [blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md#workflow-and-quality). + [blind review workflow](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md#workflow-and-quality). Record exact revisions, image/package versions, commands, results and limits. Keep keys in private operator files; never commit them or include them in logs or @@ -242,7 +259,7 @@ full board. Acceptance of one engine is not complete public protocol compatibili ## Minimal runnable example and native references -[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/testdata/onboarding/main.go) +[`testdata/onboarding/main.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/testdata/onboarding/main.go) is a runnable synthetic adapter. It demonstrates registration, a Session-owned Executor, fresh Turns, durable steering, cancellation and history binding through the actual gateway and Router. Run `TestThirdHarnessPublicOnboarding` against a @@ -251,18 +268,18 @@ not shipped and is not evidence for a real SDK or sandbox. Use the native adapters as implementation references after choosing a native API: -- [Codex](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/codex/executor.go): app-server transport. -- [Claude](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/claudesdk/executor.go): Go ownership and a [TypeScript SDK bridge](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/packages/claude-sdk-adapter/README.md). -- [MiniMax](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/agent/mcode): ACP and native workspace companion. +- [Codex](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/codex/executor.go): app-server transport. +- [Claude](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/claudesdk/executor.go): Go ownership and a [TypeScript SDK bridge](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/packages/claude-sdk-adapter/README.md). +- [MiniMax](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/agent/mcode): ACP and native workspace companion. Start with the mandatory text lifecycle, then qualify optional operations using -[harness coverage](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harnesses.md) and each operation's contract. Do not copy an +[harness coverage](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/harnesses.md) and each operation's contract. Do not copy an adapter's native limitations into the shared Core protocol. ## Optional Subagent observations A harness that supports the Subagent resource reads implements the existing -[neutral observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/subagents.md#common-adapter-contract). It reports +[neutral observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/subagents.md#common-adapter-contract). It reports verified child identity, lifecycle effects and owned Turn/Item history through the authenticated Run, then qualifies those facts with real execution. It does not add routes, storage branches or a harness-specific Core scheduler. Report @@ -281,4 +298,4 @@ installation; adapters own native layout and probes. Validate installation and actual execution on each advertised platform. Missing or incompatible native content must fail, never install itself during a Turn. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harness-onboarding.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/harness-onboarding.md) diff --git a/apps/docs/content/docs/hosted-providers.mdx b/apps/docs/content/docs/hosted-providers.mdx index abd1c09d7..25ad6bfde 100644 --- a/apps/docs/content/docs/hosted-providers.mdx +++ b/apps/docs/content/docs/hosted-providers.mdx @@ -10,7 +10,7 @@ With E2B you need no nodes. Self-hosted executors, which applications run for th own Sessions, are a different thing; see [Self-hosted executors](/self-hosted-execution). You add a node by generating a command in Web and pasting it on the host. The -[operator reference](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md) covers the +[operator reference](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md) covers the node protocol, manual registration, placement and failure handling. ## Before you add a node @@ -165,7 +165,7 @@ The installation ID is in the command (`--installation-id`) and on the **System* 1. In Web, open **Nodes** and choose **Remove node** on the node's page, or **Remove** in its list row, then **Confirm removal**. Core refuses while the node still holds sandboxes, snapshots or pending cleanup; let them finish, or - [archive their Sessions](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-reset) + [archive their Sessions](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-reset) through the Core API. Removal is permanent: the host can come back only as a new node. 2. Web then shows **Clean up the host** with the uninstall command. Run it on the host: @@ -263,7 +263,7 @@ files. Shared daemon images remain; a host administrator may remove them only af confirming no installation still needs them. Microsandbox image cleanup remains scoped to the installation's private native store and verified ownership receipts. -The [operator reference](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-reset) +The [operator reference](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-reset) describes reset and API behavior. Same-team E2B edits have a separate online Core API path and do not involve node installation. @@ -276,4 +276,4 @@ convert or delete it. Preserve the old installation, Runtime resources and histo provision a fresh node separately through **Add node**. The current installer does not adopt historical resources or remove another installation's state. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/nodes.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/nodes.md) diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx index 5bf243458..9c86b0981 100644 --- a/apps/docs/content/docs/index.mdx +++ b/apps/docs/content/docs/index.mdx @@ -5,7 +5,7 @@ description: "Install Core and Web, create a Project API key, and add execution OpenAgentCore runs AI agents on your own infrastructure behind the OpenAI Agents API. Pick the path that matches your role. New here? Read the -[architecture overview](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/architecture.md) first. +[architecture overview](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/architecture.md) first. ## Operators: install and run @@ -38,6 +38,6 @@ API. Pick the path that matches your role. New here? Read the Start with the [developer guide](/development): repository map, local setup, and how to add a Sandbox Provider, a harness or a Runtime feature. Repository rules -are in [CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md). +are in [CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/README.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/README.md) diff --git a/apps/docs/content/docs/install-options.mdx b/apps/docs/content/docs/install-options.mdx index a59b387d9..d35c1a0a0 100644 --- a/apps/docs/content/docs/install-options.mdx +++ b/apps/docs/content/docs/install-options.mdx @@ -70,4 +70,4 @@ See [deployment modes](/install#modes), [native Core](/install#native-core) and Several installations can share a machine when they use distinct installation directories and ports. Each has its own database, Core key and node registrations. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/install-options.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/install-options.md) diff --git a/apps/docs/content/docs/install.mdx b/apps/docs/content/docs/install.mdx index 292b61028..b6e3b8175 100644 --- a/apps/docs/content/docs/install.mdx +++ b/apps/docs/content/docs/install.mdx @@ -263,7 +263,7 @@ A deployment runs its sandboxes on exactly one backend: | `none` | Nothing yet | Choose on the **Nodes** page in Web | The Standard sizes come from Web's -[`standard-sizes.json`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/web/src/features/sandbox/standard-sizes.json). If +[`standard-sizes.json`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/web/src/features/sandbox/standard-sizes.json). If Core refuses the choice, for example because E2B rejects the key, the installer prints Core's message and exits; the services keep running and you choose the backend in Web. To change the backend or size later, see @@ -277,7 +277,7 @@ without a terminal, pass `--accept-docker-risks`. **E2B** needs a public HTTPS URL that is not loopback, because E2B's sandboxes call Core from E2B's cloud. Prepare the template build with the -[E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/deploy/e2b/README.md), then: +[E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/deploy/e2b/README.md), then: ```sh ./install.sh --public-url https://core.example --sandbox e2b \ @@ -455,4 +455,4 @@ release; see [installation version policy](/troubleshooting#installation-version An installation from before the rename, at `~/.parsar/core`, blocks a new default install. Keep it and choose another `--install-dir`. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/install.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/install.md) diff --git a/apps/docs/content/docs/observability.mdx b/apps/docs/content/docs/observability.mdx index 1f3791f5a..16d1ad521 100644 --- a/apps/docs/content/docs/observability.mdx +++ b/apps/docs/content/docs/observability.mdx @@ -7,7 +7,7 @@ Status: Phase 2 and initial Core Web consumption implemented. The current-snapsh `packages/agents-client` projection, and generated `core.openapi.yaml` contract are implemented and consumed by the Dashboard through complete Session/observation identity joins. Durable history uses the separate optional -[Runtime history API](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-history-api.md); lifecycle controls remain outside +[Runtime history API](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-history-api.md); lifecycle controls remain outside this phase. These are administrator reads under `/core/v1`, authenticated by the Core key, @@ -92,7 +92,7 @@ Authorization: Bearer ... ``` This returns the same object shape as a list item's `observation`, without the -administrator list's `disk` (see the [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md)). It never starts a Turn, creates +administrator list's `disk` (see the [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md)). It never starts a Turn, creates an Environment, provisions compute, renews a lease, or changes lifecycle state. A valid `environment:none` Session returns `200` with status `unsupported`; the @@ -193,7 +193,7 @@ Use the existing Agents API error envelope. | 400 | `invalid_request_error` / `invalid_request_error` | List: a repeated supported query key, or an empty or invalid limit or order, with the shared Beta list messages. Unknown list query keys are ignored. | | 400 | `invalid_request_error` / `unsupported_parameter` | Single-Session retrieval with any query parameter. | | 401 | `invalid_request_error` / `invalid_admin_key` | Missing or invalid Core key. | -| 404 | `not_found_error` / `not_found_error` | Missing, malformed or foreign Session/cursor, indistinguishably, as for the [Session list cursor](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/list-query-semantics.md#list-cursor-errors--september-23-2026). | +| 404 | `not_found_error` / `not_found_error` | Missing, malformed or foreign Session/cursor, indistinguishably, as for the [Session list cursor](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/list-query-semantics.md#list-cursor-errors--september-23-2026). | | 500 | `server_error` / `internal_error` | Integrity, ownership, or invalid provider evidence. | | 503 | `server_error` / `execution_unavailable` | Required Runtime observation service is not configured, or list collection exceeded its request budget. | @@ -273,10 +273,10 @@ deletion makes the candidate incomplete and prevents publication. - Token usage: use existing Session/Turn Usage. - Billing and cost: product/backend concern. - Historical series in these routes: the optional capability uses the separate - [Runtime history API](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-history-api.md). + [Runtime history API](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-history-api.md). - Container logs and command output. - Provider credentials or native configuration. - Start, stop, pause, resume, restart, renew, or delete operations. - Idle classification and automatic shutdown. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-observability-api.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-observability-api.md) diff --git a/apps/docs/content/docs/public-api.mdx b/apps/docs/content/docs/public-api.mdx index 6fb323940..cb46b31c8 100644 --- a/apps/docs/content/docs/public-api.mdx +++ b/apps/docs/content/docs/public-api.mdx @@ -10,8 +10,8 @@ public release content. | Namespace | Caller | Credential | Contents | Reference | | --- | --- | --- | --- | --- | | `/v1` | Applications (business systems, SDKs) | Project API key | Exactly the pinned official Agents API routes. Core-only fields live only in `x_agents_core` (`harness`, `model_provider`, `harness_config`, Session `installation`) | [Agents API guide](/sessions) | -| `/core/v1` | Core Web's server and operator scripts | [Core key](/troubleshooting#core-key) | Installation facts, Projects and keys, resource reads and deletion, Session archive, credential issuance, metrics, audit, sandbox deployment and nodes, deployment model providers | [Core API](#core-api), [Web API](/admin-api), [Core OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core.openapi.yaml) | -| `/api/v1` | Nodes, Runtime daemons, self-hosted executors | Machine credentials: short-lived Session installation grants, node enrollment tokens and executor credentials issued through `/core/v1` or claimed by installation, node credentials registered with an enrollment token, and daemon credentials Core writes into hosted sandboxes | Machine bootstrap and connections: `/api/v1/sandbox-node/*` and `/api/v1/agent-daemon/*`, including WebSockets; each credential works only on its own routes | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host), [executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-executor-credentials.md), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime.openapi.yaml) | +| `/core/v1` | Core Web's server and operator scripts | [Core key](/troubleshooting#core-key) | Installation facts, Projects and keys, resource reads and deletion, Session archive, credential issuance, metrics, audit, sandbox deployment and nodes, deployment model providers | [Core API](#core-api), [Web API](/admin-api), [Core OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core.openapi.yaml) | +| `/api/v1` | Nodes, Runtime daemons, self-hosted executors | Machine credentials: short-lived Session installation grants, node enrollment tokens and executor credentials issued through `/core/v1` or claimed by installation, node credentials registered with an enrollment token, and daemon credentials Core writes into hosted sandboxes | Machine bootstrap and connections: `/api/v1/sandbox-node/*` and `/api/v1/agent-daemon/*`, including WebSockets; each credential works only on its own routes | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host), [executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime.openapi.yaml) | A credential used in another namespace gets 401: a Project API key on `/core/v1` or `/api/v1`, the Core key on `/v1` or `/api/v1`. How Projects and keys behave is in @@ -26,7 +26,7 @@ on Core's loopback port. Details: [Web and Core](/admin-api). ## Public API Applications call `/v1` with a Project API key. The routes are exactly the 58 pairs -in [upstream-routes.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream-routes.json). The +in [upstream-routes.json](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream-routes.json). The [Agents API guide](/sessions) explains every resource with SDK and HTTP examples. @@ -61,24 +61,24 @@ core() { # core METHOD PATH [JSON body] | Issue an executor credential | See [self-hosted execution](/self-hosted-execution#operator-credential-management) | | Installation facts, including the API base URL | `core GET /installation` | -Errors use the [Core error envelope](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core-errors.md). +Errors use the [Core error envelope](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core-errors.md). ### All routes | Routes | Contents | Contract | | --- | --- | --- | -| `projects`, `projects/{project_id}[/archive]`, `projects/{project_id}/keys[/{key_id}]` | Projects and their API keys | [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) | -| `projects/{project_id}/{agents,environment-templates,skills,files,vaults,sessions}/**` | Resource reads and deletion, Session history, artifacts and archive | [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) | -| `projects/{project_id}/sessions/{session_id}/execution-configuration` | Committed harness and model selection | [Execution configuration](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/execution-configuration.md) | -| `projects/{project_id}/sessions/{session_id}/diagnostics`, `projects/{project_id}/sessions/{session_id}/turns/{turn_id}/diagnostics` | Root failure categories and Item receipt timing | [Session diagnostics](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/session-diagnostics.md) | +| `projects`, `projects/{project_id}[/archive]`, `projects/{project_id}/keys[/{key_id}]` | Projects and their API keys | [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) | +| `projects/{project_id}/{agents,environment-templates,skills,files,vaults,sessions}/**` | Resource reads and deletion, Session history, artifacts and archive | [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) | +| `projects/{project_id}/sessions/{session_id}/execution-configuration` | Committed harness and model selection | [Execution configuration](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/execution-configuration.md) | +| `projects/{project_id}/sessions/{session_id}/diagnostics`, `projects/{project_id}/sessions/{session_id}/turns/{turn_id}/diagnostics` | Root failure categories and Item receipt timing | [Session diagnostics](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/session-diagnostics.md) | | `projects/{project_id}/sessions/{session_id}/runtime-observation`, `sandbox/runtime-observations` | Current Runtime observations | [Runtime observations](/observability) | -| `projects/{project_id}/sessions/{session_id}/runtime-history` | Stored Runtime history | [Runtime history](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-history-api.md) | -| `projects/{project_id}/{resource-owners,write-operations}`, `audit-log`, `summary` | Provenance, write history, administrator audit and usage summary | [Write audit](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/write-audit.md), [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) | -| `projects/{project_id}/environments/{environment_id}/executor-credentials[/{key_id}]` | Executor credentials for a self-hosted Environment | [Executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-executor-credentials.md) | -| `installation` | Public URL, API base URL, source commit, the installer's process settings and what is bound to the public URL; available before any deployment | [Installation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/installation.md) | -| `metrics` | Core's own process metrics | [Core metrics](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core-metrics.md) | -| `sandbox/deployment[/reset]`, `sandbox/e2b/templates[/{template_id}/builds]`, `sandbox/enrollment-tokens`, `sandbox/nodes[/{node_id}[/allocations]]` | Sandbox deployment, read-only E2B template discovery, node enrollment tokens and nodes | [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md), [node operations](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md), [node host history](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/node-host-history.md) | -| `harnesses`, `harnesses/{harness}/model-configuration` | Supported harnesses and each harness's deployment default model configuration (write-only provider key) | [Model execution](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/model-execution.md#deployment-defaults) | +| `projects/{project_id}/sessions/{session_id}/runtime-history` | Stored Runtime history | [Runtime history](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-history-api.md) | +| `projects/{project_id}/{resource-owners,write-operations}`, `audit-log`, `summary` | Provenance, write history, administrator audit and usage summary | [Write audit](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/write-audit.md), [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) | +| `projects/{project_id}/environments/{environment_id}/executor-credentials[/{key_id}]` | Executor credentials for a self-hosted Environment | [Executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md) | +| `installation` | Public URL, API base URL, source commit, the installer's process settings and what is bound to the public URL; available before any deployment | [Installation](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/installation.md) | +| `metrics` | Core's own process metrics | [Core metrics](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core-metrics.md) | +| `sandbox/deployment[/reset]`, `sandbox/e2b/templates[/{template_id}/builds]`, `sandbox/enrollment-tokens`, `sandbox/nodes[/{node_id}[/allocations]]` | Sandbox deployment, read-only E2B template discovery, node enrollment tokens and nodes | [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/sandbox-deployment.md), [node operations](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md), [node host history](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/node-host-history.md) | +| `harnesses`, `harnesses/{harness}/model-configuration` | Supported harnesses and each harness's deployment default model configuration (write-only provider key) | [Model execution](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/model-execution.md#deployment-defaults) | ## Machine connection API @@ -87,37 +87,37 @@ the Core key or a Project API key. | Routes | Caller | Credential | Contract | | --- | --- | --- | --- | -| `POST sandbox-node/enroll` | Node installer | One-use enrollment token from `POST /core/v1/sandbox/enrollment-tokens` | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime.openapi.yaml) | -| `GET sandbox-node/configuration` | Node installer and node | Enrollment token, or node credential with `X-OAC-Node-ID` | [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/sandbox-deployment.md), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime.openapi.yaml) | -| `GET sandbox-node/identity`, WebSocket `GET sandbox-node/connect` | Node | Node credential registered at enrollment | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host) | -| `POST agent-daemon/enroll`, `GET agent-daemon/connection` | Self-hosted executor and its installer | Executor credential from `/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` | [Executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-executor-credentials.md) | -| WebSocket `GET agent-daemon/ws`, `POST agent-daemon/bootstrap`, `GET agent-daemon/device-status` | Runtime daemons | Daemon credential: Core writes one into each hosted sandbox it prepares; a self-hosted executor uses its executor credential | [Runtime enrollment](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/README.md#user-managed-runtime-enrollment) | +| `POST sandbox-node/enroll` | Node installer | One-use enrollment token from `POST /core/v1/sandbox/enrollment-tokens` | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime.openapi.yaml) | +| `GET sandbox-node/configuration` | Node installer and node | Enrollment token, or node credential with `X-OAC-Node-ID` | [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/sandbox-deployment.md), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime.openapi.yaml) | +| `GET sandbox-node/identity`, WebSocket `GET sandbox-node/connect` | Node | Node credential registered at enrollment | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host) | +| `POST agent-daemon/enroll`, `GET agent-daemon/connection` | Self-hosted executor and its installer | Executor credential from `/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` | [Executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md) | +| WebSocket `GET agent-daemon/ws`, `POST agent-daemon/bootstrap`, `GET agent-daemon/device-status` | Runtime daemons | Daemon credential: Core writes one into each hosted sandbox it prepares; a self-hosted executor uses its executor credential | [Runtime enrollment](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/README.md#user-managed-runtime-enrollment) | ## Contract sources -- [Pinned upstream baseline](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream.json): OpenAI +- [Pinned upstream baseline](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream.json): OpenAI Python SDK 3.13.0, exact upstream commit and `agents=v1`. -- [Pinned routes](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream-routes.json) and - [fields](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream-fields.json): extracted from the +- [Pinned routes](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream-routes.json) and + [fields](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream-fields.json): extracted from the pinned SDK by `scripts/extract-agents-api-upstream.py`. Contract tests require the public OpenAPI to have exactly these routes and to keep every other field inside `x_agents_core`. -- [Public OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/openapi.yaml): public schema snapshot; - combine it with the fixed SDK and [operation evidence](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/operation-evidence.md). -- [Core OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/core.openapi.yaml): generated `/core/v1` +- [Public OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/openapi.yaml): public schema snapshot; + combine it with the fixed SDK and [operation evidence](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/operation-evidence.md). +- [Core OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/core.openapi.yaml): generated `/core/v1` routes, all authenticated by the Core key. -- [Machine connection OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime.openapi.yaml): - generated `/api/v1` sandbox node routes. The node WebSocket is described in the [generation protocol](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/node-generation-protocol.md); the private +- [Machine connection OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime.openapi.yaml): + generated `/api/v1` sandbox node routes. The node WebSocket is described in the [generation protocol](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/node-generation-protocol.md); the private daemon transport is described in the Runtime credential guide. -- [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md): Project/key +- [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md): Project/key lifecycle, resources, explicit hosted Session archive, summary, errors/deletion preconditions, audit and historical copy provenance. - [Web integration](/admin-api): browser, console and Core boundaries and frontend handoff. -- [Design rules](/concepts) and [contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/CONTRIBUTING.md): +- [Design rules](/concepts) and [contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/CONTRIBUTING.md): ownership, security and change requirements. Generated schemas do not establish complete compatibility or real execution -support. The [coverage record](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md) identifies +support. The [coverage record](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md) identifies qualified workflows, native differences and unresolved behavior. Update the relevant contract and this index when adding or moving an API surface. @@ -132,4 +132,4 @@ subroute with the installation Bearer authorization. Qualified artifacts under the [native Runtime guide](/self-hosted-native) for expiry, retry, credential ownership and platform rules. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/api/README.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/api/README.md) diff --git a/apps/docs/content/docs/quickstart.mdx b/apps/docs/content/docs/quickstart.mdx index 32ce20a66..b1983ce82 100644 --- a/apps/docs/content/docs/quickstart.mdx +++ b/apps/docs/content/docs/quickstart.mdx @@ -126,4 +126,4 @@ cancels the work nor proves it failed. | Run the agent on your own machine | [Self-hosted execution](/self-hosted-execution) | | See a complete application | [Examples](/examples) | -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/quickstart.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/quickstart.md) diff --git a/apps/docs/content/docs/runtime-protocol.mdx b/apps/docs/content/docs/runtime-protocol.mdx index 03f7537a5..897605f6b 100644 --- a/apps/docs/content/docs/runtime-protocol.mdx +++ b/apps/docs/content/docs/runtime-protocol.mdx @@ -5,10 +5,10 @@ description: "Runtime lifecycle, messages, receipts and recovery." This is the integration entry point for a Runtime that executes work for Core. The wire definitions live once in -[`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto); -Core's [gateway](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/gateway) and the reference Runtime's -[dispatcher](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/dispatch) both use them. -The [machine HTTP API](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime.openapi.yaml) describes +[`internal/agentdaemon/proto`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto); +Core's [gateway](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/gateway) and the reference Runtime's +[dispatcher](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/dispatch) both use them. +The [machine HTTP API](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime.openapi.yaml) describes registration and connection endpoints. This document defines the meaning and ordering of the messages after connection; it does not replace the typed payloads. @@ -46,7 +46,7 @@ preparation is not proof of containment. 5. Dispatch ordered JSON envelopes over the connection. Heartbeats establish liveness only, not execution progress or a receipt for earlier messages. -The wire version is [`proto.Version`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/version.go), +The wire version is [`proto.Version`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/version.go), independent of the Runtime build version reported in heartbeats. Core accepts only an exact match, including the patch component. A mismatch returns HTTP 426 `incompatible_version` before dispatch; the daemon treats it as permanent and @@ -62,7 +62,7 @@ Session or Environment binding. ## Envelope and identity Every data frame is one JSON -[`Envelope`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/envelope.go): +[`Envelope`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/envelope.go): `type`, type-dependent `id`, typed `payload`, and optional W3C `trace`. Trace is diagnostic correlation only; missing or invalid trace data creates a local trace and never changes ownership. Do not use a trace ID as a request ID. @@ -91,16 +91,16 @@ finite error categories. There is no parallel payload schema to keep in sync. | Core → Runtime | Runtime → Core | Definition | | --- | --- | --- | -| `runtime_prepare` | `runtime_prepare_result` | [Initialization and capability transfer](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/runtime_prepare.go) | -| `execution_prepare`, `execution_start`, `execution_release` | `preparation_status` | [Execution admission](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/preparation.go) | -| `prompt_request`, `prompt_cancel`, `device_shutdown` | `delta`, `thinking`, `output_message`, `tool_call`, `usage`, `error`, `done`, `heartbeat` | [Requests](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/outbound.go), [events and capabilities](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/inbound.go) | -| `permission_decision`, `prompt_for_user_choice_decision` | `permission_request`, `permission_cancel`, `prompt_for_user_choice`, `interaction_decision_ack` | [Requests](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/outbound.go), [interactions](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/inbound.go) | -| `prompt_steer` | `prompt_steer_ack` | [Active input receipts](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/steering.go) | -| `function_result` | `function_call`, `interaction_decision_ack` | [Function calls](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/functions.go) | -| `workspace_read`, `workspace_write`, `workspace_export` | Matching `*_result` | [Read](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/workspace_read.go), [write](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/workspace_write.go), [export](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/workspace_export.go) | -| `environment_quiesce`, `environment_resume` | `environment_quiesced`, `environment_resumed` | [Suspension fencing](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/suspend.go) | - -Initial and active input share [ordered MessageInput](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/internal/agentdaemon/proto/message_input.go). +| `runtime_prepare` | `runtime_prepare_result` | [Initialization and capability transfer](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/runtime_prepare.go) | +| `execution_prepare`, `execution_start`, `execution_release` | `preparation_status` | [Execution admission](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/preparation.go) | +| `prompt_request`, `prompt_cancel`, `device_shutdown` | `delta`, `thinking`, `output_message`, `tool_call`, `usage`, `error`, `done`, `heartbeat` | [Requests](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/outbound.go), [events and capabilities](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/inbound.go) | +| `permission_decision`, `prompt_for_user_choice_decision` | `permission_request`, `permission_cancel`, `prompt_for_user_choice`, `interaction_decision_ack` | [Requests](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/outbound.go), [interactions](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/inbound.go) | +| `prompt_steer` | `prompt_steer_ack` | [Active input receipts](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/steering.go) | +| `function_result` | `function_call`, `interaction_decision_ack` | [Function calls](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/functions.go) | +| `workspace_read`, `workspace_write`, `workspace_export` | Matching `*_result` | [Read](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/workspace_read.go), [write](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/workspace_write.go), [export](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/workspace_export.go) | +| `environment_quiesce`, `environment_resume` | `environment_quiesced`, `environment_resumed` | [Suspension fencing](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/suspend.go) | + +Initial and active input share [ordered MessageInput](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/internal/agentdaemon/proto/message_input.go). Adapters preserve message/content order and explicitly reject unsupported content. Usage frames and the final usage snapshot replace earlier cumulative snapshots; do not add them. An absent measurement is unknown, not zero. @@ -177,7 +177,7 @@ truth and reconciles from confirmed facts. Runtime retains cleanup ownership until native work, input receipts, interactions and child work have settled. The public Turn status is a separate, existing projection: -[`execution/delivery.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/execution/delivery.go) +[`execution/delivery.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/execution/delivery.go) records an unsuccessful orchestration attempt as `failed`, including `delivery_unknown` after an unconfirmed send and `event_stream_incomplete` after subscription failure. A closed subscription can replace the send reason @@ -211,8 +211,8 @@ Provider's confirmed reclamation of compute. Run `make check-runtime-contract` from the repository root. It exercises the shared wire validators, gateway, transport and dispatcher, plus -[real WebSocket contract scenarios](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/apps/parsar-daemon/internal/contracttest/wire_test.go) -using a controlled Harness adapter, plus the [observation-result regression](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/execution/runtime_protocol_test.go). It requires no model credentials or external +[real WebSocket contract scenarios](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/apps/parsar-daemon/internal/contracttest/wire_test.go) +using a controlled Harness adapter, plus the [observation-result regression](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/execution/runtime_protocol_test.go). It requires no model credentials or external sandbox. These tests are also included in `make check` through `check-go` and `check-agents-api`. @@ -228,4 +228,4 @@ controlled-adapter test establishes the transport contract, not native Harness behavior, OS support, provider authentication or sandbox isolation. Update the shared types, this guide and the contract checks together when semantics change. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/runtime-protocol.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/runtime-protocol.md) diff --git a/apps/docs/content/docs/sandbox-provider.mdx b/apps/docs/content/docs/sandbox-provider.mdx index d8e341529..254b0fbc9 100644 --- a/apps/docs/content/docs/sandbox-provider.mdx +++ b/apps/docs/content/docs/sandbox-provider.mdx @@ -3,7 +3,7 @@ title: "Add a Sandbox Provider" description: "Environment allocation, bootstrap and reclamation." --- -Start at [`sandbox_provider.go`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/sandbox_provider.go). +Start at [`sandbox_provider.go`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/sandbox/sandbox_provider.go). It defines the required `SandboxProvider` and optional `CheckpointProvider` interfaces. Use the existing types; do not introduce another lifecycle protocol or an execution path for a particular vendor. @@ -33,7 +33,7 @@ Do not implement an optional interface with successful no-op methods. Core uses interface assertions to select optional operations. Advertised support requires contract and native acceptance evidence; a healthy node or an available CLI does not establish it. Observation never renews a lease, starts compute or prepares a -Harness. See the [observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/runtime-observability.md). +Harness. See the [observation contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/runtime-observability.md). `RunCommand` is an existing administrative bootstrap/diagnostic facility, not an alternate route for Skills, Plugins, MCP setup, initial files, Session execution or @@ -48,12 +48,12 @@ Registration is explicit construction, not an init-time plugin registry: configuration in an adapter package under `services/agents-api/internal/sandbox`. Assert `var _ sandbox.SandboxProvider = (*YourAdapter)(nil)` at compile time. 2. For a node-local adapter, add its typed construction and readiness probe in - [`sandbox/config`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/config/config.go). + [`sandbox/config`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/sandbox/config/config.go). The existing `Built` result returns the provider, probe, installation identity, backend fingerprint and specification digest; the factory also returns its close function. Construction must not create an Environment. 3. For a direct adapter, register its construction in - [`managedSetup.provider`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/cmd/server/managed_setup.go). + [`managedSetup.provider`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/cmd/server/managed_setup.go). Node-local adapters are selected there through the existing node proxy. Keep vendor-specific deployment validation and SDK setup at this boundary. 4. Preserve the existing `execution.RuntimeProvider` deployment binding: @@ -143,7 +143,7 @@ Persist operation IDs and provider-returned snapshot provenance unchanged. another capture or restore. `ResumeCompute` only thaws the retained source; it must not cold-start a stopped one. Cleanup targets the exact compute incarnation and snapshot, not whichever instance currently has the same display name. See -[the lifecycle implementation](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/execution/runtime_compute.go) +[the lifecycle implementation](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/execution/runtime_compute.go) and its failure tests before advertising this capability. ## Four distinct readiness facts @@ -164,7 +164,7 @@ A provider must not implement a competing preparation path. ## Validate the integration Run `make check-sandbox-provider-contract` while developing. It runs the shared -[`contracttest`](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/services/agents-api/internal/sandbox/contracttest) suite through +[`contracttest`](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/services/agents-api/internal/sandbox/contracttest) suite through real adapter boundaries using controlled native failures, plus existing adapter and node transport tests. The same packages are included in `make check`. New adapters should call the public failure runner with native-side fixtures, @@ -185,4 +185,4 @@ SDK helper tests use `make check-e2b-provider` and `make check-microsandbox-provider`. Passing controlled contract tests is not a claim of live cloud or model acceptance. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/sandbox-provider.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/sandbox-provider.md) diff --git a/apps/docs/content/docs/self-hosted-execution.mdx b/apps/docs/content/docs/self-hosted-execution.mdx index 4c67a7249..8644af430 100644 --- a/apps/docs/content/docs/self-hosted-execution.mdx +++ b/apps/docs/content/docs/self-hosted-execution.mdx @@ -93,7 +93,7 @@ from tools running as the same user. Closing an executor, cancelling a Turn or losing its connection preserves the snapshot and workspace. The compute owner remains responsible for explicit -cleanup. [Historical qualification](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/user-managed-runtime-v1.md) +cleanup. [Historical qualification](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/user-managed-runtime-v1.md) records only its stated inputs and binaries; it does not qualify the current native platforms or local capability preparation. @@ -119,7 +119,7 @@ or rotated; revocation remains available. Operators can still issue, rotate or revoke executor credentials using a Core key through Core's loopback port. This is not required for one-command onboarding. -See the [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-executor-credentials.md) +See the [credential contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-executor-credentials.md) for those routes and uncertain-response handling. ## Historical executor installations @@ -130,4 +130,4 @@ and native history; use a fresh Runtime home and Session when moving versions. Historical container-installer instructions and qualification records describe those earlier artifacts, not acceptance of the current native daemon. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/self-hosted.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/self-hosted.md) diff --git a/apps/docs/content/docs/self-hosted-native.mdx b/apps/docs/content/docs/self-hosted-native.mdx index 715d4cb6d..0e4c134bc 100644 --- a/apps/docs/content/docs/self-hosted-native.mdx +++ b/apps/docs/content/docs/self-hosted-native.mdx @@ -162,4 +162,4 @@ On Windows, stdio MCP commands named npm or npx (including explicit .cmd paths) run through the selected installation's JavaScript entrypoint with Node. Other batch wrappers require an explicit cmd.exe command and its arguments. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/self-hosted-native.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/self-hosted-native.md) diff --git a/apps/docs/content/docs/sessions.mdx b/apps/docs/content/docs/sessions.mdx index cdf6b45f7..2bd0f7da9 100644 --- a/apps/docs/content/docs/sessions.mdx +++ b/apps/docs/content/docs/sessions.mdx @@ -61,10 +61,10 @@ keeps the key out of the process list. | [Skills](#skills) | `/skills` | Versioned capability bundles | | [Environment Templates](#environment-templates) | `/agents/environments/templates` | Reusable workspace setup | | [Vaults](#vaults) | `/vaults` | Write-only credentials for MCP servers | -| Subagents | `/agents/sessions/{id}/subagents` | Read-only child work; see [subagents](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/subagents.md) | +| Subagents | `/agents/sessions/{id}/subagents` | Read-only child work; see [subagents](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/subagents.md) | Core has exactly the routes of the pinned SDK, listed in -[upstream-routes.json](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/upstream-routes.json). It adds no +[upstream-routes.json](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/upstream-routes.json). It adds no route; its additions live in [`x_agents_core`](#core-extensions-x_agents_core). ## Conventions @@ -303,7 +303,7 @@ oac "/agents/sessions/$SESSION_ID/events" -H "Idempotency-Key: $KEY" -d '{ - **Content** is `input_text`, plus `input_image` as an inline PNG or JPEG data URI. Images work with Codex and Claude Code, on `none` and Docker sandboxes only. The whole request is limited to 1 MiB. -- Full rules: [message input](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/message-input.md). +- Full rules: [message input](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/message-input.md). ### Cancel @@ -360,7 +360,7 @@ does both. `agent.session.created` first, and the stream ends at the first `idle` or `failed`. **Reconnecting:** resubscribe, then read Items and drop any you already have by ID. -Details: [history and events](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/history-events-usage.md). +Details: [history and events](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/history-events-usage.md). ## Turns and Items @@ -511,7 +511,7 @@ client.skills.versions.create(skill.id, files=[...], default=True) - Attach Skills to a Session through its `environment.skills` or a [template](#environment-templates). -Details: [Source Files and Skills](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/source-files.md). +Details: [Source Files and Skills](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/source-files.md). ## Environment Templates @@ -534,7 +534,7 @@ template = client.beta.agents.environments.templates.create( | `files`, `skills`, `plugins` | Initial content. Up to 50 files, 10 MiB inline in total | A Session freezes the template when it starts. Details: -[Environment Templates](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/environment-templates.md). +[Environment Templates](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/environment-templates.md). ## Vaults @@ -568,7 +568,7 @@ Sessions only. | Packages | `packages.system` rejected | Per-operation status and harness differences are in the -[coverage record](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/README.md). The exact schemas are in the -[public OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/openapi.yaml). +[coverage record](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/README.md). The exact schemas are in the +[public OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/openapi.yaml). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/api/public-agent-api.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/api/public-agent-api.md) diff --git a/apps/docs/content/docs/troubleshooting.mdx b/apps/docs/content/docs/troubleshooting.mdx index 8b664c7ce..d13c6b29e 100644 --- a/apps/docs/content/docs/troubleshooting.mdx +++ b/apps/docs/content/docs/troubleshooting.mdx @@ -123,7 +123,7 @@ refuses. `rotate-core-key` refuses to run on a Web-only installation. ## Projects and API keys Create Projects and issue keys in Web, on **Projects and keys**, or through the -[Core API](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md) under `/core/v1/projects`. How +[Core API](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md) under `/core/v1/projects`. How Projects and keys behave is in [Projects own assets](/concepts#projects-own-assets). To rotate an application key: @@ -246,9 +246,9 @@ Core records which key made each public resource write, for the console. Set `core.write_audit_retention` in `config.json` (Go duration, at least `1h`, default `2160h`) to control how long non-creation history is kept; creation ownership is kept for good. Removing keys or resources does not delete these records. See the -[query contract](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/write-audit.md); administrator mutations +[query contract](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/write-audit.md); administrator mutations have a separate -[audit log](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/admin-api.md#monitoring-and-audit). Neither +[audit log](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/admin-api.md#monitoring-and-audit). Neither logs bodies or secrets. -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/getting-started/operations.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/getting-started/operations.md) diff --git a/apps/docs/content/docs/user-guide.mdx b/apps/docs/content/docs/user-guide.mdx index a23ba170b..a61ff38db 100644 --- a/apps/docs/content/docs/user-guide.mdx +++ b/apps/docs/content/docs/user-guide.mdx @@ -43,7 +43,7 @@ The harness is the agent program that runs your Session. Set it in settings; see [native model configuration](/harness-onboarding#native-model-configuration). Selecting a harness doesn't make an unsupported model or operation work; see -[harness selection](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/harness-selection.md). +[harness selection](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/harness-selection.md). ### Which model provider a Session uses @@ -65,7 +65,7 @@ never merged: Self-hosted Sessions never use the default, because it holds the operator's key and your machine is outside the operator's control. A Session freezes its provider at creation; later changes affect only new Sessions. Full rules: -[model execution](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/contracts/agents-api/model-execution.md). +[model execution](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/contracts/agents-api/model-execution.md). ## Follow up and steer @@ -158,4 +158,4 @@ history; see [operating the installation](/self-hosted-native#add-harnesses-and- A 401 usually means a key from the wrong namespace; see the [API index](/public-api). -[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f45a4198aca79bf252412c55cad3647f5d704515/docs/user-guide.md) +[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/f6d258735fc601c521dd990e6f9e1ed261f4ef2d/docs/user-guide.md) diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json index 50476d35d..742468321 100644 --- a/apps/docs/content/guide-sources.json +++ b/apps/docs/content/guide-sources.json @@ -1,5 +1,5 @@ { - "revision": "f45a4198aca79bf252412c55cad3647f5d704515", + "revision": "f6d258735fc601c521dd990e6f9e1ed261f4ef2d", "sources": { "docs/getting-started/README.md": "d46e1b6cd0693a282f504297c87df6ddf26b99ce8c7afdb613666acd836767f5", "docs/design-principles.md": "21a05fb089804db6fab5674ec9a6cf849a3affedad2ca927a38d9b46eeec9a50", @@ -24,37 +24,37 @@ "docs/getting-started/operations.md": "f5c0942003373aaf604fc0f5bb88e0d022f465f1ade2db2192975a173efe7d95", "docs/user-guide.md": "1069d0f12aa42dde49693f25e8d964984e1c335fc67527123d31ee6a99617bab", "docs/development.md": "3e03e2666c92380d207ab53285e3b85fdcb042481c8ca76219ed08d629d52812", - "contracts/agents-api/harness-onboarding.md": "2c744598f39c156f868418423abdf45d0b52e04b7debaa50ffc95b1b4bb91a87", + "contracts/agents-api/harness-onboarding.md": "5517210e50e6820b2711bd66b1e5507228d0754acd661c187d89a4017a1ac0a5", "docs/runtime-protocol.md": "6baa332705743e34b8565613d743d1b30099d8af78159b06ad921ffa7e164a9f", "docs/sandbox-provider.md": "dd8c2c2b2e2acf4d6ae564f56ffaf5685d74a84fa7a7a15b4cad851015ee36ab", "apps/docs/scripts/guides.json": "f4b2e58b64a89068a68a58ed1d8fe75893ee09d415e87391121e0e99bb1fbe3f" }, "outputs": { - "content/docs/index.mdx": "63318e9faa387a608efa11af7bd67917579df22a1e7fed00f907b4794112581a", - "content/docs/concepts.mdx": "cf4dc9e8838782bbc16539c8dec1ccb16680fe14cd1472eabceab6b79885f503", - "content/docs/execution-model.mdx": "bf528e34ace32ee1c256aaa4f2e31575bcbbd62d5c5efa7177ddc7b8e275becb", - "content/docs/install.mdx": "9cf6b03ae1c0c6caca768ea4c3ff2a47d7dfbfc3648cc7a73b7c848c7e257d7b", - "content/docs/install-options.mdx": "3bbfa77af2c34f2b9f203079778b51ee9425624ca516c6392c859ee2add43591", - "content/docs/configure.mdx": "a39be53e21c65159af311724d7746e35a3c0dfcc9b87d5e79d01e54e7bb6d3ed", - "content/docs/bootstrap-projects-keys.mdx": "2ac720c071a0b37210c671b2d6fa704bb5720c2acaadac2650b893580965d2d8", - "content/docs/quickstart.mdx": "bab3cadd3cee86b6dcc11768cb81deb9d37c89521dfcace0a33d387e8d553815", - "content/docs/public-api.mdx": "055cb7e06d7647ff24e669a993ad1cfb806b2dfca51e12716f06df8fdf7ecac5", - "content/docs/agents-and-tools.mdx": "9ffa5a5883847f2e4d3641bfd5975baf448fd371542797fd93204f69ea6df5e2", - "content/docs/sessions.mdx": "4f3a5e6e774f2ad687c1e00841eb716e1370c9206a8bd7a118325fa77594630c", - "content/docs/examples.mdx": "50dc70b46605d83df289cc555c064884645cd875cc46d0688782c1fac8752a0f", - "content/docs/environments-and-files.mdx": "2b8a0e0783032ac7e187361cab2225e9fed524f0ae9bacd3edf43804356cd389", - "content/docs/hosted-providers.mdx": "c504f42cea0c8c1ac6bd218a051b523cd8cd8609bd486233a787e2f4ff4f050c", - "content/docs/self-hosted-execution.mdx": "f190a4c40e532f3f24e8969c3df70f561b892ceb61b8b447eaf119312bffa071", - "content/docs/self-hosted-native.mdx": "e95c984383679bc1b2d016f864597eb625077e286228899c28eccbc5a89cc6b0", + "content/docs/index.mdx": "432dd8f02f72edb9f39b92d272719b146c5c1bf5b0429191d0f4a56932b92353", + "content/docs/concepts.mdx": "88ba65e1ba902921d6cd5da2866620a6dbef52c041db991312a138884fb43a4e", + "content/docs/execution-model.mdx": "251e862de317ffe284fda47193e93407eb9794f7a6327449723609b55841430d", + "content/docs/install.mdx": "0154eaa12cc9c8e54d7a4bf0d6c9241e4386f6adee9c2fb225972e32c4be9896", + "content/docs/install-options.mdx": "fec1d7b6b395714deba51c53a704e0af1d5cb6195d6a1957ec4fe1348405e841", + "content/docs/configure.mdx": "f44177e3ce58c3806f42a243668bdcf4505bd368fd6e82d38b03703b00354f54", + "content/docs/bootstrap-projects-keys.mdx": "30ffbd71f718fe59bf2fc383cfb5b8d5b1e0b69d42e6f7a44f30024ec4e4b42d", + "content/docs/quickstart.mdx": "dc5e550d9eef5ba7776545f326cb6cbd34f23643bf032e5560742e121970cf6b", + "content/docs/public-api.mdx": "d0080ad359e375d26879fd8abe8a75dbf77b0640fdc5ac6d603600bd140dd720", + "content/docs/agents-and-tools.mdx": "b8a85fe92e19a71b7ad7c9357ce7ad0595d50c9190edb748ca834df615b056cb", + "content/docs/sessions.mdx": "d86996bfba873c3c17e31e3a50eb9ec26ae7a6eec326801c062c82e513a0d474", + "content/docs/examples.mdx": "587061e65ab2e841d14980539ba94e2216d4e8135c948a852b9a8bb0359c85d7", + "content/docs/environments-and-files.mdx": "fa7062118cc8f13ddbd627f9990744a2002b7ca988e0b203df7ef3fd3996d9e4", + "content/docs/hosted-providers.mdx": "06f0b13b053965bbdd1bfb0a7e85d19b50987841e0512fa8331fc9a67ff71fe0", + "content/docs/self-hosted-execution.mdx": "161a9000634b4075a55960556322a1b535d8157ba0a4abbeaa84c85c7292ad33", + "content/docs/self-hosted-native.mdx": "4893e7effe8a395856611da2216e7b7a9f27b8f7de6812fbc9c5ebea0326de34", "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2", - "content/docs/console.mdx": "0a6a04c13a130a107f74c7c6cc518d24f6985780c94a6d0b9b0eb18384fb3253", - "content/docs/admin-api.mdx": "d553f7712b22e2eb46bf7c84c2b7a1d67df51996e1533aca2451d24c2caf7628", - "content/docs/observability.mdx": "595ab9f9918d04c058b64fa3a31d81913bbcbbc4d03138dca63a590979dde112", - "content/docs/troubleshooting.mdx": "ef71d3a2d93915c55741869c40c9b2c0cb870922b60732ce65ecc4c38f60cbf0", - "content/docs/user-guide.mdx": "5389281b1fa4743479d0d4f49baa631793187d2484e7e55984efc8a9f96d1b55", - "content/docs/development.mdx": "02136ed522b0545dc84fa210cf61d86058e85e1d3acd7890160bd1cce1a13a58", - "content/docs/harness-onboarding.mdx": "115c6fcdc640c9260cbdbcb7315630045d1643c86a1b37a4c33a61ecc8053a9a", - "content/docs/runtime-protocol.mdx": "59be1013b20c9236ec3480cdc29310e19bd08bbcf2d61d5bee45a9400b43c7be", - "content/docs/sandbox-provider.mdx": "3814c84245951b404a786176f7b958d73a7f4548edded55be02be22e143f9880" + "content/docs/console.mdx": "a930ce36035de74f0c2bef71bed067fc2287ba1c70a485758bf100d484f6adfd", + "content/docs/admin-api.mdx": "39f5e5373145f8d7e24e2880216fe59ac1c7fc3e43a768be0504936c0b4221fb", + "content/docs/observability.mdx": "c81214e1865517a9163c48c7ce7c396f5490c9d162080dfa05fa5ebc147f6c8b", + "content/docs/troubleshooting.mdx": "f0291b91254a704cb7d490f5205306b229f194bd620f6b050c90c9b3cea8f8c6", + "content/docs/user-guide.mdx": "4f7d57c7199513c2947a2f01b163b5e12c474553b9a2aafcf857bbf51def091a", + "content/docs/development.mdx": "3d10a88c12be4a680d91a1b0afb0d56517ce50c12ded8431e350ba917f62ba7a", + "content/docs/harness-onboarding.mdx": "d3a8f0e39b129d3eb147ed560811b4fd56a65f01606d0d9a80d93c7ffade1157", + "content/docs/runtime-protocol.mdx": "a6a63eb1dd0c54f2bd6f406721194e744f76c25585f7db57b28d43f545a8e51b", + "content/docs/sandbox-provider.mdx": "fdf993bdcf00050cdba1d513c45e9e9159ff02f18f9f248000c1e1b9ba2de08f" } } diff --git a/apps/docs/openapi/core-api.yaml b/apps/docs/openapi/core-api.yaml index 06ba7f788..24f16f2c7 100644 --- a/apps/docs/openapi/core-api.yaml +++ b/apps/docs/openapi/core-api.yaml @@ -6390,10 +6390,6 @@ components: properties: accepts_harness_config: type: boolean - native_protocols: - items: - type: string - type: array protocols: items: type: string @@ -6402,7 +6398,6 @@ components: type: boolean required: - accepts_harness_config - - native_protocols - protocols - token_limits_required type: object diff --git a/apps/docs/openapi/sources.json b/apps/docs/openapi/sources.json index a88071dbf..23c76d8f8 100644 --- a/apps/docs/openapi/sources.json +++ b/apps/docs/openapi/sources.json @@ -1,7 +1,7 @@ { "sources": { "contracts/agents-api/openapi.yaml": "b706abf259542b7b9a833b566f20048412d3cbc27c4c4284ed6baf5edc605489", - "contracts/agents-api/core.openapi.yaml": "03275522b804b9ac467e0703b5f45c128f4f5cbf13d766387fe85fa1e0592223", + "contracts/agents-api/core.openapi.yaml": "5f17f3049a0892888bf4abe919b791b2df45c161b5fa4ee6e27efae2145c7d01", "contracts/agents-api/runtime.openapi.yaml": "505286d5eacf94a14f9527fcf4e7beb4fba4f792681be26ba966254cbf49b4aa" }, "outputs": { @@ -41,7 +41,7 @@ "content/docs/api-reference/core/vaults.mdx": "12d0aac96cd4a3472ef4eaa287f7945f78862017956e79bfc3ed0ef8e772e78a", "content/docs/api-reference/core/credentials.mdx": "27d586b9a8fd738b9947802f32c80458195370252886bc26abedccf2125ee981", "content/docs/api-reference/core/sandbox-manager.mdx": "45e65afffb25e5b6ee2ab0853d7a84dee164e0d44ea384675fbd7ffcbd6c1a58", - "openapi/core-api.yaml": "7f8e51e6b8373ae33e3069a78745ec9cfeb6dfa415e02a26db6a9feab99b668e", + "openapi/core-api.yaml": "b673d2da3c0e444a307e6c70069426ca4a3c4c8f0d74c45ff8b25c662ec47b13", "content/docs/api-reference/core/meta.json": "d0aef1f54c4cfc30b4e1d974119ae1cdce1c87d5ecba723afbeb4599119482a7", "content/docs/api-reference/core/index.mdx": "57cf9ea947ef45e79575c25a0a3d4e5e856dde6d14698ccc2c51b86201c249e9", "content/docs/api-reference/machine/native-installation.mdx": "421a858685b79fdcf8d7eff308c5768d1b62385c3988c6089f380afe80aaed69", diff --git a/apps/docs/scripts/generate-guides.mjs b/apps/docs/scripts/generate-guides.mjs index e401e5913..4b6ef85e3 100644 --- a/apps/docs/scripts/generate-guides.mjs +++ b/apps/docs/scripts/generate-guides.mjs @@ -8,7 +8,7 @@ const app = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..') const repo = path.resolve(app, '../..') const guides = JSON.parse(fs.readFileSync(path.join(app, 'scripts/guides.json'), 'utf8')) const routes = new Map(guides.map(g => [g.source, g.slug === 'index' ? '/' : '/' + g.slug])) -const sourceRevision = 'f45a4198aca79bf252412c55cad3647f5d704515' +const sourceRevision = 'f6d258735fc601c521dd990e6f9e1ed261f4ef2d' const sourceURL = relative => `https://github.com/MiniMax-AI/parsar-core/blob/${sourceRevision}/${relative}` const digest = text => crypto.createHash('sha256').update(text).digest('hex') function mdx(text) { diff --git a/apps/parsar-daemon/internal/agent/claudecode/session.go b/apps/parsar-daemon/internal/agent/claudecode/session.go index 0f2ade015..87486c8c8 100644 --- a/apps/parsar-daemon/internal/agent/claudecode/session.go +++ b/apps/parsar-daemon/internal/agent/claudecode/session.go @@ -60,7 +60,7 @@ func defaultConfig() sessionConfig { // Factory implements agent.Factory for agent_kind="claude_code". // Register during daemon startup: // -// registry.Register("claude_code", claudecode.Factory) +// registry.Register("claude_code", harnessconfig.Configuration{}, claudecode.Factory) func Factory(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { return newSession(ctx, req, out, defaultConfig()) } diff --git a/apps/parsar-daemon/internal/agent/claudesdk/executor.go b/apps/parsar-daemon/internal/agent/claudesdk/executor.go index a8ba4b561..beef6fb3e 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/executor.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/executor.go @@ -12,21 +12,17 @@ import ( "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" - harnessconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/claudesdk" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" ) type executor struct { - modelRoute modeltransport.Route - modelEndpoint *modeltransport.Endpoint - mu sync.Mutex - base *session - start startRequest - active *session - ready chan error - done chan struct{} - invalid bool - nativeID string + mu sync.Mutex + base *session + start startRequest + active *session + ready chan error + done chan struct{} + invalid bool + nativeID string } func NewExecutorFactory(config Config) agent.ExecutorFactory { @@ -43,17 +39,6 @@ func NewExecutorFactory(config Config) agent.ExecutorFactory { if req.RunID != "" || len(req.Input) != 0 || req.ConversationID != "" { return nil, errors.New("claudesdk: Executor preparation cannot submit input") } - options, endpoint, err := modeltransport.PrepareRequestOptions(req, harnessconfiguration.Configuration().NativeProtocols()...) - if err != nil { - return nil, err - } - transferred := false - defer func() { - if !transferred && endpoint != nil { - _ = endpoint.Close() - } - }() - req.AgentOptions = options start, env, err := prepareConfiguration(config, req) if err != nil { return nil, err @@ -72,11 +57,7 @@ func NewExecutorFactory(config Config) agent.ExecutorFactory { } base.reads.supported = slices.Contains(info.Features, "workspace_read") base.directories.supported = slices.Contains(info.Features, "workspace_directory") - e := &executor{modelEndpoint: endpoint, base: base, start: start, ready: make(chan error, 1), done: make(chan struct{}), nativeID: start.Resume} - if endpoint != nil { - e.modelRoute = endpoint.Route - } - transferred = true + e := &executor{base: base, start: start, ready: make(chan error, 1), done: make(chan struct{}), nativeID: start.Resume} go e.read() if err = e.write(start); err == nil { select { @@ -195,9 +176,6 @@ func (e *executor) read() { if !ready { e.ready <- readyFailure } - if e.modelEndpoint != nil { - _ = e.modelEndpoint.Close() - } close(e.done) } @@ -208,15 +186,12 @@ func (e *executor) StartTurn(ctx context.Context, run string, input proto.Messag if strings.TrimSpace(run) == "" || input.Validate() != nil || out == nil || ctx.Err() != nil { return nil, errors.New("claudesdk: Turn requires live context, identity, input and output") } - if err := e.modelRoute.ValidateInput(input); err != nil { - return nil, err - } e.mu.Lock() if e.invalid || e.active != nil || e.base.process.Context().Err() != nil { e.mu.Unlock() return nil, errors.New("claudesdk: Executor is unavailable") } - s := &session{modelRoute: e.modelRoute, owner: e, runID: run, process: e.base.process, writeMu: e.base.writeMu, frames: make(chan []byte, 64), functions: functionState{calls: map[string]*pendingFunction{}}, settled: make(chan struct{}), outputDone: make(chan struct{}), cancelOutput: make(chan struct{})} + s := &session{owner: e, runID: run, process: e.base.process, writeMu: e.base.writeMu, frames: make(chan []byte, 64), functions: functionState{calls: map[string]*pendingFunction{}}, settled: make(chan struct{}), outputDone: make(chan struct{}), cancelOutput: make(chan struct{})} start := e.start start.Resume = e.nativeID e.active = s diff --git a/apps/parsar-daemon/internal/agent/claudesdk/executor_test.go b/apps/parsar-daemon/internal/agent/claudesdk/executor_test.go index b3fef4abc..8f981f826 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/executor_test.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/executor_test.go @@ -6,7 +6,6 @@ import ( "bufio" "context" "encoding/json" - "net/http" "os" "path/filepath" "strconv" @@ -146,7 +145,7 @@ func awaitExecutorTurn(t *testing.T, turn agent.Turn, out <-chan proto.Envelope, func TestExecutorRetainsProcessAcrossTurnsAndCancellation(t *testing.T) { config, req := persistentConfig(t, "") req.AgentOptions["model_provider"] = map[string]any{ - "protocol": "chat_completions", "base_url": "https://provider.example/v1", "api_key": "fixture-key", + "protocol": "anthropic", "base_url": "https://provider.example/anthropic", "api_key": "fixture-key", } owner, err := NewExecutorFactory(config)(t.Context(), req) if err != nil { @@ -154,26 +153,8 @@ func TestExecutorRetainsProcessAcrossTurnsAndCancellation(t *testing.T) { } defer owner.Close(context.Background()) pid := owner.(*executor).base.process.Cmd.Process.Pid - endpoint := owner.(*executor).modelEndpoint - if endpoint == nil { - t.Fatal("missing model conversion endpoint") - } - client := &http.Client{Timeout: time.Second} - checkEndpoint := func() { - t.Helper() - response, err := client.Get(endpoint.BaseURL + "/v1/messages") - if err != nil { - t.Fatal("model endpoint closed before Executor", err) - } - response.Body.Close() - if response.StatusCode != http.StatusUnauthorized { - t.Fatal("model endpoint lost its credential boundary") - } - } - checkEndpoint() first, out := consumeExecutorTurn(t, owner, "first", "hello") awaitExecutorTurn(t, first, out, true) - checkEndpoint() next, out := consumeExecutorTurn(t, owner, "next", "wait") if event := <-out; event.Type != proto.TypeDelta { t.Fatal(event.Type) @@ -185,7 +166,6 @@ func TestExecutorRetainsProcessAcrossTurnsAndCancellation(t *testing.T) { t.Fatal(err) } awaitExecutorTurn(t, next, out, true) - checkEndpoint() third, out := consumeExecutorTurn(t, owner, "third", "hello") awaitExecutorTurn(t, third, out, true) if owner.(*executor).base.process.Cmd.Process.Pid != pid { @@ -196,14 +176,10 @@ func TestExecutorRetainsProcessAcrossTurnsAndCancellation(t *testing.T) { t.Fatal("native process exited between Turns") default: } - checkEndpoint() if err := owner.Close(t.Context()); err != nil { t.Fatal(err) } - if response, err := client.Get(endpoint.BaseURL + "/v1/messages"); err == nil { - response.Body.Close() - t.Fatal("model endpoint survived Executor.Close") - } + } func TestExecutorLateTurnEventInvalidatesWithoutRetargeting(t *testing.T) { config, req := persistentConfig(t, "late") diff --git a/apps/parsar-daemon/internal/agent/claudesdk/functions.go b/apps/parsar-daemon/internal/agent/claudesdk/functions.go index 89d3e56ca..aaa56e61c 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/functions.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/functions.go @@ -100,9 +100,6 @@ func (s *session) receiveFunction(event bridgeEvent, start startRequest, emit fu } func (s *session) SubmitFunctionResult(ctx context.Context, result proto.FunctionResultPayload) error { - if err := s.modelRoute.ValidateFunctionResult(result); err != nil { - return err - } if result.CallID == "" || result.DeliveryID == "" { return fmt.Errorf("claudesdk: function result identities are required") } diff --git a/apps/parsar-daemon/internal/agent/claudesdk/harness_config_test.go b/apps/parsar-daemon/internal/agent/claudesdk/harness_config_test.go index 98e9baf54..e18447a1d 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/harness_config_test.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/harness_config_test.go @@ -27,7 +27,10 @@ func TestHarnessConfigReachesBridgeAndOwnsSnapshot(t *testing.T) { if json.Unmarshal(raw, &bridge) != nil { t.Fatal("invalid bridge request") } - applied := bridge["harness_config"].(map[string]any) + if _, exists := bridge["harness_config"]; exists { + t.Fatal("public configuration crossed the private bridge") + } + applied := bridge["native_model_options"].(map[string]any) if applied["effort"] != "high" || applied["thinking"].(map[string]any)["budgetTokens"] != float64(1024) { t.Fatal("bridge lost immutable native configuration") } diff --git a/apps/parsar-daemon/internal/agent/claudesdk/model_route_test.go b/apps/parsar-daemon/internal/agent/claudesdk/model_route_test.go index 37d9a112e..8683e714a 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/model_route_test.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/model_route_test.go @@ -3,47 +3,29 @@ package claudesdk import ( + "os" + "path/filepath" + "reflect" + "strings" "testing" - - "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" ) -func TestConvertedExecutorRejectsFutureImages(t *testing.T) { - config, req := persistentConfig(t, "complete") - req.AgentOptions["model_provider"] = map[string]any{"protocol": "chat_completions", "base_url": "https://model.invalid/v1", "api_key": "private-sentinel"} - resource, err := NewExecutorFactory(config)(t.Context(), req) - if err != nil { - t.Fatal(err) - } - defer resource.Close(t.Context()) - e := resource.(*executor) - if !e.modelRoute.Converted() { - t.Fatal("conversion route was not retained") - } - out := make(chan proto.Envelope, 20) - turn, err := e.StartTurn(t.Context(), "text-first", proto.TextInput("hello"), out) - if err != nil { - t.Fatal(err) - } - settlement, err := turn.AwaitSettlement(t.Context()) - if err != nil || !settlement.Reusable { - t.Fatalf("text did not settle: %v", err) - } - for range out { - } - ref := "data:image/png;base64,AA==" - input := proto.MessageInput{{Content: []proto.InputContent{{Type: "input_image", ImageURL: &ref}}}} - out = make(chan proto.Envelope, 20) - if next, err := e.StartTurn(t.Context(), "image-next", input, out); next != nil || err != modeltransport.ErrUnsupported { - t.Fatalf("image acquired native ownership: %v", err) - } - s := turn.(*session) - if err := s.SteerWithReceipt(t.Context(), proto.PromptSteerPayload{InputID: "image", Input: input}, nil); err != modeltransport.ErrUnsupported { - t.Fatalf("steer accepted image: %v", err) - } - if err := s.SubmitFunctionResult(t.Context(), proto.FunctionResultPayload{CallID: "call", DeliveryID: "delivery", Success: true, Content: input[0].Content}); err != modeltransport.ErrUnsupported { - t.Fatalf("tool image accepted: %v", err) +func TestExecutorRejectsNonNativeFrozenProviderBeforeStartup(t *testing.T) { + for _, protocol := range []string{"responses", "chat_completions"} { + t.Run(protocol, func(t *testing.T) { + config, req := persistentConfig(t, "complete") + provider := map[string]any{"protocol": protocol, "base_url": "https://model.invalid/v1", "api_key": "private-sentinel"} + req.AgentOptions["model_provider"] = provider + resource, err := NewExecutorFactory(config)(t.Context(), req) + if resource != nil || err == nil || !strings.Contains(err.Error(), "does not support") || strings.Contains(err.Error(), "private-sentinel") { + t.Fatalf("non-native provider acquired native ownership: %v", err) + } + if _, err := os.Stat(filepath.Join(filepath.Dir(config.Entrypoint), "probes")); !os.IsNotExist(err) { + t.Fatal("unsupported snapshot reached native readiness") + } + if !reflect.DeepEqual(provider, map[string]any{"protocol": protocol, "base_url": "https://model.invalid/v1", "api_key": "private-sentinel"}) { + t.Fatal("frozen provider was rewritten") + } + }) } - close(out) } diff --git a/apps/parsar-daemon/internal/agent/claudesdk/native_model_options.go b/apps/parsar-daemon/internal/agent/claudesdk/native_model_options.go new file mode 100644 index 000000000..ebfd03f9b --- /dev/null +++ b/apps/parsar-daemon/internal/agent/claudesdk/native_model_options.go @@ -0,0 +1,24 @@ +package claudesdk + +import "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" + +// nativeModelOptions is private SDK input, compiled only after the shared +// configuration declaration validates and owns the model parameters. +type nativeModelOptions struct { + Effort string `json:"effort,omitempty"` + Thinking map[string]any `json:"thinking,omitempty"` +} + +func compileNativeModelOptions(config proto.HarnessConfig) *nativeModelOptions { + if len(config) == 0 { + return nil + } + options := &nativeModelOptions{} + if value, exists := config["effort"]; exists { + options.Effort = value.(string) + } + if value, exists := config["thinking"]; exists { + options.Thinking = value.(map[string]any) + } + return options +} diff --git a/apps/parsar-daemon/internal/agent/claudesdk/options.go b/apps/parsar-daemon/internal/agent/claudesdk/options.go index 4afdb4453..36dec3965 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/options.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/options.go @@ -24,22 +24,22 @@ type subagentOptions struct { } type startRequest struct { - HarnessConfig proto.HarnessConfig `json:"harness_config,omitempty"` - ToolSearch bool `json:"tool_search,omitempty"` - Subagents *subagentOptions `json:"subagents,omitempty"` - OutputFormat *proto.OutputFormat `json:"output_format,omitempty"` - Type string `json:"type"` - Input proto.MessageInput `json:"input,omitempty"` - Model string `json:"model"` - SystemPrompt string `json:"system_prompt"` - Cwd string `json:"cwd"` - Resume string `json:"resume,omitempty"` - ObserveMessages bool `json:"observe_messages,omitempty"` - Functions []proto.FunctionTool `json:"functions,omitempty"` - MCPHTTPServers *[]mcpHTTPServer `json:"mcp_http_servers,omitempty"` - Workspace *workspaceProfile `json:"workspace,omitempty"` - RequireHistory bool `json:"require_history,omitempty"` - observeFunctions bool + NativeModelOptions *nativeModelOptions `json:"native_model_options,omitempty"` + ToolSearch bool `json:"tool_search,omitempty"` + Subagents *subagentOptions `json:"subagents,omitempty"` + OutputFormat *proto.OutputFormat `json:"output_format,omitempty"` + Type string `json:"type"` + Input proto.MessageInput `json:"input,omitempty"` + Model string `json:"model"` + SystemPrompt string `json:"system_prompt"` + Cwd string `json:"cwd"` + Resume string `json:"resume,omitempty"` + ObserveMessages bool `json:"observe_messages,omitempty"` + Functions []proto.FunctionTool `json:"functions,omitempty"` + MCPHTTPServers *[]mcpHTTPServer `json:"mcp_http_servers,omitempty"` + Workspace *workspaceProfile `json:"workspace,omitempty"` + RequireHistory bool `json:"require_history,omitempty"` + observeFunctions bool } func prepare(config Config, req proto.PromptRequestPayload) (startRequest, []string, error) { @@ -59,11 +59,11 @@ func prepareConfiguration(config Config, req proto.PromptRequestPayload) (startR fail := func(reason string) (startRequest, []string, error) { return startRequest{}, nil, fmt.Errorf("claudesdk: %s", reason) } - nativeConfig, err := harnessconfiguration.Configuration().PrepareHarnessConfig(req.AgentOptions) + modelConfiguration, err := harnessconfiguration.Configuration().Prepare(req.AgentOptions) if err != nil { return startRequest{}, nil, err } - start.HarnessConfig = nativeConfig + start.NativeModelOptions = compileNativeModelOptions(modelConfiguration.HarnessConfig) if req.WorkspaceAuthoring || req.ObserveTools { return fail("requested capability is not available in the private SDK adapter") } diff --git a/apps/parsar-daemon/internal/agent/claudesdk/provider.go b/apps/parsar-daemon/internal/agent/claudesdk/provider.go index 61d64b125..bf17ca3dc 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/provider.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/provider.go @@ -3,15 +3,14 @@ package claudesdk import ( "strings" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" + harnessconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/claudesdk" ) -// The factory prepares the native endpoint before producing launch variables. -// Upstream credentials remain in Runtime when translation is required. +// Validate the frozen native provider before producing launch variables. func providerEnvironment(value any) ([]string, error) { - provider, err := modeltransport.ParseProvider(value) - if err != nil || provider.Protocol != modeltransport.Anthropic { - return nil, modeltransport.ErrConfiguration + provider, err := harnessconfiguration.Configuration().ParseProvider(value) + if err != nil { + return nil, err } return []string{"ANTHROPIC_BASE_URL=" + provider.BaseURL, "ANTHROPIC_AUTH_TOKEN=" + provider.APIKey}, nil } diff --git a/apps/parsar-daemon/internal/agent/claudesdk/session.go b/apps/parsar-daemon/internal/agent/claudesdk/session.go index a70dc3c48..f8d7a1f82 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/session.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/session.go @@ -9,11 +9,9 @@ import ( "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent" "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent/clirunner" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" ) type session struct { - modelRoute modeltransport.Route owner *executor runID string frames chan []byte diff --git a/apps/parsar-daemon/internal/agent/claudesdk/steering.go b/apps/parsar-daemon/internal/agent/claudesdk/steering.go index 1485ada3d..c9d2c0eae 100644 --- a/apps/parsar-daemon/internal/agent/claudesdk/steering.go +++ b/apps/parsar-daemon/internal/agent/claudesdk/steering.go @@ -34,9 +34,6 @@ func (s *session) Steer(ctx context.Context, input proto.PromptSteerPayload) err // SteerWithReceipt separates a complete bridge write from native consumption. func (s *session) SteerWithReceipt(ctx context.Context, input proto.PromptSteerPayload, written func()) error { - if err := s.modelRoute.ValidateInput(input.Input); err != nil { - return err - } select { case <-s.cancelOutput: return agent.ErrSteeringInactive diff --git a/apps/parsar-daemon/internal/agent/codex/executor.go b/apps/parsar-daemon/internal/agent/codex/executor.go index 09babe5e2..b64ba3756 100644 --- a/apps/parsar-daemon/internal/agent/codex/executor.go +++ b/apps/parsar-daemon/internal/agent/codex/executor.go @@ -44,9 +44,6 @@ func (e *Executor) StartTurn(ctx context.Context, runID string, input proto.Mess if out == nil || strings.TrimSpace(runID) == "" || input.Validate() != nil { return nil, errors.New("codex: start requires a run identity, input and output") } - if err := e.prepared.plan.ModelRoute.ValidateInput(input); err != nil { - return nil, err - } if err := ctx.Err(); err != nil { return nil, err } @@ -71,7 +68,7 @@ func (e *Executor) StartTurn(ctx context.Context, runID string, input proto.Mess } functions := &functionCalls{definitions: base.functions.definitions, names: base.functions.names, pending: map[string]*pendingFunction{}} turnCtx, cancel := context.WithCancel(base.cancelCtx) - s := &Session{modelRoute: base.modelRoute, executor: e, nativeHome: base.nativeHome, + s := &Session{executor: e, nativeHome: base.nativeHome, functions: functions, observeMessages: base.observeMessages, observeTools: base.observeTools, observeToolObservations: base.observeToolObservations, observeSubagentIdentities: base.observeSubagentIdentities, cfg: base.cfg, rpc: base.rpc, cancelCtx: turnCtx, cancelFn: cancel, diff --git a/apps/parsar-daemon/internal/agent/codex/function_receipts.go b/apps/parsar-daemon/internal/agent/codex/function_receipts.go index 9e80aa71e..8c594209d 100644 --- a/apps/parsar-daemon/internal/agent/codex/function_receipts.go +++ b/apps/parsar-daemon/internal/agent/codex/function_receipts.go @@ -26,9 +26,6 @@ type pendingFunction struct { } func (s *Session) SubmitFunctionResult(ctx context.Context, result proto.FunctionResultPayload) error { - if err := s.modelRoute.ValidateFunctionResult(result); err != nil { - return err - } if !s.beginOperation() { return agent.ErrSteeringInactive } diff --git a/apps/parsar-daemon/internal/agent/codex/model_route_test.go b/apps/parsar-daemon/internal/agent/codex/model_route_test.go index 7ed091b60..a77451574 100644 --- a/apps/parsar-daemon/internal/agent/codex/model_route_test.go +++ b/apps/parsar-daemon/internal/agent/codex/model_route_test.go @@ -1,41 +1,37 @@ package codex import ( + "reflect" + "strings" "testing" - - "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" ) -func TestConvertedExecutorRejectsFutureImageBeforeOwnership(t *testing.T) { - e, root := executorFixture(t, "complete") - route := modeltransport.Route{Native: modeltransport.Responses, Upstream: modeltransport.Anthropic} - e.prepared.plan.ModelRoute = route - e.prepared.session.modelRoute = route - out := make(chan proto.Envelope, 20) - turn, err := e.StartTurn(t.Context(), "text-first", proto.TextInput("hello"), out) - if err != nil { - t.Fatal(err) - } - if !awaitExecutorTurn(t, turn, out).Reusable { - t.Fatal("text did not settle") - } - before := len(preparationFrames(t, root)) - ref := "data:image/png;base64,AA==" - input := proto.MessageInput{{Content: []proto.InputContent{{Type: "input_image", ImageURL: &ref}}}} - out = make(chan proto.Envelope, 20) - if next, err := e.StartTurn(t.Context(), "image-next", input, out); next != nil || err != modeltransport.ErrUnsupported { - t.Fatalf("image acquired output/native ownership: %v", err) - } - session := turn.(*Session) - if err := session.SteerWithReceipt(t.Context(), proto.PromptSteerPayload{InputID: "image", Input: input}, nil); err != modeltransport.ErrUnsupported { - t.Fatalf("steer accepted image: %v", err) +func TestPlanRejectsNonNativeFrozenProvider(t *testing.T) { + t.Setenv("HOME", t.TempDir()) + for _, protocol := range []string{"anthropic", "chat_completions"} { + t.Run(protocol, func(t *testing.T) { + provider := map[string]any{"protocol": protocol, "base_url": "https://model.invalid/v1", "api_key": "private-sentinel"} + plan, err := BuildSessionPlan("recovered", "frozen-state", t.TempDir(), map[string]any{"model": "frozen-model", "model_provider": provider}) + if plan.Cleanup != nil { + plan.Cleanup() + } + if err == nil || !strings.Contains(err.Error(), "does not support") || strings.Contains(err.Error(), "private-sentinel") { + t.Fatalf("non-native snapshot accepted: %v", err) + } + if !reflect.DeepEqual(provider, map[string]any{"protocol": protocol, "base_url": "https://model.invalid/v1", "api_key": "private-sentinel"}) { + t.Fatal("frozen provider was rewritten") + } + }) } - if err := session.SubmitFunctionResult(t.Context(), proto.FunctionResultPayload{CallID: "call", DeliveryID: "delivery", Success: true, Content: input[0].Content}); err != modeltransport.ErrUnsupported { - t.Fatalf("tool image accepted: %v", err) - } - if len(preparationFrames(t, root)) != before { - t.Fatal("rejected content reached native process") +} + +func TestPlanRejectsIncompleteExplicitProvider(t *testing.T) { + for _, options := range []map[string]any{ + {"model": "chosen", "model_provider": nil}, + {"model_provider": map[string]any{"protocol": "responses", "base_url": "https://model.example/v1", "api_key": "fixture"}}, + } { + if _, err := BuildSessionPlan("frozen", "state", "", options); err == nil { + t.Fatal("explicit provider fell back to native defaults") + } } - close(out) } diff --git a/apps/parsar-daemon/internal/agent/codex/options.go b/apps/parsar-daemon/internal/agent/codex/options.go index e27e727fc..8d41d1302 100644 --- a/apps/parsar-daemon/internal/agent/codex/options.go +++ b/apps/parsar-daemon/internal/agent/codex/options.go @@ -11,14 +11,11 @@ import ( "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/paths" harnessconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/codex" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" ) // SessionPlan holds the resolved per-prompt launch plan derived from // the daemon's PromptRequestPayload. type SessionPlan struct { - ModelRoute modeltransport.Route - // Cwd is the validated working directory passed to codex (and to // the spawned app-server). Empty when the caller provided no work_dir. Cwd string @@ -111,6 +108,10 @@ func BuildSessionPlan(runID, agentStateKey, workDir string, opts map[string]any) return plan, err } + if value, present := opts["model_provider"]; present && (value == nil || stringOpt(opts, "model") == "") { + return plan, errors.New("codex: model and complete model_provider are required") + } + if value, present := opts["web_search"]; present { switch value { case "disabled", "cached", "live": @@ -175,17 +176,7 @@ func BuildSessionPlan(runID, agentStateKey, workDir string, opts map[string]any) } } - // The native adapter selects Responses; Runtime converts other upstream protocols. - preparedOptions, endpoint, err := modeltransport.PrepareOptions(opts, harnessconfiguration.Configuration().NativeProtocols()...) - if err != nil { - return plan, err - } - if endpoint != nil { - plan.ModelRoute = endpoint.Route - cleanup = func() { _ = endpoint.Close() } - plan.Cleanup = cleanup - } - provider, hasProvider, err := normaliseProviderConfig(preparedOptions["model_provider"]) + provider, hasProvider, err := normaliseProviderConfig(opts["model_provider"]) if err != nil { cleanup() return plan, err @@ -388,14 +379,14 @@ func normaliseMCPServers(raw any) (map[string]mcpServerConfig, error) { return out, nil } -// normaliseProviderConfig receives the adapter's prepared native endpoint. +// normaliseProviderConfig validates and renders the frozen native provider. func normaliseProviderConfig(raw any) (providerConfig, bool, error) { if raw == nil { return providerConfig{}, false, nil } - provider, err := modeltransport.ParseProvider(raw) - if err != nil || provider.Protocol != modeltransport.Responses { - return providerConfig{}, false, modeltransport.ErrConfiguration + provider, err := harnessconfiguration.Configuration().ParseProvider(raw) + if err != nil { + return providerConfig{}, false, err } return providerConfig{BaseURL: provider.BaseURL, BearerToken: provider.APIKey, WireAPI: "responses"}, true, nil } diff --git a/apps/parsar-daemon/internal/agent/codex/preparation.go b/apps/parsar-daemon/internal/agent/codex/preparation.go index 09641664e..5f7578418 100644 --- a/apps/parsar-daemon/internal/agent/codex/preparation.go +++ b/apps/parsar-daemon/internal/agent/codex/preparation.go @@ -87,7 +87,6 @@ func newPreparation(parent context.Context, req proto.PromptRequestPayload, cfg rpc := NewJSONRPCClient(rpcCfg) s := &Session{ - modelRoute: plan.ModelRoute, nativeHome: nativeHomeFromPlan(plan), functions: functions, observeMessages: req.ObserveMessages, diff --git a/apps/parsar-daemon/internal/agent/codex/preparation_router_test.go b/apps/parsar-daemon/internal/agent/codex/preparation_router_test.go index 4078d3b99..270dba28f 100644 --- a/apps/parsar-daemon/internal/agent/codex/preparation_router_test.go +++ b/apps/parsar-daemon/internal/agent/codex/preparation_router_test.go @@ -1,5 +1,7 @@ package codex +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -57,7 +59,7 @@ func TestPreparationRouterRetainsActualNativeChild(t *testing.T) { req.DisableExecutionEnvironment = false req.LocalEnvironment = &proto.LocalEnvironment{ID: environment, WorkspaceDirectory: "/workspace", NetworkAccess: "enabled", CapabilitySources: &agentcapabilities.Input{}} registry := agent.NewRegistry() - registry.RegisterKind(proto.SupportedAgentKind{Kind: "codex", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true, FunctionTools: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + registry.RegisterKind(proto.SupportedAgentKind{Kind: "codex", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true, FunctionTools: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("ordinary Factory must not run") }) prepared := make(chan *Prepared, 1) diff --git a/apps/parsar-daemon/internal/agent/codex/prepared.go b/apps/parsar-daemon/internal/agent/codex/prepared.go index 88c5d142e..b4e3a0b78 100644 --- a/apps/parsar-daemon/internal/agent/codex/prepared.go +++ b/apps/parsar-daemon/internal/agent/codex/prepared.go @@ -42,9 +42,6 @@ func (p *Prepared) start(ctx context.Context, runID string, prompt proto.Message if out == nil || strings.TrimSpace(runID) == "" || prompt.Validate() != nil { return nil, errors.New("codex: start requires a run identity, prompt and output channel") } - if err := p.plan.ModelRoute.ValidateInput(prompt); err != nil { - return nil, err - } p.mu.Lock() if p.claimed || p.closed { p.mu.Unlock() diff --git a/apps/parsar-daemon/internal/agent/codex/session.go b/apps/parsar-daemon/internal/agent/codex/session.go index be6b076fb..f53a49261 100644 --- a/apps/parsar-daemon/internal/agent/codex/session.go +++ b/apps/parsar-daemon/internal/agent/codex/session.go @@ -12,7 +12,6 @@ import ( "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" obslog "github.com/MiniMax-AI-Dev/parsar/internal/obs/log" ) @@ -55,7 +54,6 @@ func Factory(ctx context.Context, req proto.PromptRequestPayload, out chan<- pro // 5. turn/completed emits TypeDone + closes out. Cancel can short-cut // this by killing the child early. type Session struct { - modelRoute modeltransport.Route retiredTurns map[string]bool executor *Executor outputDone chan struct{} diff --git a/apps/parsar-daemon/internal/agent/codex/session_plan.go b/apps/parsar-daemon/internal/agent/codex/session_plan.go index 351d75852..4dad48a69 100644 --- a/apps/parsar-daemon/internal/agent/codex/session_plan.go +++ b/apps/parsar-daemon/internal/agent/codex/session_plan.go @@ -7,7 +7,6 @@ import ( "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/localworkspace" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" ) func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg sessionConfig) (SessionPlan, []string, error) { @@ -26,10 +25,6 @@ func prepareSessionPlan(ctx context.Context, req proto.PromptRequestPayload, cfg if err != nil { return SessionPlan{}, nil, fmt.Errorf("codex: build session plan: %w", err) } - if err := plan.ModelRoute.Validate(modeltransport.RequirementsFromRequest(req)); err != nil { - plan.Cleanup() - return SessionPlan{}, nil, err - } if err := configureSubagentObservations(&plan, req); err != nil { plan.Cleanup() return SessionPlan{}, nil, err diff --git a/apps/parsar-daemon/internal/agent/codex/session_steering.go b/apps/parsar-daemon/internal/agent/codex/session_steering.go index a27391b4a..f0d6be931 100644 --- a/apps/parsar-daemon/internal/agent/codex/session_steering.go +++ b/apps/parsar-daemon/internal/agent/codex/session_steering.go @@ -38,9 +38,6 @@ func (s *Session) SteerWithReceipt(ctx context.Context, input proto.PromptSteerP } func (s *Session) steer(ctx context.Context, input proto.PromptSteerPayload, written func()) error { - if err := s.modelRoute.ValidateInput(input.Input); err != nil { - return err - } if !s.beginOperation() { return agent.ErrSteeringInactive } diff --git a/apps/parsar-daemon/internal/agent/configuration_test.go b/apps/parsar-daemon/internal/agent/configuration_test.go new file mode 100644 index 000000000..617601cd9 --- /dev/null +++ b/apps/parsar-daemon/internal/agent/configuration_test.go @@ -0,0 +1,77 @@ +package agent_test + +import ( + "context" + "errors" + "strings" + "testing" + + "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent" + "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" + "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" +) + +func TestEveryRegistryEntryPreparesTheBoundModelConfiguration(t *testing.T) { + registry := agent.NewRegistry() + configuration := harnessconfig.Configuration{Providers: []harnessconfig.Provider{{Protocol: "responses"}}} + calls := 0 + expected := errors.New("native entry reached") + registry.Register("fixture", configuration, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + calls++ + return nil, expected + }) + registry.RegisterExecutor("fixture", func(context.Context, proto.PromptRequestPayload) (agent.Executor, error) { + calls++ + return nil, expected + }) + registry.RegisterPreparation("fixture", true, func(context.Context, proto.PromptRequestPayload) (agent.Prepared, error) { + calls++ + return nil, expected + }) + configuration.Providers[0].Protocol = "anthropic" + copy, err := registry.Configuration("fixture") + if err != nil { + t.Fatal(err) + } + copy.Providers[0].Protocol = "anthropic" + factory, _ := registry.Resolve("fixture") + executor, _ := registry.ResolveExecutor("fixture") + preparation, _ := registry.ResolvePreparation("fixture") + entries := []func(proto.PromptRequestPayload) error{ + func(req proto.PromptRequestPayload) error { _, err := factory(t.Context(), req, nil); return err }, + func(req proto.PromptRequestPayload) error { _, err := executor(t.Context(), req); return err }, + func(req proto.PromptRequestPayload) error { _, err := preparation(t.Context(), req); return err }, + } + for _, entry := range entries { + for _, options := range []map[string]any{ + {"model": nil}, + {"model": "fixture", "model_provider": map[string]any{"protocol": "anthropic", "base_url": "https://provider.example", "api_key": "private-sentinel"}}, + {"model_provider": map[string]any{"protocol": "responses", "base_url": "https://provider.example", "api_key": "private-sentinel"}}, + {"harness_config": map[string]any{"unknown": "private-sentinel"}}, + } { + before := calls + err := entry(proto.PromptRequestPayload{AgentOptions: options}) + if err == nil || errors.Is(err, expected) || calls != before || strings.Contains(err.Error(), "private-sentinel") { + t.Fatal("invalid configuration reached native entry or leaked values") + } + } + if err := entry(proto.PromptRequestPayload{AgentOptions: map[string]any{"model": "fixture", "model_provider": map[string]any{"protocol": "responses", "base_url": "https://provider.example", "api_key": "private-sentinel"}}}); !errors.Is(err, expected) { + t.Fatal("bound declaration was lost or mutated", err) + } + } + if calls != 3 { + t.Fatal("unexpected native calls", calls) + } + if _, err := registry.Configuration("missing"); err == nil { + t.Fatal("undeclared configuration inferred") + } +} + +func TestRegistryRejectsInvalidConfigurationDeclaration(t *testing.T) { + defer func() { + if recover() == nil { + t.Fatal("invalid configuration registered") + } + }() + agent.NewRegistry().Register("fixture", harnessconfig.Configuration{Providers: []harnessconfig.Provider{{Protocol: "unknown"}}}, stubFactory("fixture")) +} diff --git a/apps/parsar-daemon/internal/agent/harness.go b/apps/parsar-daemon/internal/agent/harness.go index 034d0c8a2..9a61199d4 100644 --- a/apps/parsar-daemon/internal/agent/harness.go +++ b/apps/parsar-daemon/internal/agent/harness.go @@ -10,8 +10,8 @@ // rather than additional Go interfaces; qualify and advertise them separately. // // Registration: RegisterKind takes the existing proto.SupportedAgentKind -// descriptor (kind, availability, native version and AgentKindCapabilities) and -// direct-call Factory. RegisterExecutor supplies the Session-owned lifecycle. +// descriptor (kind, availability, native version and AgentKindCapabilities), +// the mandatory shared Configuration and the direct-call Factory. RegisterExecutor supplies the Session-owned lifecycle. // RegisterPreparation optionally adds workspace access without model input. // RegisterKind resets the other factories, so register it first. Preparation // flags are derived by these methods; other advertised capabilities must reflect @@ -27,55 +27,21 @@ import ( "context" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" + "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" ) -// ExecutorFactory must prepare model configuration before native startup and -// before submitting model input; accepted fields must never be silently dropped. -// proto.HarnessConfig is a JSON object: omission and {} are empty configuration; -// explicit null, arrays and objects above 16 KiB of serialized UTF-8 bytes are -// invalid. The selected -// adapter owns field names, meanings and native validation. See the native model -// configuration section of contracts/agents-api/harness-onboarding.md. -// Core freezes model, model_provider and proto.HarnessConfig in the Session. -// Runtime resolves modeltransport first; adapters consume only its final effective -// connection (address, protocol and authentication). -// Each adapter declares safe native model fields in internal/harnessconfig and -// validates the same declaration at admission and again before native application. -// Unknown fields and conflicts with model, provider/authentication, workspace, -// tools/MCP, permissions or lifecycle controls fail with a value-free error. -// Apply accepted fields directly to native config or SDK options, without a -// cross-Harness parameter translation layer. Never merge arbitrary host config. -// Preparation failure cleans up owned configuration files, processes/SDK resources -// and proxies. Unconfirmed cleanup returns a non-nil Executor with the error under -// the factory ownership contract. The logical configuration snapshot remains fixed -// across Turns and reconnects; rebuilding an Executor may regenerate local proxy -// addresses or short-lived credentials. Configuration defaults and inheritance -// belong to the public API contract. - -// Configuration support is declared once by each adapter in internal/harnessconfig: -// accepted upstream protocols, native protocols, token-limit requirements and its -// native model-parameter validator. Core admission, Runtime and modeltransport -// consume that declaration; adding a Harness must not introduce another public -// configuration shape or a Harness-name branch in orchestration. -// -// modeltransport owns route selection and preservation requirements. JSON parsing, -// adapter acceptance, conversion qualification and upstream model support are -// separate facts. A registered translator alone does not prove preservation. -// Native routes keep the original protocol; conversion routes must reject an -// unqualified requirement before forwarding model input. Nonempty native parameters, -// structured output, tool search, native web search and nondefault verbosity are -// currently native-route-only. Existing text, ordinary tools and streaming retain -// their qualified conversion paths; no model-name heuristic establishes support. -// Direct transport does not prove that the selected remote model accepts a native -// parameter: the native Harness/provider remains responsible for that response. +// Model configuration has one shared contract, authored in +// internal/harnessconfig/harness.go. RegisterKind requires that declaration; +// RegisterExecutor and RegisterPreparation inherit it. Every registered entry +// validates model, provider and native parameters before calling native code. +// The declaration belongs to the adapter and is also consumed by Core. Keep +// adapter field rules and rendering private. That shared contract owns frozen +// configuration and native application obligations. This file owns execution +// lifecycle only. Native image/tool/operation support is qualified through proto +// capabilities and the Core engine profile, not model configuration declarations. // -// Input capability uses the existing MessageImages/MessageImagePlacements and -// proto.MessageInput contracts, not another image type in model configuration. -// Configuration preparation checks known requirements. Each Turn, steer and tool -// result must also check newly introduced input requirements against the fixed -// route before native submission or proxy forwarding. Unqualified image forms -// must fail explicitly, never be dropped or reduced to text. The proxy performs -// the final request check before forwarding; it cannot silently weaken semantics. +// Preparation failure retains unconfirmed native cleanup in a non-nil Executor +// under the factory ownership contract below. // Required execution lifecycle. @@ -214,7 +180,7 @@ type Factory func(ctx context.Context, req proto.PromptRequestPayload, out chan< // RegisterKind installs f and the heartbeat descriptor for an // agent_kind. Callers may set Available=false when an adapter exists // but its underlying CLI is not usable. -func (r *Registry) RegisterKind(info proto.SupportedAgentKind, f Factory) { +func (r *Registry) RegisterKind(info proto.SupportedAgentKind, configuration harnessconfig.Configuration, f Factory) { kind := info.Kind if kind == "" { panic("agent.Registry.Register: empty kind") @@ -222,9 +188,19 @@ func (r *Registry) RegisterKind(info proto.SupportedAgentKind, f Factory) { if f == nil { panic("agent.Registry.Register: nil factory") } + if err := configuration.ValidateDeclaration(); err != nil { + panic(err) + } + configuration = configuration.Clone() r.mu.Lock() defer r.mu.Unlock() - r.factories[kind] = f + r.configurations[kind] = configuration + r.factories[kind] = func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (Session, error) { + if _, err := configuration.Prepare(req.AgentOptions); err != nil { + return nil, err + } + return f(ctx, req, out) + } delete(r.preparers, kind) delete(r.executors, kind) info.Capabilities.Preparation = false @@ -241,7 +217,16 @@ func (r *Registry) RegisterExecutor(kind string, factory ExecutorFactory) { if !exists || factory == nil { panic("agent.Registry.RegisterExecutor: registered kind and factory required") } - r.executors[kind] = factory + configuration, declared := r.configurations[kind] + if !declared { + panic("agent.Registry.RegisterExecutor: configuration required") + } + r.executors[kind] = func(ctx context.Context, req proto.PromptRequestPayload) (Executor, error) { + if _, err := configuration.Prepare(req.AgentOptions); err != nil { + return nil, err + } + return factory(ctx, req) + } info.Capabilities.Preparation = true r.kinds[kind] = info } @@ -255,7 +240,16 @@ func (r *Registry) RegisterPreparation(kind string, workspaceRead bool, prepare if !exists || prepare == nil { panic("agent.Registry.RegisterPreparation: registered kind and factory required") } - r.preparers[kind] = prepare + configuration, declared := r.configurations[kind] + if !declared { + panic("agent.Registry.RegisterPreparation: configuration required") + } + r.preparers[kind] = func(ctx context.Context, req proto.PromptRequestPayload) (Prepared, error) { + if _, err := configuration.Prepare(req.AgentOptions); err != nil { + return nil, err + } + return prepare(ctx, req) + } info.Capabilities.Preparation = true info.Capabilities.WorkspaceReadPreparation = workspaceRead r.kinds[kind] = info @@ -263,6 +257,6 @@ func (r *Registry) RegisterPreparation(kind string, workspaceRead bool, prepare // Register installs f as the factory for kind with a basic available // descriptor. Panics on empty kind or nil factory. -func (r *Registry) Register(kind string, f Factory) { - r.RegisterKind(proto.SupportedAgentKind{Kind: kind, Available: true}, f) +func (r *Registry) Register(kind string, configuration harnessconfig.Configuration, f Factory) { + r.RegisterKind(proto.SupportedAgentKind{Kind: kind, Available: true}, configuration, f) } diff --git a/apps/parsar-daemon/internal/agent/mcode/model_provider.go b/apps/parsar-daemon/internal/agent/mcode/model_provider.go index c5057ca33..ba1b9e95a 100644 --- a/apps/parsar-daemon/internal/agent/mcode/model_provider.go +++ b/apps/parsar-daemon/internal/agent/mcode/model_provider.go @@ -3,24 +3,22 @@ package mcode import ( "fmt" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" + harnessconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/mcode" + "github.com/MiniMax-AI-Dev/parsar/internal/modelprovider" ) func modelProviderConfig(raw any, model string) (map[string]any, error) { - provider, err := modeltransport.ParseProvider(raw) + provider, err := harnessconfiguration.Configuration().ParseProvider(raw) if err != nil { return nil, fmt.Errorf("mcode: %w", err) } - if provider.ContextWindow <= 0 || provider.MaxOutputTokens <= 0 { - return nil, fmt.Errorf("mcode: positive model context_window and max_output_tokens are required") - } var api string switch provider.Protocol { - case modeltransport.Anthropic: + case modelprovider.Anthropic: api = "anthropic-messages" - case modeltransport.ChatCompletions: + case modelprovider.ChatCompletions: api = "openai-completions" - case modeltransport.Responses: + case modelprovider.Responses: api = "openai-responses" } return map[string]any{ diff --git a/apps/parsar-daemon/internal/agent/mcode/options.go b/apps/parsar-daemon/internal/agent/mcode/options.go index 99667df5a..385d39e32 100644 --- a/apps/parsar-daemon/internal/agent/mcode/options.go +++ b/apps/parsar-daemon/internal/agent/mcode/options.go @@ -14,7 +14,6 @@ import ( "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent/claudecode" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" harnessconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/mcode" - "github.com/MiniMax-AI-Dev/parsar/internal/modeltransport" "github.com/MiniMax-AI-Dev/parsar/internal/obs/log" ) @@ -33,15 +32,6 @@ func prepareOptionsWithSkills(ctx context.Context, req proto.PromptRequestPayloa if _, err := harnessconfiguration.Configuration().PrepareHarnessConfig(req.AgentOptions); err != nil { return result, err } - if raw, ok := req.AgentOptions["model_provider"]; ok { - provider, err := modeltransport.ParseProvider(raw) - if err != nil { - return result, err - } - if err = harnessconfiguration.Configuration().ValidateRoute(string(provider.Protocol), modeltransport.RequirementsFromRequest(req)); err != nil { - return result, err - } - } if req.StrictResume { if err := validateExecutionRequest(req); err != nil { return result, err diff --git a/apps/parsar-daemon/internal/agent/registry.go b/apps/parsar-daemon/internal/agent/registry.go index a96080c74..18396433c 100644 --- a/apps/parsar-daemon/internal/agent/registry.go +++ b/apps/parsar-daemon/internal/agent/registry.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/MiniMax-AI-Dev/parsar/internal/agentdaemon/proto" + "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" ) // ErrUnknownPermission is returned by PermissionResponder.SubmitPermission when @@ -24,19 +25,21 @@ var ErrUnsupportedKind = errors.New("agent: unsupported agent_kind") // Registry maps agent_kind → Factory and keeps the daemon-advertised // capability descriptor for each kind. Safe for concurrent use. type Registry struct { - mu sync.RWMutex - factories map[string]Factory - preparers map[string]PreparationFactory - executors map[string]ExecutorFactory - kinds map[string]proto.SupportedAgentKind + mu sync.RWMutex + factories map[string]Factory + preparers map[string]PreparationFactory + executors map[string]ExecutorFactory + kinds map[string]proto.SupportedAgentKind + configurations map[string]harnessconfig.Configuration } func NewRegistry() *Registry { return &Registry{ - factories: make(map[string]Factory), - preparers: make(map[string]PreparationFactory), - executors: make(map[string]ExecutorFactory), - kinds: make(map[string]proto.SupportedAgentKind), + factories: make(map[string]Factory), + preparers: make(map[string]PreparationFactory), + executors: make(map[string]ExecutorFactory), + kinds: make(map[string]proto.SupportedAgentKind), + configurations: make(map[string]harnessconfig.Configuration), } } @@ -107,3 +110,15 @@ func (r *Registry) ResolvePreparation(kind string) (PreparationFactory, error) { } return f, nil } + +// Configuration returns an owned declaration for registry wrappers. Wrappers +// transfer it with the factory; they must not infer configuration from kind names. +func (r *Registry) Configuration(kind string) (harnessconfig.Configuration, error) { + r.mu.RLock() + defer r.mu.RUnlock() + configuration, ok := r.configurations[kind] + if !ok { + return harnessconfig.Configuration{}, ErrUnsupportedKind + } + return configuration.Clone(), nil +} diff --git a/apps/parsar-daemon/internal/agent/registry_test.go b/apps/parsar-daemon/internal/agent/registry_test.go index e1ab3e8f7..c875a2175 100644 --- a/apps/parsar-daemon/internal/agent/registry_test.go +++ b/apps/parsar-daemon/internal/agent/registry_test.go @@ -1,5 +1,7 @@ package agent_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -28,7 +30,7 @@ func (stubSession) SubmitPromptForUserChoice(context.Context, string, proto.Prom func TestRegistryResolveReturnsRegisteredFactory(t *testing.T) { reg := agent.NewRegistry() - reg.Register("claude_code", stubFactory("cc")) + reg.Register("claude_code", harnessconfig.Configuration{}, stubFactory("cc")) f, err := reg.Resolve("claude_code") if err != nil { @@ -46,7 +48,7 @@ func TestRegistryResolveReturnsRegisteredFactory(t *testing.T) { func TestRegistryResolveUnknownKindReturnsTypedError(t *testing.T) { reg := agent.NewRegistry() - reg.Register("claude_code", stubFactory("cc")) + reg.Register("claude_code", harnessconfig.Configuration{}, stubFactory("cc")) _, err := reg.Resolve("opencode") if !errors.Is(err, agent.ErrUnsupportedKind) { @@ -56,8 +58,8 @@ func TestRegistryResolveUnknownKindReturnsTypedError(t *testing.T) { func TestRegistryRegisterOverwrites(t *testing.T) { reg := agent.NewRegistry() - reg.Register("k", stubFactory("v1")) - reg.Register("k", stubFactory("v2")) + reg.Register("k", harnessconfig.Configuration{}, stubFactory("v1")) + reg.Register("k", harnessconfig.Configuration{}, stubFactory("v2")) f, err := reg.Resolve("k") if err != nil { @@ -71,8 +73,8 @@ func TestRegistryRegisterOverwrites(t *testing.T) { func TestRegistryKindsReportsRegistered(t *testing.T) { reg := agent.NewRegistry() - reg.Register("claude_code", stubFactory("cc")) - reg.Register("opencode", stubFactory("oc")) + reg.Register("claude_code", harnessconfig.Configuration{}, stubFactory("cc")) + reg.Register("opencode", harnessconfig.Configuration{}, stubFactory("oc")) got := reg.Kinds() slices.Sort(got) @@ -88,7 +90,7 @@ func TestRegistryRegisterPanicsOnEmptyKind(t *testing.T) { t.Fatal("Register(\"\", ...) did not panic") } }() - agent.NewRegistry().Register("", stubFactory("x")) + agent.NewRegistry().Register("", harnessconfig.Configuration{}, stubFactory("x")) } func TestRegistryRegisterPanicsOnNilFactory(t *testing.T) { @@ -97,7 +99,7 @@ func TestRegistryRegisterPanicsOnNilFactory(t *testing.T) { t.Fatal("Register(kind, nil) did not panic") } }() - agent.NewRegistry().Register("k", nil) + agent.NewRegistry().Register("k", harnessconfig.Configuration{}, nil) } func TestRegistrySupportedAgentKindsReportsDescriptors(t *testing.T) { @@ -109,7 +111,7 @@ func TestRegistrySupportedAgentKindsReportsDescriptors(t *testing.T) { Capabilities: proto.AgentKindCapabilities{ Streaming: true, }, - }, stubFactory("oc")) + }, harnessconfig.Configuration{}, stubFactory("oc")) reg.RegisterKind(proto.SupportedAgentKind{ Kind: "claude_code", Available: true, @@ -120,7 +122,7 @@ func TestRegistrySupportedAgentKindsReportsDescriptors(t *testing.T) { Usage: true, Resume: true, }, - }, stubFactory("cc")) + }, harnessconfig.Configuration{}, stubFactory("cc")) got := reg.SupportedAgentKinds() if len(got) != 2 { @@ -139,7 +141,7 @@ func TestRegistrySupportedAgentKindsReportsDescriptors(t *testing.T) { func TestRegistryExecutorRequiresExplicitRegistration(t *testing.T) { registry := agent.NewRegistry() - registry.Register("native", stubFactory("native")) + registry.Register("native", harnessconfig.Configuration{}, stubFactory("native")) if _, err := registry.ResolveExecutor("native"); err == nil { t.Fatal("legacy factory implied reusable execution") } @@ -152,7 +154,7 @@ func TestRegistryExecutorRequiresExplicitRegistration(t *testing.T) { if _, err := factory(t.Context(), proto.PromptRequestPayload{}); !errors.Is(err, expected) { t.Fatal(err) } - registry.Register("native", stubFactory("replacement")) + registry.Register("native", harnessconfig.Configuration{}, stubFactory("replacement")) if _, err := registry.ResolveExecutor("native"); err == nil { t.Fatal("replacing a kind retained its old executor capability") } diff --git a/apps/parsar-daemon/internal/cli/agent_registration.go b/apps/parsar-daemon/internal/cli/agent_registration.go index eb3bf55d8..47a2c8ef8 100644 --- a/apps/parsar-daemon/internal/cli/agent_registration.go +++ b/apps/parsar-daemon/internal/cli/agent_registration.go @@ -1,5 +1,9 @@ package cli +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" +import codexconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/codex" +import mcodeconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/mcode" + import ( "context" "github.com/MiniMax-AI-Dev/parsar/apps/parsar-daemon/internal/agent" @@ -13,15 +17,15 @@ import ( ) func registerAgentKinds(registry *agent.Registry, agentCLIs agentCLIDiscovery, serverURL string) { - registerProduct := func(info proto.SupportedAgentKind, factory agent.Factory) { + registerProduct := func(info proto.SupportedAgentKind, configuration harnessconfig.Configuration, factory agent.Factory) { if agentCLIs.permits(info.Kind) { - registerProductAgentKind(registry, info, factory) + registerProductAgentKind(registry, info, configuration, factory) } } - registerProduct(agentCLIs.ClaudeCode, withSkillUploadServer(withCapabilityDownloads(claudecode.Factory, serverURL), serverURL)) - registerProduct(agentCLIs.OpenCode, withSkillUploadServer(withCapabilityDownloads(opencodeagent.Factory, serverURL), serverURL)) - registerProduct(agentCLIs.Codex, withSkillUploadServer(withCapabilityDownloads(codex.Factory, serverURL), serverURL)) + registerProduct(agentCLIs.ClaudeCode, harnessconfig.Configuration{}, withSkillUploadServer(withCapabilityDownloads(claudecode.Factory, serverURL), serverURL)) + registerProduct(agentCLIs.OpenCode, harnessconfig.Configuration{}, withSkillUploadServer(withCapabilityDownloads(opencodeagent.Factory, serverURL), serverURL)) + registerProduct(agentCLIs.Codex, codexconfiguration.Configuration(), withSkillUploadServer(withCapabilityDownloads(codex.Factory, serverURL), serverURL)) if agentCLIs.Codex.Available { registry.RegisterExecutor("codex", withExecutorCapabilities(codex.NewExecutorFactory(), serverURL)) } @@ -34,12 +38,12 @@ func registerAgentKinds(registry *agent.Registry, agentCLIs agentCLIDiscovery, s return prepared, err }) } - registerProduct(agentCLIs.Pi, withSkillUploadServer(withCapabilityDownloads(pi.Factory, serverURL), serverURL)) + registerProduct(agentCLIs.Pi, harnessconfig.Configuration{}, withSkillUploadServer(withCapabilityDownloads(pi.Factory, serverURL), serverURL)) if agentCLIs.MCodeWorkspace != nil { - registry.RegisterKind(agentCLIs.MCode, mcode.Factory) + registry.RegisterKind(agentCLIs.MCode, mcodeconfiguration.Configuration(), mcode.Factory) registry.RegisterPreparation("mcode", true, mcode.NewPreparationFactory(*agentCLIs.MCodeWorkspace)) } else { - registerProduct(agentCLIs.MCode, withSkillUploadServer(withCapabilityDownloads(mcode.Factory, serverURL), serverURL)) + registerProduct(agentCLIs.MCode, mcodeconfiguration.Configuration(), withSkillUploadServer(withCapabilityDownloads(mcode.Factory, serverURL), serverURL)) } if agentCLIs.MCode.Available { registry.RegisterExecutor("mcode", withExecutorCapabilities(mcode.NewExecutorFactory(agentCLIs.MCodeWorkspace), serverURL)) @@ -47,7 +51,7 @@ func registerAgentKinds(registry *agent.Registry, agentCLIs agentCLIDiscovery, s registerClaudeSDK(registry, agentCLIs.ClaudeSDK) } -func registerProductAgentKind(registry *agent.Registry, info proto.SupportedAgentKind, factory agent.Factory) { +func registerProductAgentKind(registry *agent.Registry, info proto.SupportedAgentKind, configuration harnessconfig.Configuration, factory agent.Factory) { info.Capabilities.WorkspaceAuthoring = true - registry.RegisterKind(info, factory) + registry.RegisterKind(info, configuration, factory) } diff --git a/apps/parsar-daemon/internal/cli/authoring.go b/apps/parsar-daemon/internal/cli/authoring.go index e8fb938ce..f75917b90 100644 --- a/apps/parsar-daemon/internal/cli/authoring.go +++ b/apps/parsar-daemon/internal/cli/authoring.go @@ -61,7 +61,11 @@ func authoringRegistry(registry *agent.Registry, bridge *authoring.Bridge) *agen if info.Capabilities.WorkspaceAuthoring { factory = withAuthoringBridge(factory, bridge) } - wrapped.RegisterKind(info, factory) + configuration, err := registry.Configuration(info.Kind) + if err != nil { + panic(err) + } + wrapped.RegisterKind(info, configuration, factory) if executor, err := registry.ResolveExecutor(info.Kind); err == nil { wrapped.RegisterExecutor(info.Kind, executor) } diff --git a/apps/parsar-daemon/internal/cli/authoring_test.go b/apps/parsar-daemon/internal/cli/authoring_test.go index 035ce2403..ffdb411f5 100644 --- a/apps/parsar-daemon/internal/cli/authoring_test.go +++ b/apps/parsar-daemon/internal/cli/authoring_test.go @@ -1,5 +1,7 @@ package cli +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -69,7 +71,7 @@ func TestAuthoringRegistryRequiresExplicitCapability(t *testing.T) { called := false stop := errors.New("controlled factory stop") out := make(chan proto.Envelope, 1) - reg.RegisterKind(proto.SupportedAgentKind{Kind: "engine", Available: true, Capabilities: proto.AgentKindCapabilities{WorkspaceAuthoring: optIn}}, func(_ context.Context, req proto.PromptRequestPayload, events chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "engine", Available: true, Capabilities: proto.AgentKindCapabilities{WorkspaceAuthoring: optIn}}, harnessconfig.Configuration{}, func(_ context.Context, req proto.PromptRequestPayload, events chan<- proto.Envelope) (agent.Session, error) { called = true env, _ := req.AgentOptions["env"].(map[string]any) socket, _ := env[proto.AuthoringSocketEnv].(string) diff --git a/apps/parsar-daemon/internal/cli/claude_sdk.go b/apps/parsar-daemon/internal/cli/claude_sdk.go index 587867740..ac146a472 100644 --- a/apps/parsar-daemon/internal/cli/claude_sdk.go +++ b/apps/parsar-daemon/internal/cli/claude_sdk.go @@ -1,5 +1,7 @@ package cli +import harnessconfiguration "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig/claudesdk" + import ( "context" "fmt" @@ -124,7 +126,7 @@ func registerClaudeSDK(registry *agent.Registry, discovery *claudeSDKDiscovery) return nil, fmt.Errorf("claude_sdk: configured runtime is unavailable") } } - registry.RegisterKind(discovery.Info, factory) + registry.RegisterKind(discovery.Info, harnessconfiguration.Configuration(), factory) if discovery.Info.Available { registry.RegisterExecutor("claude_sdk", claudesdk.NewExecutorFactory(discovery.Config)) } diff --git a/apps/parsar-daemon/internal/cli/connect_cleanup_test.go b/apps/parsar-daemon/internal/cli/connect_cleanup_test.go index e5da165fd..fa963eac0 100644 --- a/apps/parsar-daemon/internal/cli/connect_cleanup_test.go +++ b/apps/parsar-daemon/internal/cli/connect_cleanup_test.go @@ -1,5 +1,7 @@ package cli +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -113,7 +115,7 @@ func testDisconnectedPumpCleanup(t *testing.T, suspend bool) { owner := &cleanupExecutor{retry: make(chan struct{}), confirm: make(chan struct{})} registry := agent.NewRegistry() registry.RegisterKind(proto.SupportedAgentKind{Kind: "cleanup", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, - func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("unexpected legacy factory") }) var factories atomic.Int32 diff --git a/apps/parsar-daemon/internal/cli/preparation_test.go b/apps/parsar-daemon/internal/cli/preparation_test.go index 5f5b54cfe..4f6c5b7a4 100644 --- a/apps/parsar-daemon/internal/cli/preparation_test.go +++ b/apps/parsar-daemon/internal/cli/preparation_test.go @@ -1,5 +1,7 @@ package cli +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -41,7 +43,7 @@ func TestPreparationRegistrationBypassesProductWrappers(t *testing.T) { t.Fatal("real heartbeat registry lost preparation") } } - wrapped.Register("codex", func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + wrapped.Register("codex", harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, stop }) if _, err := wrapped.ResolvePreparation("codex"); err == nil { diff --git a/apps/parsar-daemon/internal/contracttest/wire_test.go b/apps/parsar-daemon/internal/contracttest/wire_test.go index d2b38ca98..9cf58ace5 100644 --- a/apps/parsar-daemon/internal/contracttest/wire_test.go +++ b/apps/parsar-daemon/internal/contracttest/wire_test.go @@ -2,6 +2,8 @@ // using the production gateway, transport and dispatcher with a controlled adapter. package contracttest +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -84,7 +86,7 @@ func connectFixture(t *testing.T, factory agent.ExecutorFactory) *wireFixture { } kinds := agent.NewRegistry() kinds.RegisterKind(proto.SupportedAgentKind{Kind: "contract", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, - func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("prepared execution must not use prompt_request") }) kinds.RegisterExecutor("contract", factory) diff --git a/apps/parsar-daemon/internal/dispatch/cancellation_test.go b/apps/parsar-daemon/internal/dispatch/cancellation_test.go index 7513d979b..f25280d05 100644 --- a/apps/parsar-daemon/internal/dispatch/cancellation_test.go +++ b/apps/parsar-daemon/internal/dispatch/cancellation_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -24,7 +26,7 @@ func TestCompletionWaitsForNativeWriterRelease(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) sess := &cancelReceiptSession{entered: make(chan struct{}), release: make(chan struct{})} - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { sess.fakeSession = &fakeSession{out: out, closeOutOnCancel: true} return sess, nil }) @@ -63,7 +65,7 @@ func TestCancellationReceiptFollowsAdapterOutcome(t *testing.T) { if fails { sess.err = errors.New("adapter could not cancel") } - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { sess.fakeSession = &fakeSession{out: out, closeOutOnCancel: true} return sess, nil }) diff --git a/apps/parsar-daemon/internal/dispatch/environment_test.go b/apps/parsar-daemon/internal/dispatch/environment_test.go index 4111edf52..4d2469c04 100644 --- a/apps/parsar-daemon/internal/dispatch/environment_test.go +++ b/apps/parsar-daemon/internal/dispatch/environment_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -13,7 +15,7 @@ func TestNoEnvironmentRejectsOtherEngineBeforeFactory(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) called := false - h.reg.Register("claude_code", func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("claude_code", harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { called = true return nil, nil }) @@ -32,7 +34,7 @@ func TestNoEnvironmentUsesAvailableCapability(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) called := false - h.reg.RegisterKind(proto.SupportedAgentKind{Kind: "claude_sdk", Available: available, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + h.reg.RegisterKind(proto.SupportedAgentKind{Kind: "claude_sdk", Available: available, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { called = true return nil, errors.New("controlled factory stop") }) @@ -51,7 +53,7 @@ func TestLocalEnvironmentRequiresAvailableCapability(t *testing.T) { called := false h.reg.RegisterKind(proto.SupportedAgentKind{Kind: "codex", Available: mode != "unavailable", Capabilities: proto.AgentKindCapabilities{LocalEnvironment: mode != "unsupported"}}, - func(_ context.Context, req proto.PromptRequestPayload, _ chan<- proto.Envelope) (agent.Session, error) { + harnessconfig.Configuration{}, func(_ context.Context, req proto.PromptRequestPayload, _ chan<- proto.Envelope) (agent.Session, error) { called = true if req.LocalEnvironment == nil || req.LocalEnvironment.ID != preparationEnvironmentID { t.Error("local descriptor lost before factory") diff --git a/apps/parsar-daemon/internal/dispatch/executor_cancel_receipt_test.go b/apps/parsar-daemon/internal/dispatch/executor_cancel_receipt_test.go index 8ea3b6ee2..d799c60c9 100644 --- a/apps/parsar-daemon/internal/dispatch/executor_cancel_receipt_test.go +++ b/apps/parsar-daemon/internal/dispatch/executor_cancel_receipt_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -124,7 +126,7 @@ func TestExecutorCancellationReachesNativeBeforeDurableReceiptJoin(t *testing.T) sender := &receiptCancelSender{recSender: &recSender{}, entered: make(chan struct{}), release: make(chan struct{})} owner := &receiptCancelExecutor{turn: make(chan *receiptCancelTurn, 2), cancelFails: mode == "cancel_failure" || mode == "close_failure_retry", closeFailsFirst: mode == "close_failure_retry", closeEntered: make(chan struct{}), closeRelease: make(chan struct{})} reg := agent.NewRegistry() - reg.RegisterKind(proto.SupportedAgentKind{Kind: "reusable", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "reusable", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("legacy factory forbidden") }) reg.RegisterExecutor("reusable", func(context.Context, proto.PromptRequestPayload) (agent.Executor, error) { return owner, nil }) diff --git a/apps/parsar-daemon/internal/dispatch/executor_handoff_test.go b/apps/parsar-daemon/internal/dispatch/executor_handoff_test.go index 730698ef6..2a3d73d4d 100644 --- a/apps/parsar-daemon/internal/dispatch/executor_handoff_test.go +++ b/apps/parsar-daemon/internal/dispatch/executor_handoff_test.go @@ -1,5 +1,7 @@ package dispatch +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -91,7 +93,7 @@ func TestPreparedDonePublishesAfterExecutorHandoff(t *testing.T) { owner := &terminalHandoffExecutor{turns: make(chan *terminalHandoffTurn, 3)} registry := agent.NewRegistry() registry.RegisterKind(proto.SupportedAgentKind{Kind: "handoff", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, - func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("legacy path forbidden") }) var creates atomic.Int32 diff --git a/apps/parsar-daemon/internal/dispatch/executor_test.go b/apps/parsar-daemon/internal/dispatch/executor_test.go index db02f9ff8..f04a7f35d 100644 --- a/apps/parsar-daemon/internal/dispatch/executor_test.go +++ b/apps/parsar-daemon/internal/dispatch/executor_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -73,7 +75,7 @@ func executorRouter(t *testing.T, owner *reusableExecutor, idle time.Duration) ( t.Helper() calls := &atomic.Int32{} reg := agent.NewRegistry() - reg.RegisterKind(proto.SupportedAgentKind{Kind: "reusable", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "reusable", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("ordinary factory is forbidden") }) reg.RegisterExecutor("reusable", func(context.Context, proto.PromptRequestPayload) (agent.Executor, error) { @@ -217,7 +219,7 @@ func TestExecutorPreInputFailureConfirmsCloseBeforeRetrySignal(t *testing.T) { func poolRouter(t *testing.T, factory agent.ExecutorFactory) (*dispatch.Router, *recSender) { t.Helper() registry := agent.NewRegistry() - registry.RegisterKind(proto.SupportedAgentKind{Kind: "reusable", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + registry.RegisterKind(proto.SupportedAgentKind{Kind: "reusable", Available: true, Capabilities: proto.AgentKindCapabilities{EnvironmentNone: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("unexpected legacy factory") }) registry.RegisterExecutor("reusable", factory) diff --git a/apps/parsar-daemon/internal/dispatch/functions_native_test.go b/apps/parsar-daemon/internal/dispatch/functions_native_test.go index 8d898357c..89aa69c74 100644 --- a/apps/parsar-daemon/internal/dispatch/functions_native_test.go +++ b/apps/parsar-daemon/internal/dispatch/functions_native_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "encoding/json" @@ -107,7 +109,7 @@ func TestNativeFunctionBridge(t *testing.T) { })) defer model.Close() reg := agent.NewRegistry() - reg.RegisterKind(proto.SupportedAgentKind{Kind: "codex", Available: true, Capabilities: proto.AgentKindCapabilities{FunctionTools: true, EnvironmentNone: true}}, codex.Factory) + reg.RegisterKind(proto.SupportedAgentKind{Kind: "codex", Available: true, Capabilities: proto.AgentKindCapabilities{FunctionTools: true, EnvironmentNone: true}}, harnessconfig.Configuration{}, codex.Factory) sender := make(nativeFunctionSender, 256) router, err := dispatch.New(dispatch.Config{Registry: reg, Sender: sender}) if err != nil { diff --git a/apps/parsar-daemon/internal/dispatch/functions_test.go b/apps/parsar-daemon/internal/dispatch/functions_test.go index 2acb2deb3..1d6c59ccd 100644 --- a/apps/parsar-daemon/internal/dispatch/functions_test.go +++ b/apps/parsar-daemon/internal/dispatch/functions_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "bytes" "context" @@ -35,7 +37,7 @@ func TestFunctionReceiptsScopeRetriesAndConflicts(t *testing.T) { reg := agent.NewRegistry() sender := &recSender{} sessions := map[string]*functionSession{} - reg.RegisterKind(proto.SupportedAgentKind{Kind: "function-test", Available: true, Capabilities: proto.AgentKindCapabilities{FunctionTools: true}}, func(ctx context.Context, p proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "function-test", Available: true, Capabilities: proto.AgentKindCapabilities{FunctionTools: true}}, harnessconfig.Configuration{}, func(ctx context.Context, p proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { s := &functionSession{fakeSession: &fakeSession{out: out, ctx: ctx, closeOutOnCancel: true}} sessions[p.RunID] = s return s, nil @@ -135,7 +137,7 @@ func TestFunctionReceiptsScopeRetriesAndConflicts(t *testing.T) { func TestFunctionToolsRequireAdvertisedSupport(t *testing.T) { reg := agent.NewRegistry() called := false - reg.Register("unsupported", func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.Register("unsupported", harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { called = true return nil, nil }) @@ -163,7 +165,7 @@ func functionResultContent(text string) []proto.InputContent { func TestDiscoveryCannotReachAnEagerOnlyAdapter(t *testing.T) { reg := agent.NewRegistry() called := false - reg.RegisterKind(proto.SupportedAgentKind{Kind: "eager-only", Available: true, Capabilities: proto.AgentKindCapabilities{FunctionTools: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "eager-only", Available: true, Capabilities: proto.AgentKindCapabilities{FunctionTools: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { called = true return nil, nil }) diff --git a/apps/parsar-daemon/internal/dispatch/local_directory_test.go b/apps/parsar-daemon/internal/dispatch/local_directory_test.go index ca534f751..b7ed94508 100644 --- a/apps/parsar-daemon/internal/dispatch/local_directory_test.go +++ b/apps/parsar-daemon/internal/dispatch/local_directory_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -26,7 +28,7 @@ func TestLocalDirectoryPreparationNeedsNoHarnessAndRejectsOtherOwners(t *testing } var harnessCalls atomic.Int32 reg := agent.NewRegistry() - reg.RegisterKind(proto.SupportedAgentKind{Kind: "native", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "native", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { harnessCalls.Add(1) return nil, errors.New("must not start a model") }) @@ -108,7 +110,7 @@ func TestLocalDirectoryKeepsNotDirectorySeparateFromFailures(t *testing.T) { t.Fatal(err) } reg := agent.NewRegistry() - reg.RegisterKind(proto.SupportedAgentKind{Kind: "native", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "native", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("must not start a model") }) reg.RegisterPreparation("native", true, func(context.Context, proto.PromptRequestPayload) (agent.Prepared, error) { diff --git a/apps/parsar-daemon/internal/dispatch/mcp_http_test.go b/apps/parsar-daemon/internal/dispatch/mcp_http_test.go index 13bb7e76e..61ee6f0dd 100644 --- a/apps/parsar-daemon/internal/dispatch/mcp_http_test.go +++ b/apps/parsar-daemon/internal/dispatch/mcp_http_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "encoding/json" @@ -53,7 +55,7 @@ func TestMCPHTTPBearerRejectsUnsupportedRequestsBeforeFactory(t *testing.T) { } called := false h.reg.RegisterKind(proto.SupportedAgentKind{Kind: req.AgentKind, Available: mode != "unavailable", Capabilities: caps}, - func(_ context.Context, got proto.PromptRequestPayload, _ chan<- proto.Envelope) (agent.Session, error) { + harnessconfig.Configuration{}, func(_ context.Context, got proto.PromptRequestPayload, _ chan<- proto.Envelope) (agent.Session, error) { called = true if mode == "required" && !(*got.MCPHTTPServers)[0].Required { t.Error("required initialization lost before adapter") @@ -98,7 +100,7 @@ func TestLocalMCPRejectsBeforePreparationFactory(t *testing.T) { req.Configuration.MCPHTTPServers = nil } entered := make(chan struct{}, 1) - h.reg.RegisterKind(proto.SupportedAgentKind{Kind: "prepared", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true, MCPHTTPTools: true, MCPHTTPBearerAuth: true, MCPHTTPRequired: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + h.reg.RegisterKind(proto.SupportedAgentKind{Kind: "prepared", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true, MCPHTTPTools: true, MCPHTTPBearerAuth: true, MCPHTTPRequired: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { t.Error("ordinary factory called") return nil, errors.New("unexpected") }) diff --git a/apps/parsar-daemon/internal/dispatch/optional_interactions_test.go b/apps/parsar-daemon/internal/dispatch/optional_interactions_test.go index 8cd80565d..f53a80708 100644 --- a/apps/parsar-daemon/internal/dispatch/optional_interactions_test.go +++ b/apps/parsar-daemon/internal/dispatch/optional_interactions_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "testing" @@ -19,7 +21,7 @@ func TestOptionalInteractionResponders(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) var output chan<- proto.Envelope - h.reg.Register("minimal", func(_ context.Context, _ proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("minimal", harnessconfig.Configuration{}, func(_ context.Context, _ proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { output = out s := &fakeSession{out: out, closeOutOnCancel: true} return lifecycleOnly{cancel: s.Cancel}, nil diff --git a/apps/parsar-daemon/internal/dispatch/preparation_test.go b/apps/parsar-daemon/internal/dispatch/preparation_test.go index 243e2b8df..7e0dcbacd 100644 --- a/apps/parsar-daemon/internal/dispatch/preparation_test.go +++ b/apps/parsar-daemon/internal/dispatch/preparation_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -116,7 +118,7 @@ func preparationRequest() proto.ExecutionPreparePayload { func preparationRouter(t *testing.T, sender dispatch.Sender, timeout time.Duration, factory agent.PreparationFactory) *dispatch.Router { t.Helper() reg := agent.NewRegistry() - reg.RegisterKind(proto.SupportedAgentKind{Kind: "prepared", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true}}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + reg.RegisterKind(proto.SupportedAgentKind{Kind: "prepared", Available: true, Capabilities: proto.AgentKindCapabilities{LocalEnvironment: true}}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("ordinary Factory must not be used for preparation") }) reg.RegisterPreparation("prepared", true, factory) diff --git a/apps/parsar-daemon/internal/dispatch/receipt_order_test.go b/apps/parsar-daemon/internal/dispatch/receipt_order_test.go index 58b086186..d5cea0ee0 100644 --- a/apps/parsar-daemon/internal/dispatch/receipt_order_test.go +++ b/apps/parsar-daemon/internal/dispatch/receipt_order_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -49,7 +51,7 @@ func TestDurableCompletionWaitsForSteeringReceiptSend(t *testing.T) { registry := agent.NewRegistry() var session *fakeSession var calls atomic.Int32 - registry.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + registry.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { session = &fakeSession{out: out, closeOutOnCancel: true} return &steeringSession{fakeSession: session, steer: func(context.Context, proto.PromptSteerPayload) error { calls.Add(1) @@ -122,7 +124,7 @@ func TestShutdownReleasesSteeringWorkerAndCompletionBarrier(t *testing.T) { registry := agent.NewRegistry() entered, exited := make(chan struct{}), make(chan struct{}) var session *fakeSession - registry.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + registry.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { session = &fakeSession{out: out, closeOutOnCancel: true} return &steeringSession{fakeSession: session, steer: func(ctx context.Context, _ proto.PromptSteerPayload) error { close(entered) diff --git a/apps/parsar-daemon/internal/dispatch/receipt_shutdown_test.go b/apps/parsar-daemon/internal/dispatch/receipt_shutdown_test.go index 81d9a847e..28d4eefee 100644 --- a/apps/parsar-daemon/internal/dispatch/receipt_shutdown_test.go +++ b/apps/parsar-daemon/internal/dispatch/receipt_shutdown_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -44,7 +46,7 @@ func TestShutdownCancelsCompletionErrorSend(t *testing.T) { sender := &shutdownAllSendsBlockSender{entered: make(chan struct{}), terminal: make(chan context.Context, 1), rescue: make(chan struct{})} registry := agent.NewRegistry() var session *fakeSession - registry.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + registry.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { session = &fakeSession{out: out, closeOutOnCancel: true} return &steeringSession{fakeSession: session, steer: func(context.Context, proto.PromptSteerPayload) error { return nil }}, nil }) diff --git a/apps/parsar-daemon/internal/dispatch/router_test.go b/apps/parsar-daemon/internal/dispatch/router_test.go index a636c622e..3e5a0e21f 100644 --- a/apps/parsar-daemon/internal/dispatch/router_test.go +++ b/apps/parsar-daemon/internal/dispatch/router_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -151,7 +153,7 @@ func newHarnessWithIdleTimeout(t *testing.T, idleTimeout time.Duration) *harness gotReq: make(chan proto.PromptRequestPayload, 16), gotSess: make(chan *fakeSession, 16), } - h.reg.Register("claude_code", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("claude_code", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { sess := &fakeSession{out: out, ctx: ctx} h.gotReq <- req h.gotSess <- sess diff --git a/apps/parsar-daemon/internal/dispatch/steering_lifetime_test.go b/apps/parsar-daemon/internal/dispatch/steering_lifetime_test.go index 55cb239ad..038839250 100644 --- a/apps/parsar-daemon/internal/dispatch/steering_lifetime_test.go +++ b/apps/parsar-daemon/internal/dispatch/steering_lifetime_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "sync/atomic" @@ -25,7 +27,7 @@ func TestDurableSteeringWaitsBeyondTransportDeadline(t *testing.T) { var session *fakeSession var calls atomic.Int32 release := make(chan struct{}) - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { session = &fakeSession{out: out, closeOutOnCancel: true} return &durableSteeringSession{steeringSession: &steeringSession{fakeSession: session}, phased: func(ctx context.Context, input proto.PromptSteerPayload, written func()) error { calls.Add(1) @@ -79,7 +81,7 @@ func TestDurableSteeringTransportTimeoutAndShutdown(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) exited := make(chan struct{}) - h.reg.Register("codex", func(_ context.Context, _ proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(_ context.Context, _ proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { return &durableSteeringSession{steeringSession: &steeringSession{fakeSession: &fakeSession{out: out, closeOutOnCancel: true}}, phased: func(ctx context.Context, _ proto.PromptSteerPayload, written func()) error { defer close(exited) if phase == "written" { @@ -128,7 +130,7 @@ func TestDurableSteeringRequiresOptInAndAdapter(t *testing.T) { t.Run(map[bool]string{false: "old-adapter", true: "retained-run"}[supported], func(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) - h.reg.Register("codex", func(_ context.Context, _ proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(_ context.Context, _ proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { s := &steeringSession{fakeSession: &fakeSession{out: out, closeOutOnCancel: true}, steer: func(context.Context, proto.PromptSteerPayload) error { t.Error("unexpected legacy call"); return nil }} if !supported { return s, nil diff --git a/apps/parsar-daemon/internal/dispatch/steering_test.go b/apps/parsar-daemon/internal/dispatch/steering_test.go index 89fe87115..d78872020 100644 --- a/apps/parsar-daemon/internal/dispatch/steering_test.go +++ b/apps/parsar-daemon/internal/dispatch/steering_test.go @@ -1,5 +1,7 @@ package dispatch_test +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "errors" @@ -30,7 +32,7 @@ func TestSteeringReceiptsAndRetries(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) calls, starts := 0, 0 - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { starts++ return &steeringSession{ fakeSession: &fakeSession{out: out, closeOutOnCancel: true}, @@ -91,7 +93,7 @@ func TestSteeringReadinessAndUnsupportedRuns(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) calls := 0 - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { return &steeringSession{ fakeSession: &fakeSession{out: out, closeOutOnCancel: true}, steer: func(context.Context, proto.PromptSteerPayload) error { @@ -169,7 +171,7 @@ func TestSteeringDoesNotBlockOtherRunCancellation(t *testing.T) { defer h.router.Shutdown(context.Background()) entered, release := make(chan struct{}), make(chan struct{}) defer close(release) - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { return &steeringSession{ fakeSession: &fakeSession{out: out, closeOutOnCancel: true}, steer: func(context.Context, proto.PromptSteerPayload) error { @@ -232,7 +234,7 @@ func TestSteeringCapacityPreservesExistingReceipts(t *testing.T) { h := newHarness(t) defer h.router.Shutdown(context.Background()) calls := 0 - h.reg.Register("codex", func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { + h.reg.Register("codex", harnessconfig.Configuration{}, func(ctx context.Context, req proto.PromptRequestPayload, out chan<- proto.Envelope) (agent.Session, error) { return &steeringSession{ fakeSession: &fakeSession{out: out, closeOutOnCancel: true}, steer: func(context.Context, proto.PromptSteerPayload) error { diff --git a/apps/parsar-daemon/testdata/onboarding/main.go b/apps/parsar-daemon/testdata/onboarding/main.go index 679f98338..7aff33e35 100644 --- a/apps/parsar-daemon/testdata/onboarding/main.go +++ b/apps/parsar-daemon/testdata/onboarding/main.go @@ -2,6 +2,8 @@ // It is test-only, has no workspace/tools, and is never in the built-in catalog. package main +import "github.com/MiniMax-AI-Dev/parsar/internal/harnessconfig" + import ( "context" "encoding/json" @@ -160,7 +162,7 @@ func run() error { h := &harness{history: map[string]string{}} registry.RegisterKind(proto.SupportedAgentKind{Kind: "fixture_harness", Available: true, Capabilities: proto.AgentKindCapabilities{ Streaming: true, Steering: true, DurableTurns: true, DurableInputReceipts: true, ExecutionControls: true, ToolObservations: true, SubagentControl: true, EnvironmentNone: true, - }}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { + }}, harnessconfig.Configuration{}, func(context.Context, proto.PromptRequestPayload, chan<- proto.Envelope) (agent.Session, error) { return nil, errors.New("fixture execution requires an Executor") }) registry.RegisterExecutor("fixture_harness", h.prepare) diff --git a/apps/web/e2e/fixture-console.mjs b/apps/web/e2e/fixture-console.mjs index 321d539e0..0a7884417 100644 --- a/apps/web/e2e/fixture-console.mjs +++ b/apps/web/e2e/fixture-console.mjs @@ -538,7 +538,7 @@ function providerProblem(harness, input) { * DELETE is 204 and safe to repeat. A disabled harness may still be configured. */ async function harnessRoute(request, response, path) { - const support = (id) => ({ protocols: ["anthropic", "responses", "chat_completions"], native_protocols: id === "codex" ? ["responses"] : id === "claude_sdk" ? ["anthropic"] : ["anthropic", "responses", "chat_completions"], accepts_harness_config: id !== "mcode", token_limits_required: id === "mcode" }); + const support = (id) => ({ protocols: id === "codex" ? ["responses"] : id === "claude_sdk" ? ["anthropic"] : ["anthropic", "responses", "chat_completions"], accepts_harness_config: id !== "mcode", token_limits_required: id === "mcode" }); const view = (id) => ({ model_configuration_support: support(id), object: "core.harness", id, enabled: state.harnesses[id].enabled, default: state.harnesses[id].default, model_configuration: state.harnesses[id].provider }); if (path === "/harnesses" && request.method === "GET") return send(response, 200, { object: "list", data: Object.keys(state.harnesses).map(view) }); const match = path.match(HARNESS_PROVIDER); @@ -566,7 +566,7 @@ async function harnessRoute(request, response, path) { if (typeof input !== "object" || Array.isArray(input)) return error(response, 400, `Invalid type: expected an object, but got ${jsonKind(input)} instead.`, "invalid_request_error"); if (typeof input.model !== "string" || !input.model.trim()) return error(response, 400, "A model ID is required.", "model_configuration_model_invalid"); if (input.harness_config !== undefined && (!input.harness_config || typeof input.harness_config !== "object" || Array.isArray(input.harness_config))) return error(response, 400, "Harness configuration must be an object.", "harness_config_invalid"); - if (Object.keys(input.harness_config ?? {}).length && !support(harness).native_protocols.includes(input.model_provider?.protocol)) return error(response, 400, "The selected route cannot preserve native model settings.", "model_configuration_route_unsupported"); + if (!support(harness).protocols.includes(input.model_provider?.protocol)) return error(response, 400, "This harness does not support this protocol.", "model_provider_protocol_unsupported"); const problem = providerProblem(harness, input.model_provider ?? {}); if (problem) return error(response, 400, problem, "invalid_request_error"); // As Core's error mapping: sealing the key needs the credential encryption key. diff --git a/apps/web/e2e/system.spec.ts b/apps/web/e2e/system.spec.ts index 87d952874..a731ed974 100644 --- a/apps/web/e2e/system.spec.ts +++ b/apps/web/e2e/system.spec.ts @@ -22,9 +22,9 @@ test("sets, replaces and clears a harness's default model configuration, and kee const set = page.getByRole("dialog", { name: "Set default model configuration for Codex" }); await expect(set.getByRole("combobox", { name: "Protocol", exact: true })).toHaveText("OpenAI Responses"); await set.getByRole("combobox", { name: "Protocol", exact: true }).click(); - await expect(page.getByRole("option")).toHaveText(["Anthropic Messages", "OpenAI Responses", "OpenAI Chat Completions"]); + await expect(page.getByRole("option")).toHaveText(["OpenAI Responses"]); await page.screenshot({ path: test.info().outputPath("provider-protocol-options.png"), animations: "disabled" }); - await page.getByRole("option", { name: "OpenAI Chat Completions", exact: true }).click(); + await page.getByRole("option", { name: "OpenAI Responses", exact: true }).click(); await set.getByLabel("Base URL").fill("https://model.example/v1"); await set.getByLabel("API key").fill(KEY); await set.getByLabel("Default model ID").fill("fixture-model"); @@ -48,12 +48,6 @@ test("sets, replaces and clears a harness's default model configuration, and kee await config.fill('{"model_reasoning_effort":"high"}'); await set.getByRole("button", { name: "Format JSON" }).click(); await expect(config).toHaveValue('{\n "model_reasoning_effort": "high"\n}'); - await expect(set.getByText("Advanced model settings require a native protocol for this harness. Change the protocol or clear the JSON object.")).toBeVisible(); - await expect(set.getByRole("button", { name: "Save", exact: true })).toBeDisabled(); - await set.getByRole("combobox", { name: "Protocol", exact: true }).click(); - await page.getByRole("option", { name: "OpenAI Responses", exact: true }).click(); - await expect(config).toHaveValue("{}"); - await config.fill('{"model_reasoning_effort":"high"}'); // Limits are Core's 32-bit whole numbers, and max output needs a context window at least as large. await set.getByLabel("Max output tokens").fill("32000"); await expect(set.getByText("Set a context window at least this large.")).toBeVisible(); @@ -89,9 +83,6 @@ test("sets, replaces and clears a harness's default model configuration, and kee await replace.getByLabel("Base URL").fill("https://another-model.example/v1"); await expect(nativeSettings).toHaveValue("{}"); await nativeSettings.fill('{"model_reasoning_effort":"high"}'); - await replace.getByRole("combobox", { name: "Protocol", exact: true }).click(); - await page.getByRole("option", { name: "Anthropic Messages", exact: true }).click(); - await expect(nativeSettings).toHaveValue("{}"); await replace.getByRole("button", { name: "Cancel" }).click(); expect(await page.content()).not.toContain(KEY); await codex.getByRole("button", { name: "Replace the default model configuration for Codex" }).click(); @@ -99,8 +90,6 @@ test("sets, replaces and clears a harness's default model configuration, and kee await expect(replace.getByRole("combobox", { name: "Protocol", exact: true })).toHaveText("OpenAI Responses"); await expect(replace.getByLabel("Default model ID")).toHaveValue("fixture-model"); await expect(replace.getByLabel("Harness configuration (JSON)")).toHaveValue(/model_reasoning_effort/); - await replace.getByRole("combobox", { name: "Protocol", exact: true }).click(); - await page.getByRole("option", { name: "Anthropic Messages", exact: true }).click(); await expect(replace.getByLabel("API key")).toHaveValue(""); await replace.getByLabel("Base URL").fill("https://model.example/v2"); await replace.getByLabel("API key").fill(KEY); @@ -109,7 +98,7 @@ test("sets, replaces and clears a harness's default model configuration, and kee await replace.getByLabel("API key").press("Enter"); await expect(replace).toBeHidden(); await expect(codex).toContainText("https://model.example/v2"); - await expect(codex).toContainText("Anthropic Messages"); + await expect(codex).toContainText("OpenAI Responses"); await codex.getByRole("button", { name: "Clear the default model configuration for Codex" }).click(); const confirm = page.getByRole("dialog", { name: "Clear default model configuration" }); @@ -122,6 +111,7 @@ test("sets, replaces and clears a harness's default model configuration, and kee const limits = page.getByRole("dialog", { name: "Set default model configuration for MiniMax Code" }); await expect(limits.getByRole("combobox", { name: "Protocol", exact: true })).toHaveText("Anthropic Messages"); await limits.getByRole("combobox", { name: "Protocol", exact: true }).click(); + await expect(page.getByRole("option")).toHaveText(["Anthropic Messages", "OpenAI Responses", "OpenAI Chat Completions"]); await page.getByRole("option", { name: "OpenAI Responses", exact: true }).click(); await limits.getByLabel("Base URL").fill("https://model.example/anthropic"); await limits.getByLabel("API key").fill(KEY); @@ -176,3 +166,33 @@ test("reports an unconfirmed save, reads the default model configurations again expect(reads).toHaveLength(1); expect(await writes(request)).toEqual(["PUT /core/v1/harnesses/codex/model-configuration"]); }); + +test("requires an explicit supported protocol choice for an older default configuration", async ({ page, request }) => { + await page.route("**/core/v1/harnesses", async (route) => { + const response = await route.fetch(); + const data = await response.json(); + const codex = data.data.find((harness: { id: string }) => harness.id === "codex"); + codex.model_configuration.model_provider.protocol = "chat_completions"; + codex.model_configuration.harness_config = { model_reasoning_effort: "high" }; + await route.fulfill({ response, json: data }); + }); + await openConsole(page, request, "system"); + const codex = page.getByRole("region", { name: "Default model configuration" }).getByRole("article", { name: "Codex" }); + await expect(codex).toContainText("OpenAI Chat Completions"); + await codex.getByRole("button", { name: "Replace the default model configuration for Codex" }).click(); + const dialog = page.getByRole("dialog", { name: "Replace default model configuration for Codex" }); + await dialog.getByLabel("API key").fill(KEY); + await expect(dialog.getByRole("combobox", { name: "Protocol", exact: true })).toHaveText("Select protocol"); + await expect(dialog.getByRole("alert")).toHaveText("This harness does not support OpenAI Chat Completions. Select a supported protocol."); + await expect(dialog.getByRole("button", { name: "Save", exact: true })).toBeDisabled(); + await dialog.getByLabel("API key").press("Enter"); + expect(await writes(request)).toEqual([]); + await page.screenshot({ path: test.info().outputPath("unsupported-saved-protocol.png"), animations: "disabled" }); + await dialog.getByRole("combobox", { name: "Protocol", exact: true }).click(); + await expect(page.getByRole("option")).toHaveText(["OpenAI Responses"]); + await page.getByRole("option", { name: "OpenAI Responses", exact: true }).click(); + await expect(dialog.getByRole("alert")).toHaveCount(0); + await expect(dialog.getByLabel("Harness configuration (JSON)")).toHaveValue("{}"); + await expect(dialog.getByRole("button", { name: "Save", exact: true })).toBeEnabled(); + expect(await writes(request)).toEqual([]); +}); diff --git a/apps/web/src/components/console-select.tsx b/apps/web/src/components/console-select.tsx index a5d452ce7..51a06c31a 100644 --- a/apps/web/src/components/console-select.tsx +++ b/apps/web/src/components/console-select.tsx @@ -18,6 +18,7 @@ export function ConsoleSelect({ label, className, disabled, + placeholder, }: { value: string; onChange: (value: string) => void; @@ -26,6 +27,7 @@ export function ConsoleSelect({ label: string; className?: string; disabled?: boolean; + placeholder?: string; }) { return (