Commit Graph
456 Commits
Author SHA1 Message Date
Karl Seguin cc33bb38d1 Merge pull request #3061 from lightpanda-io/consistent-clocks
chore: make our use of clock/timestamp more consistent
2026-07-28 09:20:18 +08:00
Adrià Arrufat cbce632401 Replace @cImport with build-system translateC
Zig 0.17 removes @cImport in favor of translate-c steps in the build
system. Each library's link function now creates an addTranslateC step
exposed as a module import: curl and isocline translate their headers
straight from the dependency tree, while sqlite3 uses the artifact's
emitted include tree since the amalgamation lives in a sub-dependency.

Terminal.zig and prompt_assist.zig previously instantiated two
independent @cImport namespaces for isocline; they now share one
translated module, so its types are identical across both files.
2026-07-27 12:07:28 +02:00
Karl Seguin dcb98853b9 chore: make our use of clock/timestamp more consistent
Zig 0.16 changed clocks/timestamps. Our migration to Zig 0.16 took the path of
least resistance. For example, we kept 'monotonic' and 'real' even though Zig
0.16 renamed them to 'boot' and 'clock'

This commit tries to standardize all timestamp usage to: lp.datetime.timestamp
and lp.datetime.milliTimestamp using the new zig 0.16 names ('boot' and 'clock')
2026-07-27 09:28:48 +08:00
Adrià Arrufat bae9388f1c Merge branch 'main' into brave-search 2026-07-22 16:27:35 +02:00
Adrià Arrufat 99262190da feat(tools): add Brave search + /searchEngine metacommand
Add Brave as a second API-backed web search engine: the search tool's
auto mode tries Brave then Tavily (keyed on BRAVE_API_KEY/TAVILY_API_KEY)
before the DuckDuckGo scrape. A new /searchEngine REPL metacommand pins
the engine explicitly (auto|tavily|brave|duckduckgo) — an explicit
engine surfaces its failures instead of silently degrading — and is
persisted to .lp-agent.zon like the other REPL settings.

Bumps zenai to pick up the zenai.search.brave client.
2026-07-22 13:44:51 +02:00
Karl Seguin 802862120f update zenai dep (zig 0.16) 2026-07-22 13:26:06 +08:00
Karl Seguin 8e42d63c1c zig: Zig 0.16
Built against https://github.com/lightpanda-io/zig-v8-fork/tree/zig-0.16 but
it doesn't require a new v8 build.

Built against https://github.com/lightpanda-io/boringssl-zig/tree/zig-0.16
since the current fork we point to isn't updated.

A global std.Io instance, lp.io. Way easier this way and requires 0 changes to
our libcurl integration / event loop.

Network code uses a new layer that does what Zig 0.15's posix package used to
do. Again, quicker migration that way. But, as long as we have the global IO,
and given the half-baked nature of networking in std.Io 0.16, this just makes
sense. Things can be migrated as needed.

The std.time.* -> std.Io.Timestamp/Clock/Duration resulted in _a lot_ of
changes. ArrayList = .{} -> ArrayList -> .empty also resulted in a lot of
changes, but that's obviously superficial. As is the trimLeft/trimRight ->
trimStart/trimEnd rename.

Locking adopt the `Uncancelable` variants, e.g. mutex.lockUncancelable() to
preserve the error-free signature (and, because cancellation would be something
we'd have to put more thought into).

std.json.ObjectMap is now unmanaged, so the allocator had to be passed along.
However, there's still a deprecated managed variant of MemoryPool, so I switched
to it (we can do a small follow up PR to move to the unmanaged after).

I tried use_llvm = false, but it locks my computer, consuming RAM until MacOS
gives me a popup I've never seen before, begging me to start killing processes.

Agent and the networking stuff saw the most significant changes.
2026-07-22 13:26:03 +08:00
Adrià Arrufat 8c75769e52 agent: render picker frames in one write
Each redraw issued one unbuffered stderr write per row plus cursor
moves, which can tear visually mid-frame. Build the frame in a stack
buffer and emit it with a single locked write, like Spinner's
renderLocked.
2026-07-16 09:15:59 +02:00
Adrià Arrufat 98134feb7a prompt_assist: use explicit PathMatchIterator.init 2026-07-15 12:34:35 +02:00
Adrià Arrufat 1d8a0b9fc4 agent: trim narrative comments from the terminal split 2026-07-15 12:20:56 +02:00
Adrià Arrufat f462223277 agent: tighten the seams the terminal split left behind
- attachCompleter now takes the CompletionSource: Agent no longer
  reaches through terminal.assist to configure what it just attached,
  and attach/configure can't be sequenced wrong.
- history_paths flows through one channel: attach() loads the initial
  history from the same State field modeCallback swaps from, instead of
  setupRepl receiving a second copy that had to agree.
- all_names + closestCommand move to SlashCommand.zig, the command
  registry - prompt_assist consumes it like everyone else and
  Terminal's closestCommand re-export disappears.
- picker treats a tty that refuses raw mode as NotInteractive and
  degrades to the line prompt instead of leaking termios errnos to
  callers that can only blanket-catch.
- kitty keyboard flags become named ansi.zig constants shared by
  Terminal.readLine and picker's RawTerminal.
- skipWhitespace was std.mem.indexOfNonePos; single-use style consts
  inline into the style table; drop the dead CompletionSource default
  and doc-comment splice in Spinner.
2026-07-15 12:16:53 +02:00
Adrià Arrufat 6845c3a542 agent: use decl-literal init syntax in picker and prompt_assist 2026-07-15 12:05:34 +02:00
Adrià Arrufat b4c971799c agent: share one filesystem scan between path completion and ghost hint
addPathCompletions and ghostPathFirstMatch were ~20-line twins (split
dir/base, openDir, iterate, prefix-match, '/' suffix for directories).
A fix landing in one copy — hidden-file filtering, the shared
symlink-to-directory suffix gap — would make Tab completion and the
ghost hint disagree. Both now consume one PathMatchIterator.
2026-07-15 11:56:27 +02:00
Adrià Arrufat 2d43d5103e agent: derive prompt style registration and kind mapping from one table
ic_style_def registration lived in setup while the Kind->style switch
lived 600 lines away in IcSink: adding a js_highlight.Kind
compile-errored the sink but silently skipped registration, rendering
the new kind unstyled with no test able to catch it (isocline is
live-only). One table now drives both; an unmapped or doubly-mapped
kind is a compile error.
2026-07-15 11:53:56 +02:00
Adrià Arrufat 673b16528f agent: extract prompt assistance into prompt_assist.zig
Completion, ghost hints, and prompt highlighting (~640 lines) were
cohabiting with Terminal's readline lifecycle and output printing. They
now live in prompt_assist.zig, which owns the isocline callbacks, the
ps-* style palette, and the slash-command name table. The C callbacks
read a small prompt_assist.State (js_mode, completion_source,
history_paths) embedded in Terminal, replacing the three loose fields.
Pure code motion otherwise; the valueAt/renderSchemaHint tests move
with their code and stay in the suite via Terminal's test hook.
2026-07-15 11:50:44 +02:00
Adrià Arrufat f48498073d agent: extract the choice picker into picker.zig
The numbered/interactive picker (ChoiceState, RawTerminal, render loop)
was ~215 self-contained lines inside Terminal.zig. It runs before - or
without - the isocline REPL (provider selection during setup), so
settings.zig no longer imports the isocline-configured Terminal at all.
Pure code motion; the ChoiceState tests move with it and stay in the
suite via Agent.zig's test hook.
2026-07-15 11:40:55 +02:00
Adrià Arrufat 772a3fc7db agent: move terminal width query into its only user, Spinner
Terminal.columns() had exactly one caller (Spinner's line renderer) and
Terminal itself never used it. Moving it breaks the Spinner<->Terminal
import cycle and stops Spinner type-checking against the isocline
@cImport for one ioctl.
2026-07-15 11:36:31 +02:00
Adrià Arrufat 4307509094 agent: improve REPL syntax highlighting and terminal colors
- Add highlighting for JS functions, methods, types, and template
  interpolations.
- Introduce teal color and adjust cyan to bright cyan.
- Import `ansi.zig` directly instead of through `Terminal`.
- Add PandaScript semantics note to the agent's system prompt.
- Refactor markdown table rendering and streaming.
2026-07-15 11:02:46 +02:00
Adrià Arrufat 683b9b412b md_term: render styled borders for fenced code blocks
Adds top and bottom borders around code blocks, displaying the language
tag if present. Also changes horizontal rules to dashed lines to
distinguish them from code block borders.
2026-07-15 09:49:59 +02:00
Adrià Arrufat 813cf7f424 agent: clean up terminal rendering and highlighting
- Extract ANSI escape codes to a dedicated `ansi.zig` module.
- Consolidate styled output rendering in `Terminal.zig`.
- Simplify dollar variable highlighting and table measurement.
2026-07-15 09:25:33 +02:00
Adrià Arrufat 8b1686f6fb agent: highlight markdown code blocks as JS
Extracts the JavaScript highlighting logic from `Terminal.zig` into a
reusable `js_highlight.zig` module. Uses this module to syntax-highlight
markdown fenced code blocks in `md_term.zig` using ANSI escape codes.
2026-07-15 09:10:11 +02:00
Adrià Arrufat 3083414798 terminal: use standard SGR dim and cyan code blocks
Replaces the 256-color gray escape sequence with standard SGR 2 dim.
Also updates fenced code blocks to render in cyan instead of dim.
2026-07-15 08:02:51 +02:00
Adrià Arrufat 53e1dbe8e8 agent: clean up terminal and markdown rendering
- Move ANSI escape definitions to `md_term.zig`.
- Fix table cell width calculation using a scratch buffer.
- Ensure `Stream.close` fully resets markdown stream state.
- Extract helper functions for styled output and fence checks.
2026-07-14 23:13:52 +02:00
Adrià Arrufat d0ce9a02a8 terminal: stream and render markdown incrementally
Introduces an incremental markdown renderer (`md_term.Stream`) to
format streamed assistant responses in real time. Buffers lines,
maintains code fence state across chunks, and temporarily withholds
tables to render them with proper column alignment once complete.
2026-07-14 22:51:43 +02:00
Adrià Arrufat aacddfdbea Merge branch 'main' into agent-repl-markdown-output 2026-07-14 22:32:26 +02:00
Adrià Arrufat 74536507dd md_term: add markdown table rendering 2026-07-14 22:32:07 +02:00
Adrià Arrufat d52e56ece8 md_term: support arbitrary nested inline styles
Introduces a stack-threaded `Style` chain to track and re-apply
parent styles upon nested resets. This fixes styling loss in
deeply nested markdown spans.
2026-07-14 22:06:25 +02:00
Adrià Arrufat 0618515132 terminal: fix nested markdown styles and dim color
- Use a 256-ramp gray for `ansi.dim` to improve readability.
- Track and re-apply active styles in nested markdown spans.
- Collapse extra spaces after list markers.
2026-07-14 21:51:09 +02:00
Adrià Arrufat c67b9f3ad3 terminal: stream markdown rendering to writer
Changes `md_term.render` to write directly to a writer instead of
allocating a string. Also simplifies horizontal rule parsing.
2026-07-14 12:19:26 +02:00
Adrià Arrufat 8c0ec3d73a Merge branch 'main' into pandascript-skill 2026-07-14 12:06:05 +02:00
Adrià Arrufat 98b89250aa agent: add OSC 8 links, blockquotes, rules, escapes to REPL markdown
- Links now emit OSC 8 hyperlinks (clickable where supported) with a dim
  url fallback; a bare link (label == url) drops the redundant suffix.
- Blockquote lines (`> `) render with a dim bar.
- Horizontal rules (`---`/`***`/`___`) render as a dim line.
- Backslash escapes unescape only markdown-special chars, leaving e.g.
  C:\\Users intact.
2026-07-13 15:46:38 +02:00
Adrià Arrufat dd1fa4135c agent: render basic markdown in REPL output
Style the assistant's answer in the interactive agent REPL with ANSI
escapes: headings, bullet/numbered lists, bold/italic/inline-code/strike,
and links. Rendering is gated to the REPL on a real terminal, so
`--task`/piped output stays verbatim for programmatic consumption.

Adds src/agent/md_term.zig (markdown -> ANSI renderer) and wires it into
Terminal.printAssistant.
2026-07-13 15:37:01 +02:00
Adrià Arrufat 3e801fb83b script: generate the PandaScript skill from the tool schemas
The /save script documentation lived as a hand-maintained string in
Agent.zig whose primitives table drifted whenever a tool changed. Move
it to src/script/skill.zig and render the reference (signatures, option
lists, enums, defaults, per-parameter descriptions) from Schema.all()
at first use, keeping the curated per-tool notes behind an exhaustive
switch on Tool so a new or renamed tool is a compile error until its
doc entry exists.

The rendered skill is shared by three consumers:
- the /save and revision system prompts (built lazily in Agent.zig)
- a new mcp://skill/pandascript resource
- `zig build skills`, which writes zig-out/skills/<name>/SKILL.md with
  Claude Code frontmatter via a registry-based generator exe, so
  future Lightpanda skills are one registry entry each

Schema.FieldEntry now retains per-parameter schema descriptions, which
previously existed only in the raw JSON.
2026-07-13 10:54:13 +02:00
Adrià Arrufat bdb122cdce Merge branch 'main' into agent-streaming 2026-07-12 22:56:54 +02:00
Adrià Arrufat 46be87f2ca test(agent): cover resolveStream precedence and stream round-trip
Verify the stream default is on, that a remembered value wins, and that
the field parses from (and is absent-safe in) .lp-agent.zon.
2026-07-12 22:13:49 +02:00
Adrià Arrufat fa2dc02721 fix(agent): reset stream_active at turn start
`stream_active` relied on every runTools exit path balancing pause with
`endStreamedText`. If one is ever missed, a stale-true flag skips the
spinner pause on the next turn, interleaving frames with streamed text.
Reset it alongside `streamed_text` so the invariant is local.
2026-07-12 22:10:29 +02:00
Adrià Arrufat 2e8e0d1a6f docs(agent): note why the streamed_text guard is turn-wide safe
Document the invariant that lets a single per-turn flag suppress the
buffered reprint without risking a dropped final answer.
2026-07-12 22:10:11 +02:00
Adrià Arrufat a64ced0ccc fix(agent): stream assistant text only in the REPL
Streaming defaulted on for every mode, so one-shot `--task` and script
runs interleaved intermediate assistant commentary into stdout, which
wrappers treat as the answer. Gate the stream hook on `Terminal.isRepl`
so non-interactive modes keep the buffered final answer.
2026-07-12 22:09:48 +02:00
Adrià Arrufat 5abae593d8 fix(agent): keep streaming off for suppressed turns
Streaming wrote assistant deltas straight to stdout regardless of
`suppress_answer`, so `--save`/capture turns (which set it to hide the
answer) leaked model text past the `runTurn` guard. Pass a null stream
hook when the turn's answer is suppressed.
2026-07-12 22:09:25 +02:00
Karl Seguin d43d11a641 Merge pull request #2912 from lightpanda-io/agent-repl-fixes
agent: multi-line slash commands and --save .js extension
2026-07-11 07:26:29 +08:00
Adrià Arrufat 049c5f08f2 feat(agent): add assistant text streaming 2026-07-11 00:50:01 +02:00
Adrià Arrufat 7547d3ac72 agent: emit intent comments in saved scripts
Instruct the /save synthesis model to annotate the generated script with
short `//` intent comments — one per logical block (navigate, extract,
fan out, aggregate, return) describing what the block accomplishes, so a
later reader (human or agent) grasps the script at a glance.

The save synthesis prompt previously ended with "no commentary", which
discouraged code comments; reword it to forbid only prose/markdown fences
outside the code while explicitly asking for the `//` intent comments. The
same prompt backs the MCP `save` tool, so both surfaces benefit. Also add a
matching best-practice bullet + example to the agent's script-writing skill.
2026-07-10 16:12:16 +02:00
Adrià Arrufat 03f6577efa agent: continue slash commands across lines inside '''...''' blocks
The docs promise triple-quoted values that span multiple lines, but the
REPL dispatched every physical line immediately, so the multi-line
/extract form died with 'unterminated quote' on its first line. A slash
command that leaves a '''...''' block open now keeps reading lines at a
continuation prompt until the block closes; Ctrl-D abandons it.
2026-07-10 11:55:50 +02:00
Adrià Arrufat 0f686381dd agent: append .js to --save destinations like /save does
The one-shot --save flag bypassed save.parseCommand, so '--save test'
wrote a file literally named 'test' while the REPL's /save appended the
extension. Share one ensureJsExtension helper so the paths can't drift.
2026-07-10 11:34:53 +02:00
Adrià Arrufat ff7545a8c3 agent: add Google Vertex AI support
Supports both express mode (via VERTEX_API_KEY) and project mode
(via GOOGLE_CLOUD_PROJECT and gcloud auth). Handles automatic
OAuth token retrieval and refresh.
2026-07-03 20:23:51 +02:00
Karl Seguin f1e49aae6e perf: Increase default max connections
max http connections 10 -> 40
max http connetions per host 4 -> 6

These are just the defaults and can still be adjusted by the command line
arguments. 6 appears to be both Chrome and FireFox's default per host (which
is probably the more important of the two settings).

The limits are really use-case specific. A use case that it multi-threading
different domains can benefit from a conservative max-host with a very large
max conn. A use case that is multi-threading the same host will need to decide
if it's safe to raise max-host.
2026-07-03 09:43:13 +08:00
Adrià Arrufat 1dd5f6548d agent: update script instructions for parallel pages
Updates the `script_skill` prompt to recommend and demonstrate parallel
page navigation using `Promise.all` and multiple `Page` instances,
rather than serial navigation on a single page.
2026-06-30 13:27:54 +02:00
Adrià Arrufat 98a5458205 agent: allow paths in --save option 2026-06-19 07:46:42 +02:00
Adrià Arrufat 69d302fabe agent: add --save flag to synthesize scripts from tasks 2026-06-18 17:45:26 +02:00
Adrià Arrufat 59dc69f1fe agent: support provider-specific default effort
Updates the zenai dependency and resolves the agent's effort based on
the configured provider's default. Also dynamically updates the active
effort when switching providers.
2026-06-18 13:22:29 +02:00