Skip to content

feat: borrow pi-sharp patterns — model capabilities, execution mode, extensions#81

Merged
eanzhao merged 7 commits intodevfrom
feat/session-extensions
Apr 18, 2026
Merged

feat: borrow pi-sharp patterns — model capabilities, execution mode, extensions#81
eanzhao merged 7 commits intodevfrom
feat/session-extensions

Conversation

@eanzhao
Copy link
Copy Markdown
Owner

@eanzhao eanzhao commented Apr 18, 2026

Summary

Borrow three patterns from pi-sharp into Aexon. Plan in docs/superpowers/plans/2026-04-18-session-extensions-and-model-capabilities.md.

  • Task A (e2e96d2): Enrich ModelCapability (add ImageInput/Streaming/ToolCalling/Reasoning/PromptCaching) and introduce ModelPricing record attached to every ClaudeModelDescriptor. Router sources model capabilities from descriptors, layering WebFetch/WebSearch on top per provider-kind rules. Pricing values are copied from the existing authoritative values in BuiltinCommands.cs.
  • Task B (977fc98): Add ToolBatchExecutionMode (Auto / Sequential / Parallel) on QueryEngineConfig. Auto is the default and preserves per-tool IsConcurrencySafe behavior; Sequential/Parallel force the whole batch one way or the other for debugging and explicit fan-out.
  • Task C (a9c429f): Introduce IAexonExtension + minimal IAexonSessionBuilder in Aexon.Core/Extensions/. Extensions run exactly once before QueryEngine is built and can register tools, commands, hook observers, and system-prompt fragments. Concrete SessionBuilder lives in Aexon.Cli. No dynamic DLL discovery in this task — in-process registration only.

Also: 74f41e6 fixes a pre-existing ContextProvider test isolation bug (the memory tests were reading the developer's real ~/.claude/CLAUDE.md); caught while running the Task A test gate and split out into its own commit per AGENTS.md scope discipline.

Task #2 from the pi-sharp comparison (full AexonSession API boundary) is deferred to #79.

Test plan

  • dotnet build Aexon.slnx -c Debug --nologo — clean (one pre-existing CS8425 warning, unrelated)
  • dotnet test tests/Aexon.Core.Tests/Aexon.Core.Tests.csproj — 805 passed, 0 failed (+14 new tests across Tasks A/B/C)
  • Manual smoke: aexon REPL still starts with zero registered extensions (behavior byte-identical to baseline)

Notes for reviewer

  • Task A's ClaudeModelCatalog uses named capability constants (StandardCapabilities, ReasoningCapabilities) and named pricing records (Haiku35Pricing, SonnetPricing, etc.) rather than inline literals — easier to audit and update.
  • Task C's SessionBuilder throws InvalidOperationException if an extension tries to register a tool/command whose name is already taken (loaded or deferred). This is intentional — extensions cannot silently override built-ins.
  • Codex helped with Task A (see Co-Authored-By trailers on e2e96d2 and 74f41e6). Tasks B and C were implemented directly after Codex's rescue flow silently failed mid-flight on Task B.

Deferred follow-ups (explicitly out of scope)

  • Wire ModelPricing into /cost and /stats commands
  • Deduplicate the BuiltinCommands.cs hardcoded pricing now that descriptors carry pricing
  • Surface ToolBatchExecutionMode via a CLI flag or /mode sub-command
  • Dynamic plugin loading from ~/.aexon/extensions/*.dll
  • Full AexonSession API façade — tracked in RFC: AexonSession as a first-class API boundary (full #2) #79

eanzhao and others added 6 commits April 18, 2026 22:18
…roxy

Introduces a top-level `storage` subcommand that browses and mutates blobs in
the caller's aevatar chrono-storage bucket via the backend's explorer proxy
(`/api/explorer/*`). Reuses the aevatar chat config store for base-URL
resolution (so a single `/aevatar config set-url` also points `/storage` at
the same backend) and the existing NyxID bearer-token flow — no new auth
surface.

Subcommands:
  aexon storage ls [prefix]                 rounded table of the scope manifest
  aexon storage cat <key>                   GET, print as UTF-8 text
  aexon storage get <key> [local-path]      GET, save to file (or write bytes
                                            to stdout when no path given —
                                            binary fidelity preserved)
  aexon storage put <key> <local-path>      multipart upload (≤50 MB), infers
                                            content-type from extension
  aexon storage put-text <key>              read stdin, PUT as text — infers
                                            media type from key extension
  aexon storage rm <key>                    DELETE

`AevatarStorageClient` escapes each path segment so keys with embedded slashes
(`chat-media/abc.png`) survive routing, and wraps failures with the response
body (capped at 500 chars) in `AevatarStorageException` so `403 /
file-not-found / blocked path` surface clearly.

Smoke-tested end-to-end against aevatar mainnet:
- `ls` rendered 17 real files (chat-histories, workflows, chat-media, config.json)
- `cat config.json` round-tripped the actual JSON
- `put-text` → `cat` → `rm` cycle intact
- `get <image.jpeg>` saved 33 KB binary, `file` recognized valid JFIF JPEG

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… capabilities

Plan covers three tasks borrowed from pi-sharp:
- Task A: enrich ModelCapability flags and add ModelPricing
- Task B: add ToolBatchExecutionMode override (Auto/Sequential/Parallel)
- Task C: introduce IAexonExtension + minimal IAexonSessionBuilder

Full session API boundary (pi-sharp #2) is deferred to #79.
The LoadMemoryAsync tests that assert "no files → null" were picking up the
developer's actual ~/.claude/CLAUDE.md because UserClaudeDirectory /
SystemClaudeDirectory defaulted to real paths. Point them at per-test temp
directories so the assertion is deterministic.

Caught while running the Task A test gate.

Co-Authored-By: Codex <noreply@openai.com>
- Extend ModelCapability with ImageInput/Streaming/ToolCalling/Reasoning/PromptCaching
- Add ModelPricing record with per-1M rates and EstimateCostUsd helper
- Attach Capabilities + Pricing to every ClaudeModelDescriptor
- Router now sources model capabilities from descriptor, layers WebFetch/WebSearch
  on top per provider-kind rules

Co-Authored-By: Codex <noreply@openai.com>
…cutor

Expose Auto/Sequential/Parallel modes via QueryEngineConfig. Auto is the default
and preserves per-tool IsConcurrencySafe behavior; Sequential/Parallel force the
whole batch one way or the other for debugging and explicit fan-out.
Extensions run exactly once during CLI startup, before QueryEngine is built,
and can register tools, commands, hook observers, and system-prompt fragments
through a builder surface. The builder is discarded after all extensions have
configured the session.

No dynamic DLL discovery yet (in-process registration only). Scope is
deliberately minimal; the full session API façade is tracked separately.
@eanzhao eanzhao changed the base branch from feat/aevatar-storage to dev April 18, 2026 15:46
# Conflicts:
#	src/Aexon.Commands/StorageCommand.cs
@eanzhao eanzhao merged commit 588cd7d into dev Apr 18, 2026
3 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