Skip to content

S19: agentDir ⇄ spec v2 round trip - #861

Merged
LinuxDevil merged 1 commit into
mainfrom
fb-s19-agent-dir-roundtrip
Nov 3, 2026
Merged

LinuxDevil merged 1 commit into
mainfrom
fb-s19-agent-dir-roundtrip

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What changed

src/agentDir/spec.ts — two new exports:

  • specToAgentDir(spec): { files, dropped } renders a validated AgentSpec v2 as agent-directory files (paths relative, / separators): agent.yaml with keys in a fixed order, instructions.md, instructions/<family>.md, skills/<name>/SKILL.md (inline skills only) and recursive subagents/<name>/. Every spec field a directory cannot hold is reported in dropped ({ field, reason }) — mcpServers, guardrails, handoffs, memory, triggers, output, reasoning, fallbackModels, rateLimit, promptCaching, toolSearch, codeMode, askQuestion, subagentOptions, maxSubagentDepth, maxHandoffs, abortListeners, flow, evals, promptRef, instructions file refs, non-file tools, extra/inline hooks, non-inline sub-agents, skill dir/registry refs and tool-file paths outside tools/.
  • agentDirToSpec(dir): Promise<{ spec, dropped }> reads a data-config directory (agent.json / agent.yaml / agent.yml, or none) back into a spec, validated with agentSpecV2Schema. It never imports a file: only fs reads, yaml, JSON.parse and loadSkills (markdown). A code config (agent.ts, agent.js, ...) throws LOUSHO_SPEC_UNSUPPORTED; a config with no model throws LOUSHO_SPEC_INVALID. Directory content a spec cannot hold is reported as dropped: memory/*, schedules/*, channels/*, root auth.*/approve.* files and the approve, store, toolConcurrency, projectInstructions, provider, engine, output config keys.
  • Result types SpecToAgentDirResult / AgentDirToSpecResult are named interfaces (the anonymous return types' members tripped the typedoc ratchet).

For specs that use only directory-expressible keys the round trip is the identity (permission ids regenerate as p1…, hooks as h1).

Also: src/agentDir/__fixtures__/yaml-inline/agent.yaml gains a model line so the fixture can be read as a spec (v2 requires one); the only consumer (loadAgentDir.test.ts) does not assert on model and still passes.

Tested

  • npx vitest run src/agentDir/spec.test.ts — 10 tests, all pass: minimal and rich round-trip identity, dropped lists for non-directory keys, code-config / missing-model errors, no-import spy on importModule (0 calls), registry/inbox-triage dropped list, skill-folder-with-extras → { dir }.
  • npx tsc --noEmit, npm run typecheck:tests, npm run lint — clean.
  • npm run test:coverage — 7184 passed; 11 failures in 3 files, all environmental and unrelated: packages/create-lousho/* needs the workspace's own dist/ (never built here) and src/spec/v2/templates.test.ts fails on CRLF line endings in template READMEs from the Windows checkout.
  • npm run fallow, npm run docs:ratchet, npm run api:check, npm run docs:llms:check, npm run size-check — all pass.

Acceptance criteria

  • Round-trip identity: specToAgentDir(spec).files written to a temp dir round-trips through agentDirToSpec as { spec, dropped: [] } (minimal and rich fixtures)
  • A spec with guardrails, mcpServers and a { builtin } tool lists exactly those three in dropped
  • agentDirToSpec(src/agentDir/__fixtures__/yaml-inline) succeeds without importing anything (spy on importModule, 0 calls)
  • A directory with agent.ts throws LOUSHO_SPEC_UNSUPPORTED
  • registry/inbox-triage reads with dropped naming its memory/, schedules/, channels/ files and approve

Generated with Devin

specToAgentDir(spec) renders a v2 spec as agent-directory files
(agent.yaml, instructions.md, instructions/<family>.md,
skills/<name>/SKILL.md, subagents/<name>/) plus a dropped list of every
field a directory cannot hold; agentDirToSpec(dir) reads a data-config
directory back into a validated v2 spec without importing any of its
code, reporting directory content a spec cannot hold (memory/*,
schedules/*, channels/*, auth/approve files and the approve, store,
toolConcurrency, projectInstructions, provider, engine, output config
keys). Code configs throw LOUSHO_SPEC_UNSUPPORTED; a config with no
model throws LOUSHO_SPEC_INVALID. The yaml-inline fixture gains a
'model' line so it can be read as a spec.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@LinuxDevil
LinuxDevil merged commit 99bf64f into main Nov 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant