stagent 0.1.4 → 0.1.5

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,34 +1,19 @@
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
+ > Governed AI agent operations with reusable profiles, workflow blueprints, scheduled runs, and local-first 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
10
-
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.
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.
14
-
15
- ## Get Started
7
+ ## Quick Start
16
8
 
17
- ### Download Desktop for macOS
18
-
19
- **Primary install path:** [Download the latest macOS desktop release](https://github.com/navam-io/stagent/releases/latest/download/Stagent.dmg)
20
-
21
- Grab the latest `Stagent.dmg` or `Stagent.app.zip` asset from GitHub Releases, then move `Stagent.app` into `/Applications`.
22
-
23
- Current desktop release notes:
24
-
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
9
+ ```bash
10
+ npx stagent
11
+ ```
28
12
 
29
- <img src="./output/playwright/home-playwright-after.png" alt="Stagent home workspace" width="1200" />
13
+ Open [localhost:3000](http://localhost:3000).
30
14
 
31
- ### Repository development
15
+ <details>
16
+ <summary>Contributor setup (clone + dev server)</summary>
32
17
 
33
18
  ```bash
34
19
  git clone <repo-url> && cd stagent && npm install
@@ -43,7 +28,29 @@ EOF
43
28
  npm run dev
44
29
  ```
45
30
 
46
- Open [localhost:3000](http://localhost:3000) to get started.
31
+ </details>
32
+
33
+ **Profiles & Policies** · **Blueprints & Schedules** · **Open Source**
34
+
35
+ <img src="https://unpkg.com/stagent@latest/public/readme/home-workspace.png" alt="Stagent home workspace" width="1200" />
36
+
37
+ | Home Workspace | Reusable Profiles | Workflow Blueprints | Governed Execution |
38
+ |:-:|:-:|:-:|:-:|
39
+ | 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 |
40
+
41
+ ---
42
+
43
+ ## Why Stagent
44
+
45
+ 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.
46
+
47
+ ---
48
+
49
+ ## Runtime Bridge
50
+
51
+ 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.
52
+
53
+ ---
47
54
 
48
55
  ## Feature Highlights
49
56
 
@@ -69,33 +76,7 @@ Open [localhost:3000](http://localhost:3000) to get started.
69
76
 
70
77
  ## Architecture
71
78
 
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
- ```
79
+ <img src="https://unpkg.com/stagent@latest/public/readme/architecture.svg" alt="Stagent architecture diagram" width="900" />
99
80
 
100
81
  **Key design decisions:**
101
82
 
@@ -125,7 +106,7 @@ Create and organize projects as containers for related tasks. Each project can s
125
106
  ### Agent
126
107
 
127
108
  #### Provider Runtimes
128
- Stagent now supports two governed execution runtimes behind a shared runtime registry:
109
+ Stagent supports two governed execution runtimes behind a shared runtime registry:
129
110
  - **Claude Code** via the Anthropic Claude Agent SDK
130
111
  - **OpenAI Codex App Server** via `codex app-server`
131
112
 
@@ -149,7 +130,7 @@ Multi-step task orchestration with three patterns:
149
130
  State machine engine with step-level retry, project association, and real-time status visualization.
150
131
 
151
132
  #### Parallel + Swarm Workflows
152
- Stagent now supports two bounded expansion patterns on top of the workflow engine:
133
+ Stagent supports two bounded expansion patterns on top of the workflow engine:
153
134
  - **Parallel research fork/join** — 2-5 concurrent branches followed by one synthesis step
154
135
  - **Swarm orchestration** — mayor → worker pool → refinery with retryable stages and configurable worker concurrency
155
136
 
@@ -217,10 +198,7 @@ File upload with drag-and-drop in task creation. Type-aware content preview for
217
198
  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
199
 
219
200
  #### 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.
201
+ 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
202
 
225
203
  #### Database
226
204
  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 +235,6 @@ npm test # Run Vitest
257
235
  npm run test:coverage # Coverage report
258
236
  ```
259
237
 
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
238
  ### Project Structure
284
239
 
285
240
  ```
@@ -383,7 +338,6 @@ All 14 features shipped across three layers:
383
338
  | **Agent Self-Improvement** | Agents learn patterns and update context with human approval |
384
339
  | **Document Output Generation** | Agent-generated documents as deliverables |
385
340
  | **Parallel Workflows** | Concurrent step execution within workflows |
386
- | **Tauri Desktop** | Native desktop app packaging |
387
341
 
388
342
  ---
389
343
 
@@ -395,7 +349,7 @@ All 14 features shipped across three layers:
395
349
  4. Run `npm test` and `npx tsc --noEmit`
396
350
  5. Submit a pull request
397
351
 
398
- See `CLAUDE.md` for architecture details and development conventions.
352
+ See `AGENTS.md` for architecture details and development conventions.
399
353
 
400
354
  ## License
401
355
 
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.5",
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
- }