feat(subagents): artifact_read + workspace staging, artifact counts, docs (M2+M3) - #160
Merged
Conversation
Sub-agent result artifacts larger than the 32 KiB inline budget were metadata-only after M1; artifact_read completes the channel: the parent reads full content on demand, by id. - session-scoped registry: delegate_tasks registers every ref that passed fail-closed validation at collation, with the validated path (512 live entries, oldest-first eviction, lazy slots so last-wins re-registration never evicts a live entry) - artifact_read tool (parent-only, gated on SelfTrust=="" via builtinTools): resolves ids internally — the model supplies only the id, never a path — re-verifies the file at read time (janitor/session cleanup may have removed it), returns bytes [offset, offset+limit) (default 64 KiB, hard cap 256 KiB) inside the untrusted boundary with full metadata header; unknown ids list the registered ids; traversal- shaped ids are simply unknown - duplicate ids across tasks: last-wins with an explicit note line in the collated summary RED first: artifact_read_test.go — registry register/lookup/evict/last-wins, happy path with no-path-leak + untrusted wrap, offset/limit slice + truncation flag + cap clamp, unknown-id listing, traversal id, vanished file, parent-only gate, duplicate-registration note.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
odek | cc5b3c3 | Commit Preview URL Branch Preview URL |
Aug 30 2026, 03:46 PM |
…s (M3) M1 shipped the artifact channel but children could not actually deliver: the canonical dir (~/.odek/artifacts) is doubly protected from child writes — confineToCWD rejects absolute paths and the danger classifier escalates ~/.odek writes to system_write (denied for approval-less children). M3 resolves this without touching either gate: - children stage deliverables INSIDE the workspace (.odek-artifacts/<task_id>/ — an ordinary local_write); the trusted child runner relocates them to the canonical dir before the exit scan (rename with cross-device copy fallback), then hashes/sizes there - wire format unchanged: artifact_root still names the canonical dir (v1.32.0 compatible); the child request carries the workspace-relative staging path via childArtifactNote - subagent_completed events carry artifact_count (count only — hash-only event policy preserved) - docs: CONFIG.md + MAINTENANCE.md (artifacts_max_age_hours knob + env), SECURITY.md (artifact invariants paragraph), SUBAGENTS.md (Result artifacts section), EXTENSIONS.md (sub-agent artifact reuse of odek.artifact-ref/v1) RED first: subagent_staging_test.go — staging path, relocation (move + staging removal), copy fallback via rename-failure hook, missing-staging no-op, event artifact_count presence/absence, note leaks no canonical path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the SUBAGENT_RESULT_ARTIFACTS_PLAN.md workstream. M1 shipped in #159 (v1.32.0); this PR completes the plan: M2 content access + M3 staging/relocation, events, docs.
M2 —
artifact_read(parent-only content access)Artifacts larger than the 32 KiB inline budget were metadata-only after M1. Now the parent reads full content on demand, by id:
delegate_tasksregisters every ref that passed fail-closed validation at collation, with the validated path. 512 live entries, oldest-first eviction with lazy slots (a last-wins re-registration can never evict a live entry).wrapUntrusted, recorded by the per-call audit ingest.SelfTrust == ""inbuiltinTools.M3 — staging + relocation, events, docs
The gap M1 left: children could not actually deliver artifacts —
~/.odek/artifactsis doubly protected from child writes (confineToCWDrejects absolute paths; the classifier escalates~/.odekwrites tosystem_write, denied for approval-less children). M3 resolves it without touching either gate:.odek-artifacts/<task_id>/— an ordinarylocal_write), pointed there by a trusted runner note (workspace-relative path only).artifact_rootstill names the canonical dir — v1.32.0 compatible).subagent_completedevents carryartifact_count(count only — hash-only event policy preserved).CONFIG.md+MAINTENANCE.md(maintenance.artifacts_max_age_hours,ODEK_MAINTENANCE_ARTIFACTS_MAX_AGE_HOURS, explicit 0 = keep forever),SECURITY.md(artifact invariants),SUBAGENTS.md(Result artifacts section),EXTENSIONS.md(sub-agent reuse ofodek.artifact-ref/v1).Also carries the CI fixes from review:
maintenance.artifacts_max_age_hoursproperly wired (config field + overlay + env + want-literals) andrunTask's empty-result error now carrieswaitErr/scannerErrfor diagnosability.Verification
artifact_read_test.go(9 tests: registry mechanics incl. eviction/last-wins, no-path-leak, untrusted wrap, paging/clamping, unknown-id listing, traversal id, vanished file, parent-only gate, duplicate notes) andsubagent_staging_test.go(staging path, relocation + staging removal, copy fallback, no-op, event count, no canonical-path leak).cmd/odeksuite: ok (darwin 73s · linux container 62s) ·-racescoped: ok ·golangci-lint: 0 issuesPlan status
With this, the SUBAGENT_RESULT_ARTIFACTS_PLAN.md milestones are complete (M0/M1 in #159+v1.32.0, M2/M3 here).