Skip to content

Commit 4fbddad

Browse files
authored
Align template and inline environment initialization (#39)
* Document template inline composition and official evidence * fix(agents-api): compose template initialization overrides * Verify template composition through official client and PostgreSQL * Record composition checks and real Codex Docker evidence
1 parent 5befbe7 commit 4fbddad

11 files changed

Lines changed: 729 additions & 28 deletions

‎CONTRIBUTING.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,13 @@ creation, freeze the effective ordinary hosted configuration and reuse inline
311311
initialization. Do not pass template IDs into Provider or Runtime. Omitted network
312312
inherits; overrides may only narrow policy. Preserve unresolved caller intent for
313313
creation retries and recover committed results before reading mutable templates.
314+
For template-reference Session initialization, omitted/null env, files, commands
315+
and packages inherit. Overlay non-null env keys; replace non-null files and command
316+
lists, including empty lists. Select each package manager independently: omitted/null
317+
inherits, while a supplied list replaces that manager. Revalidate the effective
318+
configuration through the existing validators and freeze it through the same
319+
transaction as inline initialization. Keep caller intent separate from resolved
320+
configuration; do not add a second installer or pass merge rules to adapters.
314321
Updates and deletion cannot rewrite existing Session snapshots. Initial files use
315322
one Core-owned installer for template and inline configurations. Keep confidential
316323
bytes encrypted under the execution-service key and resource-bound AEAD, separately

‎contracts/agents-api/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ user-managed enrollment remain outside this qualification.
174174
| Area | Missing or unverified scope |
175175
| --- | --- |
176176
| Subagents / multi_agent | Six reads and same-child recovery have three-harness Docker evidence; optional native operations, live child progress, full lifecycle/interactions and tool combinations remain explicit gaps |
177-
| Environment Templates | Unsupported restricted hostname forms, unqualified installation overrides/null network and exact hosted errors remain gaps. CRUD/list, files, env/setup/system/npm/Python, inline/referenced Skills, Plugins, workspace capability directories and Session references have recorded coverage. Environment Plugin MCP transport and placement limits are [listed separately](environment-templates.md#environment-origin-mcp-plugins) |
177+
| Environment Templates | Unsupported restricted hostname forms, null network/list selection and exact hosted errors remain gaps. Template-reference env/files/commands/packages composition follows [qualified field rules](environment-templates.md#template-and-inline-configuration-composition). CRUD/list, files, env/setup/system/npm/Python, inline/referenced Skills, Plugins, workspace capability directories and Session references have recorded coverage. Environment Plugin MCP transport and placement limits are [listed separately](environment-templates.md#environment-origin-mcp-plugins) |
178178
| Input and configuration | Non-text initial input, broader content/configuration unions and reasoning/verbosity combinations; [structured output](structured-output.md) has qualified Claude function profiles on none and Core-managed Docker openai_hosted, with other combinations remaining gaps |
179179
| Tools and interactions | [Deferred discovery qualification](tool-search.md), other tool types, effective tool-set enforcement and result/cancel publication ordering; MiniMax public functions and service-origin MCP remain unsupported |
180180
| Vault and Credentials | Archive semantics, in-flight token withdrawal and exact hosted selection/error behavior; static/OAuth CRUD, replacement and scoped dispatch-time refresh are implemented (see credential guide for qualification) |

‎contracts/agents-api/environment-templates.md‎

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ sizes for both variants. Initialization keeps file data out of ordinary configur
6767
resource responses, lifecycle events and command arguments. Templates keep references; each Session authorizes and
6868
freezes its own encrypted source bytes. Later source deletion cannot change them.
6969

70-
Template `files` omission preserves on update; null/[] clears. Referenced Sessions
71-
inherit files. Supplying `files` together with `environment_template_id`, including
72-
null/[], explicitly rejects while replacement/merge/null semantics remain unconfirmed.
73-
Use a complete standalone inline configuration when a different file set is needed.
70+
Template `files` omission preserves on update; null/[] clears. In a referencing
71+
Session, omission and null inherit, while a supplied list replaces the entire file
72+
set, including `[]` clearing it. Paths are not merged between the two sources.
73+
The effective list retains the existing validation and tenant-owned source checks.
7474

7575
Core initializes both paths with the same trusted file installer through Provider
7676
RunCommand. Daemon authentication remains available, but native preparation and live
@@ -495,8 +495,8 @@ precede setup commands, nonzero setup prevents start, and runtime-reserved env n
495495
must reject. The shared initialization batch implements those fields with encrypted snapshots
496496
and the existing readiness gate. Public reads show packages but omit env/commands.
497497
Template updates replace each supplied field; omission preserves it and null clears
498-
it. Referenced Sessions inherit the snapshot; explicit env/packages/setup overrides
499-
with a template ID reject while override semantics remain unconfirmed.
498+
it. Referencing Sessions use the composition rules below; these differ from
499+
Template.update replacement rules.
500500

501501
Files and resolved Skills are installed first, followed by system, npm/Python packages and ordered commands;
502502
the default cwd is `/workspace`. One command or package operation has the existing
@@ -520,6 +520,100 @@ errors, no-op timestamps, concurrent pagination and referenced Session null-netw
520520
override semantics remain unverified. The last case explicitly rejects in this
521521
batch rather than guessing inheritance. This batch is not full protocol compatibility.
522522

523+
## Template and inline configuration composition
524+
525+
The pinned Session description applies the template before inline configuration.
526+
Owned official API probes on 2026-09-23 establish the following narrower behavior:
527+
528+
| Session field | Omitted or null | Non-null inline value |
529+
|---|---|---|
530+
| `env` | Inherit template keys | Overlay by key; inline value wins, `{}` preserves all keys |
531+
| `setup_commands` | Inherit template sequence | Replace the sequence; `[]` clears it |
532+
| `files` | Inherit template file set | Replace the complete set; `[]` clears it |
533+
| `packages` | Inherit all managers | Resolve Python/npm/system independently; `{}` inherits all |
534+
| Individual package manager | Inherit its template list | Replace that list; `[]` clears it |
535+
536+
Core performs this composition once, before the existing encrypted Session snapshot
537+
transaction. Env values, command bodies and inline bytes remain absent from public
538+
metadata. Effective package/file metadata reflects the selected inputs. Caller
539+
intent still distinguishes omission, null and explicit fields for the local creation
540+
retry policy; composition does not rewrite it. Later template/source changes do not
541+
rewrite committed initialization. Existing per-source and effective-size validation,
542+
network narrowing, Skill/Plugin selection and source authorization remain in place.
543+
No harness or Provider participates in the merge.
544+
545+
The official evidence used SDK 3.13.0, upstream `d7c41ef`, and `agents=v1`:
546+
86 authenticated calls, nine owned Sessions, four lifetime Templates (including two
547+
cleaned setup-script assertion failures), and six completed real `gpt-6-astra` Turns.
548+
Four exit-zero command outputs confirmed env values and command replacement/order
549+
for omitted, populated, empty and null inputs. Two further outputs confirmed full
550+
file replacement and null inheritance, and an initialized empty listing confirmed
551+
`files=[]`. The omitted-files case reached the bounded readiness limit, so its
552+
inheritance proof is metadata-only. Package composition is also metadata evidence;
553+
these official probes do not independently establish installed package versions.
554+
All owned resources have successful public DELETE receipts; physical upstream
555+
sandbox destruction was not independently observed.
556+
557+
Private evidence: `~/.parsar/remediation/20260923/template-inline-composition/`,
558+
subdirectories `official-env-setup` and `official-files-packages`. Reports retain
559+
fixed-source snapshots, raw status/body/request IDs, command-output proofs,
560+
accounting, cleanup and credential scans. This covers the observed fixtures rather
561+
than all possible combinations. Null network and null Skill/Plugin/directory list
562+
selection remain outside this batch. No new protocol version is introduced.
563+
564+
### Core checks for composition (2026-09-23)
565+
566+
`TestTemplateCompositionOfficialClientPostgres` uses the fixed strict SDK and raw
567+
HTTP against real Core handlers and PostgreSQL. Six accepted cases and seven
568+
rejected creation keys cover effective metadata, confidential frozen bytes and
569+
ordered commands, tenant/source isolation, combined setup-size rejection without
570+
partial records, and same-intent retries after template and source deletion.
571+
A second Store/handler verifies reopened persistence; it is not an OS process
572+
restart and does not run a model. API tests additionally cover raw caller intent,
573+
non-mutating composition and retained field validation.
574+
575+
The integrated server gate ran `make -o check-web check` at `5589df0`; this includes
576+
real PostgreSQL tests, byte-for-byte sqlc generation, builds, native bridge checks
577+
and Rust tests/format/Clippy. A fresh `make check-web` ran locally against the same
578+
unchanged production diff: 287 client, 583 Web and 76 browser tests passed. Together
579+
they cover every required `make check` target. `make openapi` regenerated the
580+
Session description. The optional MiniMax packaged-native scratch/large-output
581+
probe was skipped because its optional profile variables were unset; this batch
582+
does not add MiniMax, Claude or E2B native qualification.
583+
584+
### Real Codex Docker composition acceptance (2026-09-23)
585+
586+
The exact `8019ac5` production build ran independently with Core, PostgreSQL and
587+
Docker Runtime against the real Kimi API. Three completed native model Turns prove:
588+
589+
- A populated-inline Session observed env key precedence, whole-file replacement,
590+
ordered replacement commands, Python `packaging==26.0`, and inherited npm/system
591+
tools. Its prompt named only the read operation, not the expected canary values.
592+
- A separate inheritance Session observed null env/files/commands inheritance,
593+
retained npm/system tools and absence of the explicitly cleared Python installation
594+
directory. After template mutation/deletion and an actual Core process restart,
595+
identical-key creation recovered its existing identity/configuration. A second
596+
native Turn returned the exact same values and append trace: initialization did
597+
not run again. The populated Session itself was not resumed in this run.
598+
599+
Five owned Core Sessions were created over the acceptance attempts. The first two
600+
failed before any model Turn because the reused private Skill-only runner omitted
601+
Docker `nested_sandbox: true`, already required by the documented initialization
602+
profile. Correcting that operator setting resolved the proc-mount failure without
603+
production changes. In the corrected pair, the populated Session completed its
604+
native command, but the private runner then required an optional assistant `phase`
605+
and unwrapped JSON. The original message instead contained matching fenced JSON
606+
without phase. An offline check preserved and verified the original native output
607+
and message bytes; no model call was repeated. One new inheritance Session completed
608+
the remaining two Turns. These failed assertions and operator diagnostics are
609+
retained alongside successful evidence, not counted as extra successful runs.
610+
611+
Private evidence is under the same batch's `live/` directory and records source,
612+
binary/image hashes, operator-setting changes, exact native output and cleanup.
613+
This is Codex/Docker qualification for the described composition paths, not renewed
614+
qualification of other harnesses or Providers, every package manager combination,
615+
or complete Template/Agents API semantics.
616+
523617
## Verification
524618

525619
### Restricted-network Docker acceptance (2026-09-21)

‎contracts/agents-api/openapi.yaml‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2879,13 +2879,15 @@ paths:
28792879
initialization lifecycle; requested network applies after setup. Initial inline
28802880
and tenant-owned file_id files freeze encrypted bytes before provisioning,
28812881
then install through the common Core lifecycle before native execution or
2882-
live Files access. Referenced files/env/packages/setup overrides are rejected
2883-
pending semantic verification. Tenant-owned environment_template_id references
2884-
inherit omitted network and allow only narrowing overrides. Referenced network:null
2885-
is explicitly unsupported pending semantic verification. Core freezes effective
2886-
configuration; template updates/deletion do not alter Session snapshots or
2887-
same-intent creation retries. Inline or tenant-owned skill_reference Skills
2888-
share initialization. Templates preserve default/latest/explicit selectors;
2882+
live Files access. With a template reference, omitted/null files, env, packages
2883+
and setup_commands inherit. Non-null files and command lists replace; env
2884+
overlays by key; each package manager inherits on omission/null and otherwise
2885+
replaces its list. Empty lists clear their selected field. Tenant-owned environment_template_id
2886+
references inherit omitted network and allow only narrowing overrides. Referenced
2887+
network:null is explicitly unsupported pending semantic verification. Core
2888+
freezes effective configuration; template updates/deletion do not alter Session
2889+
snapshots or same-intent creation retries. Inline or tenant-owned skill_reference
2890+
Skills share initialization. Templates preserve default/latest/explicit selectors;
28892891
Session creation freezes concrete metadata and encrypted content atomically.
28902892
Skill-list omission inherits and a supplied list replaces; null overrides
28912893
and null version selectors remain unqualified and reject. Source deletion/default

‎services/agents-api/internal/api/environment_templates_test.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func TestTemplateResolutionAndCreationIntent(t *testing.T) {
8888
if err := h.resolveTemplateEnvironment(t.Context(), "tenant-a", &narrower); err != nil {
8989
t.Fatal(err)
9090
}
91-
for _, raw := range []string{`{"type":"openai_hosted","environment_template_id":null}`, `{"type":"none","environment_template_id":"saved"}`, `{"type":"openai_hosted","environment_template_id":"saved","network":null}`, `{"type":"openai_hosted","environment_template_id":"saved","env":{"KEY":"secret"}}`} {
91+
for _, raw := range []string{`{"type":"openai_hosted","environment_template_id":null}`, `{"type":"none","environment_template_id":"saved"}`, `{"type":"openai_hosted","environment_template_id":"saved","network":null}`} {
9292
if _, _, _, err := decodeTemplateEnvironment(json.RawMessage(raw)); err == nil {
9393
t.Fatal("invalid reference accepted", raw)
9494
}

0 commit comments

Comments
 (0)