stagent 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,49 +1,38 @@
1
1
  # Stagent
2
2
 
3
- > A governed AI agent operations workspace for running, supervising, and reusing AI work through projects, workflows, documents, profiles, schedules, inbox approvals, and live monitoring.
4
-
5
- [![Download macOS Desktop](https://img.shields.io/badge/Download-macOS_Desktop-0a7cff?style=for-the-badge&logo=apple)](https://github.com/navam-io/stagent/releases/latest/download/Stagent.dmg) [![GitHub Releases](https://img.shields.io/github/v/release/navam-io/stagent?display_name=release&style=for-the-badge)](https://github.com/navam-io/stagent/releases/latest)
3
+ > Govern Your AI Agents. Operate With Oversight.
6
4
 
7
5
  [![Next.js 16](https://img.shields.io/badge/Next.js-16-black)](https://nextjs.org/) [![React 19](https://img.shields.io/badge/React-19-61DAFB)](https://react.dev/) [![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178C6)](https://www.typescriptlang.org/) [![Claude Agent SDK](https://img.shields.io/badge/Claude-Agent_SDK-D97706)](https://docs.anthropic.com/) [![OpenAI Codex App Server](https://img.shields.io/badge/OpenAI-Codex_App_Server-10A37F)](https://developers.openai.com/codex/app-server) [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
8
6
 
9
- ## Why Stagent
7
+ ## Quick Start
10
8
 
11
- AI agents are powerful, but production deployment breaks down when teams cannot see what the agent is doing, understand which rules it is following, or intervene before an unsafe action lands. Stagent solves that operating problem.
9
+ ```bash
10
+ npx stagent
11
+ ```
12
12
 
13
- Stagent is a local-first AI operations workspace built around governed execution and reusable automation primitives. Instead of treating every agent run as a one-off prompt, it gives teams a structured system of home workspace signals, execution dashboards, project context, workflow blueprints, reusable profiles, schedules, documents, inbox approvals, and live monitoring.
13
+ Open [localhost:3000](http://localhost:3000).
14
14
 
15
- ## Get Started
15
+ **Profiles & Policies** · **Blueprints & Schedules** · **Open Source**
16
16
 
17
- ### Download Desktop for macOS
17
+ <img src="https://raw.githubusercontent.com/navam-io/stagent/main/public/readme/home-workspace.png" alt="Stagent home workspace" width="1200" />
18
18
 
19
- **Primary install path:** [Download the latest macOS desktop release](https://github.com/navam-io/stagent/releases/latest/download/Stagent.dmg)
19
+ | Home Workspace | Reusable Profiles | Workflow Blueprints | Governed Execution |
20
+ |:-:|:-:|:-:|:-:|
21
+ | Workspace briefing with active work, pending review, project signals, and live activity | Specialist definitions with prompts, tool policy, and runtime tuning you can reuse | Pre-configured templates with dynamic forms, YAML editing, and lineage tracking | Human-in-the-loop approvals, tool permissions, and ambient supervision |
20
22
 
21
- Grab the latest `Stagent.dmg` or `Stagent.app.zip` asset from GitHub Releases, then move `Stagent.app` into `/Applications`.
23
+ ---
22
24
 
23
- Current desktop release notes:
25
+ ## Why Stagent
24
26
 
25
- - macOS-only for now
26
- - GitHub desktop releases are intended to be Developer ID signed and notarized; local smoke builds created with `npm run desktop:release -- --skip-upload` will still warn if Apple credentials are not configured
27
- - the current desktop wrapper expects `node` to be available on the machine
27
+ AI agents are powerful — but production use breaks down when teams cannot see what the agent is doing, which rules it follows, or intervene before an unsafe action lands. Stagent gives you a governed operations workspace where every run is visible, every profile is reusable, and every approval is auditable. Run it locally with `npx stagent` and own your data from day one.
28
28
 
29
- <img src="./output/playwright/home-playwright-after.png" alt="Stagent home workspace" width="1200" />
29
+ ---
30
30
 
31
- ### Repository development
31
+ ## Runtime Bridge
32
32
 
33
- ```bash
34
- git clone <repo-url> && cd stagent && npm install
33
+ Stagent ships a shared runtime registry that routes tasks, schedules, and workflow steps through two governed execution backends: **Claude Code** (Anthropic Claude Agent SDK) and **OpenAI Codex App Server**. Both land in the same inbox, monitoring, and task-state surfaces — so switching providers is a config change, not a rewrite.
35
34
 
36
- # Set up one or both runtime credentials
37
- cat > .env.local <<'EOF'
38
- ANTHROPIC_API_KEY=your-anthropic-key
39
- OPENAI_API_KEY=your-openai-key
40
- EOF
41
-
42
- # Start the dev server
43
- npm run dev
44
- ```
45
-
46
- Open [localhost:3000](http://localhost:3000) to get started.
35
+ ---
47
36
 
48
37
  ## Feature Highlights
49
38
 
@@ -69,33 +58,7 @@ Open [localhost:3000](http://localhost:3000) to get started.
69
58
 
70
59
  ## Architecture
71
60
 
72
- ```
73
- ┌─────────────────────────────────────────────────────────┐
74
- │ Browser (React 19) │
75
- │ Home · Dashboard · Projects · Documents · Workflows │
76
- │ Profiles · Schedules · Inbox · Monitor · Settings │
77
- └──────────────┬──────────────────────┬────────────────────┘
78
- │ Server Components │ API Routes
79
- │ (direct DB queries) │ (mutations only)
80
- ┌──────────────▼──────────────────────▼────────────────────┐
81
- │ Next.js 16 Server │
82
- │ │
83
- │ ┌─────────────┐ ┌──────────────┐ ┌────────────────┐ │
84
- │ │ Drizzle ORM │ │ Runtime │ │ SSE Stream │ │
85
- │ │ (SQLite) │ │ Registry │ │ (Logs) │ │
86
- │ └──────┬──────┘ └──────┬───────┘ └─────┬──────────┘ │
87
- │ │ │ │ │
88
- │ ┌──────┴──────┐ ┌─────┴────────────┐ ┌─┴───────────┐ │
89
- │ │ Scheduler │ │ Claude + OpenAI │ │ Permission │ │
90
- │ │ Engine │ │ runtime adapters │ │ Checker │ │
91
- │ └─────────────┘ └──────────────────┘ └─────────────┘ │
92
- └──────────┬────────────────┬─────────────────┬────────────┘
93
- │ │ │
94
- ┌──────▼──────┐ ┌─────▼─────────────┐ ┌─────▼──────┐
95
- │ ~/.stagent/ │ │ Anthropic / OpenAI│ │ Browser │
96
- │ stagent.db │ │ runtime backends │ │ EventSource│
97
- └─────────────┘ └───────────────────┘ └────────────┘
98
- ```
61
+ <img src="https://raw.githubusercontent.com/navam-io/stagent/main/public/readme/architecture.svg" alt="Stagent architecture diagram" width="900" />
99
62
 
100
63
  **Key design decisions:**
101
64
 
@@ -125,7 +88,7 @@ Create and organize projects as containers for related tasks. Each project can s
125
88
  ### Agent
126
89
 
127
90
  #### Provider Runtimes
128
- Stagent now supports two governed execution runtimes behind a shared runtime registry:
91
+ Stagent supports two governed execution runtimes behind a shared runtime registry:
129
92
  - **Claude Code** via the Anthropic Claude Agent SDK
130
93
  - **OpenAI Codex App Server** via `codex app-server`
131
94
 
@@ -149,7 +112,7 @@ Multi-step task orchestration with three patterns:
149
112
  State machine engine with step-level retry, project association, and real-time status visualization.
150
113
 
151
114
  #### Parallel + Swarm Workflows
152
- Stagent now supports two bounded expansion patterns on top of the workflow engine:
115
+ Stagent supports two bounded expansion patterns on top of the workflow engine:
153
116
  - **Parallel research fork/join** — 2-5 concurrent branches followed by one synthesis step
154
117
  - **Swarm orchestration** — mayor → worker pool → refinery with retryable stages and configurable worker concurrency
155
118
 
@@ -170,7 +133,7 @@ Curated agent profiles across work and personal domains, built as portable Claud
170
133
  #### Workflow Blueprints
171
134
  Pre-configured workflow templates across work and personal domains. Browse blueprints in a gallery with filtering and search, preview steps and required variables, fill in a dynamic form, and create draft workflows with resolved prompts and profile assignments. Create custom blueprints via YAML or import from GitHub URLs. Lineage tracking connects workflows back to their source blueprint.
172
135
 
173
- <img src="https://unpkg.com/stagent@latest/public/readme/workflow-blueprints.png" alt="Stagent workflow blueprint gallery" width="1200" />
136
+ <img src="https://raw.githubusercontent.com/navam-io/stagent/main/public/readme/workflow-blueprints.png" alt="Stagent workflow blueprint gallery" width="1200" />
174
137
 
175
138
  ### Documents
176
139
 
@@ -205,7 +168,7 @@ Provider-normalized metering tracks token and spend activity across tasks, resum
205
168
  #### Inbox & Human-in-the-Loop
206
169
  When an agent needs approval or input, a notification appears in your inbox. Review tool permission requests with "Allow Once" / "Always Allow" / "Deny" buttons, answer agent questions, and see task completion summaries. Supports bulk dismiss and 10s polling.
207
170
 
208
- <img src="https://unpkg.com/stagent@latest/public/readme/inbox-approvals.png" alt="Stagent inbox approval flow" width="1200" />
171
+ <img src="https://raw.githubusercontent.com/navam-io/stagent/main/public/readme/inbox-approvals.png" alt="Stagent inbox approval flow" width="1200" />
209
172
 
210
173
  #### Monitoring
211
174
  Real-time agent log streaming via Server-Sent Events. Filter by task or event type, click entries to jump to task details, and auto-pause polling when the tab is hidden (Page Visibility API).
@@ -217,10 +180,7 @@ File upload with drag-and-drop in task creation. Type-aware content preview for
217
180
  Configuration hub with provider-aware sections: Claude authentication (API key or OAuth), OpenAI Codex runtime API-key management, tool permissions (saved "Always Allow" patterns with revoke), and data management.
218
181
 
219
182
  #### CLI
220
- Stagent still uses a small Node sidecar under the hood. It is built from `bin/cli.ts` into `dist/cli.js`, but it is now an internal desktop bootstrap rather than a user-facing distribution channel.
221
-
222
- #### Tauri Desktop
223
- The repo produces macOS desktop artifacts via Tauri. Local development uses `npm run desktop:dev`, local packaging uses `npm run desktop:build`, and release publishing now runs locally via `npm run desktop:release` so the uploaded GitHub assets stay on stable names: `Stagent.dmg` and `Stagent.app.zip`. Published releases must be built with `APPLE_SIGNING_IDENTITY` plus notarization credentials so the downloaded app clears Gatekeeper without the "Apple could not verify" malware warning. Keep `public/desktop-icon-512.png` as the dedicated rounded desktop source icon with transparent corners; `public/icon-512.png` remains the square web/PWA icon. The local release script also stamps the same branded icon onto the mounted DMG volume so Finder shows the corrected Stagent icon during install.
183
+ The `npx stagent` entry point boots a Next.js server from the published npm package. It is built from `bin/cli.ts` into `dist/cli.js` using tsup, and serves as the primary distribution channel no clone required.
224
184
 
225
185
  #### Database
226
186
  SQLite with WAL mode via better-sqlite3 + Drizzle ORM. Eight tables: `projects`, `tasks`, `workflows`, `agent_logs`, `notifications`, `documents`, `schedules`, `settings`. Self-healing bootstrap — tables are created on startup if missing.
@@ -257,29 +217,6 @@ npm test # Run Vitest
257
217
  npm run test:coverage # Coverage report
258
218
  ```
259
219
 
260
- ## Desktop Release Checklist
261
-
262
- ```bash
263
- npm run desktop:release
264
- ```
265
-
266
- For a build-only smoke check, run `npm run desktop:release -- --skip-upload`.
267
-
268
- Before publishing a real desktop release, configure Apple signing once on the release machine:
269
-
270
- ```bash
271
- export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (TEAMID)"
272
- xcrun notarytool store-credentials stagent-notary \
273
- --apple-id "you@example.com" \
274
- --team-id "TEAMID" \
275
- --password "app-specific-password"
276
- export APPLE_NOTARY_PROFILE="stagent-notary"
277
- ```
278
-
279
- You can use direct credentials instead of a keychain profile by exporting `APPLE_ID`, `APPLE_APP_SPECIFIC_PASSWORD`, and `APPLE_TEAM_ID`.
280
-
281
- The release script builds locally on macOS, signs the app, notarizes and staples the app bundle and DMG when Apple credentials are configured, verifies the DMG, creates or updates the `desktop-v<package.json version>` GitHub release, uploads `Stagent.dmg` and `Stagent.app.zip`, and refreshes the stable download URL at `https://github.com/navam-io/stagent/releases/latest/download/Stagent.dmg`. Uploads now fail fast unless Developer ID signing and notarization are configured so unsigned artifacts are not published by accident.
282
-
283
220
  ### Project Structure
284
221
 
285
222
  ```
@@ -383,19 +320,35 @@ All 14 features shipped across three layers:
383
320
  | **Agent Self-Improvement** | Agents learn patterns and update context with human approval |
384
321
  | **Document Output Generation** | Agent-generated documents as deliverables |
385
322
  | **Parallel Workflows** | Concurrent step execution within workflows |
386
- | **Tauri Desktop** | Native desktop app packaging |
387
323
 
388
324
  ---
389
325
 
390
326
  ## Contributing
391
327
 
328
+ ### Contributor Setup
329
+
330
+ ```bash
331
+ git clone <repo-url> && cd stagent && npm install
332
+
333
+ # Set up one or both runtime credentials
334
+ cat > .env.local <<'EOF'
335
+ ANTHROPIC_API_KEY=your-anthropic-key
336
+ OPENAI_API_KEY=your-openai-key
337
+ EOF
338
+
339
+ # Start the dev server
340
+ npm run dev
341
+ ```
342
+
343
+ ### Pull Requests
344
+
392
345
  1. Fork the repository
393
346
  2. Create a feature branch (`git checkout -b feature/your-feature`)
394
347
  3. Make your changes and add tests
395
348
  4. Run `npm test` and `npx tsc --noEmit`
396
349
  5. Submit a pull request
397
350
 
398
- See `CLAUDE.md` for architecture details and development conventions.
351
+ See `AGENTS.md` for architecture details and development conventions.
399
352
 
400
353
  ## License
401
354
 
package/dist/cli.js CHANGED
@@ -361,15 +361,15 @@ Data:
361
361
  Logs ${join3(DATA_DIR, "logs")}
362
362
 
363
363
  Environment variables:
364
- STAGENT_DATA_DIR Custom data directory for the desktop sidecar and web app
364
+ STAGENT_DATA_DIR Custom data directory for the web app
365
365
  ANTHROPIC_API_KEY Claude runtime access
366
366
  OPENAI_API_KEY OpenAI Codex runtime access
367
367
 
368
368
  Examples:
369
369
  node dist/cli.js --port 3210 --no-open
370
- STAGENT_DATA_DIR=/tmp/stagent-desktop node dist/cli.js --reset
370
+ STAGENT_DATA_DIR=/tmp/stagent node dist/cli.js --reset
371
371
  `;
372
- program.name("stagent").description("Governed desktop AI agent workspace").version(pkg.version).addHelpText("after", HELP_TEXT).option("-p, --port <number>", "port to start on", "3000").option("--reset", "delete the local database before starting").option("--no-open", "don't auto-open browser").parse();
372
+ program.name("stagent").description("Governed AI agent workspace").version(pkg.version).addHelpText("after", HELP_TEXT).option("-p, --port <number>", "port to start on", "3000").option("--reset", "delete the local database before starting").option("--no-open", "don't auto-open browser").parse();
373
373
  var opts = program.opts();
374
374
  var requestedPort = Number.parseInt(opts.port, 10);
375
375
  if (Number.isNaN(requestedPort) || requestedPort <= 0) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "stagent",
3
- "version": "0.1.4",
4
- "description": "Governed desktop AI agent workspace for supervised local execution, workflows, documents, and provider runtimes.",
3
+ "version": "0.1.6",
4
+ "description": "Governed AI agent workspace for supervised local execution, workflows, documents, and provider runtimes.",
5
5
  "keywords": [
6
6
  "ai",
7
7
  "agents",
@@ -40,12 +40,6 @@
40
40
  "dev": "next dev --turbopack",
41
41
  "build": "next build",
42
42
  "build:cli": "tsup",
43
- "desktop:dev": "node scripts/tauri.mjs dev",
44
- "desktop:build": "node scripts/tauri.mjs build",
45
- "desktop:smoke": "node scripts/desktop-sidecar-smoke.mjs",
46
- "desktop:smoke:dmg": "node scripts/desktop-mounted-dmg-smoke.mjs",
47
- "desktop:icon": "node scripts/tauri.mjs icon",
48
- "desktop:release": "node scripts/release-desktop.mjs",
49
43
  "test": "vitest run",
50
44
  "test:watch": "vitest",
51
45
  "test:coverage": "vitest run --coverage",
@@ -0,0 +1,192 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 900 520" font-family="system-ui, -apple-system, 'Segoe UI', sans-serif">
2
+ <defs>
3
+ <linearGradient id="bgGrad" x1="0" y1="0" x2="0" y2="1">
4
+ <stop offset="0%" stop-color="#0F172A"/>
5
+ <stop offset="100%" stop-color="#1E293B"/>
6
+ </linearGradient>
7
+ <linearGradient id="browserGrad" x1="0" y1="0" x2="1" y2="1">
8
+ <stop offset="0%" stop-color="#2563EB"/>
9
+ <stop offset="100%" stop-color="#22D3EE"/>
10
+ </linearGradient>
11
+ <linearGradient id="serverGrad" x1="0" y1="0" x2="1" y2="1">
12
+ <stop offset="0%" stop-color="#1E293B"/>
13
+ <stop offset="100%" stop-color="#334155"/>
14
+ </linearGradient>
15
+ <linearGradient id="externalGrad" x1="0" y1="0" x2="1" y2="1">
16
+ <stop offset="0%" stop-color="#7C3AED"/>
17
+ <stop offset="100%" stop-color="#A78BFA"/>
18
+ </linearGradient>
19
+ <filter id="glow">
20
+ <feGaussianBlur stdDeviation="2" result="blur"/>
21
+ <feMerge><feMergeNode in="blur"/><feMergeNode in="SourceGraphic"/></feMerge>
22
+ </filter>
23
+ <filter id="shadow">
24
+ <feDropShadow dx="0" dy="2" stdDeviation="4" flood-color="#000" flood-opacity="0.3"/>
25
+ </filter>
26
+ </defs>
27
+
28
+ <!-- Background -->
29
+ <rect width="900" height="520" rx="16" fill="url(#bgGrad)"/>
30
+
31
+ <!-- Title -->
32
+ <text x="450" y="36" text-anchor="middle" fill="#94A3B8" font-size="12" font-weight="500" letter-spacing="2">STAGENT ARCHITECTURE</text>
33
+
34
+ <!-- ═══════════ BROWSER LAYER ═══════════ -->
35
+ <rect x="40" y="52" width="820" height="78" rx="12" fill="none" stroke="url(#browserGrad)" stroke-width="1.5" opacity="0.8" filter="url(#shadow)"/>
36
+ <rect x="40" y="52" width="820" height="78" rx="12" fill="#0F172A" opacity="0.6"/>
37
+
38
+ <text x="64" y="74" fill="#22D3EE" font-size="11" font-weight="700" letter-spacing="1.5">BROWSER</text>
39
+ <text x="152" y="74" fill="#475569" font-size="10">React 19</text>
40
+
41
+ <!-- Route pills -->
42
+ <rect x="64" y="86" width="60" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
43
+ <text x="94" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Home</text>
44
+
45
+ <rect x="132" y="86" width="76" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
46
+ <text x="170" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Dashboard</text>
47
+
48
+ <rect x="216" y="86" width="68" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
49
+ <text x="250" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Projects</text>
50
+
51
+ <rect x="292" y="86" width="56" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
52
+ <text x="320" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Docs</text>
53
+
54
+ <rect x="356" y="86" width="76" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
55
+ <text x="394" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Workflows</text>
56
+
57
+ <rect x="440" y="86" width="64" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
58
+ <text x="472" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Profiles</text>
59
+
60
+ <rect x="512" y="86" width="76" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
61
+ <text x="550" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Schedules</text>
62
+
63
+ <rect x="596" y="86" width="52" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
64
+ <text x="622" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Inbox</text>
65
+
66
+ <rect x="656" y="86" width="66" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
67
+ <text x="689" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Monitor</text>
68
+
69
+ <rect x="730" y="86" width="68" height="24" rx="12" fill="#2563EB" opacity="0.2"/>
70
+ <text x="764" y="102" text-anchor="middle" fill="#60A5FA" font-size="10" font-weight="500">Settings</text>
71
+
72
+ <!-- Connection lines from browser to server -->
73
+ <line x1="300" y1="130" x2="300" y2="162" stroke="#22D3EE" stroke-width="1" opacity="0.4"/>
74
+ <line x1="600" y1="130" x2="600" y2="162" stroke="#22D3EE" stroke-width="1" opacity="0.4"/>
75
+
76
+ <!-- Connection labels -->
77
+ <text x="240" y="150" fill="#475569" font-size="9">Server Components</text>
78
+ <text x="237" y="159" fill="#334155" font-size="8">(direct DB queries)</text>
79
+ <text x="562" y="150" fill="#475569" font-size="9">API Routes</text>
80
+ <text x="550" y="159" fill="#334155" font-size="8">(mutations only)</text>
81
+
82
+ <!-- ═══════════ SERVER LAYER ═══════════ -->
83
+ <rect x="40" y="164" width="820" height="200" rx="12" fill="url(#serverGrad)" opacity="0.5" filter="url(#shadow)"/>
84
+ <rect x="40" y="164" width="820" height="200" rx="12" fill="none" stroke="#334155" stroke-width="1"/>
85
+
86
+ <text x="64" y="186" fill="#F8FAFC" font-size="11" font-weight="700" letter-spacing="1.5">NEXT.JS 16 SERVER</text>
87
+
88
+ <!-- Row 1: Core services -->
89
+ <!-- Drizzle ORM -->
90
+ <rect x="64" y="200" width="200" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
91
+ <rect x="64" y="200" width="4" height="60" rx="2" fill="#22D3EE"/>
92
+ <text x="80" y="222" fill="#F1F5F9" font-size="12" font-weight="600">Drizzle ORM</text>
93
+ <text x="80" y="240" fill="#64748B" font-size="10">SQLite · WAL mode</text>
94
+ <text x="80" y="252" fill="#64748B" font-size="10">8 tables · Self-healing bootstrap</text>
95
+
96
+ <!-- Runtime Registry -->
97
+ <rect x="280" y="200" width="220" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
98
+ <rect x="280" y="200" width="4" height="60" rx="2" fill="#2563EB"/>
99
+ <text x="296" y="222" fill="#F1F5F9" font-size="12" font-weight="600">Runtime Registry</text>
100
+ <text x="296" y="240" fill="#64748B" font-size="10">Provider abstraction layer</text>
101
+ <text x="296" y="252" fill="#64748B" font-size="10">Claude + OpenAI adapters</text>
102
+
103
+ <!-- SSE Stream -->
104
+ <rect x="516" y="200" width="160" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
105
+ <rect x="516" y="200" width="4" height="60" rx="2" fill="#7C3AED"/>
106
+ <text x="532" y="222" fill="#F1F5F9" font-size="12" font-weight="600">SSE Stream</text>
107
+ <text x="532" y="240" fill="#64748B" font-size="10">Real-time log push</text>
108
+ <text x="532" y="252" fill="#64748B" font-size="10">/api/logs/stream</text>
109
+
110
+ <!-- Profile Engine -->
111
+ <rect x="692" y="200" width="152" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
112
+ <rect x="692" y="200" width="4" height="60" rx="2" fill="#F59E0B"/>
113
+ <text x="708" y="222" fill="#F1F5F9" font-size="12" font-weight="600">Profile Engine</text>
114
+ <text x="708" y="240" fill="#64748B" font-size="10">13 agent profiles</text>
115
+ <text x="708" y="252" fill="#64748B" font-size="10">MCP · Tools · Tuning</text>
116
+
117
+ <!-- Row 2: Engines -->
118
+ <!-- Scheduler Engine -->
119
+ <rect x="64" y="276" width="200" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
120
+ <rect x="64" y="276" width="4" height="60" rx="2" fill="#10B981"/>
121
+ <text x="80" y="298" fill="#F1F5F9" font-size="12" font-weight="600">Scheduler Engine</text>
122
+ <text x="80" y="316" fill="#64748B" font-size="10">Cron + intervals · Poll-based</text>
123
+ <text x="80" y="328" fill="#64748B" font-size="10">One-shot &amp; recurring</text>
124
+
125
+ <!-- Workflow Engine -->
126
+ <rect x="280" y="276" width="220" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
127
+ <rect x="280" y="276" width="4" height="60" rx="2" fill="#EC4899"/>
128
+ <text x="296" y="298" fill="#F1F5F9" font-size="12" font-weight="600">Workflow Engine</text>
129
+ <text x="296" y="316" fill="#64748B" font-size="10">Sequence · Planner-Executor</text>
130
+ <text x="296" y="328" fill="#64748B" font-size="10">Parallel · Swarm · Checkpoints</text>
131
+
132
+ <!-- Permission Checker -->
133
+ <rect x="516" y="276" width="160" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
134
+ <rect x="516" y="276" width="4" height="60" rx="2" fill="#EF4444"/>
135
+ <text x="532" y="298" fill="#F1F5F9" font-size="12" font-weight="600">Permission Gate</text>
136
+ <text x="532" y="316" fill="#64748B" font-size="10">Always Allow patterns</text>
137
+ <text x="532" y="328" fill="#64748B" font-size="10">canUseTool polling</text>
138
+
139
+ <!-- Document Processor -->
140
+ <rect x="692" y="276" width="152" height="60" rx="8" fill="#1E293B" stroke="#334155" stroke-width="1"/>
141
+ <rect x="692" y="276" width="4" height="60" rx="2" fill="#06B6D4"/>
142
+ <text x="708" y="298" fill="#F1F5F9" font-size="12" font-weight="600">Doc Processor</text>
143
+ <text x="708" y="316" fill="#64748B" font-size="10">5-format extraction</text>
144
+ <text x="708" y="328" fill="#64748B" font-size="10">Context injection</text>
145
+
146
+ <!-- Connection lines from server to externals -->
147
+ <line x1="164" y1="364" x2="164" y2="404" stroke="#22D3EE" stroke-width="1" opacity="0.4"/>
148
+ <line x1="450" y1="364" x2="450" y2="404" stroke="#2563EB" stroke-width="1" opacity="0.4"/>
149
+ <line x1="736" y1="364" x2="736" y2="404" stroke="#7C3AED" stroke-width="1" opacity="0.4"/>
150
+
151
+ <!-- ═══════════ EXTERNAL LAYER ═══════════ -->
152
+ <!-- Database -->
153
+ <rect x="64" y="408" width="200" height="72" rx="12" fill="none" stroke="#22D3EE" stroke-width="1" opacity="0.6"/>
154
+ <rect x="64" y="408" width="200" height="72" rx="12" fill="#0F172A" opacity="0.8"/>
155
+ <!-- Database icon -->
156
+ <ellipse cx="100" cy="432" rx="12" ry="6" fill="none" stroke="#22D3EE" stroke-width="1.2"/>
157
+ <line x1="88" y1="432" x2="88" y2="446" stroke="#22D3EE" stroke-width="1.2"/>
158
+ <line x1="112" y1="432" x2="112" y2="446" stroke="#22D3EE" stroke-width="1.2"/>
159
+ <ellipse cx="100" cy="446" rx="12" ry="6" fill="none" stroke="#22D3EE" stroke-width="1.2"/>
160
+ <text x="122" y="436" fill="#F1F5F9" font-size="12" font-weight="600">~/.stagent/</text>
161
+ <text x="122" y="452" fill="#64748B" font-size="10">stagent.db</text>
162
+ <text x="80" y="472" fill="#475569" font-size="9">SQLite · WAL · better-sqlite3</text>
163
+
164
+ <!-- Runtime Backends -->
165
+ <rect x="300" y="408" width="300" height="72" rx="12" fill="none" stroke="#2563EB" stroke-width="1" opacity="0.6"/>
166
+ <rect x="300" y="408" width="300" height="72" rx="12" fill="#0F172A" opacity="0.8"/>
167
+ <!-- Anthropic -->
168
+ <circle cx="332" cy="436" r="10" fill="none" stroke="#D97706" stroke-width="1.2"/>
169
+ <text x="332" y="440" text-anchor="middle" fill="#D97706" font-size="10" font-weight="700">A</text>
170
+ <text x="350" y="436" fill="#F1F5F9" font-size="11" font-weight="600">Anthropic</text>
171
+ <text x="350" y="450" fill="#64748B" font-size="9">Claude Agent SDK</text>
172
+ <!-- OpenAI -->
173
+ <circle cx="482" cy="436" r="10" fill="none" stroke="#10A37F" stroke-width="1.2"/>
174
+ <text x="482" y="440" text-anchor="middle" fill="#10A37F" font-size="10" font-weight="700">O</text>
175
+ <text x="500" y="436" fill="#F1F5F9" font-size="11" font-weight="600">OpenAI</text>
176
+ <text x="500" y="450" fill="#64748B" font-size="9">Codex App Server</text>
177
+ <text x="370" y="472" fill="#475569" font-size="9">Governed runtime backends</text>
178
+
179
+ <!-- Browser EventSource -->
180
+ <rect x="636" y="408" width="220" height="72" rx="12" fill="none" stroke="#7C3AED" stroke-width="1" opacity="0.6"/>
181
+ <rect x="636" y="408" width="220" height="72" rx="12" fill="#0F172A" opacity="0.8"/>
182
+ <!-- Signal icon -->
183
+ <circle cx="668" cy="436" r="3" fill="#7C3AED"/>
184
+ <path d="M656 424 A16 16 0 0 1 680 424" fill="none" stroke="#7C3AED" stroke-width="1" opacity="0.5"/>
185
+ <path d="M652 418 A22 22 0 0 1 684 418" fill="none" stroke="#7C3AED" stroke-width="1" opacity="0.3"/>
186
+ <text x="688" y="436" fill="#F1F5F9" font-size="12" font-weight="600">EventSource</text>
187
+ <text x="688" y="452" fill="#64748B" font-size="10">Browser SSE client</text>
188
+ <text x="656" y="472" fill="#475569" font-size="9">Real-time log streaming</text>
189
+
190
+ <!-- Footer -->
191
+ <text x="450" y="508" text-anchor="middle" fill="#334155" font-size="10">Local-first · Zero external dependencies · Your data stays on your machine</text>
192
+ </svg>
Binary file
Binary file
@@ -17,7 +17,7 @@ export const APPROVAL_ACTION_IDS = [
17
17
  ] as const;
18
18
 
19
19
  export type ApprovalActionId = (typeof APPROVAL_ACTION_IDS)[number];
20
- export type NotificationChannelId = "in_app" | "browser" | "tauri";
20
+ export type NotificationChannelId = "in_app" | "browser";
21
21
 
22
22
  export interface ActionableNotificationPayload {
23
23
  notificationId: string;
Binary file
@@ -1,138 +0,0 @@
1
- type TauriDialogFilter = {
2
- name: string;
3
- extensions: string[];
4
- };
5
-
6
- type TauriDialogOptions = {
7
- directory?: boolean;
8
- multiple?: boolean;
9
- filters?: TauriDialogFilter[];
10
- };
11
-
12
- type TauriNotificationPayload = {
13
- title: string;
14
- body?: string;
15
- };
16
-
17
- type TauriNotificationApi = {
18
- isPermissionGranted?: () => Promise<boolean>;
19
- requestPermission?: () => Promise<boolean | "granted" | "denied" | "default">;
20
- sendNotification?: (
21
- payload: string | TauriNotificationPayload
22
- ) => Promise<void> | void;
23
- };
24
-
25
- type TauriDialogApi = {
26
- open?: (options?: TauriDialogOptions) => Promise<unknown>;
27
- };
28
-
29
- declare global {
30
- interface Window {
31
- __TAURI__?: {
32
- dialog?: TauriDialogApi;
33
- notification?: TauriNotificationApi;
34
- };
35
- }
36
- }
37
-
38
- function getTauriApi() {
39
- if (typeof window === "undefined") return null;
40
- return window.__TAURI__ ?? null;
41
- }
42
-
43
- export const isTauri = () => Boolean(getTauriApi());
44
-
45
- export async function showNativeNotification(
46
- title: string,
47
- body?: string
48
- ): Promise<boolean> {
49
- const tauriApi = getTauriApi();
50
-
51
- if (tauriApi?.notification?.sendNotification) {
52
- const permissionCheck = tauriApi.notification.isPermissionGranted;
53
- const requestPermission = tauriApi.notification.requestPermission;
54
-
55
- let granted = true;
56
- if (permissionCheck) {
57
- granted = await permissionCheck();
58
- }
59
-
60
- if (!granted && requestPermission) {
61
- const result = await requestPermission();
62
- granted = result === true || result === "granted";
63
- }
64
-
65
- if (granted) {
66
- await tauriApi.notification.sendNotification({ title, body });
67
- return true;
68
- }
69
-
70
- return false;
71
- }
72
-
73
- if (typeof window === "undefined" || typeof Notification === "undefined") {
74
- return false;
75
- }
76
-
77
- if (Notification.permission === "granted") {
78
- new Notification(title, body ? { body } : undefined);
79
- return true;
80
- }
81
-
82
- if (Notification.permission !== "denied") {
83
- const permission = await Notification.requestPermission();
84
- if (permission === "granted") {
85
- new Notification(title, body ? { body } : undefined);
86
- return true;
87
- }
88
- }
89
-
90
- return false;
91
- }
92
-
93
- export async function selectFile(options?: TauriDialogOptions): Promise<
94
- string | string[] | null
95
- > {
96
- const tauriApi = getTauriApi();
97
-
98
- if (tauriApi?.dialog?.open) {
99
- const result = await tauriApi.dialog.open(options);
100
- if (Array.isArray(result)) {
101
- return result.filter((value): value is string => typeof value === "string");
102
- }
103
- return typeof result === "string" ? result : null;
104
- }
105
-
106
- if (typeof window === "undefined" || options?.directory) {
107
- return null;
108
- }
109
-
110
- return new Promise((resolve) => {
111
- const input = document.createElement("input");
112
- input.type = "file";
113
- input.multiple = Boolean(options?.multiple);
114
-
115
- if (options?.filters?.length) {
116
- input.accept = options.filters
117
- .flatMap((filter) => filter.extensions.map((extension) => `.${extension}`))
118
- .join(",");
119
- }
120
-
121
- input.addEventListener(
122
- "change",
123
- () => {
124
- const files = input.files;
125
- if (!files || files.length === 0) {
126
- resolve(null);
127
- return;
128
- }
129
-
130
- const names = Array.from(files, (file) => file.name);
131
- resolve(options?.multiple ? names : names[0] ?? null);
132
- },
133
- { once: true }
134
- );
135
-
136
- input.click();
137
- });
138
- }