Commit Graph

900 Commits

Author SHA1 Message Date
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 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 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 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 c796187ee5 fix(test): replace /etc/passwd with innocuous paths to avoid GitGuardian false positive 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 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 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 e01742c8b2 feat(athena): add athena to switch-agent and call-omo-agent allowed lists 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 e15ce7b279 fix(council): increase archive ID entropy and remove unused toolContext param
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 3d36f6c5d8 fix(council): handle whitespace around COUNCIL_MEMBER_RESPONSE tags
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 4a7ddae53f refactor(council): move hasCouncilResponseTag to council-response-extractor
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth 45d3eb65df fix(council): add error logging to empty catch in council-finalize-helpers
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth d87d0256ba test(council-archive): add comprehensive coverage for council-finalize-helpers
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth 88c615ee97 fix(prepare-council-prompt): suppress ENOENT in cleanup timer and eliminate redundant resolveCouncilIntent
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth ffb0fad26e refactor(council-archive): remove dead isPathEscaping check and unused log import
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth 17ac08a02b fix(council-archive): add movePromptFile error logging and fix isPathEscaping false positives
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth 36e52d28b4 fix(council-archive): handle CRLF line endings in structural tag detection
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth 6e57d060e1 fix(council-finalize): append runtime guidance to tool response for same-turn delivery
Remove contextCollector dependency from council_finalize and append
Athena runtime guidance directly to the tool's JSON response string.
This fixes the timing bug where guidance registered via contextCollector
only arrived on the next message cycle, too late for Athena's same-turn
synthesis.

- Remove ContextCollector import, RegisterContext type, and collector.register() block
- Append buildAthenaRuntimeGuidance() output after JSON result with \n\n separator
- Simplify createCouncilFinalize() factory call in tool-registry (no options)
- Update unit tests: replace collector mock assertions with direct output string checks
- Update integration tests: wrap JSON.parse with extractJson() helper for hybrid output
2026-04-15 16:48:44 +09:00
ismeth 951f004a65 fix(test): align assertions after cherry-pick merge from 3 worktrees
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:44 +09:00
ismeth b4dd379b38 fix(council-archive): handle literal tag mentions inside response content
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:19 +09:00
ismeth 7d2749cfe1 fix(athena): address 11 audit findings (H2,H3,H5,M1-M4,M8-M11)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-04-15 16:48:13 +09:00
ismeth a13cc7b877 fix(council-finalize): patch path traversal vulnerabilities in name, task_ids, and prompt_file 2026-04-15 16:48:07 +09:00
ismeth a33a3d1095 refactor(athena): remove redundant council_read tool, use Read instead
council_read was a sandboxed readFile wrapper for .sisyphus/ archives.
Now that council_finalize extracts member responses into plain files,
the standard Read tool serves the same purpose. Removes the tool, its
tests, and updates Athena's prompt to reference Read directly.
2026-04-15 16:48:07 +09:00
ismeth 225a612b3a fix(test): update council-finalize assertions to match current runtime guidance wording 2026-04-15 16:48:01 +09:00
ismeth 34faa7c67c refactor(switch-agent): replace deferred hook system with direct session.create + promptAsync
Adopt the opencode-handoff pattern: switch_agent now creates a new session
and sends the context via promptAsync immediately in the tool's execute
function, then navigates the TUI to the new session.

Removes ~1700 LOC of complexity: persistent state management, event-based
hook monitoring, retry logic, session idle waiting, apply verification,
and terminal detection — all replaced by 3 SDK calls.

Deleted: src/features/agent-switch/ (14 files), src/hooks/agent-switch/ (4 files)
Updated: Athena prompt to announce handoff before calling switch_agent
2026-04-15 16:48:01 +09:00
ismeth 227a59a796 fix(athena): implement 10 prompt audit fixes from council review
- Fix 1: Clarify 'restart at Step 3' → 'restart the council workflow from Step 3 (intent classification)'
- Fix 2: Replace {council-session-name} with static references to council_finalize archive_dir
- Fix 3: Add documenting comment for call_omo_agent in solo mode allowlist
- Fix 4: Add delegation agent rationale comment to RUNTIME_GUIDANCE_BY_INTENT
- Fix 5: Type-safe COUNCIL_INTENT_ADDENDUMS with Record<CouncilIntent, string>
- Fix 6: Clarify FREEFORM wording re COUNCIL_MEMBER_RESPONSE tag requirement
- Fix 7: Remove dead Step 15 fallback from agent.ts
- Fix 8: Add compound question intent precedence guidance
- Fix 9: Fix AUDIT double-period typo
- Fix 10: Add edge case test for extractCouncilResponse with literal tag text
2026-04-15 16:47:57 +09:00
ismeth c265200f5f feat(athena): inject intent runtime guidance and action flows 2026-04-15 16:47:52 +09:00
ismeth e52ccd1b04 fix(agent-switch): create fresh session for handoff apply 2026-04-15 16:47:37 +09:00
ismeth 2c27298dc1 fix(council-archive): simplify finalize output and archive reads 2026-04-15 16:47:33 +09:00
ismeth c27b309320 feat(athena): expand council intent system from 4 to 8 intents
Add DIAGNOSE, CREATE, PERSPECTIVES, FREEFORM intents to Athena's council
classification system. Refine existing AUDIT (add DIAGNOSE boundary),
EVALUATE (improve criteria guidance), and EXPLAIN (remove catch-all status).

Fix bug where prepare_council_prompt defaulted to AUDIT when no intent was
specified — this caused analytical frameworks to be injected into creative
requests (e.g. poetry). Now defaults to no addendum.

Derive validIntents from Object.keys(COUNCIL_INTENT_ADDENDUMS) to prevent
drift between the addendums record and the validation array.
2026-04-15 16:47:33 +09:00
ismeth f5829bf2fb fix(athena): resolve task output files to project directory instead of process.cwd()
task-output-writer wrote to process.cwd()/.sisyphus/task-outputs/ while
council_finalize read from ctx.directory/.sisyphus/task-outputs/. When
these differ, all output files appear missing.

Fix by passing directory to writeTaskOutput from BackgroundManager, and
adding basePath to council_read. Also removes process.chdir() from tests
that was masking the bug.
2026-04-15 16:47:20 +09:00
ismeth aa91c1fb19 fix(glob): extract directory prefix from pattern for ripgrep compatibility
Ripgrep's --glob flag silently returns no results when the pattern
contains a directory prefix (e.g. 'apps/backend/**/*.ts'). Models
naturally write patterns this way when exploring unfamiliar codebases.

Extract the static directory segments from the pattern, append them to
the search path, and pass only the glob portion to ripgrep.
2026-04-15 16:46:52 +09:00
ismeth 2b70130c08 feat(athena): council member continuation enforcer, tool allowlist, and prompt fixes
- Add council-continuation-enforcer to nudge council members until they
  output COUNCIL_MEMBER_RESPONSE, replacing the hacky post-compaction hack
- Add background_wait + background_cancel to council member tool allowlist
  so members can properly manage their explore agents before responding
- Update COUNCIL_DELEGATION_ADDENDUM prompt to instruct members to cancel
  pending tasks and wait for explore results before final response
- Fix council_finalize path resolution to use project directory
- Remove post-compaction-continuation.ts and recentlyCompactedSessions hack
- Add council-response-checker for detecting response tag in session messages
2026-04-15 16:46:05 +09:00
ismeth 684b746ee7 fix(tools): resolve relative paths in glob/grep against project directory
When models pass relative paths (e.g. 'apps/ios/CleanSlate') to glob/grep
tools, they were passed directly to ripgrep which resolved them against
process.cwd(). In OpenCode Desktop, process.cwd() is '/' causing all
relative path lookups to fail with 'No such file or directory'.

Fix: use path.resolve(ctx.directory, args.path) to resolve relative paths
against the project directory instead of relying on process.cwd().
2026-04-15 16:43:54 +09:00
ismeth 8676b1cdb9 fix(tools): resolve relative paths in glob/grep against project directory
When models pass relative paths (e.g. 'apps/ios/CleanSlate') to glob/grep
tools, they were passed directly to ripgrep which resolved them against
process.cwd(). In OpenCode Desktop, process.cwd() is '/' causing all
relative path lookups to fail with 'No such file or directory'.

Fix: use path.resolve(ctx.directory, args.path) to resolve relative paths
against the project directory instead of relying on process.cwd().
2026-04-15 16:43:54 +09:00
ismeth da446979f6 test(athena): add tests for intent addendums, hook enforcement, restriction parity, and council prompt regression 2026-04-15 16:43:48 +09:00
ismeth 6b450b42db feat(athena): add intent parameter to prepare_council_prompt + wire hook into plugin system + update exports 2026-04-15 16:43:17 +09:00
ismeth e198707e84 test(council-archive): add integration tests for full council flow 2026-04-15 16:43:17 +09:00