| Age | Commit message (Collapse) | Author |
|
|
|
Stage writable event-field overrides by tool call id so they can be applied at the correct point in the turn lifecycle. Capture input overrides from tool_call_complete, capture output overrides from tool_result, and clear staged overrides defensively at turn start and shutdown.
Also honor user_message text overrides when starting a turn, add the helper that applies staged overrides to the agent at dispatch boundaries, and cover the behavior with tests for staged input/output overrides.
|
|
Replace the split [tools]/[extensions] config sections and the two-stage
load gate with a single model:
- [extensions] is now one policy resolved across all layers. Rules from
every layer are kept (not clobbered) and resolved by last-match-wins
after sorting by (layer, glob specificity, deny-last), so a base layer
can carve one name out of an otherwise-denied group and a higher layer's
broad rule still wins. Default is allow; whitelist via deny=["**"].
- Registration is deferred: a source returns an entry {name, activate}
(or a list, or the sugar tool table), is always eval'd side-effect-free,
and only permitted names get activate()d. Identity is the declared name,
not the filename. Collapses the old pre/post-load two-stage gate.
- The loader runs two passes (eval -> shadow -> filter -> activate) with
precedence project>user>base and, within a layer, rocks<paths<dir.
- extensions.paths adds extra scan dirs; extensions.rocks loads luarocks
packages as extension sources (require-as-entries). Startup installs
only missing rocks (no per-launch network hit); panto update force-
(re)installs every configured rock.
deny is a feature toggle, not a security boundary: rocks is where registry
code enters, so the pin list is the trust boundary. Rock integrity
(first-party GPG signing + --verify) is designed but not yet wired; until
then rocks pins which version, not which bytes.
Breaking: [tools] is removed; extensions must return entries. Built-in
tools and the wc example keep working via the sugar tool form.
|
|
|
|
|
|
Treat 1:1 component swaps and pure insertions as differential TUI updates
instead of structural changes that force full redraws. Keep full redraws
for net removals and edits above the viewport, and add tests covering the
streaming-tool flicker regression and scrollback behavior.
Also remove the unfinished Go ToolSource batch-registration bridge.
|
|
|
|
- ensuring stream.next() sequences are interruptible
- Agent.run() takes any user message, not just chat text
|
|
|
|
|
|
|
|
|
|
|
|
closing formatting
|
|
|
|
|
|
|
|
|
|
Teach provider config and auth resolution about the Codex Responses
dialect, including user-facing `style = "openai_responses"` with
`dialect = "codex"`. Serialize and parse Responses traffic with
provider-specific reasoning replay, assistant phase metadata, and robust
function-call assembly keyed by `output_index` so streamed tool inputs
survive proxy quirks and empty terminal payloads.
Also persist thinking origins and message metadata across sessions, add
the Anthropic interleaved-thinking header switch, write per-session
debug logs, and improve the TUI and scripts for inspecting tool output
and session costs.
|
|
|
|
Collapse the auth schema: infer `type` from keys, replace key/key_env_var with
a single `key` (resolved via substitution), flatten the exchange to flat
`exchange_*` keys, and drop the auth-level headers tables in favor of reusing
the provider's `extra_headers` on the auth HTTP calls. Add `${sibling}` and
`${env:VAR}` substitution over auth values (GitHub Enterprise = set `domain`).
Restore api_key-empty → provider-drop. Update default config, docs, and tests.
|
|
Add the OpenAI Responses API wire dialect (openai_responses APIStyle +
OpenAIResponsesConfig), a request serializer (instructions/input items,
flat function tools, reasoning, store:false, include encrypted reasoning),
and a streaming state machine over the typed response.* SSE events mapping
to the shared block model. Wire dispatch, buildProviderConfig, the C-ABI
free switch, and the reasoning selectors. Ship a commented Codex example in
the default config.
Implemented from the Responses API docs + the open-source Codex client and
covered by fixture tests; live verification against a ChatGPT plan is still
required (documented).
|
|
Resolve the active provider's auth before each turn via the AuthManager,
rendering an inline device-code prompt when login is needed, and force a
single refresh/exchange retry on a provider auth failure. Wire the manager
through RunOptions from main.zig.
|
|
Add src/auth_manager.zig (turn-time resolution: load→login→refresh→exchange→
build credential, patch live config) and `panto auth status|login|logout`
subcommands with a line-based device-code presenter. Add config_file.loadFromString.
|
|
Add libpanto http_helper (non-streaming request/response over the global
client, plus dotted-JSON-path readers) and token persistence in auth.zig
(load/save/delete TokenSet under an embedder-chosen auth dir, owner-only
files). Add $PANTO_HOME/auth to the panto_home layout.
|
|
Replace provider-level api_key/api_key_env_var with named auth sessions:
providers now reference `auth = "<name>"` and credentials live under
`[auth.<name>]`. Adds the libpanto auth type surface (AuthConfig, TokenSet,
ResolvedCredential), a Header type and provider `extra_headers`, and rewires
the CLI config loader (parse [auth.<name>], require auth=, providers always
survive, eager api_key resolution). Updates the shipped default config.
|
|
|
|
|
|
|
|
|
|
Remove setFrameTime() method and fps-related rendering from Footer.
Replace with setModel() to display model name with dim styling. Update
all Footer tests accordingly, adjusting line counts and expectations for
new layout with top/bottom rules wrapping content. Remove reverse video
styling from footer.
|
|
- FSJSONLStore: don't require `cwd`, instead panto CLI provides it in
the session metadata
- Lua: expose the `SessionStore` interface and the `FSJSONLStore` and
`NullStore` implementations
- C: use idiomatic `const char *` for incoming strings
- C: expose the `FSJSONLStore` and `NullStore` implementations
|
|
|
|
output
The root cause was a two-part bug:
1. Engine slot management (tui_engine.zig): The app rebuilds its full
component list on every new transcript entry. The old approach drained
every slot then re-added them all, resetting every component's render
baseline. When earlier content had scrolled into native scrollback
(viewport_top > 0), the engine's viewport-escape rule escalated any
cut above the viewport top to a scrollback-clearing full redraw — the
visible flash on every streamed block while a tool call was expanded.
Fix: replace drain+rebuild with syncComponents(), which reconciles the
live slot list in place by finding the longest common prefix and suffix
(matched by component pointer). Prefix and suffix slots keep their
baselines verbatim; only the changed region in the middle gets new
empty slots. A pure insertion (the dominant case: a new transcript
entry appended just before the pinned input+footer suffix) stays on
the differential path with no clear. A structural change (slot
replaced or removed) sets force_full for correctness.
2. RenderCache dirty signal (tui_component.zig): firstLineChanged()
was returning the retained changed_from diff index even when the cache
was clean. A first-paint store records changed_from == 0, so a clean
but previously-rendered component was pegging the differential cut to
line 0 on every subsequent frame, making unchanged slots above the
insertion point drag the cut all the way up. Fix: when the cache is
clean, firstLineChanged() returns null unconditionally. The recorded
changed_from is now internal render bookkeeping only, not a live
signal.
Additional changes:
- ToolUse.collapsed_tail_lines raised from 5 to 8 lines.
- ToolUse collapsed-view test updated to use 12 output lines and
collapsed_tail_lines symbolically so the numbers self-document.
- tui_app.zig: override-slot test comment and assertion clarified to
reflect that the override owns the slot pointer (not just renders it),
and the SWAPPED-AT-DETAILS render check replaced with a pointer-equality
check.
- lua_event_bridge.zig: minor related fixes.
- New tests: syncComponents middle-insertion no-clear, syncComponents
structural removal forces full, syncComponents override swap; streaming
expanded-tools no-clear regression test in tui_app.
|
|
|
|
Conversation.addUserMessage now takes a []ContentBlock (symmetric with
addAssistantMessage). Introduce a thin addUserText wrapper in agent.zig
for the plain-text case and update every call site in agent.zig and
anthropic_messages_json.zig accordingly.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
determine support for the kitty protocol and then pick output sequences
accordingly.
tested on ghostty (supports kitty) and tmux-in-ghostty (tmux does not
support kitty protocol) with shift+enter as newline.
|
|
Implement TUI Phase 1: a raw-mode terminal, differential render engine,
pinned input box + footer, and component model wired into the libpanto
event stream. Adds foundation modules (theme, key, component, input,
terminal), the render engine, components, and the app loop; removes the
old print CLIRenderer and driveTurn REPL from main.zig.
Also removes scratch status reports (tui-p1/, progress.md) for the now
completed work.
|
|
|
|
Push the API-shaping we'd done in the public facade down into the internal
types, collapsing the wrappers:
- Stream: rename phase->state, Phase->State; underscore-prefix the internal
fields (_agent/_queue/_response/_start/_persisted/_pending_error). state
is the one intended-public field. public.zig now aliases Stream; run
returns *Stream.
- Agent compaction: pure transform compact()->private _compactInPlace;
compactAndPersist()->compact() (the public name, persists).
- Agent system prompt: addSystemMessage(text,mode) split into
addSystemMessage(text) + setSystemPrompt(text) over a private
_persistSystemMessage.
- UserMessage moved off Agent to module scope.
- Underscore-prefix pure-internal Agent fields: _open_stream_fn,
_auto_compacted, _retry_prng.
The Agent facade is now pure 1:1 forwarders; it stays a wrapper only because
init heap-pins the inner (copyable, move-safe handle) and conversation()/
sessionId() are accessors. Conversation and Stream are plain aliases.
|
|
A facade that forwards every method 1:1 is worse than paring the real type
down to the intended surface and aliasing it. conversation.Conversation's
interface is already the public surface we want (init/deinit, the
add*/replace* builders, and messages/allocator as plain data fields), so
public.zig now aliases it straight through and Agent.conversation() returns
a borrowed *Conversation for in-place surgery. Drops the ConversationData
alias and the pointer-wrapper.
Merged addAssistantMessageWithUsage into addAssistantMessage(blocks, ?usage)
on the real type (separate method deleted), so the alias has the intended
one-method shape; all call sites updated.
Only Agent and Stream remain true facades -- they have genuinely-internal
fields plus API-shaping renames (Stream.phase->state, Phase->State) an alias
can't express.
|
|
Close the gap that forced the CLI to import internal libpanto namespaces,
then delete the transitional re-exports. The CLI now uses only the curated
public surface.
Additions to the public Agent facade:
- init/deinit (heap-pin the inner; the handle is a copyable value).
- addSystemMessage (.append) and setSystemPrompt (.replace).
- compact(override_system_prompt, extra) falling back to the config prompt.
- sessionId() accessor.
CompactionConfig gains compaction_prompt, which owns the compaction system
prompt for both auto-compaction and the explicit compact() default; the
Agent.compaction_system_prompt field is deleted. ConversationData is the
public name for the owned conversation value type (Session.load returns it,
Agent.init adopts it), distinct from the borrowed Conversation handle.
Agent.addSystemMessage/setSystemPrompt are kept on Agent (they persist with
the SystemMode) rather than dropped as the plan first proposed.
|