Commit Graph

5265 Commits

Author SHA1 Message Date
ismeth 2c70ec604d fix(delegate-task): block self-delegation to prevent infinite recursion
Adds generic self-recursion guard in subagent-resolver using
getAgentConfigKey() to normalize display names and config keys.
Catches athena-junior spawning itself — the only subagent with
task() access not already covered by existing guards.
2026-04-15 16:48:50 +09:00
ismeth fc737f8961 fix(tool-config): use permission-only approach for council tool visibility
Remove dead agent.tools fields that only work during Zod parsing
(before plugin config hook runs). Council tool restriction now uses
config.permission deny globally + per-agent permission allow for
athena/athena-junior, which is the correct mechanism per OpenCode's
LLM.resolveTools() → PermissionNext.disabled() filtering path.
2026-04-15 16:48:50 +09:00
ismeth 6f3a1efae0 feat(tool-config): restrict council tools visibility to athena and athena-junior only 2026-04-15 16:48:50 +09:00
ismeth 9a39c423d4 fix(switch-agent): detect HTTP error responses from SDK client on TUI navigation
The v1 SDK client does not throw on 4xx/5xx — it resolves with an
{ error } object. navigateTuiToSession was treating any resolved promise
as success, silently misreporting failed navigation.
2026-04-15 16:48:50 +09:00
ismeth 0b604c8d35 fix(switch-agent): use direct HTTP POST for TUI navigation instead of missing SDK method
The v1 SDK Tui class has no selectSession method (only exists in v2).
Commit 4150e26d regressed this by replacing the working _client.post()
direct HTTP call with client.tui.selectSession() which silently failed.

Reverts to bypassing the SDK and POSTing to /tui/select-session directly,
which works regardless of SDK version.
2026-04-15 16:48:50 +09:00
ismeth 597fdb7a51 fix(switch-agent): correct selectSession call signature for TUI navigation
- Fix TuiService type to match actual API: { body: { sessionID } }
- Fix selectSession call arg shape (was { sessionID }, now { body: { sessionID } })
- Add logging on both TUI failure paths (service unavailable / navigation error)
- Remove redundant inline idle-handling from polling path (already handled by
  event-based session-idle-event-handler)
2026-04-15 16:48:50 +09:00
ismeth 6e5c5043f6 fix(athena): correct retry instructions — fresh prompt file + right step reference
Step 10 retry flow now instructs Athena to call prepare_council_prompt
again (since the original prompt file was moved into the archive by
council_finalize) and references Step 6 (background_wait) instead of
Step 5 (save prompt + launch).
2026-04-15 16:48:50 +09:00
ismeth ff2ff8143d fix(background-agent): exempt council members from TTL pruning + fix promptFileMoved scope
Council members now skip TTL check in poller via isCouncilMemberAgent()
prefix match, covering both interactive (task tool) and bulk (athena_council)
launch paths. Users can cancel manually if needed.

Also fix promptFileMoved variable scoping: move declaration before try block
so finally block can access it. Dynamic timeout in error messages.
2026-04-15 16:48:50 +09:00
ismeth 44b3efe7a3 feat(athena): structured synthesis header + audit cross-check flow
Add universal YAML front-matter header to all synthesis.md outputs (council,
question, date, members, session_ids, mode, intent, responded count).

AUDIT: structured finding format with required fields (severity, confidence,
members reported, evidence, impact, fix direction). New cross-check flow asks
user to verify minority/solo findings via a second council round before
proceeding to findings processing.

Also fix prompt-file cleanup: skip cleanupPromptFile when movePromptFile
already succeeded.
2026-04-15 16:48:50 +09:00
ismeth 974eb8e176 feat(athena): replace hard quorum gate with interactive failure handling
Interactive prompt: Athena now asks the user whether to retry failed members
or skip and synthesize with available results. Recommends based on success ratio.
Non-interactive prompt: always proceeds to synthesis with available results,
sets status to partial/failed accordingly.
2026-04-15 16:48:50 +09:00
ismeth 37f03b4238 fix(switch-agent): use native tui.selectSession, use cross-platform symlink test target 2026-04-15 16:48:50 +09:00
ismeth 68cd8814d8 refactor(switch-agent): replace as-any with type guard, use const tuple for agent names 2026-04-15 16:48:49 +09:00
ismeth 9c226948d3 fix(athena): per-task TTL for council members instead of blanket exemption
Add optional ttl field to BackgroundTask/LaunchInput. Council launcher
passes COUNCIL_MEMBER_TTL_MS (2 hours) per member. Task poller uses
task.ttl ?? TASK_TTL_MS, replacing the blanket TTL_EXEMPT_AGENTS bypass
for council members with a bounded timeout.

Co-authored-by: Vacbo <2445>
2026-04-15 16:48:49 +09:00
ismeth 77d929a3ab fix(athena): tie prompt-file lifecycle to council_finalize
Remove blind 30-min setTimeout cleanup from prepare_council_prompt. Prompt
files now stay until council_finalize archives them in a try/finally block.
Extract shared resolvePromptTempFilePath helper used by both movePromptFile
and the new cleanupPromptFile safety net.

Co-authored-by: Vacbo <2445>
2026-04-15 16:48:49 +09:00
ismeth 69bb6eda8a fix(athena): canonical realpath path validation with symlink rejection
Resolve symlinks via realpathSync before checking path policy, preventing
symlink-based escapes from .sisyphus/ sandbox. Walks up to nearest existing
ancestor for new-file writes. Adds 7 tests including real-filesystem symlink
scenarios.

Co-authored-by: Vacbo <2445>
2026-04-15 16:48:49 +09:00
ismeth 9d6dc3a8a8 chore(athena): update audit guidance prompt
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:49 +09:00
ismeth 96c9cf98ed fix(athena): harden interactive prompt to prevent implementation behavior
Add 7 guardrails to stop Athena from acting as an implementation
orchestrator when receiving feature requests:

- Role + Tool Firewall: task() exclusively for council, todowrite banned
- Implementation-first pre-check: highest precedence in routing
- Expanded Category B: broader signals (build, make a feature, etc.)
- Strengthened Category F: vague features = ambiguous by default
- Handoff context integrity: no fabricated specs in switch_agent
- Routing read budget: 3-file cap for routing context
- New constraints: task() scope, todowrite ban, drift detection

Fixes bug where Athena created 9 todos and orchestrated implementation
via task() instead of routing to Prometheus/Sisyphus.
2026-04-15 16:48:49 +09:00
ismeth a2712c57c9 fix: deny switch_agent for all sub-agents 2026-04-15 16:48:49 +09:00
ismeth 276cb9352f fix(athena): gate non-interactive synthesis on finalized task ids 2026-04-15 16:48:49 +09:00
ismeth fb1be5923f test(config): cover athena-junior in overridable agent names 2026-04-15 16:48:49 +09:00
ismeth 11f6712dde fix(athena): add termination guards to non-interactive retry loop 2026-04-15 16:48:49 +09:00
ismeth 9d284accce fix(schema): recurse additional and pattern properties in required cleanup 2026-04-15 16:48:49 +09:00
ismeth 1ae6089bb3 fix(athena): restore opportunistic retry flow and include athena-junior builtin name 2026-04-15 16:48:49 +09:00
ismeth 169118c9e1 fix: preserve tuple items in schema build and harden athena prompt path guard 2026-04-15 16:48:49 +09:00
ismeth 62cf707502 fix: resolve 11 council-audited violations across athena subsystem
- Add switch_agent to athena-junior deny list (P1 defense-in-depth)
- Add terminal status check to waitForSessionIds polling loop
- Add athena-junior to OverridableAgentNameSchema and AgentOverridesSchema
- Add explicit retry workflow instructions to non-interactive prompt Step 9
- Fix JSON schema generation to exclude defaulted fields from required arrays
- Fix docs example for non_interactive_member_list (remove Council: prefix)
- Use segment-aware regex in path-policy.ts to block fake.sisyphus/ paths
- Add defensive path resolution and contract validation for prompt_file
- Add explicit .optional() to AthenaOverrideConfigSchema fields for clarity
- Fix traversal check precision for .. prefixed directory names
2026-04-15 16:48:49 +09:00
ismeth 6db83b937f fix: resolve 7 council-validated PR violations
- Fix misleading background_wait instructions to show loop pattern with
  remaining_task_ids (race semantics, not block-all)
- Fix wrong Gemini model string: gemini-3-pro-preview → gemini-3-pro
- Add getMainSessionID() fallback when input.sessionID is undefined
- Narrow .gitignore packages/ to only ignore built binaries
- Fix contradictory config doc: non_interactive_members 'all' → 'custom'
- Add athena-junior to keyword-detector exclusion check
- Add .trim() before .toUpperCase() in resolveCouncilIntent
- Update snapshots for model string change
2026-04-15 16:48:48 +09:00
ismeth c796187ee5 fix(test): replace /etc/passwd with innocuous paths to avoid GitGuardian false positive 2026-04-15 16:48:48 +09:00
ismeth a8eb22d3f1 docs(athena): comprehensive council documentation covering all configs and behaviors
Added missing documentation for:
- Council resilience settings (retry_on_fail, stuck_threshold, member_max_running)
- bulk_launch config for launch strategy
- Council tools (prepare_council_prompt, athena_council, council_finalize)
- Council archives (.sisyphus/athena/)
- Force-background and TTL exemption for athena-junior
- Council-member agent overrides and tool allowlist
- How to invoke athena-junior (task() and CLI)
- Updated agent list to include athena and council-member
- Added Athena subsections to table of contents
- Auto-generated schema.json update for bulk_launch field
2026-04-15 16:48:48 +09:00
ismeth 67de601807 fix(athena): always show background_wait guidance for athena-junior, not just when forced
The background_wait instruction was only shown when the caller passed
run_in_background=false (forced case). But callers passing true also need
the guidance since they were polling background_output instead. Now
athena-junior always gets background_wait instructions regardless of how
it was launched.
2026-04-15 16:48:48 +09:00
ismeth 4d3cbda8ce fix(athena): guide callers to background_wait when athena-junior is force-backgrounded
When athena-junior is force-backgrounded (caller passed run_in_background=false),
the response now instructs the caller to use background_wait instead of
background_output, preventing repeated polling loops.
2026-04-15 16:48:47 +09:00
ismeth 513a5c90b1 fix(athena): force athena-junior to background and exempt from hard TTL
Council sessions are long-running: athena-junior launches members then
waits for them via background_wait. Two changes prevent premature kills:

- Force run_in_background=true when subagent_type resolves to athena-junior,
  avoiding the 10-minute sync poll timeout (MAX_POLL_TIME_MS)
- Exempt athena-junior from the 30-minute TASK_TTL_MS hard cutoff since
  council members have their own independent TTLs
- Fix missing bulk_launch field in AthenaOverrideConfigSchema (type error)
2026-04-15 16:48:47 +09:00
ismeth 5e71d84a69 feat(athena): add bulk_launch config for council launch strategy
New config: agents.athena.bulk_launch (boolean, default false)
- false: launch members one-by-one via task() for TUI inspectability
- true: launch all members at once via athena_council tool

Prompt uses {BULK_LAUNCH_STEP_5_2} placeholder injected at runtime
based on config value. Schema, caller, and tests updated.
2026-04-15 16:48:47 +09:00
ismeth febffeebb1 fix(tools): deny council tools globally via permission system
The config.tools boolean false wasn't blocking plugin-registered tools.
Add prepare_council_prompt, council_finalize, athena_council to global
permission deny — same pattern as task: deny. Per-agent allow on
athena/athena-junior overrides the global deny.
2026-04-15 16:48:47 +09:00
ismeth c384fd0a02 fix(athena): grant athena_council tool to athena primary agent
Council tools (prepare_council_prompt, council_finalize, athena_council) are
globally disabled and only re-enabled for athena and athena-junior. Athena
was missing athena_council — now both agents have symmetric access.

Also fix parity test to include council_finalize and athena_council in
ATHENA_HANDLER_GRANTS for complete coverage.
2026-04-15 16:48:47 +09:00
ismeth 30cf71a845 fix(athena-junior): remove synthesis and question from council result JSON
Keep the council result JSON lean as a metadata envelope. Full synthesis
is already persisted at {archive_dir}/synthesis.md — duplicating it in
the JSON created noise that obscured structural fields like archive_dir.

Also instructs athena-junior to remind the caller to read the full
synthesis and member responses from the archive directory.
2026-04-15 16:48:47 +09:00
ismeth 102b754f1e fix(athena): remove athena primary from delegation list and switch_agent targets
Athena primary should not be routable via switch_agent or appear in the
delegation table. Only athena-junior (subagent) remains delegatable via task().

- Remove athena from ALLOWED_AGENTS and DESCRIPTION in switch-agent tool
- Remove ATHENA_PROMPT_METADATA from agentMetadata, agent.ts, barrel export
- Update switch-agent test to assert athena rejection
- Remove ATHENA_PROMPT_METADATA test block
2026-04-15 16:48:47 +09:00
ismeth efc53de531 feat(athena): complete delegation routing fix with mode-aware delegation table
- Re-add ATHENA_PROMPT_METADATA with tight triggers and avoidWhen guardrails
- Update switch_agent tool description with permanent handoff guardrail
- Make buildDelegationTable mode-aware: switch_agent for primary, task() for subagent
- Pass agent mode through general-agents to AvailableAgent interface
- Improve primary agent error messages in subagent-resolver with alternatives
- Add council-first bias to Athena interactive prompt
- Update athena-junior trigger text for clearer task() invocation guidance
- Add tests for ATHENA_PROMPT_METADATA shape, triggers, useWhen, avoidWhen
2026-04-15 16:48:46 +09:00
ismeth 7ffdd15b52 feat(athena-junior): add athena_council bulk launch tool
- Create src/tools/athena-council/ with council-launcher, tool factory, types
- Launch all council members in parallel via BackgroundManager with writeOutputToFile: true
- Wire tool into registry with backgroundManager + councilConfig dependencies
- Grant athena-junior athena_council permission, remove task permission
- Update non-interactive prompt to use athena_council instead of task tool
- Add explicit stop-after-result instruction to prevent post-output text
- 23 tests (14 tool + 9 launcher) using real temp files, no mock.module
2026-04-15 16:48:46 +09:00
ismeth ffde7a6853 fix(test): update model-requirements test for 13 builtin agents
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:46 +09:00
ismeth 149d065d68 feat(athena): split into athena (interactive) and athena-junior (subagent)
Split the Athena council orchestrator into two distinct agents:
- athena: primary mode, interactive prompt with Question tool and switch_agent
- athena-junior: subagent mode, non-interactive prompt with structured JSON output

Key changes:
- Create athena-junior-agent.ts factory (mode=subagent, denies question+call_omo_agent)
- Revert athena agent.ts to primary mode (no env var switching)
- Make buildAthenaRuntimeGuidance mode-aware (strips action_paths for non-interactive)
- Add mode parameter to council_finalize tool
- Register athena-junior in builtin-agents, tool-config, model-requirements
- Replace "athena" with "athena-junior" in call_omo_agent ALLOWED_AGENTS
- Update all tests for new architecture

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:46 +09:00
ismeth deb2a2754e feat(athena): add non-interactive fields to override schema
Extend AthenaOverrideConfigSchema with non_interactive_mode,
non_interactive_members, non_interactive_member_list. Replace
Record<string, unknown> casts with proper typed access.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth 05d4ecf2ff feat(athena): inject non-interactive config placeholders into prompt
Add 4 new .replace() calls for NON_INTERACTIVE_MODE, NON_INTERACTIVE_MEMBERS,
NON_INTERACTIVE_MEMBER_LIST, and BACKGROUND_WAIT_TIMEOUT_MS. Pass athena
non-interactive config from agent-config-handler to createBuiltinAgents.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth 61ccd7f9a7 fix(athena): include MODE=all agents in uiSelectedModel passthrough
The general-agents.ts isPrimaryAgent check only considered mode=primary,
causing Athena (now mode=all) to lose uiSelectedModel resolution.
Also adds metadata tests for non-interactive triggers.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth 6fb6d1256c feat(athena): dual prompt selection with MODE=all
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth e01742c8b2 feat(athena): add athena to switch-agent and call-omo-agent allowed lists 2026-04-15 16:48:45 +09:00
ismeth cee2baf421 feat(athena): add non-interactive prompt
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth 6a889d472d feat(athena): extend config schema with non-interactive fields
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth 1c27867fc9 feat(athena): extract interactive prompt to dedicated file
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:45 +09:00
ismeth 3468c72dfa test(council): add comprehensive meta-yaml-formatter tests 2026-04-15 16:48:45 +09:00
ismeth 9e196a21d7 fix(council): fix nudge retry cap bypass and add memory cleanup 2026-04-15 16:48:45 +09:00