Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
ec4e872
Rename Workbench to Workshop (wb to ws)
vinniefalco Aug 25, 2026
4bdec59
Add the What PromptForge Is design document
vinniefalco Aug 26, 2026
6b33dbe
Remove the voice-status line below the chat composer
vinniefalco Aug 26, 2026
9715cb1
Open chat links externally instead of navigating the app
vinniefalco Aug 26, 2026
c1a9fa7
Memoize completed markdown blocks during streaming
vinniefalco Aug 26, 2026
aa76015
Add three-state thinking block with streaming preview
vinniefalco Aug 26, 2026
884528c
Add per-run tool activity block with one-line autoscroll
vinniefalco Aug 26, 2026
0caece2
Add hidden-by-default custom title bar with typed window IPC
vinniefalco Aug 26, 2026
573db9f
Add accessible File/Edit/Window/Help menus to the title bar
vinniefalco Aug 26, 2026
d13ea19
Skin the custom title bar and menu popovers
vinniefalco Aug 26, 2026
18a35ab
Add decorationless Windows shell with typed window IPC
vinniefalco Aug 26, 2026
0cf649f
Install the cold medallion as the tao window icon
vinniefalco Aug 26, 2026
f0eefcf
Add confined workspace file APIs
vinniefalco Aug 26, 2026
d9814ea
Deliver Windows Explorer drops to the UI as trusted paths
vinniefalco Aug 26, 2026
a49d7e6
Fix rustfmt violation in workspace.rs
vinniefalco Aug 26, 2026
22cbdc8
Add zone registry and Workshop file-tree panel
vinniefalco Aug 26, 2026
c3b63b4
Add CodeMirror 6 editor panel behind an EditorSurface interface
vinniefalco Aug 26, 2026
9d0c513
Add layout lock, persistence, and keyboard shortcuts
vinniefalco Aug 26, 2026
83c6cec
Hide the mic unless transcription can run on a GPU
vinniefalco Aug 26, 2026
f8de3ad
Replace dot loader with prefill and toggleable Thinking block
vinniefalco Aug 26, 2026
13611d9
Add model-turn footer: Copy, inert Fork, timestamp tooltip
vinniefalco Aug 26, 2026
b8329be
Move model selection into a top-level Model menu
vinniefalco Aug 26, 2026
2c99b95
Add tabbed Agent panels with File > New Agent
vinniefalco Aug 26, 2026
320d3b5
Remove layout locking; workbench is always unlocked
vinniefalco Aug 26, 2026
36d0c5a
Fix status LED stuck lit after Stop mid-stream
vinniefalco Aug 26, 2026
764d4cc
Show the custom title bar in browser mode too
vinniefalco Aug 26, 2026
26aacd4
Fix the missing thinking UI: relay reasoning frames end to end
vinniefalco Aug 27, 2026
9c0fa32
Restore Dockview dragging with a delegating OLE drop target
vinniefalco Aug 27, 2026
5e358c7
Render real chip tabs, protect the Workshop panel, drop New Chat
vinniefalco Aug 27, 2026
1d7bd07
Build with CUDA by default; never half-initialize voice on CPU
vinniefalco Aug 27, 2026
b8b72d1
Replace the OLE drop delegation with the WebView2 file bridge
vinniefalco Aug 27, 2026
c37397e
Add the workshop idiom comparison report that specs the refactor
vinniefalco Aug 27, 2026
fd7a9df
Add a UI base layer: Disposable and Emitter (idiom Findings 2-3)
vinniefalco Aug 27, 2026
e46b51d
Move model catalog into injected ModelService (idiom Finding 2)
vinniefalco Aug 27, 2026
60b3373
Layer the workshop UI into base/services/ui (idiom Finding 1)
vinniefalco Aug 27, 2026
410583a
Adopt Disposable lifecycle across workshop UI (idiom Finding 3)
vinniefalco Aug 27, 2026
268c1f8
Split style.css into per-component files (idiom Finding 5)
vinniefalco Aug 27, 2026
4fec8db
Swap hand-ordered test chain for glob discovery (idiom Finding 6)
vinniefalco Aug 27, 2026
ea3785a
Extract WS session plumbing into ws_session (idiom Finding 4)
vinniefalco Aug 27, 2026
935358f
Move all wire types into a pure protocol module (idiom Finding 4)
vinniefalco Aug 27, 2026
95a211d
Split transcribe.rs by role behind a facade (idiom Finding 4)
vinniefalco Aug 27, 2026
ec0cbb0
Split app.rs by role into assets and relay (idiom Finding 4)
vinniefalco Aug 27, 2026
1c35520
Write down the house style in STYLE.md (idiom Findings 9-10)
vinniefalco Aug 27, 2026
f157bd6
Idiom sweep: dialog and voice disposal, CSS fallback drift
vinniefalco Aug 27, 2026
d6eafcb
Queue pre-ready pushes, replay on ready() (idiom Finding 7)
vinniefalco Aug 27, 2026
d4d0ebb
Add an intent-named Push facade over the buses (idiom Finding 7)
vinniefalco Aug 27, 2026
5d2c8dd
Tag voice frames with a stream generation (idiom Finding 7)
vinniefalco Aug 27, 2026
27866bd
Add editor readOnly compartment and externalUpdate annotation
vinniefalco Aug 27, 2026
2ba203e
Defer the external-browser open through ShellEvent
vinniefalco Aug 27, 2026
ffb2506
Carve smoke.mjs into per-feature tests on a shared boot fixture
vinniefalco Aug 27, 2026
740a5e1
Document the voice stream announcement in the route table
vinniefalco Aug 27, 2026
9b2bb91
Remove every italic style from the thinking UI
vinniefalco Aug 27, 2026
cbdbf57
Make Explorer drops visible in the Workshop tree
vinniefalco Aug 27, 2026
4dd0f1a
Expose gateway profiles in the Model menu
vinniefalco Aug 27, 2026
440087b
Add the server-delivery idiom study
vinniefalco Aug 27, 2026
a9839a0
Dissolve STYLE.md into nested per-crate AGENTS.md files
vinniefalco Aug 27, 2026
816ccaf
Drop asset caching from the server refactor scope
vinniefalco Aug 27, 2026
a5ec752
Apply rustfmt to relay.rs and workspace.rs
vinniefalco Aug 27, 2026
ad698be
Build the WS test net ahead of the session rewrites
vinniefalco Aug 27, 2026
22e02a4
Fix two rustdoc links to private consts in gateway.rs
vinniefalco Aug 27, 2026
ff1aea4
Write the delivery contract into protocol.rs
vinniefalco Aug 27, 2026
4158ecd
Rewrite the chat WS session onto the Rustpad model
vinniefalco Aug 27, 2026
468bea6
Rewrite the voice WS session onto a single select! loop
vinniefalco Aug 27, 2026
1bff419
Split app.rs into per-feature route constructors
vinniefalco Aug 27, 2026
6c084ae
Split errors by boundary into an opaque wire AppError
vinniefalco Aug 27, 2026
dee80e3
Remove the two idiom studies now hosted in tools-public
vinniefalco Aug 27, 2026
105af3e
Add a module size ratchet enforced by the integration tests
vinniefalco Aug 27, 2026
f0f02d3
Bound server shutdown with watchdog and stopped barrier
vinniefalco Aug 27, 2026
0a88a23
Fix status LED sticking amber after an error frame
vinniefalco Aug 27, 2026
cf970e7
Group REC badge and LED so progress swaps both out
vinniefalco Aug 27, 2026
acdd4f1
Build workshop CI checks without default features
vinniefalco Aug 27, 2026
1836731
Fix lost keystrokes when typing during an editor save
vinniefalco Aug 27, 2026
f4be3c3
Reject workshop chat close after reasoning-only stream
vinniefalco Aug 27, 2026
08ef3df
Consume only the Drop drag event in the macOS shell
vinniefalco Aug 27, 2026
855a16c
Harden workspace conflict token; limit colon ban to Windows
vinniefalco Aug 28, 2026
dae1b43
Skip empty reasoning synonyms when extracting delta fields
vinniefalco Aug 28, 2026
ccf757a
Fix titlebar maximize/restore glyph swap for SVG elements
vinniefalco Aug 28, 2026
36db544
Add server menu module with workbench snapshot bus
vinniefalco Aug 28, 2026
43496d1
Stream gateway switch-profile stage events over SSE
vinniefalco Aug 28, 2026
3da6951
Populate profile state and reachability from boot and heartbeat
vinniefalco Aug 28, 2026
159a05d
Multiplex concurrent chats on the /ws session loop
vinniefalco Aug 28, 2026
6d81c8c
Carry workbench snapshots and Model-menu events over /ws
vinniefalco Aug 28, 2026
bddd1ff
Route workbench frames and ride aborts on the cancel frame
vinniefalco Aug 28, 2026
fee9d1a
Split model selection into command and server-owned state
vinniefalco Aug 28, 2026
4975c29
Rewire main.ts onto socket-pushed workbench state
vinniefalco Aug 28, 2026
03573c9
Render the profile switching state in the Model menu
vinniefalco Aug 28, 2026
398cc76
Gate sending and the mic on the workbench's chat_ready
vinniefalco Aug 28, 2026
120ffad
Remove the dead HTTP profile routes from the relay
vinniefalco Aug 28, 2026
9b6be7a
Poll chat opens in the session loop instead of awaiting inline
vinniefalco Aug 28, 2026
c6fa3d2
Move menu memory write off the executor and fix review nits
vinniefalco Aug 28, 2026
a0ce39f
Fix Model menu review findings and tighten test observability
vinniefalco Aug 28, 2026
2aa5b27
Populate the catalog and restore the selection on boot
vinniefalco Aug 28, 2026
628894d
Block cross-site requests to the workshop server
vinniefalco Aug 28, 2026
3ed0af9
Atomic file writes with startup orphan sweep
vinniefalco Aug 28, 2026
94c65a7
Deadlines on every route, gateway call, and probe
vinniefalco Aug 28, 2026
b473667
Conform the SSE delta decoder to the aichat transitions
vinniefalco Aug 28, 2026
93fb5ff
Reconnect backoff resets only on useful work
vinniefalco Aug 28, 2026
77d506f
Prove debug asset serving refuses path traversal
vinniefalco Aug 28, 2026
0d420e7
Gateway becomes spawnable behind a GatewayHandle
vinniefalco Aug 28, 2026
7128895
Boot-only [workshop] section in the gateway config
vinniefalco Aug 28, 2026
feaf043
Gateway optionally hosts the workshop in-process
vinniefalco Aug 28, 2026
8f4cc30
Desktop shell boots the merged gateway in-process
vinniefalco Aug 28, 2026
15a3f23
Document workshop hosting and record the merge decisions
vinniefalco Aug 28, 2026
693056f
Fix wedge fixture to pass the cross-site guard
vinniefalco Aug 28, 2026
cb1d9b2
Reconcile module ceilings with the hardening work
vinniefalco Aug 28, 2026
7de294e
Rename the Workbench to the Workshop in the design doc
vinniefalco Aug 28, 2026
9594026
Sweep legacy menu temp orphans in the startup sweep
vinniefalco Aug 28, 2026
cf4bfb5
Pause time in the stalled-route deadline test
vinniefalco Aug 28, 2026
22ae8fb
Share one xorshift and saturate the jitter span arithmetic
vinniefalco Aug 28, 2026
24cb377
Scope the asset traversal parity comment to request names
vinniefalco Aug 28, 2026
b26d581
Name gateway-thread failures and keep panic messages
vinniefalco Aug 28, 2026
f7e003a
Name the differing field in workshop boot mismatches
vinniefalco Aug 28, 2026
bd2815f
Expect the stub's unnecessary_wraps instead of allowing it
vinniefalco Aug 28, 2026
23d9404
Inject the workshop browser opener as a test seam
vinniefalco Aug 28, 2026
eb71a56
Test that shutdown drains the workshop before the gateway
vinniefalco Aug 28, 2026
698cdfc
Read both boot sections in one include pass
vinniefalco Aug 28, 2026
2e35d5d
Test the missing-[workshop] error via a pure function
vinniefalco Aug 28, 2026
9ff9f3a
Document the [server] fields in the gateway README
vinniefalco Aug 28, 2026
41fc0ea
Report a runtime build failure as a thread error
vinniefalco Aug 28, 2026
d55f4af
Cover every panic payload shape in panic_message
vinniefalco Aug 28, 2026
e0616a6
Materialize tar symlink entries as confined file copies
gregjkal Aug 28, 2026
d6ae1b5
Add a Metal backend for workshop voice on macOS
gregjkal Aug 28, 2026
df2e560
Expose microphone capture in the macOS webview
gregjkal Aug 28, 2026
e23bec8
Record the macOS voice bring-up in the changelog
gregjkal Aug 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 15 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,20 @@ jobs:
run: cargo fmt --all --check

- name: Clippy
run: cargo clippy --workspace --exclude promptforge-wb --exclude promptforge-wb-server --all-targets --all-features -- -D warnings
run: cargo clippy --workspace --exclude promptforge-ws --exclude promptforge-ws-server --all-targets --all-features -- -D warnings

- name: Test
run: cargo test --locked --workspace --exclude promptforge-wb --exclude promptforge-wb-server --all-features
run: cargo test --locked --workspace --exclude promptforge-ws --exclude promptforge-ws-server --all-features

- name: Doctests
run: cargo test --workspace --exclude promptforge-wb --exclude promptforge-wb-server --doc
run: cargo test --workspace --exclude promptforge-ws --exclude promptforge-ws-server --doc

- name: Docs
env:
RUSTDOCFLAGS: -D warnings
run: cargo doc --workspace --no-deps --all-features --exclude promptforge-wb --exclude promptforge-wb-server
run: cargo doc --workspace --no-deps --all-features --exclude promptforge-ws --exclude promptforge-ws-server

check-workbench:
check-workshop:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -56,14 +56,17 @@ jobs:
node-version: 22

- name: Install UI dependencies
working-directory: crates/promptforge-wb-server/ui
working-directory: crates/promptforge-ws-server/ui
run: npm ci

- name: Clippy (workbench)
run: cargo clippy -p promptforge-wb -p promptforge-wb-server --all-targets -- -D warnings
# --no-default-features: the desktop crate defaults to the cuda
# feature, and windows-latest has no CUDA toolkit; CI builds the
# CPU-only whisper path instead.
- name: Clippy (workshop)
run: cargo clippy -p promptforge-ws -p promptforge-ws-server --all-targets --no-default-features -- -D warnings

- name: Test (workbench)
run: cargo test --locked -p promptforge-wb -p promptforge-wb-server
- name: Test (workshop)
run: cargo test --locked -p promptforge-ws -p promptforge-ws-server --no-default-features

msrv:
runs-on: ubuntu-latest
Expand All @@ -77,8 +80,8 @@ jobs:

- name: Build and test on MSRV
run: |
cargo build --locked --workspace --exclude promptforge-wb --exclude promptforge-wb-server --all-features
cargo test --locked --workspace --exclude promptforge-wb --exclude promptforge-wb-server --all-features
cargo build --locked --workspace --exclude promptforge-ws --exclude promptforge-ws-server --all-features
cargo test --locked --workspace --exclude promptforge-ws --exclude promptforge-ws-server --all-features

supply-chain:
runs-on: ubuntu-latest
Expand Down
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
/local/
/guide/book/
*.env
# Voice test fixtures, downloaded out of band (see design-promptforge-wb-1.md).
/crates/promptforge-wb-server/tests/fixtures/
# Voice test fixtures, downloaded out of band (see design-promptforge-ws-1.md).
/crates/promptforge-ws-server/tests/fixtures/
# UI build pipeline: npm install target and esbuild output (rebuilt by build.rs).
/crates/promptforge-wb-server/ui/node_modules/
/crates/promptforge-wb-server/ui/dist/
# Workbench tape, written to the cwd when the server runs from the repo root.
/crates/promptforge-ws-server/ui/node_modules/
/crates/promptforge-ws-server/ui/dist/
# Workshop tape, written to the cwd when the server runs from the repo root.
/tape.jsonl
19 changes: 19 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,24 @@ This rule outranks every other rule here. Before you add a frontmatter field, a

- After completing work (compiles + tests pass), update README.md if the public surface changed.
- Every public type, function, and module must have a `///` doc comment. `cargo doc` is the project documentation.
- Each crate's own AGENTS.md binds its subtree. Read the ones governing the paths you touch before writing or reviewing code.
- The existing test suite stays green and intact during refactors: fix forward; never rewrite a test to make it pass.
- Library and serve paths never call `process::exit` or install process-global state; failures return through the spawn handshake.
- Do NOT look at files outside this repo for reference.
- The plan is the spec. Work from the plan and AGENTS.md only.

## Comments

`///` doc comments on public items are mandatory (above) and are not what this section governs. Any other comment earns its place by exactly four things: a non-obvious why, an invisible constraint no type or test enforces, an external-bug workaround, or a subtle ordering requirement. Comments that narrate what the code already says are deleted on sight. A module doc earns its place by documenting the domain, not by restating the file name.

Every platform or external-bug workaround carries its upstream issue URL inline, in the comment that explains it. When the workaround dies, the URL says when it can be buried.

```rust
// wry's drag-drop handler suppresses HTML5 drag events on Windows
// (https://github.com/tauri-apps/tauri/issues/15138), so ...
```

## Verify

- Rust: `cargo test` at the workspace root.
- UI: `npm run typecheck && npm test` in `crates/promptforge-ws-server/ui`.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added

- promptforge-ws / promptforge-gateway / promptforge-ws-server: `metal` / `workshop-metal` / `metal` feature chain building the whisper voice engine with Metal acceleration, making workshop voice available on macOS (`cargo build -p promptforge-ws --no-default-features --features metal`)

### Fixed

- promptforge-gateway: the archive extractor now materializes tar symlink entries as regular-file copies of their targets (confined, chain-following, cycle-rejecting), so the macOS llama.cpp release tarballs, which ship dylibs behind versioned symlink chains, provision instead of failing with an unsafe-entry error
- promptforge-ws: microphone capture works in the macOS webview; the shell builds the webview configuration itself to allow capture on the plain-http loopback origin, and embeds an Info.plist with `NSMicrophoneUsageDescription` in the executable so WKWebView exposes `navigator.mediaDevices`

## [0.1.0] - 2026-08-12

### Added
Expand Down
110 changes: 85 additions & 25 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ repository = "https://github.com/cppalliance/promptforge"

[workspace.dependencies]
promptforge-core = { path = "crates/promptforge-core", version = "0.1.0" }
promptforge-gateway = { path = "crates/promptforge-gateway", version = "0.1.0" }
promptforge-gateway-config = { path = "crates/promptforge-gateway-config", version = "0.1.0" }
promptforge-webfetch = { path = "crates/promptforge-webfetch", version = "0.1.0" }
promptforge-tool-picker = { path = "crates/promptforge-tool-picker", version = "0.1.0" }
promptforge-wb-server = { path = "crates/promptforge-wb-server", version = "0.1.0" }
promptforge-ws-server = { path = "crates/promptforge-ws-server", version = "0.1.0" }
pulldown-cmark = "0.12"
serde = { version = "1", features = ["derive"] }
serde_yaml_ng = "0.10"
Expand All @@ -27,7 +28,10 @@ htmd = "0.5"
mime = "0.3"
encoding_rs = "0.8"
url = "2"
# Percent-decoding for path parameters; already in the tree as url's own core.
percent-encoding = "2"
ipnet = "2"
dunce = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "time", "sync"] }
thiserror = "2"
anyhow = "1"
Expand All @@ -54,7 +58,7 @@ rmcp = { version = "3.1.0", features = [
] }
tower = { version = "0.5", features = ["util"] }
arc-swap = "1"
# Serves the workbench UI: reads ui/dist from disk in debug builds, embeds it
# Serves the workshop UI: reads ui/dist from disk in debug builds, embeds it
# into the binary in release builds.
rust-embed = "8"
glob = "0.3"
Expand Down Expand Up @@ -93,6 +97,8 @@ wry = "0.56"
tao = "0.36"
# Opens a URL in the system browser for the windowless server frame.
open = "5"
# Decodes the bundled program-icon PNG into RGBA for the tao window icon.
png = "0.18"

[workspace.lints.rust]
unsafe_code = "forbid"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

A runtime that executes AI prompt pipelines defined in a single markdown file. The markdown is the program, the model is the CPU. YAML frontmatter for metadata, embedded Lua for logic, prose blocks for model instructions, and a credential-holding gateway that keeps vendor keys off the prompt process. Write a prompt, run it, get a result.

![Workbench](images/banner-01.png)
![Workshop](images/banner-01.png)

## What you get

Expand Down Expand Up @@ -112,8 +112,8 @@ flowchart LR
| [promptforge-tool-picker](crates/promptforge-tool-picker) | Semantic tool resolution via sentence embeddings | [![Crates.io](https://img.shields.io/crates/v/promptforge-tool-picker.svg)](https://crates.io/crates/promptforge-tool-picker) |
| [promptforge-webfetch](crates/promptforge-webfetch) | SSRF-safe web fetch tool for model-supplied URLs | [![Crates.io](https://img.shields.io/crates/v/promptforge-webfetch.svg)](https://crates.io/crates/promptforge-webfetch) |
| [promptforge-dev](crates/promptforge-dev) | Interactive prompt development with watch mode | [![Crates.io](https://img.shields.io/crates/v/promptforge-dev.svg)](https://crates.io/crates/promptforge-dev) |
| [promptforge-wb-server](crates/promptforge-wb-server) | Workbench HTTP server: chat relay, session tape, voice transcription | not published |
| [promptforge-wb](crates/promptforge-wb) | Workbench desktop window shell (wry/tao) | not published |
| [promptforge-ws-server](crates/promptforge-ws-server) | Workshop HTTP server: chat relay, session tape, voice transcription | not published |
| [promptforge-ws](crates/promptforge-ws) | Workshop desktop window shell (wry/tao) | not published |

## Documentation

Expand Down
7 changes: 7 additions & 0 deletions crates/promptforge-gateway-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ What it provides:
including file, with cycle detection), keyed-array merging by `id`/`name`,
and the [`ProfileName`](src/profile/name.rs) confinement type that keeps a
profile selection inside the profiles directory.
- The boot-only `[workshop]` section ([`WorkshopConfig`](src/config/workshop.rs)):
the hosted workshop UI's bind and optional voice/tape sub-tables, with
tape-path anchoring against the boot-config directory, a loopback-adjusted
client URL derived from `[server]` (`ServerConfig::client_url`), and
`load_workshop` for reading the section without full validation.
`load_boot_sections` reads `[server]` and `[workshop]` together in a
single include-resolution pass.
- [`Secret`](src/config.rs): a credential wrapper that redacts in `Debug` and
`Display` and never serializes.
- [`ConfigError`](src/api_error.rs): an opaque, source-preserving error type;
Expand Down
Loading
Loading